Giter Club home page Giter Club logo

libnyoci's Introduction

LibNyoci — A Full-Featured Embedded CoAP Stack

LibNyoci is a highly-configurable CoAP stack which is suitable for a wide range of embedded devices, from bare-metal sensor nodes with kilobytes of RAM to Linux-based devices with megabytes of RAM.

LibNyoci was spun off from the SMCP project in late March of 2017.

Features include:

  • Supports RFC7252
  • Fully asynchronous I/O
  • Supports both BSD sockets and µIP
  • Sending and receiving asynchronous CoAP responses
  • Observing resources and offering observable resources
  • Retransmission of confirmable transactions
  • Experimental support for DTLS

The package also includes nyocictl, a powerful command line tool for browsing and configuring CoAP nodes.

LibNyoci is currently working toward a v1.0 API. Until v1.0 is released, all APIs are subject to change.

Getting Help

If you are having trouble with LibNyoci, you can either file an issue on github or join the official LibNyoci Developers mailing list and ask your question there.

Mailing Lists

Getting, building, and installing via Git

First:

$ git clone git://github.com/darconeous/libnyoci.git
$ cd libnyoci

To just build the latest tagged stable release:

$ git checkout full/latest-release
$ ./configure
$ make
$ sudo make install

For bleeding-edge:

$ git checkout master
$ git archive origin/autoconf/master | tar xvm
  # Next line is a work-around for timestamp problems
$ touch aclocal.m4 && touch configure && touch `find . -name '*.in'`
$ ./configure
$ make
$ sudo make install

Getting, building, and installing from an archive

$ curl https://github.com/darconeous/libnyoci/archive/full/latest-release.zip > latest-release.zip
$ unzip latest-release.zip
$ cd nyoci-latest-release
$ ./configure
$ make
$ sudo make install

Installing via Homebrew on OS X

To get the "latest-release":

$ brew tap darconeous/embedded
$ brew install libnyoci

To get the bleeding-edge release:

$ brew tap darconeous/embedded
$ brew install libnyoci --HEAD

Getting Started

The best way to get started is to have a look at some example code which uses LibNyoci. There are several included examples:

  • examples/example-1.c - Shows how to respond to a request.
  • examples/example-2.c - Shows how to respond to a request for a specific resource.
  • examples/example-3.c - Shows how to use the node router.
  • examples/example-4.c - Shows how to make resources observable.

Additionally, there is the plugtest server and client, which can be found in src/plugtest.

The Contiki version of the plugtest uses the last two files. You can find the Contiki version at contiki-src/examples/nyoci-plugtest/.

Configurability

One of the goals of LibNyoci is to implent a full-featured CoAP library, but most embedded applications don't need all of these capabilities. Because of this, LibNyoci is designed so that you can individually enable or disable features depending on your needs (See src/libnyoci/nyoci-config.h.in).

For example, LibNyoci has the ability to have more than once instance, but embedded platforms will never need more than one. Passing around a reference to a global variable that will never change is wasteful, so when compiled with NYOCI_SINGLETON turned on, we transparently (via some preprocessor magic) ignore the reference to the LibNyoci instance from all of the functions that take it. This makes it easy to use the same codebase for both embedded and non-embedded applications. There are other configuration options for doing things like limiting malloc() usage, avoiding use of printf() (and variants), enabling/disabling observing, etc.

Contiki Support

LibNyoci supports Contiki (albeit a rather old version). To build the Contiki examples, just make sure that the CONTIKI environment variable is set point to your Contiki root, like so:

$ cd contiki-src/examples/nyoci-simple
$ make CONTIKI=~/Projects/contiki TARGET=minimal-net

API Documentation

You can find an online version of the API documentation here: http://darconeous.github.com/libnyoci/doc/html/

nyocictl

nyocictl is a command-line interface for browsing, observing, and interacting with CoAP devices. It is, for the most part, self-documenting: just type in nyocictl help. You can run individual commands directly from the command line when invoking nyocictl or you can invoke with no arguments and you will enter the nyocictl shell (CLI). The shell environment allows you to use familiar unix commands like ls, cd, and cat. The CLI supports quoting and tab-completion of resource names, which is incredibly handy.

Here are a few examples of how you can use it:

GET a resource

$ nyocictl get coap://coap.me/large

Listing the contents of a resource

$ nyocictl ls coap://coap.me/.well-known/core

PUT a resource and show parsed response headers

$ nyocictl put -i coap://coap.me/test "Testing out nyocictl's PUT command"

Observe a resource for changes

$ nyocictl observe coap://vs0.inf.ethz.ch/obs

Plugtests

nyoci-plugtest-server implements some of the ESTI plugtests for CoAP.

List of Public Test Servers

These servers run a subset of the plugtest suite:

  • coap://coap.me/
  • coap://vs0.inf.ethz.ch/

These are other publically-accessable example/test servers:

  • coap://leshan.eclipse.org/coaps://leshan.eclipse.org
  • coap://californium.eclipse.org/coaps://californium.eclipse.org

Authors and Significant Contributors

Special Thanks

libnyoci's People

Contributors

darconeous avatar langchr86 avatar snej avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

libnyoci's Issues

Defining NYOCI_SINGLETON breaks build

NYOCI_SINGLETON doesn't seem to be working; if I define it I get a compile error. There are also a large number (about 50?) of warnings, which harsh my mellow because I'm a zealot about -Werror.

In GCC when building for ESP32 I get one error and a warning:

libnyoci/nyoci-transaction.c: In function 'nyoci_internal_delete_transaction_':
libnyoci/nyoci-transaction.c:122:6: error: the address of 'gNyociInstance' will always evaluate as 'true' [-Werror=address]
  if (!nyoci_get_current_instance()) {
      ^
libnyoci/nyoci-transaction.c: In function 'nyoci_transaction_new_msg_id':
libnyoci/nyoci-transaction.c:198:12: error: 'self' undeclared (first use in this function)
   (void**)&self->transactions,
            ^

In Clang (Xcode) building for macOS, I get the above, and also many warnings of the form:

In file included from libnyoci/nyoci.c:43:
In file included from libnyoci/libnyoci.h:166:
In file included from plat-net/posix/nyoci-plat-net.h:82:
libnyoci/nyoci-plat-net-func.h:128:33: error: this function declaration is not a prototype [-Werror,-Wstrict-prototypes]
NYOCI_API_EXTERN nyoci_status_t nyoci_plat_process(nyoci_t self);
                                ^
libnyoci/nyoci-plat-net-func.h:44:53: note: expanded from macro 'nyoci_plat_process'
#define nyoci_plat_process(self)                nyoci_plat_process()

and a lesser number of corresponding no-prototype warnings like this:

libnyoci/nyoci.c:128:1: error: no previous prototype for function 'nyoci_init' [-Werror,-Wmissing-prototypes]
nyoci_init(nyoci_t self) {
^
In file included from libnyoci/nyoci.c:43:
libnyoci/libnyoci.h:132:27: note: expanded from macro 'nyoci_init'
#define nyoci_init(self)                nyoci_init()

The source of the problem is that a C prototype of a zero-arg function has to have (void), not just (). Silencing or ignoring these warnings would be dangerous because without prototypes there will be no parameter checking of calls to the affected functions!

Support for lwIP

I'd like to use libnyoci on an Espressif ESP32 system (SparkFun's ESP32 Thing.) The software stack for this is the ESP IoT Development Framework, ESP-IDF, which uses lwIP for IP networking. This is apparently a pretty widespread IP stack; Wikipedia says "lwIP is used by many manufacturers of embedded systems. Examples include Altera (in the Nios II operating system), Analog Devices (for the Blackfin DSP chip), Xilinx, Honeywell ... and Freescale."

Most of libnyoci builds fine, but there are a bunch of errors in plat-net/posix/ due to missing functionality in lwIP:

  • poll isn't supported, so nyoci_plat_update_pollfds needs to be #ifdef'd out, but then nyoci_plat_wait and nyoci_plat_process break because they call it.
  • cmsgheader and all its ancillary types and constants aren't defined. This breaks sendtofrom.
  • ipv6_mreq is called ip6_mreq for some reason, and its ipv6mr_interface field is of type in6_addr not int, i.e. it wants the IP address of the interface, not its index in the interface list (which doesn't seem to exist; there is no getifaddrs.)
  • in_pktinfo and in6_pktinfo aren't defined. This breaks the pktinfo field of nyoci_plat_s (which seems to be unused. It's written to in nyoci_plat_process, but never read.)
  • IN6_IS_ADDR_V4MAPPED is not defined. I worked around this by copying in the definition from macOS's <in6.h>.
  • Some supported APIs are in different headers; for example there is no <sys/select.h> for some reason, instead select is in <lwip/sockets.h>.

The first issue is the hardest one for me, since I don't know much about either poll or select. Looks like I need to reimplement nyoci_plat_wait and nyoci_plat_process using nyoci_plat_update_fdsets. Probably easy, but I'd appreciate any clues.

The second and third issues seem to be related to multihoming; not a problem for my use case because my board only has a single (WiFi) interface.

The other issues I think I've fixed locally.

Port may be garbage, in nyoci_outbound_set_uri()

Clang's static analyzer reports that the function nyoci_outbound_set_uri() uses an uninitialized variable toport in some call paths; this seems a legit bug and not a false positive.

src/libnyoci/nyoci-outbound.c:501:9: warning: 2nd function call argument is an uninitialized value
                ret = nyoci_plat_set_remote_hostname_and_port(
                      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This occurs when components.host is non-null but components.protocol and components.port are null. The flow of control bypasses the three lines where toport is assigned but falls through to a line where it's passed to nyoci_plat_set_remote_hostname_and_port().

Xcode doesn't produce a textual version of the detailed flow-control analysis, but here's a screenshot:

screen shot 2018-05-16 at 10 23 04 am

nyoci_plat_wait() does not exit if a timer is started

I'm using nyoci in a multithreaded environment. I'm careful to wrap a mutex around all calls into nyoci to guarantee no re-entrant use, but I'm having trouble with a limitation of the event-loop/timer implementation.

  • I've got one thread that's running the event loop, i.e. repeatedly calling nyoci_plat_wait and nyoci_plat_process. To avoid consuming unnecessary CPU with polling, I pass a very long timeout into nyoci_plat_wait.
  • This thread has called poll with a long timeout, because there are no timers waking up soon.
  • Meanwhile, another thread does something like starting a transaction, which creates a new timer that will fire in (say) 10ms.
  • Unfortunately the event loop thread stays blocked in the poll system call, so the new timer doesn't fire. The system basically locks up until either an incoming packet arrives, or the very long timeout expires.

It looks like starting or rescheduling a timer needs to abort any current poll system call. I'm no expert on POSIX I/O, but I know how to search StackOverflow; the answer seems to be to create a pipe and add its output file descriptor to the set being polled; then you can write to the pipe and the poll call will exit. (The alternative is to raise a signal, but apparently this is subject to race conditions.)

(Obviously the solution will be platform-dependent since not every platform uses the POSIX poll or select calls. But it happens that I'm only using the POSIX code so far.)

buffer overrun in nyoci_outbound_set_uri()

The uri_copy string, and the components that point into it, can get corrupted in nyoci_outbound_set_uri() when HAVE_ALLOCA is false and NYOCI_AVOID_MALLOC is true. In this configuration the function steals space from the packet buffer to copy the uri into, but it misjudges how much of the buffer will be used by options:

  • Line 481 of nyoci-outbound.c may add the entire uri string as option COAP_OPTION_PROXY_URI (not in my situation, though)
  • Line 489 adds the hostname as COAP_OPTION_URI_HOST
  • Line 494 adds the port number as COAP_OPTION_URI_PORT

I'm working with a case where the uri string is coap://192.168.1.64:12345/db. By the time components.host is accessed on line 502, uri_copy has been partially overwritten and components.host is 9?2.168.1.64. This means the hostname looked up is wrong and the request fails.

Preallocated observer_table can be limiting or wasteful

The static observer_table preallocates ~1KB of RAM times NYOCI_MAX_OBSERVERS. So the client has to make a compile-time choice between supporting only a few observers — in embedded mode it defaults to 2 — or using a nontrivial amount of RAM for an embedded system. (My program will need to support quite a few observers, though the number will generally be small, so I left the max at 64; then I did some code-size profiling and saw that nyoci_observable.o is the single largest contributor to memory usage, at 77KB of .bss.)

This could be made dynamic by heap-allocating each nyoci_observer_s and making the table a heap-allocated growable array of pointers to the observers.

nyoci_plat_wait() returns wrong value when timer fires

The header doc for nyoci_plat_wait() says "it returns 0 if nyoci_plat_process() should be executed". The POSIX implementation doesn't quite agree with this: it does return 0 if a file descriptor has input, but it returns NYOCI_STATUS_TIMEOUT if a timer is ready to fire. Thus, a loop like this one won't work because it never handles timers until a packet arrives:

            while (true) {
                if (nyoci_plat_wait(_nyoci, MSEC_PER_SEC) == 0)
                    nyoci_plat_process(_nyoci);
            }

This happens because the function doesn't distinguish whether it's the timeout given by the caller that's elapsed, or the timeout till the next timer. In the latter case it should return 0.

Alternatively, the docs could be amended to match the implementation, by saying that if the function returns NYOCI_STATUS_TIMEOUT, then nyoci_handle_timers() should be called.

Frequent warning "Requirement Failed (self->timers != timer)"

I'm seeing this warning logged frequently, in debug builds:

libnyoci/src/libnyoci/nyoci-timer.c:117: Requirement Failed (self->timers != timer)

Here's the backtrace; this is happening on a background thread that's simply calling nyoci_plat_wait and nyoci_plat_process in a loop:

  * frame #0:`nyoci_schedule_timer(self=0x0000000103001e00, timer=0x00000001007019c0, cms=2147483647) at nyoci-timer.c:118
    frame #1:`nyoci_handle_response at nyoci-transaction.c:877
    frame #2:`nyoci_inbound_packet_process(self=0x0000000103001e00, buffer="bEXRURC5a2 a*\xb1%\377, packet_length=434, flags=0) at nyoci-inbound.c:433
    frame #3:`nyoci_plat_process(self=0x0000000103001e00) at nyoci-plat-net.c:925
    ...

Enumerated List of Configurable Features

It looks like the documentation says that individual features can be enabled/disabled, and there is a reference to src/libnyoci/nyoci-config.h.in. However, it looks like the only features that I can set (by passing to configure) are --enable-embedded and --enable-singleton.

Is there a list of features that can be set through the configure script or is it only these two?

Thanks!

There is a logical defect that causes a denial of service vulnerability

src/libnyoci/coap.c lines 58-116:

len = (*buffer & 0x0F);

switch((*buffer >> 4)) {
	default:
		if(key) *key += (*buffer >> 4);
		buffer += 1;
		break;

	case 13:
		buffer += 1;
		if(key)*key += 13+*buffer;
		buffer += 1;
		break;

	case 14:
		buffer += 1;
		if(key)*key += 269+buffer[1]+(buffer[0]<<8);

		buffer += 2;
		break;

	case 15:
		// End of option marker...?
		// TODO: Fail harder if len doesn't equal 15 as well!
		if (key) *key = COAP_OPTION_INVALID;
		if (value) *value = NULL;
		if (lenP) *lenP = 0;
		return NULL;
		break;
}

switch(len) {
	default:
		break;

	case 13:
		len = 13 + *buffer;
		buffer += 1;
		break;

	case 14:
		len = 269+buffer[1]+(coap_size_t)(buffer[0]<<8);
		buffer += 2;
		break;

	case 15:
		// End of option marker...?
		// TODO: Fail harder if len doesn't equal 15 as well!
		if(key)*key = COAP_OPTION_INVALID;
		if(value)*value = NULL;
		if(lenP)*lenP = 0;
		return NULL;
		break;
}

if(lenP) *lenP = len;
if(value) *value = buffer;

return (uint8_t*)buffer + len;

If the data packet is processed as shown below
image
Then the function coap_decode_option will set the length parameter to 0,and the value_len in the function nyoci_inbound_option_strequal in src/libnyoci/nyoci-inbound.c is 0(lines 157-168)

	coap_decode_option(self->inbound.this_option, &curr_key, (const uint8_t**)&value, &value_len);

	if (curr_key != key) {
		return false;
	}

	for (i = 0; i < value_len; i++) {
		if(!cstr[i] || (value[i] != cstr[i])) {
			return false;
		}
	}
	return cstr[i]==0;
}

If value_len is 0, the subsequent loop is not executed (line 163), and if the second argument cstr is an empty string, the return value is true (normal logic will return false in the loop)
The function nyoci_node_list_request_handler in src/libnyociextra/nyoci-list.c for handling requests calls nyoci_inbound_option_strequal_const, and the second argument passed in is an empty string(lines 85-89).

	if (nyoci_inbound_option_strequal_const(COAP_OPTION_URI_PATH,"")) {
		// Eat the trailing '/'.
		nyoci_inbound_next_option(NULL, NULL);
		if(prefix[0]) prefix = NULL;
	}

Therefore, the special data packet will pass the judgment, the program will enter the assignment to the variable prefix (the variable prefix is ​​empty at this time), and the program eventually crashes.

Listening on port 5683
Segmentation fault

nyoci_outbound_append_content_formatted sets incorrect length

In nyoci_outbound_append_content_formatted(), the variable fmtlen is updated but unused:

	fmtlen += self->outbound.content_len;
	ret = nyoci_outbound_set_content_len((coap_size_t)len);
bail:
	va_end(args);
	return ret;
}
/Couchbase/Embedded/libnyoci/src/libnyoci/nyoci-outbound.c:647:2: warning: Value stored to 'fmtlen' is never read
        fmtlen += self->outbound.content_len;
        ^         ~~~~~~~~~~~~~~~~~~~~~~~~~~

I was going to ignore this compiler warning, but then I looked at the code, and it seems like the parameter to nyoci_outbound_set_content_len() should be fmtlen, not len -- the latter is just the space remaining in the packet, while fmtlen is the previous content_len plus the number of bytes written by vsnprintf.

Reading the incoming data in LWIP

Hello Sir/Madam,

I am using the atmel software package for reference.

In that in httpd.c file their is no function is written for following function,

httpd_post_finished
httpd_post_begin
httpd_post_receive_data

Memory leak after DTLS request

I'm using libnyoci in a library for Lua (to make coap available in Lua). When a lot of requests are made with DTLS activated, memory is getting lost.

It looks like somehow the tls data is not cleaned up. The data is received using the same loop that is in the cmd_get code of nyocictl:

 while (ERRORCODE_INPROGRESS == gRet) {
   if (nyoci) {
     nyoci_plat_wait(nyoci, 1000);
     nyoci_plat_process(nyoci);
   }
 }

After all response data from the GET request is received, I do call SSL_CTX_free on the SSL context and eventually nyoci_release. This however does not seem to cause the internal TLS functions SSL_shutdown or SSL_free to be called.

I reproduced this with nyocictl. A valgrind trace with a GET request shows that the memory allocated using SSL_new and BIO_new in openssl/nyoci-plat-tls.c are never free'd.

How can I make sure that all resources related to a specific DTLS ssl session are released after use?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.