diff --git a/Makefile.am b/Makefile.am index 15e4881a1d..49e8499d8a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -52,6 +52,7 @@ libcoap_@LIBCOAP_API_VERSION@_la_SOURCES = \ src/async.c \ src/block.c \ src/coap_io.c \ + src/coap_list.c \ src/coap_time.c \ src/debug.c \ src/encode.c \ diff --git a/coap_config.h.contiki b/coap_config.h.contiki index 43bef3a4ed..ba5f42af70 100644 --- a/coap_config.h.contiki +++ b/coap_config.h.contiki @@ -56,6 +56,7 @@ #define HAVE_STRNLEN 1 #define HAVE_SNPRINTF 1 +#define HAVE_STRINGS_H 1 /* there is no file-oriented output */ #define COAP_DEBUG_FD NULL @@ -120,7 +121,7 @@ typedef void FILE; # endif /* UIP_CONF_BYTE_ORDER */ #endif /* BYTE_ORDER */ -/* Define assert() as emtpy directive unless HAVE_ASSERT_H is given. */ +/* Define assert() as empty directive unless HAVE_ASSERT_H is given. */ #ifndef HAVE_ASSERT_H # define assert(x) #endif @@ -130,5 +131,14 @@ typedef void FILE; #include #define coap_log(fd, ...) printf(__VA_ARGS__) +/* Make libcoap headers safe to use from C++ */ +#ifndef COAP_STATIC_INLINE +# if defined(__cplusplus) +# define COAP_STATIC_INLINE inline +# else +# define COAP_STATIC_INLINE static inline +# endif +#endif + #endif /* _CONFIG_H_ */ diff --git a/coap_config.h.lwip b/coap_config.h.lwip index 5f397a0ea6..9b0f6dad7b 100644 --- a/coap_config.h.lwip +++ b/coap_config.h.lwip @@ -22,4 +22,13 @@ #define COAP_RESOURCES_NOHASH +/* Make libcoap headers safe to use from C++ */ +#ifndef COAP_STATIC_INLINE +# if defined(__cplusplus) +# define COAP_STATIC_INLINE inline +# else +# define COAP_STATIC_INLINE static inline +# endif +#endif + #endif /* _CONFIG_H_ */ diff --git a/coap_config.h.windows b/coap_config.h.windows new file mode 100644 index 0000000000..303e58dd04 --- /dev/null +++ b/coap_config.h.windows @@ -0,0 +1,64 @@ +#ifndef _COAP_CONFIG_H_ +#define _COAP_CONFIG_H_ + +#if defined(_WIN32) + +/* Define to 1 if you have header file. */ +#define HAVE_WS2TCPIP_H 1 + +/* Define to 1 if you have header file. */ +#define HAVE_WINSOCK2_H 1 + +/* Define to 1 if you have header file. */ +#define HAVE_ASSERT_H 1 + +/* Define to 1 if you have header file. */ +#define HAVE_LIMITS_H 1 + +/* Define to 1 if you have header file. */ +#define HAVE_STDIO_H 1 + +/* Define to 1 if you have header file. */ +#define HAVE_TIME_H 1 + +/* Define to 1 if you have malloc(). */ +#define HAVE_MALLOC 1 + +/* Define to 1 if you have vprintf(). */ +#define HAVE_VPRINTF 1 + +/* Define to 1 if you have strnlen(). */ +#define HAVE_STRNLEN 1 + +/* Define to 1 if you have snprintf(). */ +#if defined(_MSC_VER) && (_MSC_VER >= 1900) +#define HAVE_SNPRINTF 1 +#endif + +#define ssize_t SSIZE_T +#define in_port_t uint16_t + +#endif + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "libcoap" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "4.1.1" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "libcoap 4.1.1" + +#ifndef COAP_STATIC_INLINE +# if defined(__cplusplus) +# define COAP_STATIC_INLINE inline +# else +# ifdef _MSC_VER +# define COAP_STATIC_INLINE static __inline +# else +# define COAP_STATIC_INLINE static inline +# endif +# endif +#endif + +#endif /* _COAP_CONFIG_H_ */ diff --git a/configure.ac b/configure.ac index 8ae8840e54..c5f91a2aab 100644 --- a/configure.ac +++ b/configure.ac @@ -292,12 +292,13 @@ fi # Checks for header files. AC_CHECK_HEADERS([assert.h arpa/inet.h limits.h netdb.h netinet/in.h \ - stdlib.h string.h strings.h sys/socket.h sys/time.h \ + stdlib.h string.h strings.h sys/select.h sys/socket.h sys/time.h \ time.h unistd.h sys/unistd.h syslog.h]) # Checks for typedefs, structures, and compiler characteristics. AC_TYPE_SIZE_T AC_TYPE_SSIZE_T +AC_C_INLINE # Checks for library functions. AC_CHECK_FUNCS([memset select socket strcasecmp strrchr getaddrinfo \ @@ -385,6 +386,17 @@ tests/Makefile libcoap-$LIBCOAP_API_VERSION.pc:libcoap-$LIBCOAP_API_VERSION.pc.in ]) +AH_BOTTOM([ +/* Make libcoap headers safe to use from C++ */ +#ifndef COAP_STATIC_INLINE +# if defined(__cplusplus) +# define COAP_STATIC_INLINE inline +# else +# define COAP_STATIC_INLINE static inline +# endif +#endif +]) + AC_OUTPUT AC_MSG_RESULT([ diff --git a/examples/Makefile.am b/examples/Makefile.am index 087aef4b4e..dfc5a1df22 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -14,7 +14,7 @@ AM_CFLAGS = -isystem$(top_builddir)/include/coap -I$(top_srcdir)/include/coap $( # etsi_iot_01 and tiny are missing bin_PROGRAMS = coap-client coap-server coap-rd -coap_client_SOURCES = client.c coap_list.c +coap_client_SOURCES = client.c coap_append.c coap_client_LDADD = $(top_builddir)/.libs/libcoap-$(LIBCOAP_API_VERSION).la coap_server_SOURCES = coap-server.c diff --git a/examples/client.c b/examples/client.c index 86902c7b39..2137e27a5d 100644 --- a/examples/client.c +++ b/examples/client.c @@ -12,18 +12,37 @@ #include #include -#include +#ifdef HAVE_UNISTD_H +# include +#else +# include "getopt.h" +#endif #include #include -#include +#ifdef HAVE_SYS_SELECT_H +# include +#endif #include #include -#include -#include -#include -#include +#ifdef HAVE_SYS_SOCKET_H +# include +#endif +#ifdef HAVE_NETINET_IN_H +# include +#endif +#ifdef HAVE_ARPA_INET_H +# include +#endif +#ifdef HAVE_NETDB_H +# include +#endif +#ifndef HAVE_STRCASECMP + /* The POSIX strcasecmp() is not supported, so use the ISO C++ conformant _stricmp instead. */ +# define strcasecmp _stricmp +#endif #include "coap.h" +#include "coap_append.h" #include "coap_list.h" int flags = 0; @@ -61,7 +80,9 @@ coap_tick_t max_wait; /* global timeout (changed by set_timeou unsigned int obs_seconds = 30; /* default observe time */ coap_tick_t obs_wait = 0; /* timeout for current subscription */ +#ifndef min #define min(a,b) ((a) < (b) ? (a) : (b)) +#endif #ifdef __GNUC__ #define UNUSED_PARAM __attribute__ ((unused)) @@ -69,7 +90,7 @@ coap_tick_t obs_wait = 0; /* timeout for current subscription */ #define UNUSED_PARAM #endif /* GCC */ -static inline void +COAP_STATIC_INLINE void set_timeout(coap_tick_t *timer, const unsigned int seconds) { coap_ticks(timer); *timer += seconds * COAP_TICKS_PER_SECOND; @@ -305,7 +326,7 @@ resolve_address(const str *server, struct sockaddr *dst) { ((Pdu)->hdr->code == COAP_RESPONSE_CODE(201) || \ (Pdu)->hdr->code == COAP_RESPONSE_CODE(204))) -static inline int +COAP_STATIC_INLINE int check_token(coap_pdu_t *received) { return received->hdr->token_length == the_token.length && memcmp(received->hdr->token, the_token.s, the_token.length) == 0; @@ -694,7 +715,7 @@ cmdline_uri(char *arg, int create_uri_opts) { if (proxy.length) { /* create Proxy-Uri from argument */ size_t len = strlen(arg); while (len > 270) { - coap_insert(&optlist, + coap_append(&optlist, new_option_node(COAP_OPTION_PROXY_URI, 270, (unsigned char *)arg)); @@ -703,7 +724,7 @@ cmdline_uri(char *arg, int create_uri_opts) { arg += 270; } - coap_insert(&optlist, + coap_append(&optlist, new_option_node(COAP_OPTION_PROXY_URI, len, (unsigned char *)arg)); @@ -714,7 +735,7 @@ cmdline_uri(char *arg, int create_uri_opts) { } if (uri.port != COAP_DEFAULT_PORT && create_uri_opts) { - coap_insert(&optlist, + coap_append(&optlist, new_option_node(COAP_OPTION_URI_PORT, coap_encode_var_bytes(portbuf, uri.port), portbuf)); @@ -725,7 +746,7 @@ cmdline_uri(char *arg, int create_uri_opts) { res = coap_split_path(uri.path.s, uri.path.length, buf, &buflen); while (res--) { - coap_insert(&optlist, + coap_append(&optlist, new_option_node(COAP_OPTION_URI_PATH, COAP_OPT_LENGTH(buf), COAP_OPT_VALUE(buf))); @@ -740,7 +761,7 @@ cmdline_uri(char *arg, int create_uri_opts) { res = coap_split_query(uri.query.s, uri.query.length, buf, &buflen); while (res--) { - coap_insert(&optlist, + coap_append(&optlist, new_option_node(COAP_OPTION_URI_QUERY, COAP_OPT_LENGTH(buf), COAP_OPT_VALUE(buf))); @@ -792,14 +813,14 @@ set_blocksize(void) { opt_length = coap_encode_var_bytes(buf, (block.num << 4 | block.m << 3 | block.szx)); - coap_insert(&optlist, new_option_node(opt, opt_length, buf)); + coap_append(&optlist, new_option_node(opt, opt_length, buf)); } } static void cmdline_subscribe(char *arg) { obs_seconds = atoi(arg); - coap_insert(&optlist, new_option_node(COAP_OPTION_SUBSCRIPTION, 0, NULL)); + coap_append(&optlist, new_option_node(COAP_OPTION_SUBSCRIPTION, 0, NULL)); } static int @@ -835,7 +856,7 @@ cmdline_proxy(char *arg) { return 1; } -static inline void +COAP_STATIC_INLINE void cmdline_token(char *arg) { strncpy((char *)the_token.s, arg, min(sizeof(_token_data), strlen(arg))); the_token.length = strlen(arg); @@ -852,7 +873,7 @@ cmdline_option(char *arg) { if (*arg == ',') ++arg; - coap_insert(&optlist, + coap_append(&optlist, new_option_node(num, strlen(arg), (unsigned char *)arg)); } @@ -1212,7 +1233,7 @@ main(int argc, char **argv) { && create_uri_opts) { /* add Uri-Host */ - coap_insert(&optlist, + coap_append(&optlist, new_option_node(COAP_OPTION_URI_HOST, uri.host.length, uri.host.s)); diff --git a/examples/coap-rd.c b/examples/coap-rd.c index 64f156f2d6..3a7efcb281 100644 --- a/examples/coap-rd.c +++ b/examples/coap-rd.c @@ -65,7 +65,7 @@ rd_t *resources = NULL; #define UNUSED_PARAM #endif /* GCC */ -static inline rd_t * +COAP_STATIC_INLINE rd_t * rd_new(void) { rd_t *rd; rd = (rd_t *)coap_malloc(sizeof(rd_t)); @@ -75,7 +75,7 @@ rd_new(void) { return rd; } -static inline void +COAP_STATIC_INLINE void rd_delete(rd_t *rd) { if (rd) { coap_free(rd->data.s); diff --git a/examples/coap_list.c b/examples/coap_append.c similarity index 62% rename from examples/coap_list.c rename to examples/coap_append.c index 06e59168d9..d3fba4b7cd 100644 --- a/examples/coap_list.c +++ b/examples/coap_append.c @@ -8,18 +8,17 @@ * use. */ -/* #include "coap_config.h" */ +#include "coap_config.h" #include #include #include "debug.h" #include "mem.h" -#include "coap_list.h" - +#include "coap_append.h" int -coap_insert(coap_list_t **head, coap_list_t *node) { +coap_append(coap_list_t **head, coap_list_t *node) { if (!node) { coap_log(LOG_WARNING, "cannot create option Proxy-Uri\n"); } else { @@ -30,24 +29,3 @@ coap_insert(coap_list_t **head, coap_list_t *node) { return node != NULL; } - -int -coap_delete(coap_list_t *node) { - if (node) { - coap_free(node); - } - return 1; -} - -void -coap_delete_list(coap_list_t *queue) { - coap_list_t *elt, *tmp; - - if (!queue) - return; - - LL_FOREACH_SAFE(queue, elt, tmp) { - coap_delete(elt); - } -} - diff --git a/examples/coap_list.h b/examples/coap_append.h similarity index 52% rename from examples/coap_list.h rename to examples/coap_append.h index 14a68a3005..c1a8f44bf5 100644 --- a/examples/coap_list.h +++ b/examples/coap_append.h @@ -8,26 +8,16 @@ * use. */ -#ifndef _COAP_LIST_H_ -#define _COAP_LIST_H_ +#ifndef _COAP_APPEND_H_ +#define _COAP_APPEND_H_ #include "utlist.h" - -typedef struct coap_list_t { - struct coap_list_t *next; - char data[]; -} coap_list_t; +#include "coap_list.h" /** * Adds node to given queue, ordered by specified order function. Returns 1 * when insert was successful, 0 otherwise. */ -int coap_insert(coap_list_t **queue, coap_list_t *node); - -/* destroys specified node */ -int coap_delete(coap_list_t *node); - -/* removes all items from given queue and frees the allocated storage */ -void coap_delete_list(coap_list_t *queue); +int coap_append(coap_list_t **queue, coap_list_t *node); -#endif /* _COAP_LIST_H_ */ +#endif /* _COAP_APPEND_H_ */ diff --git a/examples/etsi_iot_01.c b/examples/etsi_iot_01.c index 0edc8a9368..8fe0767fd6 100644 --- a/examples/etsi_iot_01.c +++ b/examples/etsi_iot_01.c @@ -88,14 +88,14 @@ coap_new_payload(size_t size) { return p; } -static inline coap_payload_t * +COAP_STATIC_INLINE coap_payload_t * coap_find_payload(const coap_key_t key) { coap_payload_t *p; HASH_FIND(hh, test_resources, key, sizeof(coap_key_t), p); return p; } -static inline void +COAP_STATIC_INLINE void coap_add_payload(const coap_key_t key, coap_payload_t *payload, coap_dynamic_uri_t *uri) { assert(payload); @@ -109,7 +109,7 @@ coap_add_payload(const coap_key_t key, coap_payload_t *payload, } } -static inline void +COAP_STATIC_INLINE void coap_delete_payload(coap_payload_t *payload) { if (payload) { coap_dynamic_uri_t *uri; diff --git a/include/coap/address.h b/include/coap/address.h index 85db2046e3..e6ce887a72 100644 --- a/include/coap/address.h +++ b/include/coap/address.h @@ -15,7 +15,13 @@ #ifndef _COAP_ADDRESS_H_ #define _COAP_ADDRESS_H_ +#ifdef HAVE_ASSERT_H #include +#else +#ifndef assert +# define assert(x) +#endif +#endif #include #include #include @@ -54,7 +60,7 @@ typedef struct coap_address_t { #define _coap_is_mcast_impl(Address) uip_is_addr_mcast(&((Address)->addr)) #endif /* WITH_CONTIKI */ -#ifdef WITH_POSIX +#if defined(WITH_POSIX) || defined(HAVE_WS2TCPIP_H) /** multi-purpose address abstraction */ typedef struct coap_address_t { socklen_t size; /**< size of addr */ @@ -73,7 +79,7 @@ typedef struct coap_address_t { */ int coap_address_equals(const coap_address_t *a, const coap_address_t *b); -static inline int +COAP_STATIC_INLINE int _coap_address_isany_impl(const coap_address_t *a) { /* need to compare only relevant parts of sockaddr_in6 */ switch (a->addr.sa.sa_family) { @@ -89,7 +95,7 @@ _coap_address_isany_impl(const coap_address_t *a) { return 0; } -#endif /* WITH_POSIX */ +#endif /* WITH_POSIX || HAVE_WS2TCPIP_H */ /** * Resets the given coap_address_t object @p addr to its default values. In @@ -98,23 +104,23 @@ _coap_address_isany_impl(const coap_address_t *a) { * * @param addr The coap_address_t object to initialize. */ -static inline void +COAP_STATIC_INLINE void coap_address_init(coap_address_t *addr) { assert(addr); memset(addr, 0, sizeof(coap_address_t)); -#ifdef WITH_POSIX - /* lwip and Contiki have constant address sizes and doesn't need the .size part */ +#if defined(WITH_POSIX) || defined(HAVE_WS2TCPIP_H) + /* lwip and Contiki have constant address sizes and don't need the .size part */ addr->size = sizeof(addr->addr); #endif } -#ifndef WITH_POSIX +#if defined(WITH_LWIP) || defined(WITH_CONTIKI) /** * Compares given address objects @p a and @p b. This function returns @c 1 if * addresses are equal, @c 0 otherwise. The parameters @p a and @p b must not be * @c NULL; */ -static inline int +COAP_STATIC_INLINE int coap_address_equals(const coap_address_t *a, const coap_address_t *b) { assert(a); assert(b); return _coap_address_equals_impl(a, b); @@ -126,27 +132,27 @@ coap_address_equals(const coap_address_t *a, const coap_address_t *b) { * function returns @c 1 if this is the case, @c 0 otherwise. The parameters @p * a must not be @c NULL; */ -static inline int +COAP_STATIC_INLINE int coap_address_isany(const coap_address_t *a) { assert(a); return _coap_address_isany_impl(a); } -#ifdef WITH_POSIX +#if defined(WITH_POSIX) || defined(HAVE_WS2TCPIP_H) /** * Checks if given address @p a denotes a multicast address. This function * returns @c 1 if @p a is multicast, @c 0 otherwise. */ int coap_is_mcast(const coap_address_t *a); -#else /* WITH_POSIX */ +#else /* WITH_POSIX || HAVE_WS2TCPIP_H */ /** * Checks if given address @p a denotes a multicast address. This function * returns @c 1 if @p a is multicast, @c 0 otherwise. */ -static inline int +COAP_STATIC_INLINE int coap_is_mcast(const coap_address_t *a) { return a && _coap_is_mcast_impl(a); } -#endif /* WITH_POSIX */ +#endif /* WITH_POSIX || HAVE_WS2TCPIP_H */ #endif /* _COAP_ADDRESS_H_ */ diff --git a/include/coap/async.h b/include/coap/async.h index 0c36defacd..3fbcc09f52 100644 --- a/include/coap/async.h +++ b/include/coap/async.h @@ -136,7 +136,7 @@ coap_async_state_t *coap_find_async(coap_context_t *context, coap_tid_t id); * * @param s The state object to update. */ -static inline void +COAP_STATIC_INLINE void coap_touch_async(coap_async_state_t *s) { coap_ticks(&s->created); } /** @} */ diff --git a/include/coap/bits.h b/include/coap/bits.h index 0b269166d5..1397dde169 100644 --- a/include/coap/bits.h +++ b/include/coap/bits.h @@ -28,9 +28,9 @@ * * @return @c -1 if @p bit does not fit into @p vec, @c 1 otherwise. */ -inline static int +COAP_STATIC_INLINE int bits_setb(uint8_t *vec, size_t size, uint8_t bit) { - if (size <= (bit >> 3)) + if (size <= ((size_t)bit >> 3)) return -1; *(vec + (bit >> 3)) |= (uint8_t)(1 << (bit & 0x07)); @@ -48,9 +48,9 @@ bits_setb(uint8_t *vec, size_t size, uint8_t bit) { * * @return @c -1 if @p bit does not fit into @p vec, @c 1 otherwise. */ -inline static int +COAP_STATIC_INLINE int bits_clrb(uint8_t *vec, size_t size, uint8_t bit) { - if (size <= (bit >> 3)) + if (size <= ((size_t)bit >> 3)) return -1; *(vec + (bit >> 3)) &= (uint8_t)(~(1 << (bit & 0x07))); @@ -67,9 +67,9 @@ bits_clrb(uint8_t *vec, size_t size, uint8_t bit) { * * @return @c 1 if the bit is set, @c 0 otherwise. */ -inline static int +COAP_STATIC_INLINE int bits_getb(const uint8_t *vec, size_t size, uint8_t bit) { - if (size <= (bit >> 3)) + if (size <= ((size_t)bit >> 3)) return -1; return (*(vec + (bit >> 3)) & (1 << (bit & 0x07))) != 0; diff --git a/include/coap/block.h b/include/coap/block.h index 9ce00311cc..9535e29bb2 100644 --- a/include/coap/block.h +++ b/include/coap/block.h @@ -61,13 +61,13 @@ unsigned int coap_opt_block_num(const coap_opt_t *block_opt); * Checks if more than @p num blocks are required to deliver @p data_len * bytes of data for a block size of 1 << (@p szx + 4). */ -static inline int +COAP_STATIC_INLINE int coap_more_blocks(size_t data_len, unsigned int num, unsigned short szx) { return ((num+1) << (szx + 4)) < data_len; } /** Sets the More-bit in @p block_opt */ -static inline void +COAP_STATIC_INLINE void coap_opt_block_set_m(coap_opt_t *block_opt, int m) { if (m) *(COAP_OPT_VALUE(block_opt) + (COAP_OPT_LENGTH(block_opt) - 1)) |= 0x08; diff --git a/include/coap/coap.h.in b/include/coap/coap.h.in index 76ebc5eb0d..6e18c6f64f 100644 --- a/include/coap/coap.h.in +++ b/include/coap/coap.h.in @@ -37,6 +37,7 @@ extern "C" { #include "bits.h" #include "block.h" #include "coap_io.h" +#include "coap_list.h" #include "coap_time.h" #include "debug.h" #include "encode.h" diff --git a/include/coap/coap_io.h b/include/coap/coap_io.h index 9996fb3d68..f36d6c7741 100644 --- a/include/coap/coap_io.h +++ b/include/coap/coap_io.h @@ -39,12 +39,12 @@ struct coap_context_t; * tuple (handle, addr) must uniquely identify this endpoint. */ typedef struct coap_endpoint_t { -#if defined(WITH_POSIX) || defined(WITH_CONTIKI) +#if defined(WITH_POSIX) || defined(WITH_CONTIKI) || defined(HAVE_WS2TCPIP_H) union { coap_socket_t fd; /**< on POSIX, Contiki and Windows systems */ void *conn; /**< opaque connection (e.g. uip_conn in Contiki) */ } handle; /**< opaque handle to identify this endpoint */ -#endif /* WITH_POSIX or WITH_CONTIKI */ +#endif /* WITH_POSIX || WITH_CONTIKI || HAVE_WS2TCPIP_H */ #ifdef WITH_LWIP struct udp_pcb *pcb; @@ -146,7 +146,7 @@ struct coap_packet_t { coap_if_handle_t hnd; /**< the interface handle */ coap_address_t src; /**< the packet's source address */ coap_address_t dst; /**< the packet's destination address */ - const coap_endpoint_t *interface; + const coap_endpoint_t *endpoint; int ifindex; void *session; /**< opaque session data */ size_t length; /**< length of payload */ diff --git a/include/coap/coap_list.h b/include/coap/coap_list.h new file mode 100644 index 0000000000..9cbd858ae8 --- /dev/null +++ b/include/coap/coap_list.h @@ -0,0 +1,48 @@ +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 * -*- */ + +/* coap_list.h -- CoAP list structures + * + * Copyright (C) 2010,2011,2015 Olaf Bergmann + * + * This file is part of the CoAP library libcoap. Please see README for terms of + * use. + */ + +#ifndef _COAP_LIST_H_ +#define _COAP_LIST_H_ + +#include "utlist.h" + +struct coap_linkedlistnode { + struct coap_linkedlistnode *next; + void *data; + + /** + * Callback function that is called from coap_delete to release + * additional memory allocated by data Set to NULL if you do not + * need this. Note that data is free'd automatically. */ + void (*delete_func)(void *); +}; + +typedef struct coap_linkedlistnode coap_list_t; + +/** + * Adds node to given queue, ordered by specified order function. Returns 1 + * when insert was successful, 0 otherwise. + */ +int coap_insert(coap_list_t **queue, coap_list_t *node, int (*order)(void *, void *)); + +/* destroys specified node */ +int coap_delete(coap_list_t *node); + +/* removes all items from given queue and frees the allocated storage */ +void coap_delete_list(coap_list_t *queue); + +/** + * Creates a new list node and adds the given data object. The memory allocated + * by data will be released by coap_delete() with the new node. Returns the + * new list node. + */ +coap_list_t *coap_new_listnode(void *data, void (*delete_func)(void *)); + +#endif /* _COAP_LIST_H_ */ diff --git a/include/coap/coap_time.h b/include/coap/coap_time.h index 9357e5ff7d..9caa2f5370 100644 --- a/include/coap/coap_time.h +++ b/include/coap/coap_time.h @@ -33,17 +33,17 @@ typedef uint32_t coap_tick_t; typedef uint32_t coap_time_t; typedef int32_t coap_tick_diff_t; -static inline void coap_ticks_impl(coap_tick_t *t) { +COAP_STATIC_INLINE void coap_ticks_impl(coap_tick_t *t) { *t = sys_now(); } -static inline void coap_clock_init_impl(void) { +COAP_STATIC_INLINE void coap_clock_init_impl(void) { } #define coap_clock_init coap_clock_init_impl #define coap_ticks coap_ticks_impl -static inline coap_time_t coap_ticks_to_rt(coap_tick_t t) { +COAP_STATIC_INLINE coap_time_t coap_ticks_to_rt(coap_tick_t t) { return t / COAP_TICKS_PER_SECOND; } #endif @@ -63,20 +63,20 @@ typedef int coap_tick_diff_t; #define COAP_TICKS_PER_SECOND CLOCK_SECOND -static inline void coap_clock_init(void) { +COAP_STATIC_INLINE void coap_clock_init(void) { clock_init(); } -static inline void coap_ticks(coap_tick_t *t) { +COAP_STATIC_INLINE void coap_ticks(coap_tick_t *t) { *t = clock_time(); } -static inline coap_time_t coap_ticks_to_rt(coap_tick_t t) { +COAP_STATIC_INLINE coap_time_t coap_ticks_to_rt(coap_tick_t t) { return t / COAP_TICKS_PER_SECOND; } #endif /* WITH_CONTIKI */ -#ifdef WITH_POSIX +#if defined(WITH_POSIX) || defined(HAVE_WS2TCPIP_H) /** * This data type represents internal timer ticks with COAP_TICKS_PER_SECOND * resolution. @@ -119,13 +119,13 @@ void coap_ticks(coap_tick_t *t); * point (seconds since epoch on POSIX). */ coap_time_t coap_ticks_to_rt(coap_tick_t t); -#endif /* WITH_POSIX */ +#endif /* WITH_POSIX || HAVE_WS2TCPIP_H */ /** * Returns @c 1 if and only if @p a is less than @p b where less is defined on a * signed data type. */ -static inline int coap_time_lt(coap_tick_t a, coap_tick_t b) { +COAP_STATIC_INLINE int coap_time_lt(coap_tick_t a, coap_tick_t b) { return ((coap_tick_diff_t)(a - b)) < 0; } @@ -133,7 +133,7 @@ static inline int coap_time_lt(coap_tick_t a, coap_tick_t b) { * Returns @c 1 if and only if @p a is less than or equal @p b where less is * defined on a signed data type. */ -static inline int coap_time_le(coap_tick_t a, coap_tick_t b) { +COAP_STATIC_INLINE int coap_time_le(coap_tick_t a, coap_tick_t b) { return a == b || coap_time_lt(a,b); } diff --git a/include/coap/encode.h b/include/coap/encode.h index f17fe659d5..fb19f5427c 100644 --- a/include/coap/encode.h +++ b/include/coap/encode.h @@ -10,11 +10,14 @@ #ifndef _COAP_ENCODE_H_ #define _COAP_ENCODE_H_ -#if (BSD >= 199103) || defined(WITH_CONTIKI) -# include -#else +#ifdef HAVE_STRINGS_H # include +#else +# include #endif +#include + +#include "option.h" #define Nn 8 /* duplicate definition of N if built on sky motes */ #define ENCODE_HEADER_SIZE 4 @@ -56,4 +59,10 @@ unsigned int coap_decode_var_bytes(unsigned char *buf,unsigned int len); */ unsigned int coap_encode_var_bytes(unsigned char *buf, unsigned int val); +/** + * Tests whether the option definition has a type that allows variable byte encoding. + * Returns true when supported, false when not supported. + */ +bool coap_is_var_bytes(coap_option_def_t* def); + #endif /* _COAP_ENCODE_H_ */ diff --git a/include/coap/libcoap.h b/include/coap/libcoap.h index 214b9e235e..5e0c9c0383 100644 --- a/include/coap/libcoap.h +++ b/include/coap/libcoap.h @@ -10,6 +10,8 @@ #ifndef _LIBCOAP_H_ #define _LIBCOAP_H_ +#include "coap_config.h" + /* The non posix embedded platforms like Contiki, TinyOS, RIOT, ... doesn't have * a POSIX compatible header structure so we have to slightly do some platform * related things. Currently there is only Contiki available so we check for a @@ -18,9 +20,14 @@ * * The CONTIKI variable is within the Contiki build environment! */ -#if !defined (CONTIKI) +#ifdef HAVE_NETINET_IN_H #include +#endif +#ifdef HAVE_SYS_SOCKET_H #include -#endif /* CONTIKI */ +#endif +#ifdef HAVE_WS2TCPIP_H +#include +#endif #endif /* _LIBCOAP_H_ */ diff --git a/include/coap/mem.h b/include/coap/mem.h index fd3c69aafd..f4bfce5d8a 100644 --- a/include/coap/mem.h +++ b/include/coap/mem.h @@ -67,14 +67,14 @@ void coap_free_type(coap_memory_tag_t type, void *p); /** * Wrapper function to coap_malloc_type() for backwards compatibility. */ -static inline void *coap_malloc(size_t size) { +COAP_STATIC_INLINE void *coap_malloc(size_t size) { return coap_malloc_type(COAP_STRING, size); } /** * Wrapper function to coap_free_type() for backwards compatibility. */ -static inline void coap_free(void *object) { +COAP_STATIC_INLINE void coap_free(void *object) { coap_free_type(COAP_STRING, object); } @@ -86,7 +86,7 @@ static inline void coap_free(void *object) { /* no initialization needed with lwip (or, more precisely: lwip must be * completely initialized anyway by the time coap gets active) */ -static inline void coap_memory_init(void) {} +COAP_STATIC_INLINE void coap_memory_init(void) {} /* It would be nice to check that size equals the size given at the memp * declaration, but i currently don't see a standard way to check that without @@ -98,11 +98,11 @@ static inline void coap_memory_init(void) {} /* Those are just here to make uri.c happy where string allocation has not been * made conditional. */ -static inline void *coap_malloc(size_t size) { +COAP_STATIC_INLINE void *coap_malloc(size_t size) { LWIP_ASSERT("coap_malloc must not be used in lwIP", 0); } -static inline void coap_free(void *pointer) { +COAP_STATIC_INLINE void coap_free(void *pointer) { LWIP_ASSERT("coap_free must not be used in lwIP", 0); } diff --git a/include/coap/net.h b/include/coap/net.h index f1ea77a1ae..628e94c0cf 100644 --- a/include/coap/net.h +++ b/include/coap/net.h @@ -10,10 +10,19 @@ #ifndef _COAP_NET_H_ #define _COAP_NET_H_ +#ifdef HAVE_ASSERT_H #include +#else +#ifndef assert +# define assert(x) +#endif +#endif + #include #include +#ifdef HAVE_SYS_TIME_H #include +#endif #include #ifdef WITH_LWIP @@ -94,6 +103,10 @@ typedef struct coap_context_t { int sockfd; /**< send/receive socket */ #endif /* WITH_POSIX */ +#ifdef HAVE_WS2TCPIP_H + SOCKET sockfd; /**< send/receive socket */ +#endif /* HAVE_WS2TCPIP_H */ + #ifdef WITH_CONTIKI struct uip_udp_conn *conn; /**< uIP connection object */ struct etimer retransmit_timer; /**< fires when the next packet must be sent */ @@ -137,7 +150,7 @@ typedef struct coap_context_t { * @param context The context to register the handler for. * @param handler The response handler to register. */ -static inline void +COAP_STATIC_INLINE void coap_register_response_handler(coap_context_t *context, coap_response_handler_t handler) { context->response_handler = handler; @@ -149,7 +162,7 @@ coap_register_response_handler(coap_context_t *context, * @param ctx The context to use. * @param type The option type to register. */ -inline static void +COAP_STATIC_INLINE void coap_register_option(coap_context_t *ctx, unsigned char type) { coap_option_setb(ctx->known_options, type); } @@ -185,7 +198,7 @@ coap_context_t *coap_new_context(const coap_address_t *listen_addr); * * @return Incremented message id in network byte order. */ -static inline unsigned short +COAP_STATIC_INLINE unsigned short coap_new_message_id(coap_context_t *context) { context->message_id++; #ifndef WITH_CONTIKI @@ -342,7 +355,7 @@ coap_tid_t coap_send_ack(coap_context_t *context, * @return The transaction id if RST was sent or @c * COAP_INVALID_TID on error. */ -static inline coap_tid_t +COAP_STATIC_INLINE coap_tid_t coap_send_rst(coap_context_t *context, const coap_endpoint_t *local_interface, const coap_address_t *dst, @@ -387,11 +400,9 @@ int coap_handle_message(coap_context_t *ctx, * @param pdu The message that initiated the transaction. * @param id Set to the new id. */ -#if defined(WITH_POSIX) || defined(WITH_LWIP) || defined(WITH_CONTIKI) void coap_transaction_id(const coap_address_t *peer, const coap_pdu_t *pdu, coap_tid_t *id); -#endif /** * This function removes the element with given @p id from the list given list. @@ -422,7 +433,7 @@ int coap_remove_from_queue(coap_queue_t **queue, * * @return @c 1 if node was found, removed and destroyed, @c 0 otherwise. */ -inline static int +COAP_STATIC_INLINE int coap_remove_transaction(coap_queue_t **queue, coap_tid_t id) { coap_queue_t *node; if (!coap_remove_from_queue(queue, id, &node)) diff --git a/include/coap/option.h b/include/coap/option.h index ace2b81c71..05c54f56f5 100644 --- a/include/coap/option.h +++ b/include/coap/option.h @@ -32,6 +32,17 @@ typedef struct { unsigned char *value; } coap_option_t; +/** Representation of the association between a CoAP option key and its + * data type and valid data length ranges. + */ +typedef struct +{ + unsigned short key; /**< The ID of the option the following values apply to. */ + unsigned char type; /**< The type of the option: u=uint, s=string, o=opaque. */ + unsigned int min; /**< The minimum number of bytes allowed for the option data */ + unsigned int max; /**< The maximum number of bytes allowed for the option data */ +} coap_option_def_t; + /** * Parses the option pointed to by @p opt into @p result. This function returns * the number of bytes that have been parsed, or @c 0 on error. An error is @@ -142,7 +153,7 @@ typedef uint16_t coap_opt_filter_t[COAP_OPT_FILTER_SIZE]; * * @param f The filter to clear. */ -static inline void +COAP_STATIC_INLINE void coap_option_filter_clear(coap_opt_filter_t f) { memset(f, 0, sizeof(coap_opt_filter_t)); } @@ -195,7 +206,7 @@ int coap_option_filter_get(const coap_opt_filter_t filter, unsigned short type); * * @return @c 1 if bit was set, @c -1 otherwise. */ -inline static int +COAP_STATIC_INLINE int coap_option_setb(coap_opt_filter_t filter, unsigned short type) { return coap_option_filter_set(filter, type) ? 1 : -1; } @@ -212,7 +223,7 @@ coap_option_setb(coap_opt_filter_t filter, unsigned short type) { * * @return @c 1 if bit was set, @c -1 otherwise. */ -inline static int +COAP_STATIC_INLINE int coap_option_clrb(coap_opt_filter_t filter, unsigned short type) { return coap_option_filter_unset(filter, type) ? 1 : -1; } @@ -229,7 +240,7 @@ coap_option_clrb(coap_opt_filter_t filter, unsigned short type) { * * @return @c 1 if bit was set, @c 0 if not, @c -1 on error. */ -inline static int +COAP_STATIC_INLINE int coap_option_getb(const coap_opt_filter_t filter, unsigned short type) { return coap_option_filter_get(filter, type); } @@ -279,6 +290,26 @@ coap_opt_iterator_t *coap_option_iterator_init(coap_pdu_t *pdu, coap_opt_iterator_t *oi, const coap_opt_filter_t filter); +/** + * Initializes the given option iterator @p oi to point to the + * beginning of the @p pdu's option list. This function returns @p oi + * on success, @c NULL otherwise (i.e. when no options exist). + * Note that a length check on the option list must be performed before + * coap_option_iterator_init() is called. + * + * @param pdu The PDU the options of which should be walked through. + * @param oi An iterator object that will be initilized. + * @param filter An optional option type filter. + * With @p type != @c COAP_OPT_ALL, coap_option_next() + * will return only options matching this bitmask. + * Fence-post options @c 14, @c 28, @c 42, ... are always + * skipped. + * + * @return The iterator object @p oi on success, @c NULL otherwise. + */ +coap_opt_iterator_t *coap_option_iterator_init2(coap_pdu_t *pdu, coap_opt_iterator_t *oi, + const coap_opt_filter_t filter, coap_transport_t transport); + /** * Updates the iterator @p oi to point to the next option. This function returns * a pointer to that option or @c NULL if no more options exist. The contents of @@ -402,6 +433,16 @@ unsigned short coap_opt_length(const coap_opt_t *opt); */ unsigned char *coap_opt_value(coap_opt_t *opt); +/** + * Returns a pointer to the coap option range definitions. @key + * must be a valid option ID. This function returns @c NULL if + * @p key is not a valid option ID. + * + * @param key The option ID whose definition should be returned. + * @return A pointer to the option definition. + */ +coap_option_def_t* coap_opt_def(unsigned short key); + /** @deprecated { Use coap_opt_value() instead. } */ #define COAP_OPT_VALUE(opt) coap_opt_value((coap_opt_t *)opt) diff --git a/include/coap/pdu.h b/include/coap/pdu.h index 7ed482deee..ca37071520 100644 --- a/include/coap/pdu.h +++ b/include/coap/pdu.h @@ -21,6 +21,11 @@ #include #endif +#ifdef __cplusplus +extern "C" +{ +#endif + #define COAP_DEFAULT_PORT 5683 /* CoAP default UDP port */ #define COAP_DEFAULT_MAX_AGE 60 /* default maximum object lifetime in seconds */ #ifndef COAP_MAX_PDU_SIZE @@ -53,11 +58,14 @@ #define COAP_REQUEST_DELETE 4 /* CoAP option types (be sure to update check_critical when adding options */ +/* See http://www.iana.org/assignments/core-parameters/core-parameters.xhtml#option-numbers */ #define COAP_OPTION_IF_MATCH 1 /* C, opaque, 0-8 B, (none) */ #define COAP_OPTION_URI_HOST 3 /* C, String, 1-255 B, destination address */ #define COAP_OPTION_ETAG 4 /* E, opaque, 1-8 B, (none) */ #define COAP_OPTION_IF_NONE_MATCH 5 /* empty, 0 B, (none) */ +#define COAP_OPTION_OBSERVE 6 /* E, empty/uint, 0 B/0-3 B, (none) */ +#define COAP_OPTION_SUBSCRIPTION COAP_OPTION_OBSERVE #define COAP_OPTION_URI_PORT 7 /* C, uint, 0-2 B, destination port */ #define COAP_OPTION_LOCATION_PATH 8 /* E, String, 0-255 B, - */ #define COAP_OPTION_URI_PATH 11 /* C, String, 0-255 B, (none) */ @@ -78,8 +86,9 @@ /* selected option types from RFC 7959 */ -#define COAP_OPTION_BLOCK2 23 /* C, uint, 0--3 B, (none) */ -#define COAP_OPTION_BLOCK1 27 /* C, uint, 0--3 B, (none) */ +#define COAP_OPTION_BLOCK2 23 /* C, uint, 0-3 B, (none) */ +#define COAP_OPTION_BLOCK1 27 /* C, uint, 0-3 B, (none) */ +#define COAP_OPTION_SIZE2 28 /* E, uint, 0-4 B, (none) */ /* selected option types from RFC 7967 */ @@ -171,28 +180,109 @@ typedef int coap_tid_t; */ #define COAP_DROPPED_RESPONSE -2 +#define COAP_WS_HEADER 2 + +#define COAP_TCP_HEADER_NO_FIELD 2 +#define COAP_TCP_HEADER_8_BIT 3 +#define COAP_TCP_HEADER_16_BIT 4 +#define COAP_TCP_HEADER_32_BIT 6 + +#define COAP_TCP_LENGTH_FIELD_8_BIT 13 +#define COAP_TCP_LENGTH_FIELD_16_BIT 269 +#define COAP_TCP_LENGTH_FIELD_32_BIT 65805 + +#define COAP_TCP_LENGTH_LIMIT_8_BIT 13 +#define COAP_TCP_LENGTH_LIMIT_16_BIT 256 +#define COAP_TCP_LENGTH_LIMIT_32_BIT 65536 + +#define COAP_TCP_LENGTH_FIELD_NUM_8_BIT 13 +#define COAP_TCP_LENGTH_FIELD_NUM_16_BIT 14 +#define COAP_TCP_LENGTH_FIELD_NUM_32_BIT 15 + +#define COAP_OPTION_FIELD_8_BIT 12 +#define COAP_OPTION_FIELD_16_BIT 256 +#define COAP_OPTION_FIELD_32_BIT 65536 + +typedef enum { + COAP_UDP = 0, +#ifdef WITH_TCP + COAP_TCP, + COAP_TCP_8BIT, + COAP_TCP_16BIT, + COAP_TCP_32BIT, +#endif +#ifdef WITH_WS + COAP_WS +#endif +} coap_transport_t; + #ifdef WORDS_BIGENDIAN typedef struct { - unsigned int version:2; /* protocol version */ - unsigned int type:2; /* type flag */ - unsigned int token_length:4; /* length of Token */ - unsigned int code:8; /* request method (value 1--10) or response - code (value 40-255) */ - unsigned short id; /* message id */ - unsigned char token[]; /* the actual token, if any */ -} coap_hdr_t; + unsigned short version:2; /* protocol version */ + unsigned short type:2; /* type flag */ + unsigned short token_length:4; /* length of Token */ + unsigned short code:8; /* request method (value 1--10) or response + code (value 40-255) */ + unsigned short id; /* message id */ + unsigned char token[]; /* the actual token, if any */ +} coap_hdr_udp_t; #else typedef struct { - unsigned int token_length:4; /* length of Token */ - unsigned int type:2; /* type flag */ - unsigned int version:2; /* protocol version */ - unsigned int code:8; /* request method (value 1--10) or response - code (value 40-255) */ - unsigned short id; /* transaction id (network byte order!) */ - unsigned char token[]; /* the actual token, if any */ -} coap_hdr_t; + unsigned short token_length:4; /* length of Token */ + unsigned short type:2; /* type flag */ + unsigned short version:2; /* protocol version */ + unsigned short code:8; /* request method (value 1--10) or response + code (value 40-255) */ + unsigned short id; /* transaction id (network byte order!) */ + unsigned char token[]; /* the actual token, if any */ +} coap_hdr_udp_t; #endif +#ifdef WITH_TCP +typedef struct { + unsigned char header_data[COAP_TCP_HEADER_NO_FIELD]; + unsigned char token[]; /* the actual token, if any */ +} coap_hdr_tcp_t; + +typedef struct { + unsigned char header_data[COAP_TCP_HEADER_8_BIT]; + unsigned char token[]; /* the actual token, if any */ +} coap_hdr_tcp_8bit_t; + +typedef struct { + unsigned char header_data[COAP_TCP_HEADER_16_BIT]; + unsigned char token[]; /* the actual token, if any */ +} coap_hdr_tcp_16bit_t; + +typedef struct { + unsigned char header_data[6]; + unsigned char token[]; /* the actual token, if any */ +} coap_hdr_tcp_32bit_t; +#endif /* WITH_TCP */ + +#ifdef WITH_WS +typedef struct { + unsigned char header_data[COAP_WS_HEADER]; + unsigned char token[]; /* the actual token, if any */ +} coap_hdr_ws_t; +#endif /* WITH_WS */ + +typedef union { + coap_hdr_udp_t udp; +#ifdef WITH_TCP + coap_hdr_tcp_t tcp; + coap_hdr_tcp_8bit_t tcp_8bit; + coap_hdr_tcp_16bit_t tcp_16bit; + coap_hdr_tcp_32bit_t tcp_32bit; +#endif /* WITH_TCP */ +#ifdef WITH_WS + coap_hdr_ws_t ws; +#endif /* WITH_WS */ +} coap_hdr_transport_t; + +// Typedef for backwards compatibility. +typedef coap_hdr_udp_t coap_hdr_t; + #define COAP_MESSAGE_IS_EMPTY(MSG) ((MSG)->code == 0) #define COAP_MESSAGE_IS_REQUEST(MSG) (!COAP_MESSAGE_IS_EMPTY(MSG) \ && ((MSG)->code < 32)) @@ -225,11 +315,17 @@ typedef struct { */ typedef struct { - size_t max_size; /**< allocated storage for options and data */ - coap_hdr_t *hdr; /**< Address of the first byte of the CoAP message. - * This may or may not equal (coap_hdr_t*)(pdu+1) - * depending on the memory management - * implementation. */ + size_t max_size; /**< allocated storage for options and data */ + union { + coap_hdr_t *hdr; /**< Address of the first byte of the CoAP message. + * This may or may not equal (coap_hdr_t*)(pdu+1) + * depending on the memory management + * implementation. */ + coap_hdr_transport_t *transport_hdr; /**< Address of the first byte of the CoAP message. + * This may or may not equal (coap_hdr_t*)(pdu+1) + * depending on the memory management + * implementation. */ + }; unsigned short max_delta; /**< highest option number */ unsigned short length; /**< PDU length (including header, options, data) */ unsigned char *data; /**< payload */ @@ -290,6 +386,25 @@ coap_pdu_init(unsigned char type, unsigned short id, size_t size); +/** + * Creates a new CoAP PDU of given @p size (must be large enough to hold the + * basic CoAP message header (coap_hdr_t). The function returns a pointer to + * the node coap_pdu_t object on success, or @c NULL on error. The storage + * allocated for the result must be released with coap_delete_pdu(). + * + * @param type The type of the PDU (one of COAP_MESSAGE_CON, + * COAP_MESSAGE_NON, COAP_MESSAGE_ACK, COAP_MESSAGE_RST). + * @param code The message code. + * @param id The message id to set or COAP_INVALID_TID if unknown. + * @param size The number of bytes to allocate for the actual message. + * @param transport The transport type. + * + * @return A pointer to the new PDU object or @c NULL on error. + */ +coap_pdu_t * +coap_pdu_init2(unsigned char type, unsigned char code, unsigned short id, + size_t size, coap_transport_t transport); + /** * Clears any contents from @p pdu and resets @c version field, @c * length and @c data pointers. @c max_size is set to @p size, any @@ -298,6 +413,15 @@ coap_pdu_init(unsigned char type, */ void coap_pdu_clear(coap_pdu_t *pdu, size_t size); +/** + * Clears any contents from @p pdu and resets @c version field, @c + * length and @c data pointers. @c max_size is set to @p size, any + * other field is set to @c 0. Note that @p pdu must be a valid + * pointer to a coap_pdu_t object created e.g. by coap_pdu_init(). + */ +void coap_pdu_clear2(coap_pdu_t *pdu, size_t size, coap_transport_t transport, + unsigned int length); + /** * Creates a new CoAP PDU. * The object is created on the heap and must be released using @@ -308,6 +432,15 @@ void coap_pdu_clear(coap_pdu_t *pdu, size_t size); */ coap_pdu_t *coap_new_pdu(void); +/** + * Creates a new CoAP PDU. The object is created on the heap and must be released + * using coap_delete_pdu(); + * + * @deprecated This function allocates the maximum storage for each + * PDU. Use coap_pdu_init2() instead. + */ +coap_pdu_t *coap_new_pdu2(coap_transport_t transport, unsigned int size); + void coap_delete_pdu(coap_pdu_t *); /** @@ -326,6 +459,126 @@ int coap_pdu_parse(unsigned char *data, size_t length, coap_pdu_t *result); +/** + * Parses @p data into the CoAP PDU structure given in @p result. This + * function returns @c 0 on error or a number greater than zero on + * success. + * + * @param data The raw data to parse as CoAP PDU + * @param length The actual size of @p data + * @param result The PDU structure to fill. Note that the structure must + * provide space for at least @p length bytes to hold the + * entire CoAP PDU. + * @param transport The transport type. + * @return A value greater than zero on success or @c 0 on error. + */ +int coap_pdu_parse2(unsigned char *data, size_t length, coap_pdu_t *pdu, + coap_transport_t transport); + +#ifdef WITH_TCP +/** + * Get total pdu size including header + option + payload (with marker) from pdu data. + * + * @param data The raw data to parse as CoAP PDU. + * @param size payload size of pdu. + * @return Total message length. + */ +size_t coap_get_total_message_length(const unsigned char *data, size_t size); + +/** + * Get transport type of coap header for coap over tcp + * through payload size(including payload marker) + option size. + * + * @param size payload size of pdu. + * @return The transport type. + */ +coap_transport_t coap_get_tcp_header_type_from_size(unsigned int size); + +/** + * Get transport type of coap header for coap over tcp + * through first nibble(0~E) of init-byte . + * + * @param legnth length value of init byte. +* @return The transport type. + */ +coap_transport_t coap_get_tcp_header_type_from_initbyte(unsigned int length); + +/** + * Add length of option/payload into 'Len+ byte...' field of coap header + * for coap over tcp. + * + * @param pdu The pdu pointer. + * @param transport The transport type. + * @param length length value of init byte. + */ +void coap_add_length(const coap_pdu_t *pdu, coap_transport_t transport, + unsigned int length); + +/** + * Get the length of option/payload field of coap header for coap over tcp. + * + * @param pdu The pdu pointer. + * @param transport The transport type. + * @return length value of init byte. + */ +unsigned int coap_get_length(const coap_pdu_t *pdu, coap_transport_t transport); + +/** + * Get the length of option/payload field of coap header for coap over tcp. + * + * @param header The header to parse. + * @return transport The transport type. + */ +unsigned int coap_get_length_from_header(const unsigned char *header, + coap_transport_t transport); + +/** + * Get length of header including len, TKL, Len+bytes, Code, token bytes for coap over tcp. + * + * @param data The raw data to parse as CoAP PDU + * @return header length + token length + */ +unsigned int coap_get_tcp_header_length(unsigned char *data); + +/** + * Get length of header including len, TKL, Len+bytes, Code + * without token bytes for coap over tcp. + * + * @param transport The transport type. + * @return header length. + */ +unsigned int coap_get_tcp_header_length_for_transport(coap_transport_t transport); + +#endif /* WITH_TCP */ + +/** + * Get option length. + * + * @param key delta of option + * @param length length of option + * @return total option length + */ +size_t coap_get_opt_header_length(unsigned short key, size_t length); + +/** + * Add code in coap header. + * + * @param pdu The pdu pointer. + * @param transport The transport type. + * @param code The message code. + */ +void coap_add_code(const coap_pdu_t *pdu, coap_transport_t transport, + unsigned int code); + +/** + * Get message code from coap header + * + * @param pdu The pdu pointer. + * @param transport The transport type. + * @return The message code. + */ +unsigned int coap_get_code(const coap_pdu_t *pdu, coap_transport_t transport); + /** * Adds token of length @p len to @p pdu. * Adding the token destroys any following contents of the pdu. Hence options @@ -344,8 +597,54 @@ int coap_add_token(coap_pdu_t *pdu, const unsigned char *data); /** - * Adds option of given type to pdu that is passed as first - * parameter. + * Adds token of length @p len to @p pdu. Adding the token destroys + * any following contents of the pdu. Hence options and data must be + * added after coap_add_token2() has been called. In @p pdu, length is + * set to @p len + @c 4, and max_delta is set to @c 0. This funtion + * returns @c 0 on error or a value greater than zero on success. + * + * @param pdu The pdu pointer. + * @param len The length of the new token. + * @param data The token to add. + * @param transport The transport type. + * @return A value greater than zero on success, or @c 0 on error. + */ +int coap_add_token2(coap_pdu_t *pdu, size_t len, const unsigned char *data, + coap_transport_t transport); + +/** + * @param pdu The pdu pointer. + * @param len The length of the new token. + * @param data The token to add. + * @param transport The transport type. + * @return A value greater than zero on success, or @c 0 on error. + */ +int coap_add_token_to_empty_message(coap_pdu_t *pdu, size_t len, const unsigned char *data, + coap_transport_t transport); + +/** + * Get token from coap header + * + * @param pdu_hdr The header pointer of PDU. + * @param token out parameter to get token. + * @param token_length out parameter to get token length. + */ +void coap_get_token(const coap_hdr_t *pdu_hdr, + unsigned char **token, unsigned int *token_length); + +/** + * Get token from coap header based on transport type + * + * @param pdu_hdr The header pointer of PDU. + * @param transport The transport type. + * @param token out parameter to get token. + * @param token_length out parameter to get token length. + */ +void coap_get_token2(const coap_hdr_transport_t *pdu_hdr, coap_transport_t transport, + unsigned char **token, unsigned int *token_length); + +/** + * Adds option of given type to pdu that is passed as first parameter. * coap_add_option() destroys the PDU's data, so coap_add_data() must be called * after all options have been added. As coap_add_token() destroys the options * following the token, the token must be added before coap_add_option() is @@ -356,6 +655,17 @@ size_t coap_add_option(coap_pdu_t *pdu, unsigned int len, const unsigned char *data); +/** + * Adds option of given type to pdu that is passed as first + * parameter. coap_add_option2() destroys the PDU's data, so + * coap_add_data() must be called after all options have been added. + * As coap_add_token2() destroys the options following the token, + * the token must be added before coap_add_option2() is called. + * This function returns the number of bytes written or @c 0 on error. + */ +size_t coap_add_option2(coap_pdu_t *pdu, unsigned short type, unsigned int len, + const unsigned char *data, coap_transport_t transport); + /** * Adds option of given type to pdu that is passed as first parameter, but does * not write a value. It works like coap_add_option with respect to calling @@ -381,8 +691,11 @@ int coap_add_data(coap_pdu_t *pdu, * 1 if *len and *data have correct values. Note that these values are destroyed * with the pdu. */ -int coap_get_data(coap_pdu_t *pdu, +int coap_get_data(const coap_pdu_t *pdu, size_t *len, unsigned char **data); +#ifdef __cplusplus +} /* extern "C" */ +#endif #endif /* _COAP_PDU_H_ */ diff --git a/include/coap/prng.h b/include/coap/prng.h index da6d953440..f19e7eff35 100644 --- a/include/coap/prng.h +++ b/include/coap/prng.h @@ -20,7 +20,7 @@ * @{ */ -#if defined(WITH_POSIX) || (defined(WITH_LWIP) && !defined(LWIP_RAND)) +#if defined(WITH_POSIX) || (defined(WITH_LWIP) && !defined(LWIP_RAND)) || defined(HAVE_WS2TCPIP_H) #include /** @@ -28,13 +28,13 @@ * prng(). You might want to change prng() to use a better PRNG on your specific * platform. */ -static inline int +COAP_STATIC_INLINE int coap_prng_impl(unsigned char *buf, size_t len) { while (len--) *buf++ = rand() & 0xFF; return 1; } -#endif /* WITH_POSIX */ +#endif /* WITH_POSIX || (WITH_LWIP && !LWIP_RAND) || HAVE_WS2TCPIP_H */ #ifdef WITH_CONTIKI #include @@ -44,7 +44,7 @@ coap_prng_impl(unsigned char *buf, size_t len) { * prng(). You might want to change prng() to use a better PRNG on your specific * platform. */ -static inline int +COAP_STATIC_INLINE int contiki_prng_impl(unsigned char *buf, size_t len) { unsigned short v = random_rand(); while (len > sizeof(v)) { @@ -63,7 +63,7 @@ contiki_prng_impl(unsigned char *buf, size_t len) { #endif /* WITH_CONTIKI */ #if defined(WITH_LWIP) && defined(LWIP_RAND) -static inline int +COAP_STATIC_INLINE int lwip_prng_impl(unsigned char *buf, size_t len) { u32_t v = LWIP_RAND(); while (len > sizeof(v)) { diff --git a/include/coap/resource.h b/include/coap/resource.h index dbb19a8d1f..3e4cb7934e 100644 --- a/include/coap/resource.h +++ b/include/coap/resource.h @@ -15,7 +15,9 @@ #ifndef _COAP_RESOURCE_H_ #define _COAP_RESOURCE_H_ +#if defined(HAVE_ASSERT_H) && !defined(assert) # include +#endif #ifndef COAP_RESOURCE_CHECK_TIME /** The interval in seconds to check if resources have changed. */ @@ -126,7 +128,7 @@ coap_resource_t *coap_resource_init(const unsigned char *uri, * @p mode which must be one of @c COAP_RESOURCE_FLAGS_NOTIFY_NON * or @c COAP_RESOURCE_FLAGS_NOTIFY_CON. */ -static inline void +COAP_STATIC_INLINE void coap_resource_set_mode(coap_resource_t *r, int mode) { r->flags = (r->flags & !COAP_RESOURCE_FLAGS_NOTIFY_CON) | mode; } @@ -255,7 +257,7 @@ coap_print_status_t coap_print_link(const coap_resource_t *resource, * @param method The CoAP request method to handle. * @param handler The handler to register with @p resource. */ -static inline void +COAP_STATIC_INLINE void coap_register_handler(coap_resource_t *resource, unsigned char method, coap_method_handler_t handler) { diff --git a/include/coap/uri.h b/include/coap/uri.h index 2340a7a6c8..fbd2bd0ab8 100644 --- a/include/coap/uri.h +++ b/include/coap/uri.h @@ -63,6 +63,60 @@ int coap_hash_path(const unsigned char *path, size_t len, coap_key_t key); * @{ */ +/** + * 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 diff --git a/include/coap/utlist.h b/include/coap/utlist.h index b5f3f04c10..b0380a8d16 100644 --- a/include/coap/utlist.h +++ b/include/coap/utlist.h @@ -111,6 +111,7 @@ do { LDECLTYPE(list) _ls_q; \ LDECLTYPE(list) _ls_e; \ LDECLTYPE(list) _ls_tail; \ + LDECLTYPE(list) _tmp; \ int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ if (list) { \ _ls_insize = 1; \ diff --git a/src/address.c b/src/address.c index b4db76f031..98e39aa83e 100644 --- a/src/address.c +++ b/src/address.c @@ -6,11 +6,22 @@ * README for terms of use. */ -#ifdef WITH_POSIX +#include "coap_config.h" + +#if defined(WITH_POSIX) || defined(HAVE_WS2TCPIP_H) #include +#ifdef HAVE_ARPA_INET_H #include +#endif +#ifdef HAVE_NETINET_IN_H #include +#endif +#ifdef HAVE_SYS_SOCKET_H #include +#endif +#ifdef HAVE_WS2TCPIP_H +#include +#endif #include "address.h" @@ -52,12 +63,11 @@ int coap_is_mcast(const coap_address_t *a) { } return 0; } -#else /* WITH_POSIX */ +#else /* WITH_POSIX || HAVE_WS2TCPIP_H */ /* make compilers happy that do not like empty modules */ -static inline void dummy() +COAP_STATIC_INLINE void dummy() { } -#endif /* not WITH_POSIX */ - +#endif diff --git a/src/block.c b/src/block.c index c48674728a..ef5f427c24 100644 --- a/src/block.c +++ b/src/block.c @@ -19,7 +19,9 @@ #error "COAP_MAX_BLOCK_SZX too large" #endif +#ifndef min #define min(a,b) ((a) < (b) ? (a) : (b)) +#endif #ifndef WITHOUT_BLOCK unsigned int @@ -49,7 +51,7 @@ coap_get_block(coap_pdu_t *pdu, unsigned short type, coap_block_t *block) { assert(block); memset(block, 0, sizeof(coap_block_t)); - if (pdu && (option = coap_check_option(pdu, type, &opt_iter))) { + if (pdu && (option = coap_check_option(pdu, type, &opt_iter)) != NULL) { unsigned int num; block->szx = COAP_OPT_BLOCK_SZX(option); @@ -77,6 +79,12 @@ coap_write_block_opt(coap_block_t *block, unsigned short type, assert(pdu); + /* Block2 */ + if (type != COAP_OPTION_BLOCK2) { + warn("coap_write_block_opt: skipped unknown option\n"); + return -1; + } + start = block->num << (block->szx + 4); if (data_length <= start) { debug("illegal block requested\n"); diff --git a/src/coap_io.c b/src/coap_io.c index 44a9639677..9c7257d881 100644 --- a/src/coap_io.c +++ b/src/coap_io.c @@ -17,10 +17,26 @@ #endif #ifdef HAVE_SYS_SOCKET_H # include +# define OPTVAL_T(t) (t) +# define CLOSE_SOCKET(fd) close(fd) +# define COAP_SOCKET_ERROR (-1) +# define COAP_INVALID_SOCKET (-1) #endif #ifdef HAVE_NETINET_IN_H # include #endif +#ifdef HAVE_WS2TCPIP_H +# include +# define OPTVAL_T(t) (const char*)(t) +# define CLOSE_SOCKET(fd) { closesocket(fd); WSACleanup(); } +# define COAP_SOCKET_ERROR SOCKET_ERROR +# define COAP_INVALID_SOCKET INVALID_SOCKET +# undef CMSG_DATA +# define CMSG_DATA WSA_CMSG_DATA +# define CMSG_FIRSTHDR WSA_CMSG_FIRSTHDR +# define CMSG_LEN WSA_CMSG_LEN +# define CMSG_SPACE WSA_CMSG_SPACE +#endif #ifdef HAVE_SYS_UIO_H # include #endif @@ -36,8 +52,9 @@ #include "debug.h" #include "mem.h" #include "coap_io.h" +#include "pdu.h" -#ifdef WITH_POSIX +#if defined(WITH_POSIX) || defined(HAVE_WS2TCPIP_H) /* define generic PKTINFO for IPv4 */ #if defined(IP_PKTINFO) # define GEN_IP_PKTINFO IP_PKTINFO @@ -60,7 +77,7 @@ struct coap_packet_t { coap_if_handle_t hnd; /**< the interface handle */ coap_address_t src; /**< the packet's source address */ coap_address_t dst; /**< the packet's destination address */ - const coap_endpoint_t *interface; + const coap_endpoint_t *endpoint; int ifindex; void *session; /**< opaque session data */ @@ -68,14 +85,14 @@ struct coap_packet_t { size_t length; /**< length of payload */ unsigned char payload[]; /**< payload */ }; -#endif +#endif /* WITH_POSIX || HAVE_WS2TCPIP_H */ #ifndef CUSTOM_COAP_NETWORK_ENDPOINT #ifdef WITH_CONTIKI static int ep_initialized = 0; -static inline struct coap_endpoint_t * +COAP_STATIC_INLINE struct coap_endpoint_t * coap_malloc_contiki_endpoint() { static struct coap_endpoint_t ep; @@ -87,7 +104,7 @@ coap_malloc_contiki_endpoint() { } } -static inline void +COAP_STATIC_INLINE void coap_free_contiki_endpoint(struct coap_endpoint_t *ep) { ep_initialized = 0; } @@ -124,54 +141,64 @@ coap_free_endpoint(coap_endpoint_t *ep) { } #else /* WITH_CONTIKI */ -static inline struct coap_endpoint_t * +COAP_STATIC_INLINE struct coap_endpoint_t * coap_malloc_posix_endpoint(void) { return (struct coap_endpoint_t *)coap_malloc(sizeof(struct coap_endpoint_t)); } -static inline void +COAP_STATIC_INLINE void coap_free_posix_endpoint(struct coap_endpoint_t *ep) { coap_free(ep); } coap_endpoint_t * coap_new_endpoint(const coap_address_t *addr, int flags) { - int sockfd = socket(addr->addr.sa.sa_family, SOCK_DGRAM, 0); +#ifdef HAVE_WS2TCPIP_H + WSADATA Data; + if (WSAStartup(MAKEWORD(2, 2), &Data) != NO_ERROR) { + coap_log(LOG_WARNING, "coap_new_endpoint: WSAStartup failed"); + return NULL; + } +#endif + coap_socket_t sockfd = socket(addr->addr.sa.sa_family, SOCK_DGRAM, 0); int on = 1; struct coap_endpoint_t *ep; - if (sockfd < 0) { + if (sockfd == COAP_INVALID_SOCKET) { +#ifdef HAVE_WS2TCPIP_H + WSACleanup(); +#endif coap_log(LOG_WARNING, "coap_new_endpoint: socket"); return NULL; } - if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, OPTVAL_T(&on), sizeof(on)) == COAP_SOCKET_ERROR) coap_log(LOG_WARNING, "coap_new_endpoint: setsockopt SO_REUSEADDR"); on = 1; switch(addr->addr.sa.sa_family) { case AF_INET: - if (setsockopt(sockfd, IPPROTO_IP, GEN_IP_PKTINFO, &on, sizeof(on)) < 0) + if (setsockopt(sockfd, IPPROTO_IP, GEN_IP_PKTINFO, OPTVAL_T(&on), sizeof(on)) < 0) coap_log(LOG_ALERT, "coap_new_endpoint: setsockopt IP_PKTINFO\n"); break; case AF_INET6: - if (setsockopt(sockfd, IPPROTO_IPV6, GEN_IPV6_PKTINFO, &on, sizeof(on)) < 0) + if (setsockopt(sockfd, IPPROTO_IPV6, GEN_IPV6_PKTINFO, OPTVAL_T(&on), sizeof(on)) < 0) coap_log(LOG_ALERT, "coap_new_endpoint: setsockopt IPV6_PKTINFO\n"); break; default: coap_log(LOG_ALERT, "coap_new_endpoint: unsupported sa_family\n"); } - if (bind(sockfd, &addr->addr.sa, addr->size) < 0) { + if (bind(sockfd, &addr->addr.sa, addr->size) == COAP_SOCKET_ERROR) { coap_log(LOG_WARNING, "coap_new_endpoint: bind"); - close (sockfd); + CLOSE_SOCKET(sockfd); return NULL; } ep = coap_malloc_posix_endpoint(); if (!ep) { coap_log(LOG_WARNING, "coap_new_endpoint: malloc"); - close(sockfd); + CLOSE_SOCKET(sockfd); return NULL; } @@ -180,9 +207,9 @@ coap_new_endpoint(const coap_address_t *addr, int flags) { ep->flags = flags; ep->addr.size = addr->size; - if (getsockname(sockfd, &ep->addr.addr.sa, &ep->addr.size) < 0) { + if (getsockname(sockfd, &ep->addr.addr.sa, &ep->addr.size) == COAP_SOCKET_ERROR) { coap_log(LOG_WARNING, "coap_new_endpoint: cannot determine local address"); - close (sockfd); + CLOSE_SOCKET(sockfd); return NULL; } @@ -206,9 +233,9 @@ coap_new_endpoint(const coap_address_t *addr, int flags) { void coap_free_endpoint(coap_endpoint_t *ep) { - if(ep) { - if (ep->handle.fd >= 0) - close(ep->handle.fd); + if (ep) { + if (ep->handle.fd != COAP_INVALID_SOCKET) + CLOSE_SOCKET(ep->handle.fd); coap_free_posix_endpoint((struct coap_endpoint_t *)ep); } } @@ -234,8 +261,8 @@ struct in_pktinfo { }; #endif -#if defined(WITH_POSIX) && !defined(SOL_IP) -/* Solaris expects level IPPROTO_IP for ancillary data. */ +#if (defined(WITH_POSIX) || defined(HAVE_WS2TCPIP_H)) && !defined(SOL_IP) +/* Solaris and Windows expect level IPPROTO_IP for ancillary data. */ #define SOL_IP IPPROTO_IP #endif @@ -260,28 +287,49 @@ coap_network_send(struct coap_context_t *context UNUSED_PARAM, char buf[CMSG_LEN(sizeof(struct sockaddr_storage))]; (void)context; +#ifdef WSA_CMSG_SPACE + (void)datalen; + (void)data; + (void)context; + + WSAMSG mhdr; + WSABUF dataBuf; + + memset(&mhdr, 0, sizeof(mhdr)); + + mhdr.name = (PSOCKADDR)&dst->addr.sa; + mhdr.lpBuffers = &dataBuf; + mhdr.dwBufferCount = 1; + mhdr.Control.buf = buf; +#else struct msghdr mhdr; struct iovec iov[1]; - assert(local_interface); - iov[0].iov_base = data; iov[0].iov_len = datalen; - memset(&mhdr, 0, sizeof(struct msghdr)); + memset(&mhdr, 0, sizeof(mhdr)); mhdr.msg_name = (void *)&dst->addr; mhdr.msg_namelen = dst->size; mhdr.msg_iov = iov; mhdr.msg_iovlen = 1; + mhdr.msg_control = buf; +#endif switch (dst->addr.sa.sa_family) { case AF_INET6: { struct cmsghdr *cmsg; struct in6_pktinfo *pktinfo; - mhdr.msg_control = buf; +#ifdef WSA_CMSG_SPACE + /* Warning C4116 "unnamed type definition in parantheses" is harmless in + * this case and the unnamed type is part of a system header macro. */ +#pragma warning( suppress : 4116 ) + mhdr.Control.len = CMSG_SPACE(sizeof(struct in6_pktinfo)); +#else mhdr.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); +#endif cmsg = CMSG_FIRSTHDR(&mhdr); cmsg->cmsg_level = IPPROTO_IPV6; @@ -310,8 +358,14 @@ coap_network_send(struct coap_context_t *context UNUSED_PARAM, struct cmsghdr *cmsg; struct in_pktinfo *pktinfo; - mhdr.msg_control = buf; +#ifdef WSA_CMSG_SPACE + /* Warning C4116 "unnamed type definition in parantheses" is harmless in + * this case and the unnamed type is part of a system header macro. */ +#pragma warning( suppress : 4116 ) + mhdr.Control.len = CMSG_SPACE(sizeof(struct in_pktinfo)); +#else mhdr.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo)); +#endif cmsg = CMSG_FIRSTHDR(&mhdr); cmsg->cmsg_level = SOL_IP; @@ -325,12 +379,30 @@ coap_network_send(struct coap_context_t *context UNUSED_PARAM, /* We cannot send with multicast address as source address * and hence let the kernel pick the outgoing interface. */ pktinfo->ipi_ifindex = 0; + +#ifdef WSA_CMSG_SPACE + /* OS's without ipi_spec_dst put the source address in ipi_addr. */ + memset(&pktinfo->ipi_addr, 0, sizeof(pktinfo->ipi_addr)); +#else + /* OS's with ipi_spec_dst seem to put the source address in ipi_spec_dst + * which is apparently a misnomer. */ memset(&pktinfo->ipi_spec_dst, 0, sizeof(pktinfo->ipi_spec_dst)); +#endif } else { pktinfo->ipi_ifindex = ep->ifindex; + +#ifdef WSA_CMSG_SPACE + /* OS's without ipi_spec_dst put the source address in ipi_addr. */ + memcpy(&pktinfo->ipi_addr, + &local_interface->addr.addr.sin.sin_addr, + local_interface->addr.size); +#else + /* OS's with ipi_spec_dst seem to put the source address in ipi_spec_dst + * which is apparently a misnomer. */ memcpy(&pktinfo->ipi_spec_dst, &local_interface->addr.addr.sin.sin_addr, local_interface->addr.size); +#endif } #endif /* IP_PKTINFO */ break; @@ -341,7 +413,15 @@ coap_network_send(struct coap_context_t *context UNUSED_PARAM, return -1; } +#ifdef WSA_CMSG_SPACE + { + DWORD sent = 0; + WSASendMsg(ep->handle.fd, &mhdr, 0, &sent, NULL, NULL); + return sent; + } +#else return sendmsg(ep->handle.fd, &mhdr, 0); +#endif #else /* WITH_CONTIKI */ /* FIXME: untested */ /* FIXME: is there a way to check if send was successful? */ @@ -358,8 +438,7 @@ coap_network_send(struct coap_context_t *context UNUSED_PARAM, #ifndef CUSTOM_COAP_NETWORK_READ #define SIN6(A) ((struct sockaddr_in6 *)(A)) - -#ifdef WITH_POSIX +#if defined(WITH_POSIX) || defined(HAVE_WS2TCPIP_H) static coap_packet_t * coap_malloc_packet(void) { coap_packet_t *packet; @@ -376,9 +455,9 @@ void coap_free_packet(coap_packet_t *packet) { coap_free(packet); } -#endif /* WITH_POSIX */ +#endif /* WITH_POSIX || HAVE_WS2TCPIP_H */ #ifdef WITH_CONTIKI -static inline coap_packet_t * +COAP_STATIC_INLINE coap_packet_t * coap_malloc_packet(void) { return (coap_packet_t *)coap_malloc_type(COAP_PACKET, 0); } @@ -389,8 +468,8 @@ coap_free_packet(coap_packet_t *packet) { } #endif /* WITH_CONTIKI */ -static inline size_t -coap_get_max_packetlength(const coap_packet_t *packet) { +COAP_STATIC_INLINE size_t +coap_get_max_packetlength(const coap_packet_t *packet UNUSED_PARAM) { (void)packet; return COAP_MAX_PDU_SIZE; } @@ -398,7 +477,7 @@ coap_get_max_packetlength(const coap_packet_t *packet) { void coap_packet_populate_endpoint(coap_packet_t *packet, coap_endpoint_t *target) { - target->handle = packet->interface->handle; + target->handle = packet->endpoint->handle; memcpy(&target->addr, &packet->dst, sizeof(target->addr)); target->ifindex = packet->ifindex; target->flags = 0; /* FIXME */ @@ -420,7 +499,7 @@ coap_packet_get_memmapped(coap_packet_t *packet, unsigned char **address, size_t * local interface with address @p local. This function returns @c 1 * if @p dst is a valid match, and @c 0 otherwise. */ -static inline int +COAP_STATIC_INLINE int is_local_if(const coap_address_t *local, const coap_address_t *dst) { return coap_address_isany(local) || coap_address_equals(dst, local) || coap_is_mcast(dst); @@ -477,7 +556,7 @@ coap_network_read(coap_endpoint_t *ep, coap_packet_t **packet) { /* use getsockname() to get the local port */ (*packet)->dst.size = sizeof((*packet)->dst.addr); - if (getsockname(ep->handle.fd, &(*packet)->dst.addr.sa, &(*packet)->dst.size) < 0) { + if (getsockname(ep->handle.fd, &(*packet)->dst.addr.sa, &(*packet)->dst.size) == COAP_SOCKET_ERROR) { coap_log(LOG_DEBUG, "cannot determine local port\n"); goto error; } @@ -612,7 +691,7 @@ coap_network_read(coap_endpoint_t *ep, coap_packet_t **packet) { #error "coap_network_read() not implemented on this platform" #endif - (*packet)->interface = ep; + (*packet)->endpoint = ep; return len; #if defined(WITH_POSIX) || defined(WITH_CONTIKI) diff --git a/src/coap_list.c b/src/coap_list.c new file mode 100644 index 0000000000..1932d4e6c9 --- /dev/null +++ b/src/coap_list.c @@ -0,0 +1,91 @@ +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 * -*- */ + +/* coap_list.c -- CoAP list structures + * + * Copyright (C) 2010,2011,2015 Olaf Bergmann + * + * This file is part of the CoAP library libcoap. Please see README for terms of + * use. + */ + +#include "coap_config.h" + +#include +#include + +#include "debug.h" +#include "mem.h" +#include "coap_list.h" + +int +coap_insert(coap_list_t **queue, coap_list_t *node, int (*order)(void *, void *node)) { + coap_list_t *p, *q; + if (!queue || !node) + return 0; + + /* set queue head if empty */ + if (!*queue) { + *queue = node; + return 1; + } + + /* replace queue head if new node has to be added before the existing queue head */ + q = *queue; + if (order(node->data, q->data) < 0) { + node->next = q; + *queue = node; + return 1; + } + + /* search for right place to insert */ + do { + p = q; + q = q->next; + } while (q && order(node->data, q->data) >= 0); + + /* insert new item */ + node->next = q; + p->next = node; + return 1; +} + +int +coap_delete(coap_list_t *node) { + if (!node) + return 0; + + if (node->delete_func) + node->delete_func(node->data); + coap_free( node->data); + coap_free( node); + + return 1; +} + +void +coap_delete_list(coap_list_t *queue) { + coap_list_t *elt, *tmp; + + if (!queue) + return; + + LL_FOREACH_SAFE(queue, elt, tmp) { + coap_delete(elt); + } +} + +coap_list_t * +coap_new_listnode(void *data, void (*delete_func)(void *)) { + coap_list_t *node = (coap_list_t *)coap_malloc(sizeof(coap_list_t)); + if (!node) { +#ifndef NDEBUG + coap_log(LOG_CRIT, "coap_new_listnode: malloc\n"); +#endif + return NULL; + } + + memset(node, 0, sizeof(coap_list_t)); + node->data = data; + node->delete_func = delete_func; + return node; +} diff --git a/src/coap_time.c b/src/coap_time.c index 9e0c860215..e8d04061f4 100644 --- a/src/coap_time.c +++ b/src/coap_time.c @@ -6,12 +6,21 @@ * README for terms of use. */ -#ifdef WITH_POSIX +#include "coap_config.h" + +#ifdef HAVE_TIME_H #include +#ifdef HAVE_SYS_TIME_H #include +#endif +#ifdef HAVE_UNISTD_H #include /* _POSIX_TIMERS */ +#endif +#ifdef HAVE_WINSOCK2_H +#include +#include +#endif -#include "coap_config.h" #include "coap_time.h" static coap_tick_t coap_clock_offset = 0; @@ -23,6 +32,27 @@ static coap_tick_t coap_clock_offset = 0; #define COAP_CLOCK CLOCK_REALTIME #endif +#ifdef HAVE_WINSOCK2_H +static int +gettimeofday(struct timeval *tp, TIME_ZONE_INFORMATION *tzp) { + (void)tzp; + static const uint64_t EPOCH = ((uint64_t) 116444736000000000ULL); + + SYSTEMTIME system_time; + FILETIME file_time; + uint64_t time; + + GetSystemTime(&system_time); + SystemTimeToFileTime(&system_time, &file_time); + time = ((uint64_t)file_time.dwLowDateTime); + time += ((uint64_t)file_time.dwHighDateTime) << 32; + + tp->tv_sec = (long)((time - EPOCH) / 10000000L); + tp->tv_usec = (long)(system_time.wMilliseconds * 1000); + return 0; +} +#endif + void coap_clock_init(void) { #ifdef COAP_CLOCK @@ -47,7 +77,7 @@ coap_clock_init(void) { void coap_ticks(coap_tick_t *t) { - unsigned long tmp; + coap_tick_t tmp; #ifdef COAP_CLOCK struct timespec tv; @@ -90,7 +120,7 @@ coap_ticks_to_rt(coap_tick_t t) { #else /* WITH_POSIX */ /* make compilers happy that do not like empty modules */ -static inline void dummy() +COAP_STATIC_INLINE void dummy() { } diff --git a/src/debug.c b/src/debug.c index 87880c2903..a3df152afe 100644 --- a/src/debug.c +++ b/src/debug.c @@ -7,6 +7,7 @@ */ #include "coap_config.h" +#include "coap.h" #if defined(HAVE_STRNLEN) && defined(__GNUC__) && !defined(_GNU_SOURCE) #define _GNU_SOURCE 1 @@ -23,6 +24,12 @@ #ifdef HAVE_ARPA_INET_H #include +#if defined(__ANDROID__) +typedef __uint16_t in_port_t; +#endif +#endif +#ifdef HAVE_WS2TCPIP_H +#include #endif #ifdef HAVE_TIME_H @@ -73,7 +80,7 @@ static char *loglevels[] = { #ifdef HAVE_TIME_H -static inline size_t +COAP_STATIC_INLINE size_t print_timestamp(char *s, size_t len, coap_tick_t t) { struct tm *tmp; time_t now = coap_ticks_to_rt(t); @@ -83,7 +90,7 @@ print_timestamp(char *s, size_t len, coap_tick_t t) { #else /* alternative implementation: just print the timestamp */ -static inline size_t +COAP_STATIC_INLINE size_t print_timestamp(char *s, size_t len, coap_tick_t t) { #ifdef HAVE_SNPRINTF return snprintf(s, len, "%u.%03u", @@ -108,7 +115,7 @@ print_timestamp(char *s, size_t len, coap_tick_t t) { * * @return The length of @p s. */ -static inline size_t +COAP_STATIC_INLINE size_t strnlen(const char *s, size_t maxlen) { size_t n = 0; while(*s++ && n < maxlen) @@ -160,7 +167,7 @@ print_readable( const unsigned char *data, size_t len, size_t coap_print_addr(const struct coap_address_t *addr, unsigned char *buf, size_t len) { -#ifdef HAVE_ARPA_INET_H +#if defined(HAVE_ARPA_INET_H) || defined(HAVE_WS2TCPIP_H) const void *addrptr = NULL; in_port_t port; unsigned char *p = buf; @@ -200,7 +207,11 @@ coap_print_addr(const struct coap_address_t *addr, unsigned char *buf, size_t le return 0; } +#ifdef HAVE_SNPRINTF p += snprintf((char *)p, buf + len - p + 1, ":%d", port); +#else /* HAVE_SNPRINTF */ + /* @todo manual conversion of port number */ +#endif /* HAVE_SNPRINTF */ return buf + len - p; #else /* HAVE_ARPA_INET_H */ @@ -277,7 +288,11 @@ msg_code_string(unsigned short c) { if (c < sizeof(methods)/sizeof(char *)) { return methods[c]; } else { +#ifdef HAVE_SNPRINTF snprintf(buf, sizeof(buf), "%u.%02u", c >> 5, c & 0x1f); +#else + sprintf(buf, "%u.%02u", c >> 5, c & 0x1f); +#endif return buf; } } @@ -323,13 +338,17 @@ msg_option_string(uint16_t option_type) { } /* unknown option type, just print to buf */ +#ifdef HAVE_SNPRINTF snprintf(buf, sizeof(buf), "%u", option_type); +#else + sprintf(buf, "%u", option_type); +#endif return buf; } static unsigned int print_content_format(unsigned int format_type, - unsigned char *result, unsigned int buflen) { + unsigned char *result, unsigned int buflen) { struct desc_t { unsigned int type; const char *name; @@ -350,12 +369,24 @@ print_content_format(unsigned int format_type, /* search format_type in list of known content formats */ for (i = 0; i < sizeof(formats)/sizeof(struct desc_t); i++) { if (format_type == formats[i].type) { +#ifdef HAVE_SNPRINTF return snprintf((char *)result, buflen, "%s", formats[i].name); +#else + /* @todo manual conversion of content format */ + return 0; +#endif } } /* unknown content format, just print numeric value to buf */ +#ifdef HAVE_SNPRINTF return snprintf((char *)result, buflen, "%d", format_type); +#else + if (buflen >= 10) { + return sprintf((char *)result, "%d", format_type); + } + return 0; +#endif } /** @@ -363,7 +394,7 @@ print_content_format(unsigned int format_type, * to carry binary data. The return value @c 0 hence indicates * printable data which is also assumed if @p content_format is @c 01. */ -static inline int +COAP_STATIC_INLINE int is_binary(int content_format) { return !(content_format == -1 || content_format == COAP_MEDIATYPE_TEXT_PLAIN || @@ -415,11 +446,17 @@ coap_show_pdu(const coap_pdu_t *pdu) { case COAP_OPTION_BLOCK2: /* split block option into number/more/size where more is the * letter M if set, the _ otherwise */ +#ifdef HAVE_SNPRINTF buf_len = snprintf((char *)buf, sizeof(buf), "%u/%c/%u", coap_opt_block_num(option), /* block number */ COAP_OPT_BLOCK_MORE(option) ? 'M' : '_', /* M bit */ (1 << (COAP_OPT_BLOCK_SZX(option) + 4))); /* block size */ - +#else + buf_len = sprintf((char *)buf, "%u/%c/%u", + coap_opt_block_num(option), /* block number */ + COAP_OPT_BLOCK_MORE(option) ? 'M' : '_', /* M bit */ + (1 << (COAP_OPT_BLOCK_SZX(option) + 4))); /* block size */ +#endif break; case COAP_OPTION_URI_PORT: @@ -427,9 +464,15 @@ coap_show_pdu(const coap_pdu_t *pdu) { case COAP_OPTION_OBSERVE: case COAP_OPTION_SIZE1: /* show values as unsigned decimal value */ +#ifdef HAVE_SNPRINTF buf_len = snprintf((char *)buf, sizeof(buf), "%u", coap_decode_var_bytes(COAP_OPT_VALUE(option), COAP_OPT_LENGTH(option))); +#else + buf_len = sprintf((char *)buf, "%u", + coap_decode_var_bytes(COAP_OPT_VALUE(option), + COAP_OPT_LENGTH(option))); +#endif break; default: diff --git a/src/encode.c b/src/encode.c index 78b3a14230..bfea17c36f 100644 --- a/src/encode.c +++ b/src/encode.c @@ -13,6 +13,10 @@ #include "coap_config.h" #include "encode.h" +#if defined(HAVE_ASSERT_H) && !defined(assert) +# include +#endif + /* Carsten suggested this when fls() is not available: */ #ifndef HAVE_FLS int coap_fls(unsigned int i) { @@ -55,3 +59,13 @@ coap_encode_var_bytes(unsigned char *buf, unsigned int val) { return n; } +bool +coap_is_var_bytes(coap_option_def_t* def) { + assert (def); + + if ('u' == def->type) { + return 1; + } else { + return 0; + } +} diff --git a/src/net.c b/src/net.c index d186ba7029..a61ee0ef8d 100644 --- a/src/net.c +++ b/src/net.c @@ -19,7 +19,9 @@ #elif HAVE_SYS_UNISTD_H #include #endif -#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif #ifdef HAVE_SYS_SOCKET_H #include #endif @@ -29,6 +31,9 @@ #ifdef HAVE_ARPA_INET_H #include #endif +#ifdef HAVE_WS2TCPIP_H +#include +#endif #ifdef WITH_LWIP #include @@ -113,16 +118,16 @@ /** creates a Qx.FRAC_BITS from COAP_DEFAULT_ACK_TIMEOUT */ #define ACK_TIMEOUT Q(FRAC_BITS, COAP_DEFAULT_ACK_TIMEOUT) -#if defined(WITH_POSIX) +#if defined(WITH_POSIX) || defined(HAVE_WS2TCPIP_H) -time_t clock_offset; +time_t clock_offset = 0; -static inline coap_queue_t * +COAP_STATIC_INLINE coap_queue_t * coap_malloc_node(void) { return (coap_queue_t *)coap_malloc_type(COAP_NODE, sizeof(coap_queue_t)); } -static inline void +COAP_STATIC_INLINE void coap_free_node(coap_queue_t *node) { coap_free_type(COAP_NODE, node); } @@ -134,12 +139,12 @@ coap_free_node(coap_queue_t *node) { static void coap_retransmittimer_execute(void *arg); static void coap_retransmittimer_restart(coap_context_t *ctx); -static inline coap_queue_t * +COAP_STATIC_INLINE coap_queue_t * coap_malloc_node() { return (coap_queue_t *)memp_malloc(MEMP_COAP_NODE); } -static inline void +COAP_STATIC_INLINE void coap_free_node(coap_queue_t *node) { memp_free(MEMP_COAP_NODE, node); } @@ -153,7 +158,7 @@ coap_free_node(coap_queue_t *node) { #include "mem.h" #include "net/ip/uip-debug.h" -clock_time_t clock_offset; +clock_time_t clock_offset = 0; #define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN]) #define UIP_UDP_BUF ((struct uip_udp_hdr *)&uip_buf[UIP_LLIPH_LEN]) @@ -165,12 +170,12 @@ coap_context_t the_coap_context; PROCESS(coap_retransmit_process, "message retransmit process"); -static inline coap_queue_t * +COAP_STATIC_INLINE coap_queue_t * coap_malloc_node() { return (coap_queue_t *)coap_malloc_type(COAP_NODE, 0); } -static inline void +COAP_STATIC_INLINE void coap_free_node(coap_queue_t *node) { coap_free_type(COAP_NODE, node); } @@ -491,19 +496,16 @@ coap_option_check_critical(coap_context_t *ctx, return ok; } -#if defined(WITH_POSIX) || defined(WITH_LWIP) || defined(WITH_CONTIKI) void coap_transaction_id(const coap_address_t *peer, const coap_pdu_t *pdu, coap_tid_t *id) { - (void)peer; - coap_key_t h; memset(h, 0, sizeof(coap_key_t)); /* Compare the transport address. */ -#ifdef WITH_POSIX +#if defined(WITH_POSIX) || defined(HAVE_WS2TCPIP_H) switch (peer->addr.sa.sa_family) { case AF_INET: coap_hash((const unsigned char *)&peer->addr.sin.sin_port, @@ -531,7 +533,6 @@ coap_transaction_id(const coap_address_t *peer, const coap_pdu_t *pdu, *id = (((h[0] << 8) | h[1]) ^ ((h[2] << 8) | h[3])) & INT_MAX; } -#endif coap_tid_t coap_send_ack(coap_context_t *context, @@ -552,7 +553,7 @@ coap_send_ack(coap_context_t *context, return result; } -#if defined(WITH_POSIX) || defined(WITH_CONTIKI) +#if defined(WITH_POSIX) || defined(WITH_CONTIKI) || defined(HAVE_WS2TCPIP_H) static coap_tid_t coap_send_impl(coap_context_t *context, const coap_endpoint_t *local_interface, @@ -673,7 +674,7 @@ coap_send_message_type(coap_context_t *context, * value * @return COAP_TICKS_PER_SECOND * ACK_TIMEOUT * (1 + (ACK_RANDOM_FACTOR - 1) * r) */ -static inline unsigned int +COAP_STATIC_INLINE unsigned int calc_timeout(unsigned char r) { unsigned int result; @@ -974,7 +975,7 @@ coap_remove_from_queue(coap_queue_t **queue, coap_tid_t id, coap_queue_t **node) } -static inline int +COAP_STATIC_INLINE int token_match(const unsigned char *a, size_t alen, const unsigned char *b, size_t blen) { return alen == blen && (alen == 0 || memcmp(a, b, alen) == 0); @@ -1126,7 +1127,7 @@ coap_new_error_response(coap_pdu_t *request, unsigned char code, * Quick hack to determine the size of the resource description for * .well-known/core. */ -static inline size_t +COAP_STATIC_INLINE size_t get_wkc_len(coap_context_t *context, coap_opt_t *query_filter) { unsigned char buf[1]; size_t len = 0; @@ -1259,8 +1260,7 @@ coap_wellknown_response(coap_context_t *context, coap_pdu_t *request) { } unsigned int new_resp_length = resp->length + COAP_PRINT_OUTPUT_LENGTH(result); - if (new_resp_length > USHRT_MAX) - { + if (new_resp_length > USHRT_MAX) { debug("coap_print_wellknown failed - print result too large\n"); goto error; } @@ -1539,7 +1539,7 @@ handle_request(coap_context_t *context, coap_queue_t *node) { assert(response == NULL); } -static inline void +COAP_STATIC_INLINE void handle_response(coap_context_t *context, coap_queue_t *sent, coap_queue_t *rcvd) { @@ -1561,6 +1561,19 @@ handle_response(coap_context_t *context, } } +COAP_STATIC_INLINE int +#ifdef __GNUC__ +handle_locally(coap_context_t *context __attribute__ ((unused)), + coap_queue_t *node __attribute__ ((unused))) { +#else /* not a GCC */ +handle_locally(coap_context_t *context, coap_queue_t *node) { +#endif /* GCC */ + /* this function can be used to check if node->pdu is really for us */ + (void)context; + (void)node; + return 1; +} + void coap_dispatch(coap_context_t *context, coap_queue_t *rcvd) { coap_queue_t *sent = NULL; diff --git a/src/option.c b/src/option.c index d3e5aa5f7e..712e809f4f 100644 --- a/src/option.c +++ b/src/option.c @@ -119,22 +119,79 @@ coap_opt_parse(const coap_opt_t *opt, size_t length, coap_option_t *result) { coap_opt_iterator_t * coap_option_iterator_init(coap_pdu_t *pdu, coap_opt_iterator_t *oi, const coap_opt_filter_t filter) { + return coap_option_iterator_init2(pdu, oi, filter, COAP_UDP); +} + +coap_opt_iterator_t * +coap_option_iterator_init2(coap_pdu_t *pdu, coap_opt_iterator_t *oi, + const coap_opt_filter_t filter, coap_transport_t transport) { assert(pdu); assert(pdu->hdr); assert(oi); memset(oi, 0, sizeof(coap_opt_iterator_t)); - oi->next_option = (unsigned char *)pdu->hdr + sizeof(coap_hdr_t) - + pdu->hdr->token_length; - if ((unsigned char *)pdu->hdr + pdu->length <= oi->next_option) { - oi->bad = 1; - return NULL; + unsigned int token_length; + unsigned int headerSize; + + switch (transport) { +#ifdef WITH_TCP + case COAP_TCP: + token_length = (pdu->transport_hdr->tcp.header_data[0]) & 0x0f; + headerSize = COAP_TCP_HEADER_NO_FIELD; + break; + case COAP_TCP_8BIT: + token_length = (pdu->transport_hdr->tcp_8bit.header_data[0]) & 0x0f; + headerSize = COAP_TCP_HEADER_8_BIT; + break; + case COAP_TCP_16BIT: + token_length = (pdu->transport_hdr->tcp_16bit.header_data[0]) & 0x0f; + headerSize = COAP_TCP_HEADER_16_BIT; + break; + case COAP_TCP_32BIT: + token_length = pdu->transport_hdr->tcp_32bit.header_data[0] & 0x0f; + headerSize = COAP_TCP_HEADER_32_BIT; + break; +#endif +#ifdef WITH_WS + case COAP_WS: + token_length = pdu->transport_hdr->ws.header_data[0] & 0x0f; + headerSize = COAP_WS_HEADER; + break; +#endif + default: + token_length = pdu->transport_hdr->udp.token_length; + headerSize = sizeof(pdu->transport_hdr->udp); + break; + } + + oi->next_option = (unsigned char *)pdu->hdr + headerSize + token_length; + if (COAP_UDP == transport) { + if ((unsigned char *) &(pdu->transport_hdr->udp) + pdu->length <= oi->next_option) { + oi->bad = 1; + return NULL; + } } +#ifdef WITH_WS + else if (COAP_WS == transport) { + if ((unsigned char *) &(pdu->transport_hdr->ws) + pdu->length <= oi->next_option) { + oi->bad = 1; + return NULL; + } + } +#endif +#ifdef WITH_TCP + else { + if ((unsigned char *) &(pdu->transport_hdr->tcp) + pdu->length <= oi->next_option) { + oi->bad = 1; + return NULL; + } + } +#endif - assert((sizeof(coap_hdr_t) + pdu->hdr->token_length) <= pdu->length); + assert((headerSize + token_length) <= pdu->length); - oi->length = pdu->length - (sizeof(coap_hdr_t) + pdu->hdr->token_length); + oi->length = pdu->length - (headerSize + token_length); if (filter) { memcpy(oi->filter, filter, sizeof(coap_opt_filter_t)); @@ -143,7 +200,7 @@ coap_option_iterator_init(coap_pdu_t *pdu, coap_opt_iterator_t *oi, return oi; } -static inline int +COAP_STATIC_INLINE int opt_finished(coap_opt_iterator_t *oi) { assert(oi); @@ -420,7 +477,7 @@ typedef struct { } opt_filter; /** Returns true iff @p type denotes an option type larger than 255. */ -static inline int +COAP_STATIC_INLINE int is_long_option(unsigned short type) { return type > 255; } /** Operation specifiers for coap_filter_op(). */ @@ -521,3 +578,40 @@ coap_option_filter_get(const coap_opt_filter_t filter, unsigned short type) { * but as _set and _unset do, the function does not take a const). */ return coap_option_filter_op((uint16_t *)filter, type, FILTER_GET); } + +static coap_option_def_t coap_option_def[] = { + { COAP_OPTION_IF_MATCH, 'o', 0, 8 }, + { COAP_OPTION_URI_HOST, 's', 1, 255 }, + { COAP_OPTION_ETAG, 'o', 1, 8 }, + { COAP_OPTION_IF_NONE_MATCH, 'e', 0, 0 }, + { COAP_OPTION_URI_PORT, 'u', 0, 2 }, + { COAP_OPTION_LOCATION_PATH, 's', 0, 255 }, + { COAP_OPTION_URI_PATH, 's', 0, 255 }, + { COAP_OPTION_CONTENT_TYPE, 'u', 0, 2 }, + { COAP_OPTION_MAXAGE, 'u', 0, 4 }, + { COAP_OPTION_URI_QUERY, 's', 1, 255 }, + { COAP_OPTION_ACCEPT, 'u', 0, 2 }, + { COAP_OPTION_LOCATION_QUERY, 's', 0, 255 }, + { COAP_OPTION_PROXY_URI, 's', 1,1034 }, + { COAP_OPTION_PROXY_SCHEME, 's', 1, 255 }, + { COAP_OPTION_SIZE1, 'u', 0, 4 }, + { COAP_OPTION_SIZE2, 'u', 0, 4 }, + { COAP_OPTION_OBSERVE, 'u', 0, 3 }, + { COAP_OPTION_BLOCK2, 'u', 0, 3 }, + { COAP_OPTION_BLOCK1, 'u', 0, 3 }, +}; + +coap_option_def_t* +coap_opt_def(unsigned short key) { + int i; + + if (COAP_MAX_OPT < key) { + return NULL; + } + for (i = 0; i < (int)(sizeof(coap_option_def)/sizeof(coap_option_def_t)); i++) { + if (key == coap_option_def[i].key) + return &(coap_option_def[i]); + } + debug("coap_opt_def: add key:[%d] to coap_is_var_bytes", key); + return NULL; +} diff --git a/src/pdu.c b/src/pdu.c index 72a3ebb90a..5d531888ae 100644 --- a/src/pdu.c +++ b/src/pdu.c @@ -22,6 +22,9 @@ #ifdef HAVE_ARPA_INET_H #include #endif +#ifdef HAVE_WINSOCK2_H +#include +#endif #include "debug.h" #include "pdu.h" @@ -31,6 +34,12 @@ void coap_pdu_clear(coap_pdu_t *pdu, size_t size) { + coap_pdu_clear2(pdu, size, COAP_UDP, 0); +} + +void +coap_pdu_clear2(coap_pdu_t *pdu, size_t size, coap_transport_t transport, unsigned int length) { + assert(length <= USHRT_MAX); assert(pdu); #ifdef WITH_LWIP @@ -45,14 +54,22 @@ coap_pdu_clear(coap_pdu_t *pdu, size_t size) { pdu->max_size = size; pdu->hdr->version = COAP_DEFAULT_VERSION; - /* data is NULL unless explicitly set by coap_add_data() */ - pdu->length = sizeof(coap_hdr_t); + if (COAP_UDP == transport) { + pdu->transport_hdr->udp.version = COAP_DEFAULT_VERSION; + /* data is NULL unless explicitly set by coap_add_data() */ + pdu->length = sizeof(coap_hdr_t); + } +#if defined(WITH_TCP) || defined(WITH_WS) + else { + /* data is NULL unless explicitly set by coap_add_data() */ + pdu->length = (unsigned short)length; + } +#endif } #ifdef WITH_LWIP coap_pdu_t * -coap_pdu_from_pbuf(struct pbuf *pbuf) -{ +coap_pdu_from_pbuf(struct pbuf *pbuf) { if (pbuf == NULL) return NULL; LWIP_ASSERT("Can only deal with contiguous PBUFs", pbuf->tot_len == pbuf->len); @@ -78,26 +95,64 @@ coap_pdu_from_pbuf(struct pbuf *pbuf) coap_pdu_t * coap_pdu_init(unsigned char type, unsigned char code, unsigned short id, size_t size) { + return coap_pdu_init2(type, code, id, size, COAP_UDP); +} + +coap_pdu_t * +coap_pdu_init2(unsigned char type, unsigned char code, unsigned short id, + size_t size, coap_transport_t transport) { coap_pdu_t *pdu; #ifdef WITH_LWIP - struct pbuf *p; + struct pbuf *p; +#endif + + size_t length = 0; + switch (transport) { + case COAP_UDP: + length = sizeof(coap_hdr_t); + break; +#ifdef WITH_TCP + case COAP_TCP: + length = COAP_TCP_HEADER_NO_FIELD; + break; + case COAP_TCP_8BIT: + length = COAP_TCP_HEADER_8_BIT; + break; + case COAP_TCP_16BIT: + length = COAP_TCP_HEADER_16_BIT; + break; + case COAP_TCP_32BIT: + length = COAP_TCP_HEADER_32_BIT; + break; #endif +#ifdef WITH_WS + case COAP_WS: + length = COAP_WS_HEADER; + break; +#endif + default: + debug("it has wrong type\n"); + } + assert(length <= UINT_MAX); + +#if !defined(WITH_TCP) && !defined(WITH_WS) assert(size <= COAP_MAX_PDU_SIZE); /* Size must be large enough to fit the header. */ - if (size < sizeof(coap_hdr_t) || size > COAP_MAX_PDU_SIZE) + if (size < length || size > COAP_MAX_PDU_SIZE) return NULL; +#endif /* size must be large enough for hdr */ -#if defined(WITH_POSIX) || defined(WITH_CONTIKI) - pdu = coap_malloc_type(COAP_PDU, sizeof(coap_pdu_t)); +#if defined(WITH_POSIX) || defined(WITH_CONTIKI) || defined(WITH_ARDUINO) || defined(_WIN32) + pdu = (coap_pdu_t *)coap_malloc_type(COAP_PDU, sizeof(coap_pdu_t)); if (!pdu) return NULL; pdu->hdr = coap_malloc_type(COAP_PDU_BUF, size); if (pdu->hdr == NULL) { coap_free_type(COAP_PDU, pdu); pdu = NULL; } -#endif /* WITH_POSIX or WITH_CONTIKI */ +#endif #ifdef WITH_LWIP pdu = (coap_pdu_t*)coap_malloc_type(COAP_PDU, sizeof(coap_pdu_t)); if (!pdu) return NULL; @@ -111,23 +166,61 @@ coap_pdu_init(unsigned char type, unsigned char code, #ifdef WITH_LWIP pdu->pbuf = p; #endif - coap_pdu_clear(pdu, size); - pdu->hdr->id = id; - pdu->hdr->type = type; - pdu->hdr->code = code; + coap_pdu_clear2(pdu, size, transport, (unsigned int)length); + switch (transport) { + case COAP_UDP: + pdu->transport_hdr->udp.id = id; + pdu->transport_hdr->udp.type = type; + pdu->transport_hdr->udp.code = code; + break; +#ifdef WITH_TCP + case COAP_TCP: + pdu->transport_hdr->tcp.header_data[0] = 0; + pdu->transport_hdr->tcp.header_data[1] = code; + break; + case COAP_TCP_8BIT: + pdu->transport_hdr->tcp_8bit.header_data[0] = COAP_TCP_LENGTH_FIELD_NUM_8_BIT << 4; + pdu->transport_hdr->tcp_8bit.header_data[2] = code; + break; + case COAP_TCP_16BIT: + pdu->transport_hdr->tcp_16bit.header_data[0] = COAP_TCP_LENGTH_FIELD_NUM_16_BIT << 4; + pdu->transport_hdr->tcp_16bit.header_data[3] = code; + break; + case COAP_TCP_32BIT: + pdu->transport_hdr->tcp_32bit.header_data[0] = COAP_TCP_LENGTH_FIELD_NUM_32_BIT << 4; + pdu->transport_hdr->tcp_32bit.header_data[5] = code; + break; +#endif +#ifdef WITH_WS + case COAP_WS: + pdu->transport_hdr->ws.header_data[0] = 0; + pdu->transport_hdr->ws.header_data[1] = code; + break; +#endif + default: + debug("it has wrong type\n"); + } } return pdu; } coap_pdu_t * coap_new_pdu(void) { + return coap_new_pdu2(COAP_UDP, COAP_MAX_PDU_SIZE); +} + +coap_pdu_t * +coap_new_pdu2(coap_transport_t transport, unsigned int size) { coap_pdu_t *pdu; - + + pdu = coap_pdu_init2(0, 0, #ifndef WITH_CONTIKI - pdu = coap_pdu_init(0, 0, ntohs((uint16_t)COAP_INVALID_TID), COAP_MAX_PDU_SIZE); + ntohs((uint16_t)COAP_INVALID_TID), #else /* WITH_CONTIKI */ - pdu = coap_pdu_init(0, 0, uip_ntohs(COAP_INVALID_TID), COAP_MAX_PDU_SIZE); + uip_ntohs(COAP_INVALID_TID), #endif /* WITH_CONTIKI */ + size, + transport); #ifndef NDEBUG if (!pdu) @@ -138,44 +231,489 @@ coap_new_pdu(void) { void coap_delete_pdu(coap_pdu_t *pdu) { -#if defined(WITH_POSIX) || defined(WITH_CONTIKI) if (pdu != NULL) { +#ifdef WITH_LWIP + pbuf_free(pdu->pbuf); +#else if (pdu->hdr != NULL) { coap_free_type(COAP_PDU_BUF, pdu->hdr); } +#endif coap_free_type(COAP_PDU, pdu); } +} + +#ifdef WITH_TCP +size_t +coap_get_total_message_length(const unsigned char *data, size_t size) { + if (!data || !size) { + debug("received data length is null\n"); + return 0; + } + + coap_transport_t transport = coap_get_tcp_header_type_from_initbyte( + ((unsigned char *)data)[0] >> 4); + size_t optPaylaodLen = coap_get_length_from_header((unsigned char *)data, + transport); + size_t headerLen = coap_get_tcp_header_length((unsigned char *)data); + + return headerLen + optPaylaodLen; +} + +coap_transport_t +coap_get_tcp_header_type_from_size(unsigned int size) { + if (size < COAP_TCP_LENGTH_FIELD_8_BIT) { + return COAP_TCP; + } else if (size < COAP_TCP_LENGTH_FIELD_16_BIT) { + return COAP_TCP_8BIT; + } else if (size < COAP_TCP_LENGTH_FIELD_32_BIT) { + return COAP_TCP_16BIT; + } else { + return COAP_TCP_32BIT; + } +} + +coap_transport_t +coap_get_tcp_header_type_from_initbyte(unsigned int length) { + coap_transport_t type; + switch (length) { + case COAP_TCP_LENGTH_FIELD_NUM_8_BIT: + type = COAP_TCP_8BIT; + break; + case COAP_TCP_LENGTH_FIELD_NUM_16_BIT: + type = COAP_TCP_16BIT; + break; + case COAP_TCP_LENGTH_FIELD_NUM_32_BIT: + type = COAP_TCP_32BIT; + break; + default: + type = COAP_TCP; + } + return type; +} + +void +coap_add_length(const coap_pdu_t *pdu, coap_transport_t transport, unsigned int length) { + assert(pdu); + + switch (transport) { + case COAP_TCP: + assert(length < COAP_TCP_LENGTH_FIELD_8_BIT); + pdu->transport_hdr->tcp.header_data[0] = (length << 4) & 0x0000ff; + break; + case COAP_TCP_8BIT: + if (length > COAP_TCP_LENGTH_FIELD_8_BIT) { + unsigned int total_length = length - COAP_TCP_LENGTH_FIELD_8_BIT; + assert(total_length <= UCHAR_MAX); + pdu->transport_hdr->tcp_8bit.header_data[1] = + total_length & 0x0000ff; + } + break; + case COAP_TCP_16BIT: + if (length > COAP_TCP_LENGTH_FIELD_16_BIT) { + unsigned int total_length = length - COAP_TCP_LENGTH_FIELD_16_BIT; + assert(total_length <= USHRT_MAX); + pdu->transport_hdr->tcp_16bit.header_data[1] = (total_length >> 8) & 0x0000ff; + pdu->transport_hdr->tcp_16bit.header_data[2] = total_length & 0x000000ff; + } + break; + case COAP_TCP_32BIT: + if (length > COAP_TCP_LENGTH_FIELD_32_BIT) { + unsigned int total_length = length - COAP_TCP_LENGTH_FIELD_32_BIT; + pdu->transport_hdr->tcp_32bit.header_data[1] = total_length >> 24; + pdu->transport_hdr->tcp_32bit.header_data[2] = (total_length >> 16) & 0x00ff; + pdu->transport_hdr->tcp_32bit.header_data[3] = (total_length >> 8) & 0x0000ff; + pdu->transport_hdr->tcp_32bit.header_data[4] = total_length & 0x000000ff; + } + break; + default: + debug("it has wrong type\n"); + } +} + +unsigned int +coap_get_length_from_header(const unsigned char *header, coap_transport_t transport) { + assert(header); + + unsigned int length = 0; + unsigned int length_field_data = 0; + switch (transport) { + case COAP_TCP: + length = header[0] >> 4; + break; + case COAP_TCP_8BIT: + length = header[1] + COAP_TCP_LENGTH_FIELD_8_BIT; + break; + case COAP_TCP_16BIT: + length_field_data = (header[1] << 8 | header[2]); + length = length_field_data + COAP_TCP_LENGTH_FIELD_16_BIT; + break; + case COAP_TCP_32BIT: + length_field_data = header[1] << 24 | header[2] << 16 | header[3] << 8 | header[4]; + length = length_field_data + COAP_TCP_LENGTH_FIELD_32_BIT; + break; + default: + debug("it has wrong type\n"); + } + + return length; +} + +unsigned int +coap_get_length(const coap_pdu_t *pdu, coap_transport_t transport) { + assert(pdu); + + unsigned int length = 0; + unsigned int length_field_data = 0; + switch (transport) { + case COAP_TCP: + length = pdu->transport_hdr->tcp.header_data[0] >> 4; + break; + case COAP_TCP_8BIT: + length = pdu->transport_hdr->tcp_8bit.header_data[1] + COAP_TCP_LENGTH_FIELD_8_BIT; + break; + case COAP_TCP_16BIT: + length_field_data = + pdu->transport_hdr->tcp_16bit.header_data[1] << 8 | + pdu->transport_hdr->tcp_16bit.header_data[2]; + length = length_field_data + COAP_TCP_LENGTH_FIELD_16_BIT; + break; + case COAP_TCP_32BIT: + length_field_data = + pdu->transport_hdr->tcp_32bit.header_data[1] << 24 | + pdu->transport_hdr->tcp_32bit.header_data[2] << 16 | + pdu->transport_hdr->tcp_32bit.header_data[3] << 8 | + pdu->transport_hdr->tcp_32bit.header_data[4]; + length = length_field_data + COAP_TCP_LENGTH_FIELD_32_BIT; + break; + default: + debug("it has wrong type\n"); + } + + return length; +} + +unsigned int +coap_get_tcp_header_length(unsigned char *data) { + assert(data); + + unsigned int tokenLength = data[0] & 0x0f; + coap_transport_t transport = + coap_get_tcp_header_type_from_initbyte(data[0] >> 4); + unsigned int length = 0; + + length = coap_get_tcp_header_length_for_transport(transport) + tokenLength; + return length; +} + +unsigned int +coap_get_tcp_header_length_for_transport(coap_transport_t transport) { + unsigned int length = 0; + switch (transport) { + case COAP_TCP: + length = COAP_TCP_HEADER_NO_FIELD; + break; + case COAP_TCP_8BIT: /* len(4bit) + TKL(4bit) + Len+bytes(1byte) + Code(1byte) */ + length = COAP_TCP_HEADER_8_BIT; + break; + case COAP_TCP_16BIT: /* len(4bit) + TKL(4bit) + Len+bytes(2byte) + Code(1byte) */ + length = COAP_TCP_HEADER_16_BIT; + break; + case COAP_TCP_32BIT: /* len(4bit) + TKL(4bit) + Len+bytes(4byte) + Code(1byte) */ + length = COAP_TCP_HEADER_32_BIT; + break; + default: + debug("it has wrong type\n"); + } + + return length; +} #endif -#ifdef WITH_LWIP - if (pdu != NULL) /* accepting double free as the other implementation accept that too */ - pbuf_free(pdu->pbuf); - coap_free_type(COAP_PDU, pdu); + +size_t +coap_get_opt_header_length(unsigned short key, size_t length) { + size_t headerLength = 0; + + unsigned short optDeltaLength = 0; + if (COAP_OPTION_FIELD_8_BIT >= key) { + optDeltaLength = 0; + } else if (COAP_OPTION_FIELD_8_BIT < key && COAP_OPTION_FIELD_16_BIT >= key) { + optDeltaLength = 1; + } else { + optDeltaLength = 2; + } + + size_t optLength = 0; + if (COAP_OPTION_FIELD_8_BIT >= length) { + optLength = 0; + } else if (COAP_OPTION_FIELD_8_BIT < length && COAP_OPTION_FIELD_16_BIT >= length) { + optLength = 1; + } else if (COAP_OPTION_FIELD_16_BIT < length && COAP_OPTION_FIELD_32_BIT >= length) { + optLength = 2; + } else { + printf("Error : Reserved for the Payload marker for length"); + return 0; + } + + headerLength = length + optDeltaLength + optLength + 1; + + return headerLength; +} + +void +coap_add_code(const coap_pdu_t *pdu, coap_transport_t transport, unsigned int code) { + unsigned int long_code = COAP_RESPONSE_CODE(code); + assert(long_code <= UINT8_MAX); + assert(pdu); + + unsigned char coap_code = (unsigned char)long_code; + switch (transport) { + case COAP_UDP: + pdu->transport_hdr->udp.code = coap_code; + break; +#ifdef WITH_TCP + case COAP_TCP: + pdu->transport_hdr->tcp.header_data[1] = coap_code; + break; + case COAP_TCP_8BIT: + pdu->transport_hdr->tcp_8bit.header_data[2] = coap_code; + break; + case COAP_TCP_16BIT: + pdu->transport_hdr->tcp_16bit.header_data[3] = coap_code; + break; + case COAP_TCP_32BIT: + pdu->transport_hdr->tcp_32bit.header_data[5] = coap_code; + break; +#endif +#ifdef WITH_WS + case COAP_WS: + pdu->transport_hdr->ws.header_data[1] = coap_code; + break; #endif + default: + debug("it has wrong type\n"); + } +} + +unsigned int +coap_get_code(const coap_pdu_t *pdu, coap_transport_t transport) { + assert(pdu); + + unsigned int code = 0; + switch (transport) { + case COAP_UDP: + code = pdu->transport_hdr->udp.code; + break; +#ifdef WITH_TCP + case COAP_TCP: + code = pdu->transport_hdr->tcp.header_data[1]; + break; + case COAP_TCP_8BIT: + code = pdu->transport_hdr->tcp_8bit.header_data[2]; + break; + case COAP_TCP_16BIT: + code = pdu->transport_hdr->tcp_16bit.header_data[3]; + break; + case COAP_TCP_32BIT: + code = pdu->transport_hdr->tcp_32bit.header_data[5]; + break; +#endif +#ifdef WITH_WS + case COAP_WS: + code = pdu->transport_hdr->ws.header_data[1]; + break; +#endif + default: + debug("it has wrong type\n"); + } + return code; } int coap_add_token(coap_pdu_t *pdu, size_t len, const unsigned char *data) { + return coap_add_token2(pdu, len, data, COAP_UDP); +} + +int +coap_add_token2(coap_pdu_t *pdu, size_t len, const unsigned char *data, + coap_transport_t transport) { const size_t HEADERLENGTH = len + 4; /* must allow for pdu == NULL as callers may rely on this */ if (!pdu || len > 8 || pdu->max_size < HEADERLENGTH) return 0; - pdu->hdr->token_length = (unsigned char)len; - if (len) - memcpy(pdu->hdr->token, data, len); + unsigned char token_len = (unsigned char)len; + unsigned char* token = NULL; + switch (transport) { + case COAP_UDP: + pdu->transport_hdr->udp.token_length = token_len; + token = pdu->transport_hdr->udp.token; + pdu->length = (unsigned short)HEADERLENGTH; + break; +#ifdef WITH_TCP + case COAP_TCP: + pdu->transport_hdr->tcp.header_data[0] = + pdu->transport_hdr->tcp.header_data[0] | token_len; + token = pdu->transport_hdr->tcp.token; + pdu->length = token_len + COAP_TCP_HEADER_NO_FIELD; + break; + case COAP_TCP_8BIT: + pdu->transport_hdr->tcp_8bit.header_data[0] = + pdu->transport_hdr->tcp_8bit.header_data[0] | token_len; + token = pdu->transport_hdr->tcp_8bit.token; + pdu->length = token_len + COAP_TCP_HEADER_8_BIT; + break; + case COAP_TCP_16BIT: + pdu->transport_hdr->tcp_16bit.header_data[0] = + pdu->transport_hdr->tcp_16bit.header_data[0] | token_len; + token = pdu->transport_hdr->tcp_16bit.token; + pdu->length = token_len + COAP_TCP_HEADER_16_BIT; + break; + case COAP_TCP_32BIT: + pdu->transport_hdr->tcp_32bit.header_data[0] = + pdu->transport_hdr->tcp_32bit.header_data[0] | token_len; + token = pdu->transport_hdr->tcp_32bit.token; + pdu->length = token_len + COAP_TCP_HEADER_32_BIT; + break; +#endif +#ifdef WITH_WS + case COAP_WS: + pdu->transport_hdr->ws.header_data[0] = + pdu->transport_hdr->ws.header_data[0] | token_len; + token = pdu->transport_hdr->ws.token; + pdu->length = token_len + COAP_WS_HEADER; + break; +#endif + default: + debug("it has wrong type\n"); + } + + if (token_len) { + memcpy(token, data, token_len); + } + pdu->max_delta = 0; - pdu->length = HEADERLENGTH; pdu->data = NULL; return 1; } -/** @FIXME de-duplicate code with coap_add_option_later */ +int coap_add_token_to_empty_message(coap_pdu_t *pdu, size_t len, const unsigned char *data, + coap_transport_t transport) +{ + const size_t HEADERLENGTH = len; + /* must allow for pdu == NULL as callers may rely on this */ + if (!pdu || len > 8) + return 0; + + unsigned char* token = NULL; + switch(transport) + { + case COAP_UDP: + pdu->transport_hdr->udp.token_length = len; + token = pdu->transport_hdr->udp.token; + pdu->length = HEADERLENGTH; + break; +#ifdef WITH_TCP + case COAP_TCP: + pdu->transport_hdr->tcp.header_data[0] = + pdu->transport_hdr->tcp.header_data[0] | len; + token = pdu->transport_hdr->tcp.token; + pdu->length = len + COAP_TCP_HEADER_NO_FIELD; + break; + case COAP_TCP_8BIT: + pdu->transport_hdr->tcp_8bit.header_data[0] = + pdu->transport_hdr->tcp_8bit.header_data[0] | len; + token = pdu->transport_hdr->tcp_8bit.token; + pdu->length = len + COAP_TCP_HEADER_8_BIT; + break; + case COAP_TCP_16BIT: + pdu->transport_hdr->tcp_16bit.header_data[0] = + pdu->transport_hdr->tcp_16bit.header_data[0] | len; + token = pdu->transport_hdr->tcp_16bit.token; + pdu->length = len + COAP_TCP_HEADER_16_BIT; + break; + case COAP_TCP_32BIT: + pdu->transport_hdr->tcp_32bit.header_data[0] = + pdu->transport_hdr->tcp_32bit.header_data[0] | len; + token = pdu->transport_hdr->tcp_32bit.token; + pdu->length = len + COAP_TCP_HEADER_32_BIT; + break; +#endif + default: + debug("it has wrong type\n"); + } + + if (len) + { + memcpy(token, data, len); + } + + pdu->max_delta = 0; + pdu->data = NULL; + + return 1; +} + +void +coap_get_token(const coap_hdr_t *pdu_hdr, + unsigned char **token, unsigned int *token_length) { + coap_get_token2((const coap_hdr_transport_t *)pdu_hdr, COAP_UDP, token, token_length); +} + +void +coap_get_token2(const coap_hdr_transport_t *pdu_hdr, coap_transport_t transport, + unsigned char **token, unsigned int *token_length) { + assert(pdu_hdr); + assert(token); + assert(token_length); + + switch (transport) { + case COAP_UDP: + *token_length = pdu_hdr->udp.token_length; + *token = (unsigned char *)pdu_hdr->udp.token; + break; +#ifdef WITH_TCP + case COAP_TCP: + *token_length = (pdu_hdr->tcp.header_data[0]) & 0x0f; + *token = (unsigned char *)pdu_hdr->tcp.token; + break; + case COAP_TCP_8BIT: + *token_length = (pdu_hdr->tcp_8bit.header_data[0]) & 0x0f; + *token = (unsigned char *)pdu_hdr->tcp_8bit.token; + break; + case COAP_TCP_16BIT: + *token_length = (pdu_hdr->tcp_16bit.header_data[0]) & 0x0f; + *token = (unsigned char *)pdu_hdr->tcp_16bit.token; + break; + case COAP_TCP_32BIT: + *token_length = (pdu_hdr->tcp_32bit.header_data[0]) & 0x0f; + *token = (unsigned char *)pdu_hdr->tcp_32bit.token; + break; +#endif +#ifdef WITH_WS + case COAP_WS: + *token_length = (pdu_hdr->ws.header_data[0]) & 0x0f; + *token = (unsigned char *)pdu_hdr->ws.token; + break; +#endif + default: + debug("it has wrong type\n"); + } +} + size_t coap_add_option(coap_pdu_t *pdu, unsigned short type, unsigned int len, const unsigned char *data) { + return coap_add_option2(pdu, type, len, data, COAP_UDP); +} + +/** @FIXME de-duplicate code with coap_add_option_later */ +size_t +coap_add_option2(coap_pdu_t *pdu, unsigned short type, unsigned int len, + const unsigned char *data, coap_transport_t transport) { size_t optsize; coap_opt_t *opt; - + assert(pdu); pdu->data = NULL; @@ -184,7 +722,30 @@ coap_add_option(coap_pdu_t *pdu, unsigned short type, unsigned int len, const un return 0; } - opt = (unsigned char *)pdu->hdr + pdu->length; + switch (transport) { +#ifdef WITH_TCP + case COAP_TCP: + opt = (unsigned char *) &(pdu->transport_hdr->tcp) + pdu->length; + break; + case COAP_TCP_8BIT: + opt = (unsigned char *) &(pdu->transport_hdr->tcp_8bit) + pdu->length; + break; + case COAP_TCP_16BIT: + opt = (unsigned char *) &(pdu->transport_hdr->tcp_16bit) + pdu->length; + break; + case COAP_TCP_32BIT: + opt = (unsigned char *) &(pdu->transport_hdr->tcp_32bit) + pdu->length; + break; +#endif +#ifdef WITH_WS + case COAP_WS: + opt = (unsigned char *) &(pdu->transport_hdr->ws) + pdu->length; + break; +#endif + default: + opt = (unsigned char *) &(pdu->transport_hdr->udp) + pdu->length; + break; + } /* encode option and check length */ optsize = coap_opt_encode(opt, pdu->max_size - pdu->length, @@ -261,7 +822,7 @@ coap_add_data(coap_pdu_t *pdu, unsigned int len, const unsigned char *data) { } int -coap_get_data(coap_pdu_t *pdu, size_t *len, unsigned char **data) { +coap_get_data(const coap_pdu_t *pdu, size_t *len, unsigned char **data) { assert(pdu); assert(len); assert(data); @@ -329,14 +890,14 @@ coap_response_phrase(unsigned char code) { * on error. */ static size_t -next_option_safe(coap_opt_t **optp, size_t *length) { - coap_option_t option; +next_option_safe(coap_opt_t **optp, size_t *length, coap_option_t* option) { size_t optsize; - assert(optp); assert(*optp); + assert(optp); + assert(*optp); assert(length); - optsize = coap_opt_parse(*optp, *length, &option); + optsize = coap_opt_parse(*optp, *length, option); if (optsize) { assert(optsize <= *length); @@ -349,71 +910,166 @@ next_option_safe(coap_opt_t **optp, size_t *length) { int coap_pdu_parse(unsigned char *data, size_t length, coap_pdu_t *pdu) { - coap_opt_t *opt; + return coap_pdu_parse2(data, length, pdu, COAP_UDP); +} +int +coap_pdu_parse2(unsigned char *data, size_t length, coap_pdu_t *pdu, + coap_transport_t transport) { assert(data); assert(pdu); if (pdu->max_size < length || length > USHRT_MAX) { debug("insufficient space to store parsed PDU\n"); - return 0; + return -1; } - if (length < sizeof(coap_hdr_t)) { - debug("discarded invalid PDU\n"); + unsigned int headerSize = 0; + + if (COAP_UDP == transport) { + headerSize = sizeof(coap_hdr_t); } +#ifdef WITH_WS + else if(COAP_WS == transport) { + headerSize = COAP_WS_HEADER; + } +#endif +#ifdef WITH_TCP + else { + headerSize = coap_get_tcp_header_length_for_transport(transport); + } +#endif -#ifdef WITH_LWIP - /* this verifies that with the classical copy-at-parse-time and lwip's - * zerocopy-into-place approaches, both share the same idea of destination - * addresses */ - LWIP_ASSERT("coap_pdu_parse with unexpected addresses", data == (void*)pdu->hdr); - LWIP_ASSERT("coap_pdu_parse with unexpected length", length == pdu->length); -#else + if (length < headerSize) { + debug("discarded invalid PDU\n"); + } - pdu->hdr->version = data[0] >> 6; - pdu->hdr->type = (data[0] >> 4) & 0x03; - pdu->hdr->token_length = data[0] & 0x0f; - pdu->hdr->code = data[1]; + coap_opt_t *opt = NULL; + unsigned int tokenLength = 0; +#if defined(WITH_TCP) || defined(WITH_WS) + switch (transport) { + case COAP_UDP: + break; +#if defined(WITH_TCP) + case COAP_TCP: + for (size_t i = 0 ; i < headerSize ; i++) { + pdu->transport_hdr->tcp.header_data[i] = data[i]; + } + + tokenLength = data[0] & 0x0f; + opt = (unsigned char *) (&(pdu->transport_hdr->tcp) + 1) + tokenLength; + break; + case COAP_TCP_8BIT: + for (size_t i = 0 ; i < headerSize ; i++) { + pdu->transport_hdr->tcp_8bit.header_data[i] = data[i]; + } + + tokenLength = data[0] & 0x0f; + opt = (unsigned char *) (&(pdu->transport_hdr->tcp_8bit)) + + tokenLength + COAP_TCP_HEADER_8_BIT; + break; + case COAP_TCP_16BIT: + for (size_t i = 0 ; i < headerSize ; i++) { + pdu->transport_hdr->tcp_16bit.header_data[i] = data[i]; + } + + tokenLength = data[0] & 0x0f; + opt = (unsigned char *) (&(pdu->transport_hdr->tcp_16bit) + 1) + tokenLength; + break; + case COAP_TCP_32BIT: + for (size_t i = 0 ; i < headerSize ; i++) { + pdu->transport_hdr->tcp_32bit.header_data[i] = data[i]; + } + + tokenLength = data[0] & 0x0f; + opt = ((unsigned char *) &(pdu->transport_hdr->tcp_32bit)) + + headerSize + tokenLength; + break; #endif - pdu->data = NULL; +#if defined(WITH_WS) + case COAP_WS: + for (size_t i = 0 ; i < headerSize ; i++) { + pdu->transport_hdr->ws.header_data[i] = data[i]; + } + + tokenLength = data[0] & 0x0f; + opt = ((unsigned char *) &(pdu->transport_hdr->ws)) + + headerSize + tokenLength; + break; +#endif + default: + printf("it has wrong type\n"); + } +#endif + pdu->length = (unsigned short)length; + + if (COAP_UDP == transport) { + pdu->transport_hdr->udp.version = data[0] >> 6; + pdu->transport_hdr->udp.type = (data[0] >> 4) & 0x03; + pdu->transport_hdr->udp.token_length = data[0] & 0x0f; + pdu->transport_hdr->udp.code = data[1]; + pdu->data = NULL; + + tokenLength = pdu->transport_hdr->udp.token_length; + + /* sanity checks */ + if (pdu->transport_hdr->udp.code == 0) { + if (length != headerSize || tokenLength) { + debug("coap_pdu_parse2: empty message is not empty\n"); + goto discard; + } + } - /* sanity checks */ - if (pdu->hdr->code == 0) { - if (length != sizeof(coap_hdr_t) || pdu->hdr->token_length) { - debug("coap_pdu_parse: empty message is not empty\n"); + if (length < headerSize + tokenLength || tokenLength > 8) { + debug("coap_pdu_parse2: invalid Token\n"); goto discard; } - } - if (length < sizeof(coap_hdr_t) + pdu->hdr->token_length - || pdu->hdr->token_length > 8) { - debug("coap_pdu_parse: invalid Token\n"); - goto discard; + memcpy(&pdu->transport_hdr->udp.id, data + 2, 2); + + /* Finally calculate beginning of data block and thereby check integrity + * of the PDU structure. */ + + /* append data (including the Token) to pdu structure */ + memcpy(&(pdu->transport_hdr->udp) + 1, data + headerSize, length - headerSize); + + /* skip header + token */ + length -= (tokenLength + headerSize); + opt = (unsigned char *) (&(pdu->transport_hdr->udp) + 1) + tokenLength; } +#if defined(WITH_TCP) || defined(WITH_WS) + else { // common for tcp and websocket header setting + pdu->data = NULL; -#ifndef WITH_LWIP - /* Copy message id in network byte order, so we can easily write the - * response back to the network. */ - memcpy(&pdu->hdr->id, data + 2, 2); + if (length < headerSize + tokenLength || tokenLength > 8) { + debug("coap_pdu_parse2: invalid Token\n"); + goto discard; + } + /* Finally calculate beginning of data block and thereby check integrity + * of the PDU structure. */ + + /* append data (including the Token) to pdu structure */ + memcpy(((unsigned char *) pdu->hdr) + headerSize, + data + headerSize, length - headerSize); + + /* skip header + token */ + length -= (tokenLength + headerSize); + } +#endif /* Append data (including the Token) to pdu structure, if any. */ if (length > sizeof(coap_hdr_t)) { memcpy(pdu->hdr + 1, data + sizeof(coap_hdr_t), length - sizeof(coap_hdr_t)); } - pdu->length = length; /* Finally calculate beginning of data block and thereby check integrity * of the PDU structure. */ -#endif - - /* skip header + token */ - length -= (pdu->hdr->token_length + sizeof(coap_hdr_t)); - opt = (unsigned char *)(pdu->hdr + 1) + pdu->hdr->token_length; while (length && *opt != COAP_PAYLOAD_START) { - if (!next_option_safe(&opt, (size_t *)&length)) { - debug("coap_pdu_parse: drop\n"); + coap_option_t option; + memset(&option, 0, sizeof(coap_option_t)); + if (!next_option_safe(&opt, (size_t *) &length, &option)) { + debug("coap_pdu_parse2: drop\n"); goto discard; } } @@ -421,7 +1077,8 @@ coap_pdu_parse(unsigned char *data, size_t length, coap_pdu_t *pdu) { /* end of packet or start marker */ if (length) { assert(*opt == COAP_PAYLOAD_START); - opt++; length--; + opt++; + length--; if (!length) { debug("coap_pdu_parse: message ending in payload start marker\n"); diff --git a/src/resource.c b/src/resource.c index 2967d4fa7b..005124dbbb 100644 --- a/src/resource.c +++ b/src/resource.c @@ -29,7 +29,7 @@ #endif -#ifdef WITH_POSIX +#if defined(WITH_POSIX) || defined(HAVE_WS2TCPIP_H) #define COAP_MALLOC_TYPE(Type) \ ((coap_##Type##_t *)coap_malloc(sizeof(coap_##Type##_t))) @@ -50,12 +50,12 @@ coap_resources_init() { memb_init(&subscription_storage); } -static inline coap_subscription_t * +COAP_STATIC_INLINE coap_subscription_t * coap_malloc_subscription() { return memb_alloc(&subscription_storage); } -static inline void +COAP_STATIC_INLINE void coap_free_subscription(coap_subscription_t *subscription) { memb_free(&subscription_storage, subscription); } @@ -64,7 +64,9 @@ coap_free_subscription(coap_subscription_t *subscription) { #define COAP_PRINT_STATUS_MAX (~COAP_PRINT_STATUS_MASK) +#ifndef min #define min(a,b) ((a) < (b) ? (a) : (b)) +#endif /* Helper functions for conditional output of character sequences into * a given buffer. The first Offset characters are skipped. @@ -116,7 +118,7 @@ match(const str *text, const str *pattern, int match_prefix, int match_substring while (remaining_length) { size_t token_length; unsigned char *token = next_token; - next_token = memchr(token, ' ', remaining_length); + next_token = (unsigned char *)memchr(token, ' ', remaining_length); if (next_token) { token_length = next_token - token; @@ -282,8 +284,7 @@ coap_print_wellknown(coap_context_t *context, unsigned char *buf, size_t *buflen *buflen = written; output_length = p - buf; - if (output_length > COAP_PRINT_STATUS_MAX) - { + if (output_length > COAP_PRINT_STATUS_MAX) { return COAP_PRINT_STATUS_ERROR; } @@ -525,8 +526,7 @@ coap_print_link(const coap_resource_t *resource, output_length = p - buf; - if (output_length > COAP_PRINT_STATUS_MAX) - { + if (output_length > COAP_PRINT_STATUS_MAX) { return COAP_PRINT_STATUS_ERROR; } @@ -690,8 +690,7 @@ coap_notify_observers(coap_context_t *context, coap_resource_t *r) { if (COAP_INVALID_TID == tid || response->hdr->type != COAP_MESSAGE_CON) coap_delete_pdu(response); - if (COAP_INVALID_TID == tid) - { + if (COAP_INVALID_TID == tid) { debug("coap_check_notify: sending failed, resource stays partially dirty\n"); obs->dirty = 1; r->partiallydirty = 1; diff --git a/src/str.c b/src/str.c index f5800c257b..6f4872f222 100644 --- a/src/str.c +++ b/src/str.c @@ -15,8 +15,8 @@ #include "str.h" str *coap_new_string(size_t size) { - str *s = coap_malloc(sizeof(str) + size + 1); - if ( !s ) { + str *s = (str *)coap_malloc(sizeof(str) + size + 1); + if (!s) { #ifndef NDEBUG coap_log(LOG_CRIT, "coap_new_string: malloc\n"); #endif diff --git a/src/uri.c b/src/uri.c index b111a0eef7..4bb788b686 100644 --- a/src/uri.c +++ b/src/uri.c @@ -37,7 +37,7 @@ * @return A pointer to the first occurence of @p c, or @c NULL * if not found. */ -static inline unsigned char * +COAP_STATIC_INLINE unsigned char * strnchr(unsigned char *s, size_t len, unsigned char c) { while (len && *s++ != c) --len; @@ -317,7 +317,7 @@ typedef void (*segment_handler_t)(unsigned char *, size_t, void *); /** * Checks if path segment @p s consists of one or two dots. */ -static inline int +COAP_STATIC_INLINE int dots(unsigned char *s, size_t len) { return *s == '.' && (len == 1 || (*(s+1) == '.' && len == 2)); } @@ -424,7 +424,7 @@ coap_uri_t * coap_new_uri(const unsigned char *uri, unsigned int length) { unsigned char *result; - result = coap_malloc(length + 1 + sizeof(coap_uri_t)); + result = (unsigned char *)coap_malloc(length + 1 + sizeof(coap_uri_t)); if (!result) return NULL; @@ -484,7 +484,7 @@ coap_clone_uri(const coap_uri_t *uri) { /* The function signature of coap_hash() is different from * segment_handler_t hence we use this wrapper as safe typecast. */ -static inline void +COAP_STATIC_INLINE void hash_segment(unsigned char *s, size_t len, void *data) { assert(len <= UINT_MAX); coap_hash(s, (unsigned int)len, (unsigned char *)data); @@ -501,3 +501,62 @@ coap_hash_path(const unsigned char *path, size_t len, coap_key_t key) { return 1; } + +/* 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, *s; + + if (!pi) + return NULL; + + /* proceed to the next segment */ + pi->n -= pi->segment_length; + pi->pos += pi->segment_length; + pi->segment_length = 0; + s = pi->separator; + + /* 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 (strchr((const char*)s, *(pi->pos))) { + ++pi->pos; + --pi->n; + } + + p = pi->pos; + + while ((pi->segment_length < pi->n) && (!strchr((const char*)s, *p)) + && (!strnchr(pi->delim, pi->dlen, *p))) { + ++p; + ++pi->segment_length; + } + + if (!pi->n) { + pi->pos = NULL; + pi->segment_length = 0; + } + return pi->pos; +} diff --git a/tests/test_options.c b/tests/test_options.c index eb94afb08d..5d8386bce6 100644 --- a/tests/test_options.c +++ b/tests/test_options.c @@ -435,10 +435,16 @@ t_access_option7(void) { #define TEST_MAX_SIZE 1000 +#ifdef _MSC_VER +# define ALIGNED(x) +#else +# define ALIGNED(x) __attribute__ ((aligned (x))) +#endif + static void t_iterate_option1(void) { /* CoAP PDU without token, options, or data */ - uint8_t teststr[] __attribute__ ((aligned (8))) = { + uint8_t teststr[] ALIGNED(8) = { 0x00, 0x00, 0x00, 0x00 }; @@ -463,7 +469,7 @@ t_iterate_option1(void) { static void t_iterate_option2(void) { /* CoAP PDU with token but without options and data */ - uint8_t teststr[] __attribute__ ((aligned (8))) = { + uint8_t teststr[] ALIGNED(8) = { 0x03, 0x00, 0x00, 0x00, 't', 'o', 'k' }; @@ -488,7 +494,7 @@ t_iterate_option2(void) { static void t_iterate_option3(void) { /* CoAP PDU with token and options */ - uint8_t teststr[] __attribute__ ((aligned (8))) = { + uint8_t teststr[] ALIGNED(8) = { 0x03, 0x00, 0x00, 0x00, 't', 'o', 'k', 0x13, 'o', 'p', 't', 0x00, 0xd1, 0x10, 'x' }; @@ -529,7 +535,7 @@ t_iterate_option3(void) { static void t_iterate_option4(void) { /* CoAP PDU with token, options, and data */ - uint8_t teststr[] __attribute__ ((aligned (8))) = { + uint8_t teststr[] ALIGNED(8) = { 0x03, 0x00, 0x00, 0x00, 't', 'o', 'k', 0x13, 'o', 'p', 't', 0x00, 0xd1, 0x10, 'x', 0xff, 'd', 'a', 't', 'a' @@ -571,7 +577,7 @@ t_iterate_option4(void) { static void t_iterate_option5(void) { /* CoAP PDU with malformed option */ - uint8_t teststr[] __attribute__ ((aligned (8))) = { + uint8_t teststr[] ALIGNED(8) = { 0x00, 0x00, 0x00, 0x00, 0x52, 'o', 'p', 0xee, 0x12, 0x03, 0x00 }; @@ -603,7 +609,7 @@ static void t_iterate_option6(void) { /* option filter */ /* CoAP PDU with token, options, and data */ - uint8_t teststr[] __attribute__ ((aligned (8))) = { + uint8_t teststr[] ALIGNED(8) = { 0x00, 0x00, 0x00, 0x00, 0x80, 0x20, 0x00, 0x00, 0xc0, 0x00 }; @@ -647,7 +653,7 @@ t_iterate_option6(void) { static void t_iterate_option7(void) { /* option filter */ - uint8_t teststr[] __attribute__ ((aligned (8))) = { + uint8_t teststr[] ALIGNED(8) = { 0x00, 0x00, 0x00, 0x00, 0x80, 0x20, 0x00, 0x00, 0xc0, 0x00, 0x10, 0x10, 0x00 }; @@ -693,7 +699,7 @@ t_iterate_option7(void) { static void t_iterate_option8(void) { /* option filter */ - uint8_t teststr[] __attribute__ ((aligned (8))) = { + uint8_t teststr[] ALIGNED(8) = { 0x00, 0x00, 0x00, 0x00, 0x80, 0x20, 0x00, 0x00, 0xc0, 0x00, 0x10, 0x10, 0x00 }; @@ -723,7 +729,7 @@ t_iterate_option8(void) { static void t_iterate_option9(void) { /* options filter: option number too large for filter */ - uint8_t teststr[] __attribute__ ((aligned (8))) = { + uint8_t teststr[] ALIGNED(8) = { 0x00, 0x00, 0x00, 0x00, 0x80, 0x20, 0x00, 0x00, 0xc0, 0x00, 0x10, 0x10, 0x00 }; @@ -753,7 +759,7 @@ t_iterate_option9(void) { static void t_iterate_option10(void) { /* options filter: option numbers in PDU exceed filter size */ - uint8_t teststr[] __attribute__ ((aligned (8))) = { + uint8_t teststr[] ALIGNED(8) = { 0x00, 0x00, 0x00, 0x00, 0x80, 0x20, 0x00, 0x00, 0xd0, 0x26, 0xe0, 0x10, 0x00 }; diff --git a/tests/test_uri.c b/tests/test_uri.c index 004cbd6ab8..0f87c51b7d 100644 --- a/tests/test_uri.c +++ b/tests/test_uri.c @@ -316,9 +316,15 @@ t_parse_uri12(void) { } } +#ifdef _MSC_VER +# define ALIGNED(x) +#else +# define ALIGNED(x) __attribute__ ((aligned (x))) +#endif + static void t_parse_uri13(void) { - uint8_t teststr[] __attribute__ ((aligned (8))) = { + uint8_t teststr[] ALIGNED(8) = { 0x00, 0x00, 0x00, 0x00, 0x80, 0x03, 'f', 'o', 'o', 0x3b, '.', 'w', 'e', 'l', 'l', '-', 'k', 'n', 'o', 'w', 'n', 0x04, 'c', 'o', diff --git a/tests/test_wellknown.c b/tests/test_wellknown.c index 57a4db7a41..aa445a1886 100644 --- a/tests/test_wellknown.c +++ b/tests/test_wellknown.c @@ -12,7 +12,9 @@ #include #include +#ifdef HAVE_NETINET_IN_H #include +#endif #include #include #include @@ -139,8 +141,12 @@ t_wellknown3(void) { /* , (TEST_URI_LEN + 4 chars) */ for (j = 0; j < num_resources; j++) { +#ifdef HAVE_SNPRINTF int len = snprintf((char *)uribuf, TEST_URI_LEN + 1, "%0*d", TEST_URI_LEN, j); +#else + int len = sprintf((char *)uribuf, "%0*d", TEST_URI_LEN, j); +#endif r = coap_resource_init(uribuf, len, 0); coap_add_resource(ctx, r); uribuf += TEST_URI_LEN;