diff options
Diffstat (limited to 'thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps')
43 files changed, 19492 insertions, 0 deletions
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fs.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fs.c new file mode 100644 index 0000000..35b5e31 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fs.c @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels <adam@sics.se> + * + */ + +#include "lwip/apps/httpd_opts.h" +#include "lwip/def.h" +#include "lwip/apps/fs.h" +#include "fsdata.h" +#include <string.h> + + +#if HTTPD_USE_CUSTOM_FSDATA +#include "fsdata_custom.c" +#else /* HTTPD_USE_CUSTOM_FSDATA */ +#include "fsdata.c" +#endif /* HTTPD_USE_CUSTOM_FSDATA */ + +/*-----------------------------------------------------------------------------------*/ + +#if LWIP_HTTPD_CUSTOM_FILES +int fs_open_custom(struct fs_file *file, const char *name); +void fs_close_custom(struct fs_file *file); +#if LWIP_HTTPD_FS_ASYNC_READ +u8_t fs_canread_custom(struct fs_file *file); +u8_t fs_wait_read_custom(struct fs_file *file, fs_wait_cb callback_fn, void *callback_arg); +int fs_read_async_custom(struct fs_file *file, char *buffer, int count, fs_wait_cb callback_fn, void *callback_arg); +#else /* LWIP_HTTPD_FS_ASYNC_READ */ +int fs_read_custom(struct fs_file *file, char *buffer, int count); +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ +#endif /* LWIP_HTTPD_CUSTOM_FILES */ + +/*-----------------------------------------------------------------------------------*/ +err_t +fs_open(struct fs_file *file, const char *name) +{ + const struct fsdata_file *f; + + if ((file == NULL) || (name == NULL)) { + return ERR_ARG; + } + +#if LWIP_HTTPD_CUSTOM_FILES + if (fs_open_custom(file, name)) { + file->is_custom_file = 1; + return ERR_OK; + } + file->is_custom_file = 0; +#endif /* LWIP_HTTPD_CUSTOM_FILES */ + + for (f = FS_ROOT; f != NULL; f = f->next) { + if (!strcmp(name, (const char *)f->name)) { + file->data = (const char *)f->data; + file->len = f->len; + file->index = f->len; + file->pextension = NULL; + file->flags = f->flags; +#if HTTPD_PRECALCULATED_CHECKSUM + file->chksum_count = f->chksum_count; + file->chksum = f->chksum; +#endif /* HTTPD_PRECALCULATED_CHECKSUM */ +#if LWIP_HTTPD_FILE_STATE + file->state = fs_state_init(file, name); +#endif /* #if LWIP_HTTPD_FILE_STATE */ + return ERR_OK; + } + } + /* file not found */ + return ERR_VAL; +} + +/*-----------------------------------------------------------------------------------*/ +void +fs_close(struct fs_file *file) +{ +#if LWIP_HTTPD_CUSTOM_FILES + if (file->is_custom_file) { + fs_close_custom(file); + } +#endif /* LWIP_HTTPD_CUSTOM_FILES */ +#if LWIP_HTTPD_FILE_STATE + fs_state_free(file, file->state); +#endif /* #if LWIP_HTTPD_FILE_STATE */ + LWIP_UNUSED_ARG(file); +} +/*-----------------------------------------------------------------------------------*/ +#if LWIP_HTTPD_DYNAMIC_FILE_READ +#if LWIP_HTTPD_FS_ASYNC_READ +int +fs_read_async(struct fs_file *file, char *buffer, int count, fs_wait_cb callback_fn, void *callback_arg) +#else /* LWIP_HTTPD_FS_ASYNC_READ */ +int +fs_read(struct fs_file *file, char *buffer, int count) +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ +{ + int read; + if(file->index == file->len) { + return FS_READ_EOF; + } +#if LWIP_HTTPD_FS_ASYNC_READ + LWIP_UNUSED_ARG(callback_fn); + LWIP_UNUSED_ARG(callback_arg); +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ +#if LWIP_HTTPD_CUSTOM_FILES + if (file->is_custom_file) { +#if LWIP_HTTPD_FS_ASYNC_READ + return fs_read_async_custom(file, buffer, count, callback_fn, callback_arg); +#else /* LWIP_HTTPD_FS_ASYNC_READ */ + return fs_read_custom(file, buffer, count); +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ + } +#endif /* LWIP_HTTPD_CUSTOM_FILES */ + + read = file->len - file->index; + if(read > count) { + read = count; + } + + MEMCPY(buffer, (file->data + file->index), read); + file->index += read; + + return(read); +} +#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */ +/*-----------------------------------------------------------------------------------*/ +#if LWIP_HTTPD_FS_ASYNC_READ +int +fs_is_file_ready(struct fs_file *file, fs_wait_cb callback_fn, void *callback_arg) +{ + if (file != NULL) { +#if LWIP_HTTPD_FS_ASYNC_READ +#if LWIP_HTTPD_CUSTOM_FILES + if (!fs_canread_custom(file)) { + if (fs_wait_read_custom(file, callback_fn, callback_arg)) { + return 0; + } + } +#else /* LWIP_HTTPD_CUSTOM_FILES */ + LWIP_UNUSED_ARG(callback_fn); + LWIP_UNUSED_ARG(callback_arg); +#endif /* LWIP_HTTPD_CUSTOM_FILES */ +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ + } + return 1; +} +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ +/*-----------------------------------------------------------------------------------*/ +int +fs_bytes_left(struct fs_file *file) +{ + return file->len - file->index; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fs/404.html b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fs/404.html new file mode 100644 index 0000000..40b343a --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fs/404.html @@ -0,0 +1,21 @@ +<html> +<head><title>lwIP - A Lightweight TCP/IP Stack</title></head> +<body bgcolor="white" text="black"> + + <table width="100%"> + <tr valign="top"><td width="80"> + <a href="http://www.sics.se/"><img src="/img/sics.gif" + border="0" alt="SICS logo" title="SICS logo"></a> + </td><td width="500"> + <h1>lwIP - A Lightweight TCP/IP Stack</h1> + <h2>404 - Page not found</h2> + <p> + Sorry, the page you are requesting was not found on this + server. + </p> + </td><td> + + </td></tr> + </table> +</body> +</html> diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fs/img/sics.gif b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fs/img/sics.gif Binary files differnew file mode 100644 index 0000000..0a4fc7b --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fs/img/sics.gif diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fs/index.html b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fs/index.html new file mode 100644 index 0000000..ab575ef --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fs/index.html @@ -0,0 +1,47 @@ +<html> +<head><title>lwIP - A Lightweight TCP/IP Stack</title></head> +<body bgcolor="white" text="black"> + + <table width="100%"> + <tr valign="top"><td width="80"> + <a href="http://www.sics.se/"><img src="/img/sics.gif" + border="0" alt="SICS logo" title="SICS logo"></a> + </td><td width="500"> + <h1>lwIP - A Lightweight TCP/IP Stack</h1> + <p> + The web page you are watching was served by a simple web + server running on top of the lightweight TCP/IP stack <a + href="http://www.sics.se/~adam/lwip/">lwIP</a>. + </p> + <p> + lwIP is an open source implementation of the TCP/IP + protocol suite that was originally written by <a + href="http://www.sics.se/~adam/lwip/">Adam Dunkels + of the Swedish Institute of Computer Science</a> but now is + being actively developed by a team of developers + distributed world-wide. Since it's release, lwIP has + spurred a lot of interest and has been ported to several + platforms and operating systems. lwIP can be used either + with or without an underlying OS. + </p> + <p> + The focus of the lwIP TCP/IP implementation is to reduce + the RAM usage while still having a full scale TCP. This + makes lwIP suitable for use in embedded systems with tens + of kilobytes of free RAM and room for around 40 kilobytes + of code ROM. + </p> + <p> + More information about lwIP can be found at the lwIP + homepage at <a + href="http://savannah.nongnu.org/projects/lwip/">http://savannah.nongnu.org/projects/lwip/</a> + or at the lwIP wiki at <a + href="http://lwip.wikia.com/">http://lwip.wikia.com/</a>. + </p> + </td><td> + + </td></tr> + </table> +</body> +</html> + diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fsdata.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fsdata.c new file mode 100644 index 0000000..6170ce6 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fsdata.c @@ -0,0 +1,298 @@ +#include "lwip/apps/fs.h" +#include "lwip/def.h" +#include "fsdata.h" + + +#define file_NULL (struct fsdata_file *) NULL + + +static const unsigned int dummy_align__img_sics_gif = 0; +static const unsigned char data__img_sics_gif[] = { +/* /img/sics.gif (14 chars) */ +0x2f,0x69,0x6d,0x67,0x2f,0x73,0x69,0x63,0x73,0x2e,0x67,0x69,0x66,0x00,0x00,0x00, + +/* HTTP header */ +/* "HTTP/1.0 200 OK +" (17 bytes) */ +0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x30,0x20,0x32,0x30,0x30,0x20,0x4f,0x4b,0x0d, +0x0a, +/* "Server: lwIP/1.3.1 (http://savannah.nongnu.org/projects/lwip) +" (63 bytes) */ +0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x6c,0x77,0x49,0x50,0x2f,0x31,0x2e,0x33, +0x2e,0x31,0x20,0x28,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x61,0x76,0x61,0x6e, +0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,0x6e,0x75,0x2e,0x6f,0x72,0x67,0x2f,0x70, +0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,0x6c,0x77,0x69,0x70,0x29,0x0d,0x0a, +/* "Content-type: image/gif + +" (27 bytes) */ +0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x74,0x79,0x70,0x65,0x3a,0x20,0x69,0x6d, +0x61,0x67,0x65,0x2f,0x67,0x69,0x66,0x0d,0x0a,0x0d,0x0a, +/* raw file data (724 bytes) */ +0x47,0x49,0x46,0x38,0x39,0x61,0x46,0x00,0x22,0x00,0xa5,0x00,0x00,0xd9,0x2b,0x39, +0x6a,0x6a,0x6a,0xbf,0xbf,0xbf,0x93,0x93,0x93,0x0f,0x0f,0x0f,0xb0,0xb0,0xb0,0xa6, +0xa6,0xa6,0x80,0x80,0x80,0x76,0x76,0x76,0x1e,0x1e,0x1e,0x9d,0x9d,0x9d,0x2e,0x2e, +0x2e,0x49,0x49,0x49,0x54,0x54,0x54,0x8a,0x8a,0x8a,0x60,0x60,0x60,0xc6,0xa6,0x99, +0xbd,0xb5,0xb2,0xc2,0xab,0xa1,0xd9,0x41,0x40,0xd5,0x67,0x55,0xc0,0xb0,0xaa,0xd5, +0x5e,0x4e,0xd6,0x50,0x45,0xcc,0x93,0x7d,0xc8,0xa1,0x90,0xce,0x8b,0x76,0xd2,0x7b, +0x65,0xd1,0x84,0x6d,0xc9,0x99,0x86,0x3a,0x3a,0x3a,0x00,0x00,0x00,0xb8,0xb8,0xb8, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x2c,0x00,0x00, +0x00,0x00,0x46,0x00,0x22,0x00,0x00,0x06,0xfe,0x40,0x90,0x70,0x48,0x2c,0x1a,0x8f, +0xc8,0xa4,0x72,0xc9,0x6c,0x3a,0x9f,0xd0,0xa8,0x74,0x4a,0xad,0x5a,0xaf,0xd8,0xac, +0x76,0xa9,0x40,0x04,0xbe,0x83,0xe2,0x60,0x3c,0x50,0x20,0x0d,0x8e,0x6f,0x00,0x31, +0x28,0x1c,0x0d,0x07,0xb5,0xc3,0x60,0x75,0x24,0x3e,0xf8,0xfc,0x87,0x11,0x06,0xe9, +0x3d,0x46,0x07,0x0b,0x7a,0x7a,0x7c,0x43,0x06,0x1e,0x84,0x78,0x0b,0x07,0x6e,0x51, +0x01,0x8a,0x84,0x08,0x7e,0x79,0x80,0x87,0x89,0x91,0x7a,0x93,0x0a,0x04,0x99,0x78, +0x96,0x4f,0x03,0x9e,0x79,0x01,0x94,0x9f,0x43,0x9c,0xa3,0xa4,0x05,0x77,0xa3,0xa0, +0x4e,0x98,0x79,0x0b,0x1e,0x83,0xa4,0xa6,0x1f,0x96,0x05,0x9d,0xaa,0x78,0x01,0x07, +0x84,0x04,0x1e,0x1e,0xbb,0xb8,0x51,0x84,0x0e,0x43,0x05,0x07,0x77,0xa5,0x7f,0x42, +0xb1,0xb2,0x01,0x63,0x08,0x0d,0xbb,0x01,0x0c,0x7a,0x0d,0x44,0x0e,0xd8,0xaf,0x4c, +0x05,0x7a,0x04,0x47,0x07,0x07,0xb7,0x80,0xa2,0xe1,0x7d,0x44,0x05,0x01,0x04,0x01, +0xd0,0xea,0x87,0x93,0x4f,0xe0,0x9a,0x49,0xce,0xd8,0x79,0x04,0x66,0x20,0x15,0x10, +0x10,0x11,0x92,0x29,0x80,0xb6,0xc0,0x91,0x15,0x45,0x1e,0x90,0x19,0x71,0x46,0xa8, +0x5c,0x04,0x0e,0x00,0x22,0x4e,0xe8,0x40,0x24,0x9f,0x3e,0x04,0x06,0xa7,0x58,0xd4, +0x93,0xa0,0x1c,0x91,0x3f,0xe8,0xf0,0x88,0x03,0xb1,0x21,0xa2,0x49,0x00,0x19,0x86, +0xfc,0x52,0x44,0xe0,0x01,0x9d,0x29,0x21,0x15,0x25,0x50,0xf7,0x67,0x25,0x1e,0x06, +0xfd,0x4e,0x9a,0xb4,0x90,0xac,0x15,0xfa,0xcb,0x52,0x53,0x1e,0x8c,0xf2,0xf8,0x07, +0x92,0x2d,0x08,0x3a,0x4d,0x12,0x49,0x95,0x49,0xdb,0x14,0x04,0xc4,0x14,0x85,0x29, +0xaa,0xe7,0x01,0x08,0xa4,0x49,0x01,0x14,0x51,0xe0,0x53,0x91,0xd5,0x29,0x06,0x1a, +0x64,0x02,0xf4,0xc7,0x81,0x9e,0x05,0x20,0x22,0x64,0xa5,0x30,0xae,0xab,0x9e,0x97, +0x53,0xd8,0xb9,0xfd,0x50,0xef,0x93,0x02,0x42,0x74,0x34,0xe8,0x9c,0x20,0x21,0xc9, +0x01,0x68,0x78,0xe6,0x55,0x29,0x20,0x56,0x4f,0x4c,0x40,0x51,0x71,0x82,0xc0,0x70, +0x21,0x22,0x85,0xbe,0x4b,0x1c,0x44,0x05,0xea,0xa4,0x01,0xbf,0x22,0xb5,0xf0,0x1c, +0x06,0x51,0x38,0x8f,0xe0,0x22,0xec,0x18,0xac,0x39,0x22,0xd4,0xd6,0x93,0x44,0x01, +0x32,0x82,0xc8,0xfc,0x61,0xb3,0x01,0x45,0x0c,0x2e,0x83,0x30,0xd0,0x0e,0x17,0x24, +0x0f,0x70,0x85,0x94,0xee,0x05,0x05,0x53,0x4b,0x32,0x1b,0x3f,0x98,0xd3,0x1d,0x29, +0x81,0xb0,0xae,0x1e,0x8c,0x7e,0x68,0xe0,0x60,0x5a,0x54,0x8f,0xb0,0x78,0x69,0x73, +0x06,0xa2,0x00,0x6b,0x57,0xca,0x3d,0x11,0x50,0xbd,0x04,0x30,0x4b,0x3a,0xd4,0xab, +0x5f,0x1f,0x9b,0x3d,0x13,0x74,0x27,0x88,0x3c,0x25,0xe0,0x17,0xbe,0x7a,0x79,0x45, +0x0d,0x0c,0xb0,0x8b,0xda,0x90,0xca,0x80,0x06,0x5d,0x17,0x60,0x1c,0x22,0x4c,0xd8, +0x57,0x22,0x06,0x20,0x00,0x98,0x07,0x08,0xe4,0x56,0x80,0x80,0x1c,0xc5,0xb7,0xc5, +0x82,0x0c,0x36,0xe8,0xe0,0x83,0x10,0x46,0x28,0xe1,0x84,0x14,0x56,0x68,0xa1,0x10, +0x41,0x00,0x00,0x3b,}; + +static const unsigned int dummy_align__404_html = 1; +static const unsigned char data__404_html[] = { +/* /404.html (10 chars) */ +0x2f,0x34,0x30,0x34,0x2e,0x68,0x74,0x6d,0x6c,0x00,0x00,0x00, + +/* HTTP header */ +/* "HTTP/1.0 404 File not found +" (29 bytes) */ +0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x30,0x20,0x34,0x30,0x34,0x20,0x46,0x69,0x6c, +0x65,0x20,0x6e,0x6f,0x74,0x20,0x66,0x6f,0x75,0x6e,0x64,0x0d,0x0a, +/* "Server: lwIP/1.3.1 (http://savannah.nongnu.org/projects/lwip) +" (63 bytes) */ +0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x6c,0x77,0x49,0x50,0x2f,0x31,0x2e,0x33, +0x2e,0x31,0x20,0x28,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x61,0x76,0x61,0x6e, +0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,0x6e,0x75,0x2e,0x6f,0x72,0x67,0x2f,0x70, +0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,0x6c,0x77,0x69,0x70,0x29,0x0d,0x0a, +/* "Content-type: text/html + +" (27 bytes) */ +0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x74,0x79,0x70,0x65,0x3a,0x20,0x74,0x65, +0x78,0x74,0x2f,0x68,0x74,0x6d,0x6c,0x0d,0x0a,0x0d,0x0a, +/* raw file data (565 bytes) */ +0x3c,0x68,0x74,0x6d,0x6c,0x3e,0x0d,0x0a,0x3c,0x68,0x65,0x61,0x64,0x3e,0x3c,0x74, +0x69,0x74,0x6c,0x65,0x3e,0x6c,0x77,0x49,0x50,0x20,0x2d,0x20,0x41,0x20,0x4c,0x69, +0x67,0x68,0x74,0x77,0x65,0x69,0x67,0x68,0x74,0x20,0x54,0x43,0x50,0x2f,0x49,0x50, +0x20,0x53,0x74,0x61,0x63,0x6b,0x3c,0x2f,0x74,0x69,0x74,0x6c,0x65,0x3e,0x3c,0x2f, +0x68,0x65,0x61,0x64,0x3e,0x0d,0x0a,0x3c,0x62,0x6f,0x64,0x79,0x20,0x62,0x67,0x63, +0x6f,0x6c,0x6f,0x72,0x3d,0x22,0x77,0x68,0x69,0x74,0x65,0x22,0x20,0x74,0x65,0x78, +0x74,0x3d,0x22,0x62,0x6c,0x61,0x63,0x6b,0x22,0x3e,0x0d,0x0a,0x0d,0x0a,0x20,0x20, +0x20,0x20,0x3c,0x74,0x61,0x62,0x6c,0x65,0x20,0x77,0x69,0x64,0x74,0x68,0x3d,0x22, +0x31,0x30,0x30,0x25,0x22,0x3e,0x0d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x74, +0x72,0x20,0x76,0x61,0x6c,0x69,0x67,0x6e,0x3d,0x22,0x74,0x6f,0x70,0x22,0x3e,0x3c, +0x74,0x64,0x20,0x77,0x69,0x64,0x74,0x68,0x3d,0x22,0x38,0x30,0x22,0x3e,0x09,0x20, +0x20,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x61,0x20,0x68,0x72,0x65,0x66,0x3d,0x22,0x68, +0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x73,0x69,0x63,0x73,0x2e,0x73, +0x65,0x2f,0x22,0x3e,0x3c,0x69,0x6d,0x67,0x20,0x73,0x72,0x63,0x3d,0x22,0x2f,0x69, +0x6d,0x67,0x2f,0x73,0x69,0x63,0x73,0x2e,0x67,0x69,0x66,0x22,0x0d,0x0a,0x09,0x20, +0x20,0x62,0x6f,0x72,0x64,0x65,0x72,0x3d,0x22,0x30,0x22,0x20,0x61,0x6c,0x74,0x3d, +0x22,0x53,0x49,0x43,0x53,0x20,0x6c,0x6f,0x67,0x6f,0x22,0x20,0x74,0x69,0x74,0x6c, +0x65,0x3d,0x22,0x53,0x49,0x43,0x53,0x20,0x6c,0x6f,0x67,0x6f,0x22,0x3e,0x3c,0x2f, +0x61,0x3e,0x0d,0x0a,0x09,0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x74,0x64,0x20,0x77,0x69, +0x64,0x74,0x68,0x3d,0x22,0x35,0x30,0x30,0x22,0x3e,0x09,0x20,0x20,0x0d,0x0a,0x09, +0x20,0x20,0x3c,0x68,0x31,0x3e,0x6c,0x77,0x49,0x50,0x20,0x2d,0x20,0x41,0x20,0x4c, +0x69,0x67,0x68,0x74,0x77,0x65,0x69,0x67,0x68,0x74,0x20,0x54,0x43,0x50,0x2f,0x49, +0x50,0x20,0x53,0x74,0x61,0x63,0x6b,0x3c,0x2f,0x68,0x31,0x3e,0x0d,0x0a,0x09,0x20, +0x20,0x3c,0x68,0x32,0x3e,0x34,0x30,0x34,0x20,0x2d,0x20,0x50,0x61,0x67,0x65,0x20, +0x6e,0x6f,0x74,0x20,0x66,0x6f,0x75,0x6e,0x64,0x3c,0x2f,0x68,0x32,0x3e,0x0d,0x0a, +0x09,0x20,0x20,0x3c,0x70,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x53,0x6f,0x72, +0x72,0x79,0x2c,0x20,0x74,0x68,0x65,0x20,0x70,0x61,0x67,0x65,0x20,0x79,0x6f,0x75, +0x20,0x61,0x72,0x65,0x20,0x72,0x65,0x71,0x75,0x65,0x73,0x74,0x69,0x6e,0x67,0x20, +0x77,0x61,0x73,0x20,0x6e,0x6f,0x74,0x20,0x66,0x6f,0x75,0x6e,0x64,0x20,0x6f,0x6e, +0x20,0x74,0x68,0x69,0x73,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x73,0x65,0x72,0x76, +0x65,0x72,0x2e,0x20,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x2f,0x70,0x3e,0x0d,0x0a,0x09, +0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x74,0x64,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x26,0x6e, +0x62,0x73,0x70,0x3b,0x0d,0x0a,0x09,0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x2f,0x74,0x72, +0x3e,0x0d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x2f,0x74,0x61,0x62,0x6c,0x65, +0x3e,0x0d,0x0a,0x3c,0x2f,0x62,0x6f,0x64,0x79,0x3e,0x0d,0x0a,0x3c,0x2f,0x68,0x74, +0x6d,0x6c,0x3e,0x0d,0x0a,}; + +static const unsigned int dummy_align__index_html = 2; +static const unsigned char data__index_html[] = { +/* /index.html (12 chars) */ +0x2f,0x69,0x6e,0x64,0x65,0x78,0x2e,0x68,0x74,0x6d,0x6c,0x00, + +/* HTTP header */ +/* "HTTP/1.0 200 OK +" (17 bytes) */ +0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x30,0x20,0x32,0x30,0x30,0x20,0x4f,0x4b,0x0d, +0x0a, +/* "Server: lwIP/1.3.1 (http://savannah.nongnu.org/projects/lwip) +" (63 bytes) */ +0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x6c,0x77,0x49,0x50,0x2f,0x31,0x2e,0x33, +0x2e,0x31,0x20,0x28,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x61,0x76,0x61,0x6e, +0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,0x6e,0x75,0x2e,0x6f,0x72,0x67,0x2f,0x70, +0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,0x6c,0x77,0x69,0x70,0x29,0x0d,0x0a, +/* "Content-type: text/html + +" (27 bytes) */ +0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x74,0x79,0x70,0x65,0x3a,0x20,0x74,0x65, +0x78,0x74,0x2f,0x68,0x74,0x6d,0x6c,0x0d,0x0a,0x0d,0x0a, +/* raw file data (1751 bytes) */ +0x3c,0x68,0x74,0x6d,0x6c,0x3e,0x0d,0x0a,0x3c,0x68,0x65,0x61,0x64,0x3e,0x3c,0x74, +0x69,0x74,0x6c,0x65,0x3e,0x6c,0x77,0x49,0x50,0x20,0x2d,0x20,0x41,0x20,0x4c,0x69, +0x67,0x68,0x74,0x77,0x65,0x69,0x67,0x68,0x74,0x20,0x54,0x43,0x50,0x2f,0x49,0x50, +0x20,0x53,0x74,0x61,0x63,0x6b,0x3c,0x2f,0x74,0x69,0x74,0x6c,0x65,0x3e,0x3c,0x2f, +0x68,0x65,0x61,0x64,0x3e,0x0d,0x0a,0x3c,0x62,0x6f,0x64,0x79,0x20,0x62,0x67,0x63, +0x6f,0x6c,0x6f,0x72,0x3d,0x22,0x77,0x68,0x69,0x74,0x65,0x22,0x20,0x74,0x65,0x78, +0x74,0x3d,0x22,0x62,0x6c,0x61,0x63,0x6b,0x22,0x3e,0x0d,0x0a,0x0d,0x0a,0x20,0x20, +0x20,0x20,0x3c,0x74,0x61,0x62,0x6c,0x65,0x20,0x77,0x69,0x64,0x74,0x68,0x3d,0x22, +0x31,0x30,0x30,0x25,0x22,0x3e,0x0d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x74, +0x72,0x20,0x76,0x61,0x6c,0x69,0x67,0x6e,0x3d,0x22,0x74,0x6f,0x70,0x22,0x3e,0x3c, +0x74,0x64,0x20,0x77,0x69,0x64,0x74,0x68,0x3d,0x22,0x38,0x30,0x22,0x3e,0x09,0x20, +0x20,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x61,0x20,0x68,0x72,0x65,0x66,0x3d,0x22,0x68, +0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x73,0x69,0x63,0x73,0x2e,0x73, +0x65,0x2f,0x22,0x3e,0x3c,0x69,0x6d,0x67,0x20,0x73,0x72,0x63,0x3d,0x22,0x2f,0x69, +0x6d,0x67,0x2f,0x73,0x69,0x63,0x73,0x2e,0x67,0x69,0x66,0x22,0x0d,0x0a,0x09,0x20, +0x20,0x62,0x6f,0x72,0x64,0x65,0x72,0x3d,0x22,0x30,0x22,0x20,0x61,0x6c,0x74,0x3d, +0x22,0x53,0x49,0x43,0x53,0x20,0x6c,0x6f,0x67,0x6f,0x22,0x20,0x74,0x69,0x74,0x6c, +0x65,0x3d,0x22,0x53,0x49,0x43,0x53,0x20,0x6c,0x6f,0x67,0x6f,0x22,0x3e,0x3c,0x2f, +0x61,0x3e,0x0d,0x0a,0x09,0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x74,0x64,0x20,0x77,0x69, +0x64,0x74,0x68,0x3d,0x22,0x35,0x30,0x30,0x22,0x3e,0x09,0x20,0x20,0x0d,0x0a,0x09, +0x20,0x20,0x3c,0x68,0x31,0x3e,0x6c,0x77,0x49,0x50,0x20,0x2d,0x20,0x41,0x20,0x4c, +0x69,0x67,0x68,0x74,0x77,0x65,0x69,0x67,0x68,0x74,0x20,0x54,0x43,0x50,0x2f,0x49, +0x50,0x20,0x53,0x74,0x61,0x63,0x6b,0x3c,0x2f,0x68,0x31,0x3e,0x0d,0x0a,0x09,0x20, +0x20,0x3c,0x70,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x54,0x68,0x65,0x20,0x77, +0x65,0x62,0x20,0x70,0x61,0x67,0x65,0x20,0x79,0x6f,0x75,0x20,0x61,0x72,0x65,0x20, +0x77,0x61,0x74,0x63,0x68,0x69,0x6e,0x67,0x20,0x77,0x61,0x73,0x20,0x73,0x65,0x72, +0x76,0x65,0x64,0x20,0x62,0x79,0x20,0x61,0x20,0x73,0x69,0x6d,0x70,0x6c,0x65,0x20, +0x77,0x65,0x62,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x73,0x65,0x72,0x76,0x65,0x72, +0x20,0x72,0x75,0x6e,0x6e,0x69,0x6e,0x67,0x20,0x6f,0x6e,0x20,0x74,0x6f,0x70,0x20, +0x6f,0x66,0x20,0x74,0x68,0x65,0x20,0x6c,0x69,0x67,0x68,0x74,0x77,0x65,0x69,0x67, +0x68,0x74,0x20,0x54,0x43,0x50,0x2f,0x49,0x50,0x20,0x73,0x74,0x61,0x63,0x6b,0x20, +0x3c,0x61,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x68,0x72,0x65,0x66,0x3d,0x22,0x68, +0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x73,0x69,0x63,0x73,0x2e,0x73, +0x65,0x2f,0x7e,0x61,0x64,0x61,0x6d,0x2f,0x6c,0x77,0x69,0x70,0x2f,0x22,0x3e,0x6c, +0x77,0x49,0x50,0x3c,0x2f,0x61,0x3e,0x2e,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x2f,0x70, +0x3e,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x70,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20, +0x6c,0x77,0x49,0x50,0x20,0x69,0x73,0x20,0x61,0x6e,0x20,0x6f,0x70,0x65,0x6e,0x20, +0x73,0x6f,0x75,0x72,0x63,0x65,0x20,0x69,0x6d,0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74, +0x61,0x74,0x69,0x6f,0x6e,0x20,0x6f,0x66,0x20,0x74,0x68,0x65,0x20,0x54,0x43,0x50, +0x2f,0x49,0x50,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x70,0x72,0x6f,0x74,0x6f,0x63, +0x6f,0x6c,0x20,0x73,0x75,0x69,0x74,0x65,0x20,0x74,0x68,0x61,0x74,0x20,0x77,0x61, +0x73,0x20,0x6f,0x72,0x69,0x67,0x69,0x6e,0x61,0x6c,0x6c,0x79,0x20,0x77,0x72,0x69, +0x74,0x74,0x65,0x6e,0x20,0x62,0x79,0x20,0x3c,0x61,0x0d,0x0a,0x09,0x20,0x20,0x20, +0x20,0x68,0x72,0x65,0x66,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77, +0x77,0x2e,0x73,0x69,0x63,0x73,0x2e,0x73,0x65,0x2f,0x7e,0x61,0x64,0x61,0x6d,0x2f, +0x6c,0x77,0x69,0x70,0x2f,0x22,0x3e,0x41,0x64,0x61,0x6d,0x20,0x44,0x75,0x6e,0x6b, +0x65,0x6c,0x73,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x6f,0x66,0x20,0x74,0x68,0x65, +0x20,0x53,0x77,0x65,0x64,0x69,0x73,0x68,0x20,0x49,0x6e,0x73,0x74,0x69,0x74,0x75, +0x74,0x65,0x20,0x6f,0x66,0x20,0x43,0x6f,0x6d,0x70,0x75,0x74,0x65,0x72,0x20,0x53, +0x63,0x69,0x65,0x6e,0x63,0x65,0x3c,0x2f,0x61,0x3e,0x20,0x62,0x75,0x74,0x20,0x6e, +0x6f,0x77,0x20,0x69,0x73,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x62,0x65,0x69,0x6e, +0x67,0x20,0x61,0x63,0x74,0x69,0x76,0x65,0x6c,0x79,0x20,0x64,0x65,0x76,0x65,0x6c, +0x6f,0x70,0x65,0x64,0x20,0x62,0x79,0x20,0x61,0x20,0x74,0x65,0x61,0x6d,0x20,0x6f, +0x66,0x20,0x64,0x65,0x76,0x65,0x6c,0x6f,0x70,0x65,0x72,0x73,0x0d,0x0a,0x09,0x20, +0x20,0x20,0x20,0x64,0x69,0x73,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x64,0x20,0x77, +0x6f,0x72,0x6c,0x64,0x2d,0x77,0x69,0x64,0x65,0x2e,0x20,0x53,0x69,0x6e,0x63,0x65, +0x20,0x69,0x74,0x27,0x73,0x20,0x72,0x65,0x6c,0x65,0x61,0x73,0x65,0x2c,0x20,0x6c, +0x77,0x49,0x50,0x20,0x68,0x61,0x73,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x73,0x70, +0x75,0x72,0x72,0x65,0x64,0x20,0x61,0x20,0x6c,0x6f,0x74,0x20,0x6f,0x66,0x20,0x69, +0x6e,0x74,0x65,0x72,0x65,0x73,0x74,0x20,0x61,0x6e,0x64,0x20,0x68,0x61,0x73,0x20, +0x62,0x65,0x65,0x6e,0x20,0x70,0x6f,0x72,0x74,0x65,0x64,0x20,0x74,0x6f,0x20,0x73, +0x65,0x76,0x65,0x72,0x61,0x6c,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x70,0x6c,0x61, +0x74,0x66,0x6f,0x72,0x6d,0x73,0x20,0x61,0x6e,0x64,0x20,0x6f,0x70,0x65,0x72,0x61, +0x74,0x69,0x6e,0x67,0x20,0x73,0x79,0x73,0x74,0x65,0x6d,0x73,0x2e,0x20,0x6c,0x77, +0x49,0x50,0x20,0x63,0x61,0x6e,0x20,0x62,0x65,0x20,0x75,0x73,0x65,0x64,0x20,0x65, +0x69,0x74,0x68,0x65,0x72,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x77,0x69,0x74,0x68, +0x20,0x6f,0x72,0x20,0x77,0x69,0x74,0x68,0x6f,0x75,0x74,0x20,0x61,0x6e,0x20,0x75, +0x6e,0x64,0x65,0x72,0x6c,0x79,0x69,0x6e,0x67,0x20,0x4f,0x53,0x2e,0x0d,0x0a,0x09, +0x20,0x20,0x3c,0x2f,0x70,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x70,0x3e,0x0d,0x0a, +0x09,0x20,0x20,0x20,0x20,0x54,0x68,0x65,0x20,0x66,0x6f,0x63,0x75,0x73,0x20,0x6f, +0x66,0x20,0x74,0x68,0x65,0x20,0x6c,0x77,0x49,0x50,0x20,0x54,0x43,0x50,0x2f,0x49, +0x50,0x20,0x69,0x6d,0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x61,0x74,0x69,0x6f,0x6e, +0x20,0x69,0x73,0x20,0x74,0x6f,0x20,0x72,0x65,0x64,0x75,0x63,0x65,0x0d,0x0a,0x09, +0x20,0x20,0x20,0x20,0x74,0x68,0x65,0x20,0x52,0x41,0x4d,0x20,0x75,0x73,0x61,0x67, +0x65,0x20,0x77,0x68,0x69,0x6c,0x65,0x20,0x73,0x74,0x69,0x6c,0x6c,0x20,0x68,0x61, +0x76,0x69,0x6e,0x67,0x20,0x61,0x20,0x66,0x75,0x6c,0x6c,0x20,0x73,0x63,0x61,0x6c, +0x65,0x20,0x54,0x43,0x50,0x2e,0x20,0x54,0x68,0x69,0x73,0x0d,0x0a,0x09,0x20,0x20, +0x20,0x20,0x6d,0x61,0x6b,0x65,0x73,0x20,0x6c,0x77,0x49,0x50,0x20,0x73,0x75,0x69, +0x74,0x61,0x62,0x6c,0x65,0x20,0x66,0x6f,0x72,0x20,0x75,0x73,0x65,0x20,0x69,0x6e, +0x20,0x65,0x6d,0x62,0x65,0x64,0x64,0x65,0x64,0x20,0x73,0x79,0x73,0x74,0x65,0x6d, +0x73,0x20,0x77,0x69,0x74,0x68,0x20,0x74,0x65,0x6e,0x73,0x0d,0x0a,0x09,0x20,0x20, +0x20,0x20,0x6f,0x66,0x20,0x6b,0x69,0x6c,0x6f,0x62,0x79,0x74,0x65,0x73,0x20,0x6f, +0x66,0x20,0x66,0x72,0x65,0x65,0x20,0x52,0x41,0x4d,0x20,0x61,0x6e,0x64,0x20,0x72, +0x6f,0x6f,0x6d,0x20,0x66,0x6f,0x72,0x20,0x61,0x72,0x6f,0x75,0x6e,0x64,0x20,0x34, +0x30,0x20,0x6b,0x69,0x6c,0x6f,0x62,0x79,0x74,0x65,0x73,0x0d,0x0a,0x09,0x20,0x20, +0x20,0x20,0x6f,0x66,0x20,0x63,0x6f,0x64,0x65,0x20,0x52,0x4f,0x4d,0x2e,0x0d,0x0a, +0x09,0x20,0x20,0x3c,0x2f,0x70,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x70,0x3e,0x0d, +0x0a,0x09,0x20,0x20,0x20,0x20,0x4d,0x6f,0x72,0x65,0x20,0x69,0x6e,0x66,0x6f,0x72, +0x6d,0x61,0x74,0x69,0x6f,0x6e,0x20,0x61,0x62,0x6f,0x75,0x74,0x20,0x6c,0x77,0x49, +0x50,0x20,0x63,0x61,0x6e,0x20,0x62,0x65,0x20,0x66,0x6f,0x75,0x6e,0x64,0x20,0x61, +0x74,0x20,0x74,0x68,0x65,0x20,0x6c,0x77,0x49,0x50,0x0d,0x0a,0x09,0x20,0x20,0x20, +0x20,0x68,0x6f,0x6d,0x65,0x70,0x61,0x67,0x65,0x20,0x61,0x74,0x20,0x3c,0x61,0x0d, +0x0a,0x09,0x20,0x20,0x20,0x20,0x68,0x72,0x65,0x66,0x3d,0x22,0x68,0x74,0x74,0x70, +0x3a,0x2f,0x2f,0x73,0x61,0x76,0x61,0x6e,0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67, +0x6e,0x75,0x2e,0x6f,0x72,0x67,0x2f,0x70,0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f, +0x6c,0x77,0x69,0x70,0x2f,0x22,0x3e,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x61, +0x76,0x61,0x6e,0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,0x6e,0x75,0x2e,0x6f,0x72, +0x67,0x2f,0x70,0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,0x6c,0x77,0x69,0x70,0x2f, +0x3c,0x2f,0x61,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x6f,0x72,0x20,0x61,0x74, +0x20,0x74,0x68,0x65,0x20,0x6c,0x77,0x49,0x50,0x20,0x77,0x69,0x6b,0x69,0x20,0x61, +0x74,0x20,0x3c,0x61,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x68,0x72,0x65,0x66,0x3d, +0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x6c,0x77,0x69,0x70,0x2e,0x77,0x69,0x6b, +0x69,0x61,0x2e,0x63,0x6f,0x6d,0x2f,0x22,0x3e,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f, +0x6c,0x77,0x69,0x70,0x2e,0x77,0x69,0x6b,0x69,0x61,0x2e,0x63,0x6f,0x6d,0x2f,0x3c, +0x2f,0x61,0x3e,0x2e,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x2f,0x70,0x3e,0x0d,0x0a,0x09, +0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x74,0x64,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x26,0x6e, +0x62,0x73,0x70,0x3b,0x0d,0x0a,0x09,0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x2f,0x74,0x72, +0x3e,0x0d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x2f,0x74,0x61,0x62,0x6c,0x65, +0x3e,0x0d,0x0a,0x3c,0x2f,0x62,0x6f,0x64,0x79,0x3e,0x0d,0x0a,0x3c,0x2f,0x68,0x74, +0x6d,0x6c,0x3e,0x0d,0x0a,0x0d,0x0a,}; + + + +const struct fsdata_file file__img_sics_gif[] = { { +file_NULL, +data__img_sics_gif, +data__img_sics_gif + 16, +sizeof(data__img_sics_gif) - 16, +1, +}}; + +const struct fsdata_file file__404_html[] = { { +file__img_sics_gif, +data__404_html, +data__404_html + 12, +sizeof(data__404_html) - 12, +1, +}}; + +const struct fsdata_file file__index_html[] = { { +file__404_html, +data__index_html, +data__index_html + 12, +sizeof(data__index_html) - 12, +1, +}}; + +#define FS_ROOT file__index_html +#define FS_NUMFILES 3 + diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fsdata.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fsdata.h new file mode 100644 index 0000000..ac4548c --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fsdata.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels <adam@sics.se> + * + */ +#ifndef LWIP_FSDATA_H +#define LWIP_FSDATA_H + +#include "lwip/apps/httpd_opts.h" +#include "lwip/apps/fs.h" + +struct fsdata_file { + const struct fsdata_file *next; + const unsigned char *name; + const unsigned char *data; + int len; + u8_t flags; +#if HTTPD_PRECALCULATED_CHECKSUM + u16_t chksum_count; + const struct fsdata_chksum *chksum; +#endif /* HTTPD_PRECALCULATED_CHECKSUM */ +}; + +#endif /* LWIP_FSDATA_H */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/httpd.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/httpd.c new file mode 100644 index 0000000..43195d7 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/httpd.c @@ -0,0 +1,2629 @@ +/** + * @file + * LWIP HTTP server implementation + */ + +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels <adam@sics.se> + * Simon Goldschmidt + * + */ + +/** + * @defgroup httpd HTTP server + * @ingroup apps + * + * This httpd supports for a + * rudimentary server-side-include facility which will replace tags of the form + * <!--#tag--> in any file whose extension is .shtml, .shtm or .ssi with + * strings provided by an include handler whose pointer is provided to the + * module via function http_set_ssi_handler(). + * Additionally, a simple common + * gateway interface (CGI) handling mechanism has been added to allow clients + * to hook functions to particular request URIs. + * + * To enable SSI support, define label LWIP_HTTPD_SSI in lwipopts.h. + * To enable CGI support, define label LWIP_HTTPD_CGI in lwipopts.h. + * + * By default, the server assumes that HTTP headers are already present in + * each file stored in the file system. By defining LWIP_HTTPD_DYNAMIC_HEADERS in + * lwipopts.h, this behavior can be changed such that the server inserts the + * headers automatically based on the extension of the file being served. If + * this mode is used, be careful to ensure that the file system image used + * does not already contain the header information. + * + * File system images without headers can be created using the makefsfile + * tool with the -h command line option. + * + * + * Notes about valid SSI tags + * -------------------------- + * + * The following assumptions are made about tags used in SSI markers: + * + * 1. No tag may contain '-' or whitespace characters within the tag name. + * 2. Whitespace is allowed between the tag leadin "<!--#" and the start of + * the tag name and between the tag name and the leadout string "-->". + * 3. The maximum tag name length is LWIP_HTTPD_MAX_TAG_NAME_LEN, currently 8 characters. + * + * Notes on CGI usage + * ------------------ + * + * The simple CGI support offered here works with GET method requests only + * and can handle up to 16 parameters encoded into the URI. The handler + * function may not write directly to the HTTP output but must return a + * filename that the HTTP server will send to the browser as a response to + * the incoming CGI request. + * + * + * + * The list of supported file types is quite short, so if makefsdata complains + * about an unknown extension, make sure to add it (and its doctype) to + * the 'g_psHTTPHeaders' list. + */ +#include "lwip/init.h" +#include "lwip/apps/httpd.h" +#include "lwip/debug.h" +#include "lwip/stats.h" +#include "lwip/apps/fs.h" +#include "httpd_structs.h" +#include "lwip/def.h" +#include "lwip/ip.h" +#include "lwip/tcp.h" + +#include <string.h> /* memset */ +#include <stdlib.h> /* atoi */ +#include <stdio.h> + +#if LWIP_TCP && LWIP_CALLBACK_API + +/** Minimum length for a valid HTTP/0.9 request: "GET /\r\n" -> 7 bytes */ +#define MIN_REQ_LEN 7 + +#define CRLF "\r\n" +#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE +#define HTTP11_CONNECTIONKEEPALIVE "Connection: keep-alive" +#define HTTP11_CONNECTIONKEEPALIVE2 "Connection: Keep-Alive" +#endif + +/** These defines check whether tcp_write has to copy data or not */ + +/** This was TI's check whether to let TCP copy data or not + * \#define HTTP_IS_DATA_VOLATILE(hs) ((hs->file < (char *)0x20000000) ? 0 : TCP_WRITE_FLAG_COPY) + */ +#ifndef HTTP_IS_DATA_VOLATILE +#if LWIP_HTTPD_SSI +/* Copy for SSI files, no copy for non-SSI files */ +#define HTTP_IS_DATA_VOLATILE(hs) ((hs)->ssi ? TCP_WRITE_FLAG_COPY : 0) +#else /* LWIP_HTTPD_SSI */ +/** Default: don't copy if the data is sent from file-system directly */ +#define HTTP_IS_DATA_VOLATILE(hs) (((hs->file != NULL) && (hs->handle != NULL) && (hs->file == \ + (const char*)hs->handle->data + hs->handle->len - hs->left)) \ + ? 0 : TCP_WRITE_FLAG_COPY) +#endif /* LWIP_HTTPD_SSI */ +#endif + +/** Default: headers are sent from ROM */ +#ifndef HTTP_IS_HDR_VOLATILE +#define HTTP_IS_HDR_VOLATILE(hs, ptr) 0 +#endif + +/* Return values for http_send_*() */ +#define HTTP_DATA_TO_SEND_BREAK 2 +#define HTTP_DATA_TO_SEND_CONTINUE 1 +#define HTTP_NO_DATA_TO_SEND 0 + +typedef struct +{ + const char *name; + u8_t shtml; +} default_filename; + +const default_filename g_psDefaultFilenames[] = { + {"/index.shtml", 1 }, + {"/index.ssi", 1 }, + {"/index.shtm", 1 }, + {"/index.html", 0 }, + {"/index.htm", 0 } +}; + +#define NUM_DEFAULT_FILENAMES (sizeof(g_psDefaultFilenames) / \ + sizeof(default_filename)) + +#if LWIP_HTTPD_SUPPORT_REQUESTLIST +/** HTTP request is copied here from pbufs for simple parsing */ +static char httpd_req_buf[LWIP_HTTPD_MAX_REQ_LENGTH+1]; +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ + +#if LWIP_HTTPD_SUPPORT_POST +#if LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN > LWIP_HTTPD_MAX_REQUEST_URI_LEN +#define LWIP_HTTPD_URI_BUF_LEN LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN +#endif +#endif +#ifndef LWIP_HTTPD_URI_BUF_LEN +#define LWIP_HTTPD_URI_BUF_LEN LWIP_HTTPD_MAX_REQUEST_URI_LEN +#endif +#if LWIP_HTTPD_URI_BUF_LEN +/* Filename for response file to send when POST is finished or + * search for default files when a directory is requested. */ +static char http_uri_buf[LWIP_HTTPD_URI_BUF_LEN+1]; +#endif + +#if LWIP_HTTPD_DYNAMIC_HEADERS +/* The number of individual strings that comprise the headers sent before each + * requested file. + */ +#define NUM_FILE_HDR_STRINGS 5 +#define HDR_STRINGS_IDX_HTTP_STATUS 0 /* e.g. "HTTP/1.0 200 OK\r\n" */ +#define HDR_STRINGS_IDX_SERVER_NAME 1 /* e.g. "Server: "HTTPD_SERVER_AGENT"\r\n" */ +#define HDR_STRINGS_IDX_CONTENT_LEN_KEPALIVE 2 /* e.g. "Content-Length: xy\r\n" and/or "Connection: keep-alive\r\n" */ +#define HDR_STRINGS_IDX_CONTENT_LEN_NR 3 /* the byte count, when content-length is used */ +#define HDR_STRINGS_IDX_CONTENT_TYPE 4 /* the content type (or default answer content type including default document) */ + +/* The dynamically generated Content-Length buffer needs space for CRLF + NULL */ +#define LWIP_HTTPD_MAX_CONTENT_LEN_OFFSET 3 +#ifndef LWIP_HTTPD_MAX_CONTENT_LEN_SIZE +/* The dynamically generated Content-Length buffer shall be able to work with + ~953 MB (9 digits) */ +#define LWIP_HTTPD_MAX_CONTENT_LEN_SIZE (9 + LWIP_HTTPD_MAX_CONTENT_LEN_OFFSET) +#endif +#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ + +#if LWIP_HTTPD_SSI + +#define HTTPD_LAST_TAG_PART 0xFFFF + +enum tag_check_state { + TAG_NONE, /* Not processing an SSI tag */ + TAG_LEADIN, /* Tag lead in "<!--#" being processed */ + TAG_FOUND, /* Tag name being read, looking for lead-out start */ + TAG_LEADOUT, /* Tag lead out "-->" being processed */ + TAG_SENDING /* Sending tag replacement string */ +}; + +struct http_ssi_state { + const char *parsed; /* Pointer to the first unparsed byte in buf. */ +#if !LWIP_HTTPD_SSI_INCLUDE_TAG + const char *tag_started;/* Pointer to the first opening '<' of the tag. */ +#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG */ + const char *tag_end; /* Pointer to char after the closing '>' of the tag. */ + u32_t parse_left; /* Number of unparsed bytes in buf. */ + u16_t tag_index; /* Counter used by tag parsing state machine */ + u16_t tag_insert_len; /* Length of insert in string tag_insert */ +#if LWIP_HTTPD_SSI_MULTIPART + u16_t tag_part; /* Counter passed to and changed by tag insertion function to insert multiple times */ +#endif /* LWIP_HTTPD_SSI_MULTIPART */ + u8_t tag_name_len; /* Length of the tag name in string tag_name */ + char tag_name[LWIP_HTTPD_MAX_TAG_NAME_LEN + 1]; /* Last tag name extracted */ + char tag_insert[LWIP_HTTPD_MAX_TAG_INSERT_LEN + 1]; /* Insert string for tag_name */ + enum tag_check_state tag_state; /* State of the tag processor */ +}; +#endif /* LWIP_HTTPD_SSI */ + +struct http_state { +#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED + struct http_state *next; +#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */ + struct fs_file file_handle; + struct fs_file *handle; + const char *file; /* Pointer to first unsent byte in buf. */ + + struct tcp_pcb *pcb; +#if LWIP_HTTPD_SUPPORT_REQUESTLIST + struct pbuf *req; +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ + +#if LWIP_HTTPD_DYNAMIC_FILE_READ + char *buf; /* File read buffer. */ + int buf_len; /* Size of file read buffer, buf. */ +#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */ + u32_t left; /* Number of unsent bytes in buf. */ + u8_t retries; +#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE + u8_t keepalive; +#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */ +#if LWIP_HTTPD_SSI + struct http_ssi_state *ssi; +#endif /* LWIP_HTTPD_SSI */ +#if LWIP_HTTPD_CGI + char *params[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Params extracted from the request URI */ + char *param_vals[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Values for each extracted param */ +#endif /* LWIP_HTTPD_CGI */ +#if LWIP_HTTPD_DYNAMIC_HEADERS + const char *hdrs[NUM_FILE_HDR_STRINGS]; /* HTTP headers to be sent. */ + char hdr_content_len[LWIP_HTTPD_MAX_CONTENT_LEN_SIZE]; + u16_t hdr_pos; /* The position of the first unsent header byte in the + current string */ + u16_t hdr_index; /* The index of the hdr string currently being sent. */ +#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ +#if LWIP_HTTPD_TIMING + u32_t time_started; +#endif /* LWIP_HTTPD_TIMING */ +#if LWIP_HTTPD_SUPPORT_POST + u32_t post_content_len_left; +#if LWIP_HTTPD_POST_MANUAL_WND + u32_t unrecved_bytes; + u8_t no_auto_wnd; + u8_t post_finished; +#endif /* LWIP_HTTPD_POST_MANUAL_WND */ +#endif /* LWIP_HTTPD_SUPPORT_POST*/ +}; + +#if HTTPD_USE_MEM_POOL +LWIP_MEMPOOL_DECLARE(HTTPD_STATE, MEMP_NUM_PARALLEL_HTTPD_CONNS, sizeof(struct http_state), "HTTPD_STATE") +#if LWIP_HTTPD_SSI +LWIP_MEMPOOL_DECLARE(HTTPD_SSI_STATE, MEMP_NUM_PARALLEL_HTTPD_SSI_CONNS, sizeof(struct http_ssi_state), "HTTPD_SSI_STATE") +#define HTTP_FREE_SSI_STATE(x) LWIP_MEMPOOL_FREE(HTTPD_SSI_STATE, (x)) +#define HTTP_ALLOC_SSI_STATE() (struct http_ssi_state *)LWIP_MEMPOOL_ALLOC(HTTPD_SSI_STATE) +#endif /* LWIP_HTTPD_SSI */ +#define HTTP_ALLOC_HTTP_STATE() (struct http_state *)LWIP_MEMPOOL_ALLOC(HTTPD_STATE) +#define HTTP_FREE_HTTP_STATE(x) LWIP_MEMPOOL_FREE(HTTPD_STATE, (x)) +#else /* HTTPD_USE_MEM_POOL */ +#define HTTP_ALLOC_HTTP_STATE() (struct http_state *)mem_malloc(sizeof(struct http_state)) +#define HTTP_FREE_HTTP_STATE(x) mem_free(x) +#if LWIP_HTTPD_SSI +#define HTTP_ALLOC_SSI_STATE() (struct http_ssi_state *)mem_malloc(sizeof(struct http_ssi_state)) +#define HTTP_FREE_SSI_STATE(x) mem_free(x) +#endif /* LWIP_HTTPD_SSI */ +#endif /* HTTPD_USE_MEM_POOL */ + +static err_t http_close_conn(struct tcp_pcb *pcb, struct http_state *hs); +static err_t http_close_or_abort_conn(struct tcp_pcb *pcb, struct http_state *hs, u8_t abort_conn); +static err_t http_find_file(struct http_state *hs, const char *uri, int is_09); +static err_t http_init_file(struct http_state *hs, struct fs_file *file, int is_09, const char *uri, u8_t tag_check, char* params); +static err_t http_poll(void *arg, struct tcp_pcb *pcb); +static u8_t http_check_eof(struct tcp_pcb *pcb, struct http_state *hs); +#if LWIP_HTTPD_FS_ASYNC_READ +static void http_continue(void *connection); +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ + +#if LWIP_HTTPD_SSI +/* SSI insert handler function pointer. */ +tSSIHandler g_pfnSSIHandler; +#if !LWIP_HTTPD_SSI_RAW +int g_iNumTags; +const char **g_ppcTags; +#endif /* !LWIP_HTTPD_SSI_RAW */ + +#define LEN_TAG_LEAD_IN 5 +const char * const g_pcTagLeadIn = "<!--#"; + +#define LEN_TAG_LEAD_OUT 3 +const char * const g_pcTagLeadOut = "-->"; +#endif /* LWIP_HTTPD_SSI */ + +#if LWIP_HTTPD_CGI +/* CGI handler information */ +const tCGI *g_pCGIs; +int g_iNumCGIs; +int http_cgi_paramcount; +#define http_cgi_params hs->params +#define http_cgi_param_vals hs->param_vals +#elif LWIP_HTTPD_CGI_SSI +char *http_cgi_params[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Params extracted from the request URI */ +char *http_cgi_param_vals[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Values for each extracted param */ +#endif /* LWIP_HTTPD_CGI */ + +#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED +/** global list of active HTTP connections, use to kill the oldest when + running out of memory */ +static struct http_state *http_connections; + +static void +http_add_connection(struct http_state *hs) +{ + /* add the connection to the list */ + hs->next = http_connections; + http_connections = hs; +} + +static void +http_remove_connection(struct http_state *hs) +{ + /* take the connection off the list */ + if (http_connections) { + if (http_connections == hs) { + http_connections = hs->next; + } else { + struct http_state *last; + for(last = http_connections; last->next != NULL; last = last->next) { + if (last->next == hs) { + last->next = hs->next; + break; + } + } + } + } +} + +static void +http_kill_oldest_connection(u8_t ssi_required) +{ + struct http_state *hs = http_connections; + struct http_state *hs_free_next = NULL; + while(hs && hs->next) { +#if LWIP_HTTPD_SSI + if (ssi_required) { + if (hs->next->ssi != NULL) { + hs_free_next = hs; + } + } else +#else /* LWIP_HTTPD_SSI */ + LWIP_UNUSED_ARG(ssi_required); +#endif /* LWIP_HTTPD_SSI */ + { + hs_free_next = hs; + } + LWIP_ASSERT("broken list", hs != hs->next); + hs = hs->next; + } + if (hs_free_next != NULL) { + LWIP_ASSERT("hs_free_next->next != NULL", hs_free_next->next != NULL); + LWIP_ASSERT("hs_free_next->next->pcb != NULL", hs_free_next->next->pcb != NULL); + /* send RST when killing a connection because of memory shortage */ + http_close_or_abort_conn(hs_free_next->next->pcb, hs_free_next->next, 1); /* this also unlinks the http_state from the list */ + } +} +#else /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */ + +#define http_add_connection(hs) +#define http_remove_connection(hs) + +#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */ + +#if LWIP_HTTPD_SSI +/** Allocate as struct http_ssi_state. */ +static struct http_ssi_state* +http_ssi_state_alloc(void) +{ + struct http_ssi_state *ret = HTTP_ALLOC_SSI_STATE(); +#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED + if (ret == NULL) { + http_kill_oldest_connection(1); + ret = HTTP_ALLOC_SSI_STATE(); + } +#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */ + if (ret != NULL) { + memset(ret, 0, sizeof(struct http_ssi_state)); + } + return ret; +} + +/** Free a struct http_ssi_state. */ +static void +http_ssi_state_free(struct http_ssi_state *ssi) +{ + if (ssi != NULL) { + HTTP_FREE_SSI_STATE(ssi); + } +} +#endif /* LWIP_HTTPD_SSI */ + +/** Initialize a struct http_state. + */ +static void +http_state_init(struct http_state* hs) +{ + /* Initialize the structure. */ + memset(hs, 0, sizeof(struct http_state)); +#if LWIP_HTTPD_DYNAMIC_HEADERS + /* Indicate that the headers are not yet valid */ + hs->hdr_index = NUM_FILE_HDR_STRINGS; +#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ +} + +/** Allocate a struct http_state. */ +static struct http_state* +http_state_alloc(void) +{ + struct http_state *ret = HTTP_ALLOC_HTTP_STATE(); +#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED + if (ret == NULL) { + http_kill_oldest_connection(0); + ret = HTTP_ALLOC_HTTP_STATE(); + } +#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */ + if (ret != NULL) { + http_state_init(ret); + http_add_connection(ret); + } + return ret; +} + +/** Free a struct http_state. + * Also frees the file data if dynamic. + */ +static void +http_state_eof(struct http_state *hs) +{ + if(hs->handle) { +#if LWIP_HTTPD_TIMING + u32_t ms_needed = sys_now() - hs->time_started; + u32_t needed = LWIP_MAX(1, (ms_needed/100)); + LWIP_DEBUGF(HTTPD_DEBUG_TIMING, ("httpd: needed %"U32_F" ms to send file of %d bytes -> %"U32_F" bytes/sec\n", + ms_needed, hs->handle->len, ((((u32_t)hs->handle->len) * 10) / needed))); +#endif /* LWIP_HTTPD_TIMING */ + fs_close(hs->handle); + hs->handle = NULL; + } +#if LWIP_HTTPD_DYNAMIC_FILE_READ + if (hs->buf != NULL) { + mem_free(hs->buf); + hs->buf = NULL; + } +#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */ +#if LWIP_HTTPD_SSI + if (hs->ssi) { + http_ssi_state_free(hs->ssi); + hs->ssi = NULL; + } +#endif /* LWIP_HTTPD_SSI */ +#if LWIP_HTTPD_SUPPORT_REQUESTLIST + if (hs->req) { + pbuf_free(hs->req); + hs->req = NULL; + } +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ +} + +/** Free a struct http_state. + * Also frees the file data if dynamic. + */ +static void +http_state_free(struct http_state *hs) +{ + if (hs != NULL) { + http_state_eof(hs); + http_remove_connection(hs); + HTTP_FREE_HTTP_STATE(hs); + } +} + +/** Call tcp_write() in a loop trying smaller and smaller length + * + * @param pcb tcp_pcb to send + * @param ptr Data to send + * @param length Length of data to send (in/out: on return, contains the + * amount of data sent) + * @param apiflags directly passed to tcp_write + * @return the return value of tcp_write + */ +static err_t +http_write(struct tcp_pcb *pcb, const void* ptr, u16_t *length, u8_t apiflags) +{ + u16_t len, max_len; + err_t err; + LWIP_ASSERT("length != NULL", length != NULL); + len = *length; + if (len == 0) { + return ERR_OK; + } + /* We cannot send more data than space available in the send buffer. */ + max_len = tcp_sndbuf(pcb); + if (max_len < len) { + len = max_len; + } +#ifdef HTTPD_MAX_WRITE_LEN + /* Additional limitation: e.g. don't enqueue more than 2*mss at once */ + max_len = HTTPD_MAX_WRITE_LEN(pcb); + if(len > max_len) { + len = max_len; + } +#endif /* HTTPD_MAX_WRITE_LEN */ + do { + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Trying go send %d bytes\n", len)); + err = tcp_write(pcb, ptr, len, apiflags); + if (err == ERR_MEM) { + if ((tcp_sndbuf(pcb) == 0) || + (tcp_sndqueuelen(pcb) >= TCP_SND_QUEUELEN)) { + /* no need to try smaller sizes */ + len = 1; + } else { + len /= 2; + } + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, + ("Send failed, trying less (%d bytes)\n", len)); + } + } while ((err == ERR_MEM) && (len > 1)); + + if (err == ERR_OK) { + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Sent %d bytes\n", len)); + *length = len; + } else { + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Send failed with err %d (\"%s\")\n", err, lwip_strerr(err))); + *length = 0; + } + +#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE + /* ensure nagle is normally enabled (only disabled for persistent connections + when all data has been enqueued but the connection stays open for the next + request */ + tcp_nagle_enable(pcb); +#endif + + return err; +} + +/** + * The connection shall be actively closed (using RST to close from fault states). + * Reset the sent- and recv-callbacks. + * + * @param pcb the tcp pcb to reset callbacks + * @param hs connection state to free + */ +static err_t +http_close_or_abort_conn(struct tcp_pcb *pcb, struct http_state *hs, u8_t abort_conn) +{ + err_t err; + LWIP_DEBUGF(HTTPD_DEBUG, ("Closing connection %p\n", (void*)pcb)); + +#if LWIP_HTTPD_SUPPORT_POST + if (hs != NULL) { + if ((hs->post_content_len_left != 0) +#if LWIP_HTTPD_POST_MANUAL_WND + || ((hs->no_auto_wnd != 0) && (hs->unrecved_bytes != 0)) +#endif /* LWIP_HTTPD_POST_MANUAL_WND */ + ) { + /* make sure the post code knows that the connection is closed */ + http_uri_buf[0] = 0; + httpd_post_finished(hs, http_uri_buf, LWIP_HTTPD_URI_BUF_LEN); + } + } +#endif /* LWIP_HTTPD_SUPPORT_POST*/ + + + tcp_arg(pcb, NULL); + tcp_recv(pcb, NULL); + tcp_err(pcb, NULL); + tcp_poll(pcb, NULL, 0); + tcp_sent(pcb, NULL); + if (hs != NULL) { + http_state_free(hs); + } + + if (abort_conn) { + tcp_abort(pcb); + return ERR_OK; + } + err = tcp_close(pcb); + if (err != ERR_OK) { + LWIP_DEBUGF(HTTPD_DEBUG, ("Error %d closing %p\n", err, (void*)pcb)); + /* error closing, try again later in poll */ + tcp_poll(pcb, http_poll, HTTPD_POLL_INTERVAL); + } + return err; +} + +/** + * The connection shall be actively closed. + * Reset the sent- and recv-callbacks. + * + * @param pcb the tcp pcb to reset callbacks + * @param hs connection state to free + */ +static err_t +http_close_conn(struct tcp_pcb *pcb, struct http_state *hs) +{ + return http_close_or_abort_conn(pcb, hs, 0); +} + +/** End of file: either close the connection (Connection: close) or + * close the file (Connection: keep-alive) + */ +static void +http_eof(struct tcp_pcb *pcb, struct http_state *hs) +{ + /* HTTP/1.1 persistent connection? (Not supported for SSI) */ +#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE + if (hs->keepalive) { + http_remove_connection(hs); + + http_state_eof(hs); + http_state_init(hs); + /* restore state: */ + hs->pcb = pcb; + hs->keepalive = 1; + http_add_connection(hs); + /* ensure nagle doesn't interfere with sending all data as fast as possible: */ + tcp_nagle_disable(pcb); + } else +#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */ + { + http_close_conn(pcb, hs); + } +} + +#if LWIP_HTTPD_CGI || LWIP_HTTPD_CGI_SSI +/** + * Extract URI parameters from the parameter-part of an URI in the form + * "test.cgi?x=y" @todo: better explanation! + * Pointers to the parameters are stored in hs->param_vals. + * + * @param hs http connection state + * @param params pointer to the NULL-terminated parameter string from the URI + * @return number of parameters extracted + */ +static int +extract_uri_parameters(struct http_state *hs, char *params) +{ + char *pair; + char *equals; + int loop; + + LWIP_UNUSED_ARG(hs); + + /* If we have no parameters at all, return immediately. */ + if(!params || (params[0] == '\0')) { + return(0); + } + + /* Get a pointer to our first parameter */ + pair = params; + + /* Parse up to LWIP_HTTPD_MAX_CGI_PARAMETERS from the passed string and ignore the + * remainder (if any) */ + for(loop = 0; (loop < LWIP_HTTPD_MAX_CGI_PARAMETERS) && pair; loop++) { + + /* Save the name of the parameter */ + http_cgi_params[loop] = pair; + + /* Remember the start of this name=value pair */ + equals = pair; + + /* Find the start of the next name=value pair and replace the delimiter + * with a 0 to terminate the previous pair string. */ + pair = strchr(pair, '&'); + if(pair) { + *pair = '\0'; + pair++; + } else { + /* We didn't find a new parameter so find the end of the URI and + * replace the space with a '\0' */ + pair = strchr(equals, ' '); + if(pair) { + *pair = '\0'; + } + + /* Revert to NULL so that we exit the loop as expected. */ + pair = NULL; + } + + /* Now find the '=' in the previous pair, replace it with '\0' and save + * the parameter value string. */ + equals = strchr(equals, '='); + if(equals) { + *equals = '\0'; + http_cgi_param_vals[loop] = equals + 1; + } else { + http_cgi_param_vals[loop] = NULL; + } + } + + return loop; +} +#endif /* LWIP_HTTPD_CGI || LWIP_HTTPD_CGI_SSI */ + +#if LWIP_HTTPD_SSI +/** + * Insert a tag (found in an shtml in the form of "<!--#tagname-->" into the file. + * The tag's name is stored in ssi->tag_name (NULL-terminated), the replacement + * should be written to hs->tag_insert (up to a length of LWIP_HTTPD_MAX_TAG_INSERT_LEN). + * The amount of data written is stored to ssi->tag_insert_len. + * + * @todo: return tag_insert_len - maybe it can be removed from struct http_state? + * + * @param hs http connection state + */ +static void +get_tag_insert(struct http_state *hs) +{ +#if LWIP_HTTPD_SSI_RAW + const char* tag; +#else /* LWIP_HTTPD_SSI_RAW */ + int tag; +#endif /* LWIP_HTTPD_SSI_RAW */ + size_t len; + struct http_ssi_state *ssi; +#if LWIP_HTTPD_SSI_MULTIPART + u16_t current_tag_part; +#endif /* LWIP_HTTPD_SSI_MULTIPART */ + + LWIP_ASSERT("hs != NULL", hs != NULL); + ssi = hs->ssi; + LWIP_ASSERT("ssi != NULL", ssi != NULL); +#if LWIP_HTTPD_SSI_MULTIPART + current_tag_part = ssi->tag_part; + ssi->tag_part = HTTPD_LAST_TAG_PART; +#endif /* LWIP_HTTPD_SSI_MULTIPART */ +#if LWIP_HTTPD_SSI_RAW + tag = ssi->tag_name; +#endif + + if(g_pfnSSIHandler +#if !LWIP_HTTPD_SSI_RAW + && g_ppcTags && g_iNumTags +#endif /* !LWIP_HTTPD_SSI_RAW */ + ) { + + /* Find this tag in the list we have been provided. */ +#if LWIP_HTTPD_SSI_RAW + { +#else /* LWIP_HTTPD_SSI_RAW */ + for(tag = 0; tag < g_iNumTags; tag++) { + if(strcmp(ssi->tag_name, g_ppcTags[tag]) == 0) +#endif /* LWIP_HTTPD_SSI_RAW */ + { + ssi->tag_insert_len = g_pfnSSIHandler(tag, ssi->tag_insert, + LWIP_HTTPD_MAX_TAG_INSERT_LEN +#if LWIP_HTTPD_SSI_MULTIPART + , current_tag_part, &ssi->tag_part +#endif /* LWIP_HTTPD_SSI_MULTIPART */ +#if LWIP_HTTPD_FILE_STATE + , (hs->handle ? hs->handle->state : NULL) +#endif /* LWIP_HTTPD_FILE_STATE */ + ); +#if LWIP_HTTPD_SSI_RAW + if (ssi->tag_insert_len != HTTPD_SSI_TAG_UNKNOWN) +#endif /* LWIP_HTTPD_SSI_RAW */ + { + return; + } + } + } + } + + /* If we drop out, we were asked to serve a page which contains tags that + * we don't have a handler for. Merely echo back the tags with an error + * marker. */ +#define UNKNOWN_TAG1_TEXT "<b>***UNKNOWN TAG " +#define UNKNOWN_TAG1_LEN 18 +#define UNKNOWN_TAG2_TEXT "***</b>" +#define UNKNOWN_TAG2_LEN 7 + len = LWIP_MIN(sizeof(ssi->tag_name), LWIP_MIN(strlen(ssi->tag_name), + LWIP_HTTPD_MAX_TAG_INSERT_LEN - (UNKNOWN_TAG1_LEN + UNKNOWN_TAG2_LEN))); + MEMCPY(ssi->tag_insert, UNKNOWN_TAG1_TEXT, UNKNOWN_TAG1_LEN); + MEMCPY(&ssi->tag_insert[UNKNOWN_TAG1_LEN], ssi->tag_name, len); + MEMCPY(&ssi->tag_insert[UNKNOWN_TAG1_LEN + len], UNKNOWN_TAG2_TEXT, UNKNOWN_TAG2_LEN); + ssi->tag_insert[UNKNOWN_TAG1_LEN + len + UNKNOWN_TAG2_LEN] = 0; + + len = strlen(ssi->tag_insert); + LWIP_ASSERT("len <= 0xffff", len <= 0xffff); + ssi->tag_insert_len = (u16_t)len; +} +#endif /* LWIP_HTTPD_SSI */ + +#if LWIP_HTTPD_DYNAMIC_HEADERS +/** + * Generate the relevant HTTP headers for the given filename and write + * them into the supplied buffer. + */ +static void +get_http_headers(struct http_state *hs, const char *uri) +{ + size_t content_type; + char *tmp; + char *ext; + char *vars; + u8_t add_content_len; + + /* In all cases, the second header we send is the server identification + so set it here. */ + hs->hdrs[HDR_STRINGS_IDX_SERVER_NAME] = g_psHTTPHeaderStrings[HTTP_HDR_SERVER]; + hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEPALIVE] = NULL; + hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_NR] = NULL; + + /* Is this a normal file or the special case we use to send back the + default "404: Page not found" response? */ + if (uri == NULL) { + hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_FOUND]; +#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE + if (hs->keepalive) { + hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = g_psHTTPHeaderStrings[DEFAULT_404_HTML_PERSISTENT]; + } else +#endif + { + hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = g_psHTTPHeaderStrings[DEFAULT_404_HTML]; + } + + /* Set up to send the first header string. */ + hs->hdr_index = 0; + hs->hdr_pos = 0; + return; + } + /* We are dealing with a particular filename. Look for one other + special case. We assume that any filename with "404" in it must be + indicative of a 404 server error whereas all other files require + the 200 OK header. */ + if (strstr(uri, "404")) { + hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_FOUND]; + } else if (strstr(uri, "400")) { + hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_BAD_REQUEST]; + } else if (strstr(uri, "501")) { + hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_IMPL]; + } else { + hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_OK]; + } + + /* Determine if the URI has any variables and, if so, temporarily remove + them. */ + vars = strchr(uri, '?'); + if(vars) { + *vars = '\0'; + } + + /* Get a pointer to the file extension. We find this by looking for the + last occurrence of "." in the filename passed. */ + ext = NULL; + tmp = strchr(uri, '.'); + while (tmp) { + ext = tmp + 1; + tmp = strchr(ext, '.'); + } + if (ext != NULL) { + /* Now determine the content type and add the relevant header for that. */ + for (content_type = 0; content_type < NUM_HTTP_HEADERS; content_type++) { + /* Have we found a matching extension? */ + if(!lwip_stricmp(g_psHTTPHeaders[content_type].extension, ext)) { + break; + } + } + } else { + content_type = NUM_HTTP_HEADERS; + } + + /* Reinstate the parameter marker if there was one in the original URI. */ + if (vars) { + *vars = '?'; + } + +#if LWIP_HTTPD_OMIT_HEADER_FOR_EXTENSIONLESS_URI + /* Does the URL passed have any file extension? If not, we assume it + is a special-case URL used for control state notification and we do + not send any HTTP headers with the response. */ + if (!ext) { + /* Force the header index to a value indicating that all headers + have already been sent. */ + hs->hdr_index = NUM_FILE_HDR_STRINGS; + return; + } +#endif /* LWIP_HTTPD_OMIT_HEADER_FOR_EXTENSIONLESS_URI */ + add_content_len = 1; + /* Did we find a matching extension? */ + if(content_type < NUM_HTTP_HEADERS) { + /* yes, store it */ + hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = g_psHTTPHeaders[content_type].content_type; + } else if (!ext) { + /* no, no extension found -> use binary transfer to prevent the browser adding '.txt' on save */ + hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = HTTP_HDR_APP; + } else { + /* No - use the default, plain text file type. */ + hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = HTTP_HDR_DEFAULT_TYPE; + } + /* Add content-length header? */ +#if LWIP_HTTPD_SSI + if (hs->ssi != NULL) { + add_content_len = 0; /* @todo: get maximum file length from SSI */ + } else +#endif /* LWIP_HTTPD_SSI */ + if ((hs->handle == NULL) || + ((hs->handle->flags & (FS_FILE_FLAGS_HEADER_INCLUDED|FS_FILE_FLAGS_HEADER_PERSISTENT)) == FS_FILE_FLAGS_HEADER_INCLUDED)) { + add_content_len = 0; + } + if (add_content_len) { + size_t len; + lwip_itoa(hs->hdr_content_len, (size_t)LWIP_HTTPD_MAX_CONTENT_LEN_SIZE, + hs->handle->len); + len = strlen(hs->hdr_content_len); + if (len <= LWIP_HTTPD_MAX_CONTENT_LEN_SIZE - LWIP_HTTPD_MAX_CONTENT_LEN_OFFSET) { + SMEMCPY(&hs->hdr_content_len[len], CRLF "\0", 3); + hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_NR] = hs->hdr_content_len; + } else { + add_content_len = 0; + } + } +#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE + if (add_content_len) { + hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEPALIVE] = g_psHTTPHeaderStrings[HTTP_HDR_KEEPALIVE_LEN]; + } else { + hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEPALIVE] = g_psHTTPHeaderStrings[HTTP_HDR_CONN_CLOSE]; + } +#else /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */ + if (add_content_len) { + hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEPALIVE] = g_psHTTPHeaderStrings[HTTP_HDR_CONTENT_LENGTH]; + } +#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */ + + /* Set up to send the first header string. */ + hs->hdr_index = 0; + hs->hdr_pos = 0; +} + +/** Sub-function of http_send(): send dynamic headers + * + * @returns: - HTTP_NO_DATA_TO_SEND: no new data has been enqueued + * - HTTP_DATA_TO_SEND_CONTINUE: continue with sending HTTP body + * - HTTP_DATA_TO_SEND_BREAK: data has been enqueued, headers pending, + * so don't send HTTP body yet + */ +static u8_t +http_send_headers(struct tcp_pcb *pcb, struct http_state *hs) +{ + err_t err; + u16_t len; + u8_t data_to_send = HTTP_NO_DATA_TO_SEND; + u16_t hdrlen, sendlen; + + /* How much data can we send? */ + len = tcp_sndbuf(pcb); + sendlen = len; + + while(len && (hs->hdr_index < NUM_FILE_HDR_STRINGS) && sendlen) { + const void *ptr; + u16_t old_sendlen; + u8_t apiflags; + /* How much do we have to send from the current header? */ + hdrlen = (u16_t)strlen(hs->hdrs[hs->hdr_index]); + + /* How much of this can we send? */ + sendlen = (len < (hdrlen - hs->hdr_pos)) ? len : (hdrlen - hs->hdr_pos); + + /* Send this amount of data or as much as we can given memory + * constraints. */ + ptr = (const void *)(hs->hdrs[hs->hdr_index] + hs->hdr_pos); + old_sendlen = sendlen; + apiflags = HTTP_IS_HDR_VOLATILE(hs, ptr); + if (hs->hdr_index == HDR_STRINGS_IDX_CONTENT_LEN_NR) { + /* content-length is always volatile */ + apiflags |= TCP_WRITE_FLAG_COPY; + } + if (hs->hdr_index < NUM_FILE_HDR_STRINGS - 1) { + apiflags |= TCP_WRITE_FLAG_MORE; + } + err = http_write(pcb, ptr, &sendlen, apiflags); + if ((err == ERR_OK) && (old_sendlen != sendlen)) { + /* Remember that we added some more data to be transmitted. */ + data_to_send = HTTP_DATA_TO_SEND_CONTINUE; + } else if (err != ERR_OK) { + /* special case: http_write does not try to send 1 byte */ + sendlen = 0; + } + + /* Fix up the header position for the next time round. */ + hs->hdr_pos += sendlen; + len -= sendlen; + + /* Have we finished sending this string? */ + if(hs->hdr_pos == hdrlen) { + /* Yes - move on to the next one */ + hs->hdr_index++; + /* skip headers that are NULL (not all headers are required) */ + while ((hs->hdr_index < NUM_FILE_HDR_STRINGS) && + (hs->hdrs[hs->hdr_index] == NULL)) { + hs->hdr_index++; + } + hs->hdr_pos = 0; + } + } + + if ((hs->hdr_index >= NUM_FILE_HDR_STRINGS) && (hs->file == NULL)) { + /* When we are at the end of the headers, check for data to send + * instead of waiting for ACK from remote side to continue + * (which would happen when sending files from async read). */ + if(http_check_eof(pcb, hs)) { + data_to_send = HTTP_DATA_TO_SEND_CONTINUE; + } + } + /* If we get here and there are still header bytes to send, we send + * the header information we just wrote immediately. If there are no + * more headers to send, but we do have file data to send, drop through + * to try to send some file data too. */ + if((hs->hdr_index < NUM_FILE_HDR_STRINGS) || !hs->file) { + LWIP_DEBUGF(HTTPD_DEBUG, ("tcp_output\n")); + return HTTP_DATA_TO_SEND_BREAK; + } + return data_to_send; +} +#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ + +/** Sub-function of http_send(): end-of-file (or block) is reached, + * either close the file or read the next block (if supported). + * + * @returns: 0 if the file is finished or no data has been read + * 1 if the file is not finished and data has been read + */ +static u8_t +http_check_eof(struct tcp_pcb *pcb, struct http_state *hs) +{ + int bytes_left; +#if LWIP_HTTPD_DYNAMIC_FILE_READ + int count; +#ifdef HTTPD_MAX_WRITE_LEN + int max_write_len; +#endif /* HTTPD_MAX_WRITE_LEN */ +#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */ + + /* Do we have a valid file handle? */ + if (hs->handle == NULL) { + /* No - close the connection. */ + http_eof(pcb, hs); + return 0; + } + bytes_left = fs_bytes_left(hs->handle); + if (bytes_left <= 0) { + /* We reached the end of the file so this request is done. */ + LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n")); + http_eof(pcb, hs); + return 0; + } +#if LWIP_HTTPD_DYNAMIC_FILE_READ + /* Do we already have a send buffer allocated? */ + if(hs->buf) { + /* Yes - get the length of the buffer */ + count = LWIP_MIN(hs->buf_len, bytes_left); + } else { + /* We don't have a send buffer so allocate one now */ + count = tcp_sndbuf(pcb); + if(bytes_left < count) { + count = bytes_left; + } +#ifdef HTTPD_MAX_WRITE_LEN + /* Additional limitation: e.g. don't enqueue more than 2*mss at once */ + max_write_len = HTTPD_MAX_WRITE_LEN(pcb); + if (count > max_write_len) { + count = max_write_len; + } +#endif /* HTTPD_MAX_WRITE_LEN */ + do { + hs->buf = (char*)mem_malloc((mem_size_t)count); + if (hs->buf != NULL) { + hs->buf_len = count; + break; + } + count = count / 2; + } while (count > 100); + + /* Did we get a send buffer? If not, return immediately. */ + if (hs->buf == NULL) { + LWIP_DEBUGF(HTTPD_DEBUG, ("No buff\n")); + return 0; + } + } + + /* Read a block of data from the file. */ + LWIP_DEBUGF(HTTPD_DEBUG, ("Trying to read %d bytes.\n", count)); + +#if LWIP_HTTPD_FS_ASYNC_READ + count = fs_read_async(hs->handle, hs->buf, count, http_continue, hs); +#else /* LWIP_HTTPD_FS_ASYNC_READ */ + count = fs_read(hs->handle, hs->buf, count); +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ + if (count < 0) { + if (count == FS_READ_DELAYED) { + /* Delayed read, wait for FS to unblock us */ + return 0; + } + /* We reached the end of the file so this request is done. + * @todo: close here for HTTP/1.1 when reading file fails */ + LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n")); + http_eof(pcb, hs); + return 0; + } + + /* Set up to send the block of data we just read */ + LWIP_DEBUGF(HTTPD_DEBUG, ("Read %d bytes.\n", count)); + hs->left = count; + hs->file = hs->buf; +#if LWIP_HTTPD_SSI + if (hs->ssi) { + hs->ssi->parse_left = count; + hs->ssi->parsed = hs->buf; + } +#endif /* LWIP_HTTPD_SSI */ +#else /* LWIP_HTTPD_DYNAMIC_FILE_READ */ + LWIP_ASSERT("SSI and DYNAMIC_HEADERS turned off but eof not reached", 0); +#endif /* LWIP_HTTPD_SSI || LWIP_HTTPD_DYNAMIC_HEADERS */ + return 1; +} + +/** Sub-function of http_send(): This is the normal send-routine for non-ssi files + * + * @returns: - 1: data has been written (so call tcp_ouput) + * - 0: no data has been written (no need to call tcp_output) + */ +static u8_t +http_send_data_nonssi(struct tcp_pcb *pcb, struct http_state *hs) +{ + err_t err; + u16_t len; + u8_t data_to_send = 0; + + /* We are not processing an SHTML file so no tag checking is necessary. + * Just send the data as we received it from the file. */ + len = (u16_t)LWIP_MIN(hs->left, 0xffff); + + err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs)); + if (err == ERR_OK) { + data_to_send = 1; + hs->file += len; + hs->left -= len; + } + + return data_to_send; +} + +#if LWIP_HTTPD_SSI +/** Sub-function of http_send(): This is the send-routine for ssi files + * + * @returns: - 1: data has been written (so call tcp_ouput) + * - 0: no data has been written (no need to call tcp_output) + */ +static u8_t +http_send_data_ssi(struct tcp_pcb *pcb, struct http_state *hs) +{ + err_t err = ERR_OK; + u16_t len; + u8_t data_to_send = 0; + + struct http_ssi_state *ssi = hs->ssi; + LWIP_ASSERT("ssi != NULL", ssi != NULL); + /* We are processing an SHTML file so need to scan for tags and replace + * them with insert strings. We need to be careful here since a tag may + * straddle the boundary of two blocks read from the file and we may also + * have to split the insert string between two tcp_write operations. */ + + /* How much data could we send? */ + len = tcp_sndbuf(pcb); + + /* Do we have remaining data to send before parsing more? */ + if(ssi->parsed > hs->file) { + len = (u16_t)LWIP_MIN(ssi->parsed - hs->file, 0xffff); + + err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs)); + if (err == ERR_OK) { + data_to_send = 1; + hs->file += len; + hs->left -= len; + } + + /* If the send buffer is full, return now. */ + if(tcp_sndbuf(pcb) == 0) { + return data_to_send; + } + } + + LWIP_DEBUGF(HTTPD_DEBUG, ("State %d, %d left\n", ssi->tag_state, (int)ssi->parse_left)); + + /* We have sent all the data that was already parsed so continue parsing + * the buffer contents looking for SSI tags. */ + while((ssi->parse_left) && (err == ERR_OK)) { + if (len == 0) { + return data_to_send; + } + switch(ssi->tag_state) { + case TAG_NONE: + /* We are not currently processing an SSI tag so scan for the + * start of the lead-in marker. */ + if(*ssi->parsed == g_pcTagLeadIn[0]) { + /* We found what could be the lead-in for a new tag so change + * state appropriately. */ + ssi->tag_state = TAG_LEADIN; + ssi->tag_index = 1; +#if !LWIP_HTTPD_SSI_INCLUDE_TAG + ssi->tag_started = ssi->parsed; +#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG */ + } + + /* Move on to the next character in the buffer */ + ssi->parse_left--; + ssi->parsed++; + break; + + case TAG_LEADIN: + /* We are processing the lead-in marker, looking for the start of + * the tag name. */ + + /* Have we reached the end of the leadin? */ + if(ssi->tag_index == LEN_TAG_LEAD_IN) { + ssi->tag_index = 0; + ssi->tag_state = TAG_FOUND; + } else { + /* Have we found the next character we expect for the tag leadin? */ + if(*ssi->parsed == g_pcTagLeadIn[ssi->tag_index]) { + /* Yes - move to the next one unless we have found the complete + * leadin, in which case we start looking for the tag itself */ + ssi->tag_index++; + } else { + /* We found an unexpected character so this is not a tag. Move + * back to idle state. */ + ssi->tag_state = TAG_NONE; + } + + /* Move on to the next character in the buffer */ + ssi->parse_left--; + ssi->parsed++; + } + break; + + case TAG_FOUND: + /* We are reading the tag name, looking for the start of the + * lead-out marker and removing any whitespace found. */ + + /* Remove leading whitespace between the tag leading and the first + * tag name character. */ + if((ssi->tag_index == 0) && ((*ssi->parsed == ' ') || + (*ssi->parsed == '\t') || (*ssi->parsed == '\n') || + (*ssi->parsed == '\r'))) { + /* Move on to the next character in the buffer */ + ssi->parse_left--; + ssi->parsed++; + break; + } + + /* Have we found the end of the tag name? This is signalled by + * us finding the first leadout character or whitespace */ + if((*ssi->parsed == g_pcTagLeadOut[0]) || + (*ssi->parsed == ' ') || (*ssi->parsed == '\t') || + (*ssi->parsed == '\n') || (*ssi->parsed == '\r')) { + + if(ssi->tag_index == 0) { + /* We read a zero length tag so ignore it. */ + ssi->tag_state = TAG_NONE; + } else { + /* We read a non-empty tag so go ahead and look for the + * leadout string. */ + ssi->tag_state = TAG_LEADOUT; + LWIP_ASSERT("ssi->tag_index <= 0xff", ssi->tag_index <= 0xff); + ssi->tag_name_len = (u8_t)ssi->tag_index; + ssi->tag_name[ssi->tag_index] = '\0'; + if(*ssi->parsed == g_pcTagLeadOut[0]) { + ssi->tag_index = 1; + } else { + ssi->tag_index = 0; + } + } + } else { + /* This character is part of the tag name so save it */ + if(ssi->tag_index < LWIP_HTTPD_MAX_TAG_NAME_LEN) { + ssi->tag_name[ssi->tag_index++] = *ssi->parsed; + } else { + /* The tag was too long so ignore it. */ + ssi->tag_state = TAG_NONE; + } + } + + /* Move on to the next character in the buffer */ + ssi->parse_left--; + ssi->parsed++; + + break; + + /* We are looking for the end of the lead-out marker. */ + case TAG_LEADOUT: + /* Remove leading whitespace between the tag leading and the first + * tag leadout character. */ + if((ssi->tag_index == 0) && ((*ssi->parsed == ' ') || + (*ssi->parsed == '\t') || (*ssi->parsed == '\n') || + (*ssi->parsed == '\r'))) { + /* Move on to the next character in the buffer */ + ssi->parse_left--; + ssi->parsed++; + break; + } + + /* Have we found the next character we expect for the tag leadout? */ + if(*ssi->parsed == g_pcTagLeadOut[ssi->tag_index]) { + /* Yes - move to the next one unless we have found the complete + * leadout, in which case we need to call the client to process + * the tag. */ + + /* Move on to the next character in the buffer */ + ssi->parse_left--; + ssi->parsed++; + + if(ssi->tag_index == (LEN_TAG_LEAD_OUT - 1)) { + /* Call the client to ask for the insert string for the + * tag we just found. */ +#if LWIP_HTTPD_SSI_MULTIPART + ssi->tag_part = 0; /* start with tag part 0 */ +#endif /* LWIP_HTTPD_SSI_MULTIPART */ + get_tag_insert(hs); + + /* Next time through, we are going to be sending data + * immediately, either the end of the block we start + * sending here or the insert string. */ + ssi->tag_index = 0; + ssi->tag_state = TAG_SENDING; + ssi->tag_end = ssi->parsed; +#if !LWIP_HTTPD_SSI_INCLUDE_TAG + ssi->parsed = ssi->tag_started; +#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/ + + /* If there is any unsent data in the buffer prior to the + * tag, we need to send it now. */ + if (ssi->tag_end > hs->file) { + /* How much of the data can we send? */ +#if LWIP_HTTPD_SSI_INCLUDE_TAG + len = (u16_t)LWIP_MIN(ssi->tag_end - hs->file, 0xffff); +#else /* LWIP_HTTPD_SSI_INCLUDE_TAG*/ + /* we would include the tag in sending */ + len = (u16_t)LWIP_MIN(ssi->tag_started - hs->file, 0xffff); +#endif /* LWIP_HTTPD_SSI_INCLUDE_TAG*/ + + err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs)); + if (err == ERR_OK) { + data_to_send = 1; +#if !LWIP_HTTPD_SSI_INCLUDE_TAG + if(ssi->tag_started <= hs->file) { + /* pretend to have sent the tag, too */ + len += ssi->tag_end - ssi->tag_started; + } +#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/ + hs->file += len; + hs->left -= len; + } + } + } else { + ssi->tag_index++; + } + } else { + /* We found an unexpected character so this is not a tag. Move + * back to idle state. */ + ssi->parse_left--; + ssi->parsed++; + ssi->tag_state = TAG_NONE; + } + break; + + /* + * We have found a valid tag and are in the process of sending + * data as a result of that discovery. We send either remaining data + * from the file prior to the insert point or the insert string itself. + */ + case TAG_SENDING: + /* Do we have any remaining file data to send from the buffer prior + * to the tag? */ + if(ssi->tag_end > hs->file) { + /* How much of the data can we send? */ +#if LWIP_HTTPD_SSI_INCLUDE_TAG + len = (u16_t)LWIP_MIN(ssi->tag_end - hs->file, 0xffff); +#else /* LWIP_HTTPD_SSI_INCLUDE_TAG*/ + LWIP_ASSERT("hs->started >= hs->file", ssi->tag_started >= hs->file); + /* we would include the tag in sending */ + len = (u16_t)LWIP_MIN(ssi->tag_started - hs->file, 0xffff); +#endif /* LWIP_HTTPD_SSI_INCLUDE_TAG*/ + if (len != 0) { + err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs)); + } else { + err = ERR_OK; + } + if (err == ERR_OK) { + data_to_send = 1; +#if !LWIP_HTTPD_SSI_INCLUDE_TAG + if(ssi->tag_started <= hs->file) { + /* pretend to have sent the tag, too */ + len += ssi->tag_end - ssi->tag_started; + } +#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/ + hs->file += len; + hs->left -= len; + } + } else { +#if LWIP_HTTPD_SSI_MULTIPART + if(ssi->tag_index >= ssi->tag_insert_len) { + /* Did the last SSIHandler have more to send? */ + if (ssi->tag_part != HTTPD_LAST_TAG_PART) { + /* If so, call it again */ + ssi->tag_index = 0; + get_tag_insert(hs); + } + } +#endif /* LWIP_HTTPD_SSI_MULTIPART */ + + /* Do we still have insert data left to send? */ + if(ssi->tag_index < ssi->tag_insert_len) { + /* We are sending the insert string itself. How much of the + * insert can we send? */ + len = (ssi->tag_insert_len - ssi->tag_index); + + /* Note that we set the copy flag here since we only have a + * single tag insert buffer per connection. If we don't do + * this, insert corruption can occur if more than one insert + * is processed before we call tcp_output. */ + err = http_write(pcb, &(ssi->tag_insert[ssi->tag_index]), &len, + HTTP_IS_TAG_VOLATILE(hs)); + if (err == ERR_OK) { + data_to_send = 1; + ssi->tag_index += len; + /* Don't return here: keep on sending data */ + } + } else { +#if LWIP_HTTPD_SSI_MULTIPART + if (ssi->tag_part == HTTPD_LAST_TAG_PART) +#endif /* LWIP_HTTPD_SSI_MULTIPART */ + { + /* We have sent all the insert data so go back to looking for + * a new tag. */ + LWIP_DEBUGF(HTTPD_DEBUG, ("Everything sent.\n")); + ssi->tag_index = 0; + ssi->tag_state = TAG_NONE; +#if !LWIP_HTTPD_SSI_INCLUDE_TAG + ssi->parsed = ssi->tag_end; +#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/ + } + } + break; + default: + break; + } + } + } + + /* If we drop out of the end of the for loop, this implies we must have + * file data to send so send it now. In TAG_SENDING state, we've already + * handled this so skip the send if that's the case. */ + if((ssi->tag_state != TAG_SENDING) && (ssi->parsed > hs->file)) { + len = (u16_t)LWIP_MIN(ssi->parsed - hs->file, 0xffff); + + err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs)); + if (err == ERR_OK) { + data_to_send = 1; + hs->file += len; + hs->left -= len; + } + } + return data_to_send; +} +#endif /* LWIP_HTTPD_SSI */ + +/** + * Try to send more data on this pcb. + * + * @param pcb the pcb to send data + * @param hs connection state + */ +static u8_t +http_send(struct tcp_pcb *pcb, struct http_state *hs) +{ + u8_t data_to_send = HTTP_NO_DATA_TO_SEND; + + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_send: pcb=%p hs=%p left=%d\n", (void*)pcb, + (void*)hs, hs != NULL ? (int)hs->left : 0)); + +#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND + if (hs->unrecved_bytes != 0) { + return 0; + } +#endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */ + + /* If we were passed a NULL state structure pointer, ignore the call. */ + if (hs == NULL) { + return 0; + } + +#if LWIP_HTTPD_FS_ASYNC_READ + /* Check if we are allowed to read from this file. + (e.g. SSI might want to delay sending until data is available) */ + if (!fs_is_file_ready(hs->handle, http_continue, hs)) { + return 0; + } +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ + +#if LWIP_HTTPD_DYNAMIC_HEADERS + /* Do we have any more header data to send for this file? */ + if (hs->hdr_index < NUM_FILE_HDR_STRINGS) { + data_to_send = http_send_headers(pcb, hs); + if ((data_to_send != HTTP_DATA_TO_SEND_CONTINUE) && + (hs->hdr_index < NUM_FILE_HDR_STRINGS)) { + return data_to_send; + } + } +#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ + + /* Have we run out of file data to send? If so, we need to read the next + * block from the file. */ + if (hs->left == 0) { + if (!http_check_eof(pcb, hs)) { + return 0; + } + } + +#if LWIP_HTTPD_SSI + if(hs->ssi) { + data_to_send = http_send_data_ssi(pcb, hs); + } else +#endif /* LWIP_HTTPD_SSI */ + { + data_to_send = http_send_data_nonssi(pcb, hs); + } + + if((hs->left == 0) && (fs_bytes_left(hs->handle) <= 0)) { + /* We reached the end of the file so this request is done. + * This adds the FIN flag right into the last data segment. */ + LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n")); + http_eof(pcb, hs); + return 0; + } + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("send_data end.\n")); + return data_to_send; +} + +#if LWIP_HTTPD_SUPPORT_EXTSTATUS +/** Initialize a http connection with a file to send for an error message + * + * @param hs http connection state + * @param error_nr HTTP error number + * @return ERR_OK if file was found and hs has been initialized correctly + * another err_t otherwise + */ +static err_t +http_find_error_file(struct http_state *hs, u16_t error_nr) +{ + const char *uri1, *uri2, *uri3; + err_t err; + + if (error_nr == 501) { + uri1 = "/501.html"; + uri2 = "/501.htm"; + uri3 = "/501.shtml"; + } else { + /* 400 (bad request is the default) */ + uri1 = "/400.html"; + uri2 = "/400.htm"; + uri3 = "/400.shtml"; + } + err = fs_open(&hs->file_handle, uri1); + if (err != ERR_OK) { + err = fs_open(&hs->file_handle, uri2); + if (err != ERR_OK) { + err = fs_open(&hs->file_handle, uri3); + if (err != ERR_OK) { + LWIP_DEBUGF(HTTPD_DEBUG, ("Error page for error %"U16_F" not found\n", + error_nr)); + return ERR_ARG; + } + } + } + return http_init_file(hs, &hs->file_handle, 0, NULL, 0, NULL); +} +#else /* LWIP_HTTPD_SUPPORT_EXTSTATUS */ +#define http_find_error_file(hs, error_nr) ERR_ARG +#endif /* LWIP_HTTPD_SUPPORT_EXTSTATUS */ + +/** + * Get the file struct for a 404 error page. + * Tries some file names and returns NULL if none found. + * + * @param uri pointer that receives the actual file name URI + * @return file struct for the error page or NULL no matching file was found + */ +static struct fs_file * +http_get_404_file(struct http_state *hs, const char **uri) +{ + err_t err; + + *uri = "/404.html"; + err = fs_open(&hs->file_handle, *uri); + if (err != ERR_OK) { + /* 404.html doesn't exist. Try 404.htm instead. */ + *uri = "/404.htm"; + err = fs_open(&hs->file_handle, *uri); + if (err != ERR_OK) { + /* 404.htm doesn't exist either. Try 404.shtml instead. */ + *uri = "/404.shtml"; + err = fs_open(&hs->file_handle, *uri); + if (err != ERR_OK) { + /* 404.htm doesn't exist either. Indicate to the caller that it should + * send back a default 404 page. + */ + *uri = NULL; + return NULL; + } + } + } + + return &hs->file_handle; +} + +#if LWIP_HTTPD_SUPPORT_POST +static err_t +http_handle_post_finished(struct http_state *hs) +{ +#if LWIP_HTTPD_POST_MANUAL_WND + /* Prevent multiple calls to httpd_post_finished, since it might have already + been called before from httpd_post_data_recved(). */ + if (hs->post_finished) { + return ERR_OK; + } + hs->post_finished = 1; +#endif /* LWIP_HTTPD_POST_MANUAL_WND */ + /* application error or POST finished */ + /* NULL-terminate the buffer */ + http_uri_buf[0] = 0; + httpd_post_finished(hs, http_uri_buf, LWIP_HTTPD_URI_BUF_LEN); + return http_find_file(hs, http_uri_buf, 0); +} + +/** Pass received POST body data to the application and correctly handle + * returning a response document or closing the connection. + * ATTENTION: The application is responsible for the pbuf now, so don't free it! + * + * @param hs http connection state + * @param p pbuf to pass to the application + * @return ERR_OK if passed successfully, another err_t if the response file + * hasn't been found (after POST finished) + */ +static err_t +http_post_rxpbuf(struct http_state *hs, struct pbuf *p) +{ + err_t err; + + if (p != NULL) { + /* adjust remaining Content-Length */ + if (hs->post_content_len_left < p->tot_len) { + hs->post_content_len_left = 0; + } else { + hs->post_content_len_left -= p->tot_len; + } + } +#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND + /* prevent connection being closed if httpd_post_data_recved() is called nested */ + hs->unrecved_bytes++; +#endif + err = httpd_post_receive_data(hs, p); +#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND + hs->unrecved_bytes--; +#endif + if (err != ERR_OK) { + /* Ignore remaining content in case of application error */ + hs->post_content_len_left = 0; + } + if (hs->post_content_len_left == 0) { +#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND + if (hs->unrecved_bytes != 0) { + return ERR_OK; + } +#endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */ + /* application error or POST finished */ + return http_handle_post_finished(hs); + } + + return ERR_OK; +} + +/** Handle a post request. Called from http_parse_request when method 'POST' + * is found. + * + * @param p The input pbuf (containing the POST header and body). + * @param hs The http connection state. + * @param data HTTP request (header and part of body) from input pbuf(s). + * @param data_len Size of 'data'. + * @param uri The HTTP URI parsed from input pbuf(s). + * @param uri_end Pointer to the end of 'uri' (here, the rest of the HTTP + * header starts). + * @return ERR_OK: POST correctly parsed and accepted by the application. + * ERR_INPROGRESS: POST not completely parsed (no error yet) + * another err_t: Error parsing POST or denied by the application + */ +static err_t +http_post_request(struct pbuf *inp, struct http_state *hs, + char *data, u16_t data_len, char *uri, char *uri_end) +{ + err_t err; + /* search for end-of-header (first double-CRLF) */ + char* crlfcrlf = lwip_strnstr(uri_end + 1, CRLF CRLF, data_len - (uri_end + 1 - data)); + + if (crlfcrlf != NULL) { + /* search for "Content-Length: " */ +#define HTTP_HDR_CONTENT_LEN "Content-Length: " +#define HTTP_HDR_CONTENT_LEN_LEN 16 +#define HTTP_HDR_CONTENT_LEN_DIGIT_MAX_LEN 10 + char *scontent_len = lwip_strnstr(uri_end + 1, HTTP_HDR_CONTENT_LEN, crlfcrlf - (uri_end + 1)); + if (scontent_len != NULL) { + char *scontent_len_end = lwip_strnstr(scontent_len + HTTP_HDR_CONTENT_LEN_LEN, CRLF, HTTP_HDR_CONTENT_LEN_DIGIT_MAX_LEN); + if (scontent_len_end != NULL) { + int content_len; + char *content_len_num = scontent_len + HTTP_HDR_CONTENT_LEN_LEN; + content_len = atoi(content_len_num); + if (content_len == 0) { + /* if atoi returns 0 on error, fix this */ + if ((content_len_num[0] != '0') || (content_len_num[1] != '\r')) { + content_len = -1; + } + } + if (content_len >= 0) { + /* adjust length of HTTP header passed to application */ + const char *hdr_start_after_uri = uri_end + 1; + u16_t hdr_len = (u16_t)LWIP_MIN(data_len, crlfcrlf + 4 - data); + u16_t hdr_data_len = (u16_t)LWIP_MIN(data_len, crlfcrlf + 4 - hdr_start_after_uri); + u8_t post_auto_wnd = 1; + http_uri_buf[0] = 0; + /* trim http header */ + *crlfcrlf = 0; + err = httpd_post_begin(hs, uri, hdr_start_after_uri, hdr_data_len, content_len, + http_uri_buf, LWIP_HTTPD_URI_BUF_LEN, &post_auto_wnd); + if (err == ERR_OK) { + /* try to pass in data of the first pbuf(s) */ + struct pbuf *q = inp; + u16_t start_offset = hdr_len; +#if LWIP_HTTPD_POST_MANUAL_WND + hs->no_auto_wnd = !post_auto_wnd; +#endif /* LWIP_HTTPD_POST_MANUAL_WND */ + /* set the Content-Length to be received for this POST */ + hs->post_content_len_left = (u32_t)content_len; + + /* get to the pbuf where the body starts */ + while((q != NULL) && (q->len <= start_offset)) { + start_offset -= q->len; + q = q->next; + } + if (q != NULL) { + /* hide the remaining HTTP header */ + pbuf_header(q, -(s16_t)start_offset); +#if LWIP_HTTPD_POST_MANUAL_WND + if (!post_auto_wnd) { + /* already tcp_recved() this data... */ + hs->unrecved_bytes = q->tot_len; + } +#endif /* LWIP_HTTPD_POST_MANUAL_WND */ + pbuf_ref(q); + return http_post_rxpbuf(hs, q); + } else if (hs->post_content_len_left == 0) { + q = pbuf_alloc(PBUF_RAW, 0, PBUF_REF); + return http_post_rxpbuf(hs, q); + } else { + return ERR_OK; + } + } else { + /* return file passed from application */ + return http_find_file(hs, http_uri_buf, 0); + } + } else { + LWIP_DEBUGF(HTTPD_DEBUG, ("POST received invalid Content-Length: %s\n", + content_len_num)); + return ERR_ARG; + } + } + } + /* If we come here, headers are fully received (double-crlf), but Content-Length + was not included. Since this is currently the only supported method, we have + to fail in this case! */ + LWIP_DEBUGF(HTTPD_DEBUG, ("Error when parsing Content-Length\n")); + return ERR_ARG; + } + /* if we come here, the POST is incomplete */ +#if LWIP_HTTPD_SUPPORT_REQUESTLIST + return ERR_INPROGRESS; +#else /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ + return ERR_ARG; +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ +} + +#if LWIP_HTTPD_POST_MANUAL_WND +/** A POST implementation can call this function to update the TCP window. + * This can be used to throttle data reception (e.g. when received data is + * programmed to flash and data is received faster than programmed). + * + * @param connection A connection handle passed to httpd_post_begin for which + * httpd_post_finished has *NOT* been called yet! + * @param recved_len Length of data received (for window update) + */ +void httpd_post_data_recved(void *connection, u16_t recved_len) +{ + struct http_state *hs = (struct http_state*)connection; + if (hs != NULL) { + if (hs->no_auto_wnd) { + u16_t len = recved_len; + if (hs->unrecved_bytes >= recved_len) { + hs->unrecved_bytes -= recved_len; + } else { + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_LEVEL_WARNING, ("httpd_post_data_recved: recved_len too big\n")); + len = (u16_t)hs->unrecved_bytes; + hs->unrecved_bytes = 0; + } + if (hs->pcb != NULL) { + if (len != 0) { + tcp_recved(hs->pcb, len); + } + if ((hs->post_content_len_left == 0) && (hs->unrecved_bytes == 0)) { + /* finished handling POST */ + http_handle_post_finished(hs); + http_send(hs->pcb, hs); + } + } + } + } +} +#endif /* LWIP_HTTPD_POST_MANUAL_WND */ + +#endif /* LWIP_HTTPD_SUPPORT_POST */ + +#if LWIP_HTTPD_FS_ASYNC_READ +/** Try to send more data if file has been blocked before + * This is a callback function passed to fs_read_async(). + */ +static void +http_continue(void *connection) +{ + struct http_state *hs = (struct http_state*)connection; + if (hs && (hs->pcb) && (hs->handle)) { + LWIP_ASSERT("hs->pcb != NULL", hs->pcb != NULL); + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("httpd_continue: try to send more data\n")); + if (http_send(hs->pcb, hs)) { + /* If we wrote anything to be sent, go ahead and send it now. */ + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("tcp_output\n")); + tcp_output(hs->pcb); + } + } +} +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ + +/** + * When data has been received in the correct state, try to parse it + * as a HTTP request. + * + * @param inp the received pbuf + * @param hs the connection state + * @param pcb the tcp_pcb which received this packet + * @return ERR_OK if request was OK and hs has been initialized correctly + * ERR_INPROGRESS if request was OK so far but not fully received + * another err_t otherwise + */ +static err_t +http_parse_request(struct pbuf *inp, struct http_state *hs, struct tcp_pcb *pcb) +{ + char *data; + char *crlf; + u16_t data_len; + struct pbuf *p = inp; +#if LWIP_HTTPD_SUPPORT_REQUESTLIST + u16_t clen; +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ +#if LWIP_HTTPD_SUPPORT_POST + err_t err; +#endif /* LWIP_HTTPD_SUPPORT_POST */ + + LWIP_UNUSED_ARG(pcb); /* only used for post */ + LWIP_ASSERT("p != NULL", p != NULL); + LWIP_ASSERT("hs != NULL", hs != NULL); + + if ((hs->handle != NULL) || (hs->file != NULL)) { + LWIP_DEBUGF(HTTPD_DEBUG, ("Received data while sending a file\n")); + /* already sending a file */ + /* @todo: abort? */ + return ERR_USE; + } + +#if LWIP_HTTPD_SUPPORT_REQUESTLIST + + LWIP_DEBUGF(HTTPD_DEBUG, ("Received %"U16_F" bytes\n", p->tot_len)); + + /* first check allowed characters in this pbuf? */ + + /* enqueue the pbuf */ + if (hs->req == NULL) { + LWIP_DEBUGF(HTTPD_DEBUG, ("First pbuf\n")); + hs->req = p; + } else { + LWIP_DEBUGF(HTTPD_DEBUG, ("pbuf enqueued\n")); + pbuf_cat(hs->req, p); + } + /* increase pbuf ref counter as it is freed when we return but we want to + keep it on the req list */ + pbuf_ref(p); + + if (hs->req->next != NULL) { + data_len = LWIP_MIN(hs->req->tot_len, LWIP_HTTPD_MAX_REQ_LENGTH); + pbuf_copy_partial(hs->req, httpd_req_buf, data_len, 0); + data = httpd_req_buf; + } else +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ + { + data = (char *)p->payload; + data_len = p->len; + if (p->len != p->tot_len) { + LWIP_DEBUGF(HTTPD_DEBUG, ("Warning: incomplete header due to chained pbufs\n")); + } + } + + /* received enough data for minimal request? */ + if (data_len >= MIN_REQ_LEN) { + /* wait for CRLF before parsing anything */ + crlf = lwip_strnstr(data, CRLF, data_len); + if (crlf != NULL) { +#if LWIP_HTTPD_SUPPORT_POST + int is_post = 0; +#endif /* LWIP_HTTPD_SUPPORT_POST */ + int is_09 = 0; + char *sp1, *sp2; + u16_t left_len, uri_len; + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("CRLF received, parsing request\n")); + /* parse method */ + if (!strncmp(data, "GET ", 4)) { + sp1 = data + 3; + /* received GET request */ + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Received GET request\"\n")); +#if LWIP_HTTPD_SUPPORT_POST + } else if (!strncmp(data, "POST ", 5)) { + /* store request type */ + is_post = 1; + sp1 = data + 4; + /* received GET request */ + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Received POST request\n")); +#endif /* LWIP_HTTPD_SUPPORT_POST */ + } else { + /* null-terminate the METHOD (pbuf is freed anyway wen returning) */ + data[4] = 0; + /* unsupported method! */ + LWIP_DEBUGF(HTTPD_DEBUG, ("Unsupported request method (not implemented): \"%s\"\n", + data)); + return http_find_error_file(hs, 501); + } + /* if we come here, method is OK, parse URI */ + left_len = (u16_t)(data_len - ((sp1 +1) - data)); + sp2 = lwip_strnstr(sp1 + 1, " ", left_len); +#if LWIP_HTTPD_SUPPORT_V09 + if (sp2 == NULL) { + /* HTTP 0.9: respond with correct protocol version */ + sp2 = lwip_strnstr(sp1 + 1, CRLF, left_len); + is_09 = 1; +#if LWIP_HTTPD_SUPPORT_POST + if (is_post) { + /* HTTP/0.9 does not support POST */ + goto badrequest; + } +#endif /* LWIP_HTTPD_SUPPORT_POST */ + } +#endif /* LWIP_HTTPD_SUPPORT_V09 */ + uri_len = (u16_t)(sp2 - (sp1 + 1)); + if ((sp2 != 0) && (sp2 > sp1)) { + /* wait for CRLFCRLF (indicating end of HTTP headers) before parsing anything */ + if (lwip_strnstr(data, CRLF CRLF, data_len) != NULL) { + char *uri = sp1 + 1; +#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE + /* This is HTTP/1.0 compatible: for strict 1.1, a connection + would always be persistent unless "close" was specified. */ + if (!is_09 && (lwip_strnstr(data, HTTP11_CONNECTIONKEEPALIVE, data_len) || + lwip_strnstr(data, HTTP11_CONNECTIONKEEPALIVE2, data_len))) { + hs->keepalive = 1; + } else { + hs->keepalive = 0; + } +#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */ + /* null-terminate the METHOD (pbuf is freed anyway wen returning) */ + *sp1 = 0; + uri[uri_len] = 0; + LWIP_DEBUGF(HTTPD_DEBUG, ("Received \"%s\" request for URI: \"%s\"\n", + data, uri)); +#if LWIP_HTTPD_SUPPORT_POST + if (is_post) { +#if LWIP_HTTPD_SUPPORT_REQUESTLIST + struct pbuf *q = hs->req; +#else /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ + struct pbuf *q = inp; +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ + err = http_post_request(q, hs, data, data_len, uri, sp2); + if (err != ERR_OK) { + /* restore header for next try */ + *sp1 = ' '; + *sp2 = ' '; + uri[uri_len] = ' '; + } + if (err == ERR_ARG) { + goto badrequest; + } + return err; + } else +#endif /* LWIP_HTTPD_SUPPORT_POST */ + { + return http_find_file(hs, uri, is_09); + } + } + } else { + LWIP_DEBUGF(HTTPD_DEBUG, ("invalid URI\n")); + } + } + } + +#if LWIP_HTTPD_SUPPORT_REQUESTLIST + clen = pbuf_clen(hs->req); + if ((hs->req->tot_len <= LWIP_HTTPD_REQ_BUFSIZE) && + (clen <= LWIP_HTTPD_REQ_QUEUELEN)) { + /* request not fully received (too short or CRLF is missing) */ + return ERR_INPROGRESS; + } else +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ + { +#if LWIP_HTTPD_SUPPORT_POST +badrequest: +#endif /* LWIP_HTTPD_SUPPORT_POST */ + LWIP_DEBUGF(HTTPD_DEBUG, ("bad request\n")); + /* could not parse request */ + return http_find_error_file(hs, 400); + } +} + +/** Try to find the file specified by uri and, if found, initialize hs + * accordingly. + * + * @param hs the connection state + * @param uri the HTTP header URI + * @param is_09 1 if the request is HTTP/0.9 (no HTTP headers in response) + * @return ERR_OK if file was found and hs has been initialized correctly + * another err_t otherwise + */ +static err_t +http_find_file(struct http_state *hs, const char *uri, int is_09) +{ + size_t loop; + struct fs_file *file = NULL; + char *params = NULL; + err_t err; +#if LWIP_HTTPD_CGI + int i; +#endif /* LWIP_HTTPD_CGI */ +#if !LWIP_HTTPD_SSI + const +#endif /* !LWIP_HTTPD_SSI */ + /* By default, assume we will not be processing server-side-includes tags */ + u8_t tag_check = 0; + + /* Have we been asked for the default file (in root or a directory) ? */ +#if LWIP_HTTPD_MAX_REQUEST_URI_LEN + size_t uri_len = strlen(uri); + if ((uri_len > 0) && (uri[uri_len-1] == '/') && + ((uri != http_uri_buf) || (uri_len == 1))) { + size_t copy_len = LWIP_MIN(sizeof(http_uri_buf) - 1, uri_len - 1); + if (copy_len > 0) { + MEMCPY(http_uri_buf, uri, copy_len); + http_uri_buf[copy_len] = 0; + } +#else /* LWIP_HTTPD_MAX_REQUEST_URI_LEN */ + if ((uri[0] == '/') && (uri[1] == 0)) { +#endif /* LWIP_HTTPD_MAX_REQUEST_URI_LEN */ + /* Try each of the configured default filenames until we find one + that exists. */ + for (loop = 0; loop < NUM_DEFAULT_FILENAMES; loop++) { + const char* file_name; +#if LWIP_HTTPD_MAX_REQUEST_URI_LEN + if (copy_len > 0) { + size_t len_left = sizeof(http_uri_buf) - copy_len - 1; + if (len_left > 0) { + size_t name_len = strlen(g_psDefaultFilenames[loop].name); + size_t name_copy_len = LWIP_MIN(len_left, name_len); + MEMCPY(&http_uri_buf[copy_len], g_psDefaultFilenames[loop].name, name_copy_len); + } + file_name = http_uri_buf; + } else +#endif /* LWIP_HTTPD_MAX_REQUEST_URI_LEN */ + { + file_name = g_psDefaultFilenames[loop].name; + } + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Looking for %s...\n", file_name)); + err = fs_open(&hs->file_handle, file_name); + if(err == ERR_OK) { + uri = file_name; + file = &hs->file_handle; + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Opened.\n")); +#if LWIP_HTTPD_SSI + tag_check = g_psDefaultFilenames[loop].shtml; +#endif /* LWIP_HTTPD_SSI */ + break; + } + } + } + if (file == NULL) { + /* No - we've been asked for a specific file. */ + /* First, isolate the base URI (without any parameters) */ + params = (char *)strchr(uri, '?'); + if (params != NULL) { + /* URI contains parameters. NULL-terminate the base URI */ + *params = '\0'; + params++; + } + +#if LWIP_HTTPD_CGI + http_cgi_paramcount = -1; + /* Does the base URI we have isolated correspond to a CGI handler? */ + if (g_iNumCGIs && g_pCGIs) { + for (i = 0; i < g_iNumCGIs; i++) { + if (strcmp(uri, g_pCGIs[i].pcCGIName) == 0) { + /* + * We found a CGI that handles this URI so extract the + * parameters and call the handler. + */ + http_cgi_paramcount = extract_uri_parameters(hs, params); + uri = g_pCGIs[i].pfnCGIHandler(i, http_cgi_paramcount, hs->params, + hs->param_vals); + break; + } + } + } +#endif /* LWIP_HTTPD_CGI */ + + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Opening %s\n", uri)); + + err = fs_open(&hs->file_handle, uri); + if (err == ERR_OK) { + file = &hs->file_handle; + } else { + file = http_get_404_file(hs, &uri); + } +#if LWIP_HTTPD_SSI + if (file != NULL) { + /* See if we have been asked for an shtml file and, if so, + enable tag checking. */ + const char* ext = NULL, *sub; + char* param = (char*)strstr(uri, "?"); + if (param != NULL) { + /* separate uri from parameters for now, set back later */ + *param = 0; + } + sub = uri; + ext = uri; + for (sub = strstr(sub, "."); sub != NULL; sub = strstr(sub, ".")) + { + ext = sub; + sub++; + } + tag_check = 0; + for (loop = 0; loop < NUM_SHTML_EXTENSIONS; loop++) { + if (!lwip_stricmp(ext, g_pcSSIExtensions[loop])) { + tag_check = 1; + break; + } + } + if (param != NULL) { + *param = '?'; + } + } +#endif /* LWIP_HTTPD_SSI */ + } + if (file == NULL) { + /* None of the default filenames exist so send back a 404 page */ + file = http_get_404_file(hs, &uri); + } + return http_init_file(hs, file, is_09, uri, tag_check, params); +} + +/** Initialize a http connection with a file to send (if found). + * Called by http_find_file and http_find_error_file. + * + * @param hs http connection state + * @param file file structure to send (or NULL if not found) + * @param is_09 1 if the request is HTTP/0.9 (no HTTP headers in response) + * @param uri the HTTP header URI + * @param tag_check enable SSI tag checking + * @param params != NULL if URI has parameters (separated by '?') + * @return ERR_OK if file was found and hs has been initialized correctly + * another err_t otherwise + */ +static err_t +http_init_file(struct http_state *hs, struct fs_file *file, int is_09, const char *uri, + u8_t tag_check, char* params) +{ + if (file != NULL) { + /* file opened, initialise struct http_state */ +#if LWIP_HTTPD_SSI + if (tag_check) { + struct http_ssi_state *ssi = http_ssi_state_alloc(); + if (ssi != NULL) { + ssi->tag_index = 0; + ssi->tag_state = TAG_NONE; + ssi->parsed = file->data; + ssi->parse_left = file->len; + ssi->tag_end = file->data; + hs->ssi = ssi; + } + } +#else /* LWIP_HTTPD_SSI */ + LWIP_UNUSED_ARG(tag_check); +#endif /* LWIP_HTTPD_SSI */ + hs->handle = file; + hs->file = file->data; + LWIP_ASSERT("File length must be positive!", (file->len >= 0)); +#if LWIP_HTTPD_CUSTOM_FILES + if (file->is_custom_file && (file->data == NULL)) { + /* custom file, need to read data first (via fs_read_custom) */ + hs->left = 0; + } else +#endif /* LWIP_HTTPD_CUSTOM_FILES */ + { + hs->left = file->len; + } + hs->retries = 0; +#if LWIP_HTTPD_TIMING + hs->time_started = sys_now(); +#endif /* LWIP_HTTPD_TIMING */ +#if !LWIP_HTTPD_DYNAMIC_HEADERS + LWIP_ASSERT("HTTP headers not included in file system", + (hs->handle->flags & FS_FILE_FLAGS_HEADER_INCLUDED) != 0); +#endif /* !LWIP_HTTPD_DYNAMIC_HEADERS */ +#if LWIP_HTTPD_SUPPORT_V09 + if (is_09 && ((hs->handle->flags & FS_FILE_FLAGS_HEADER_INCLUDED) != 0)) { + /* HTTP/0.9 responses are sent without HTTP header, + search for the end of the header. */ + char *file_start = lwip_strnstr(hs->file, CRLF CRLF, hs->left); + if (file_start != NULL) { + size_t diff = file_start + 4 - hs->file; + hs->file += diff; + hs->left -= (u32_t)diff; + } + } +#endif /* LWIP_HTTPD_SUPPORT_V09*/ +#if LWIP_HTTPD_CGI_SSI + if (params != NULL) { + /* URI contains parameters, call generic CGI handler */ + int count; +#if LWIP_HTTPD_CGI + if (http_cgi_paramcount >= 0) { + count = http_cgi_paramcount; + } else +#endif + { + count = extract_uri_parameters(hs, params); + } + httpd_cgi_handler(uri, count, http_cgi_params, http_cgi_param_vals +#if defined(LWIP_HTTPD_FILE_STATE) && LWIP_HTTPD_FILE_STATE + , hs->handle->state +#endif /* LWIP_HTTPD_FILE_STATE */ + ); + } +#else /* LWIP_HTTPD_CGI_SSI */ + LWIP_UNUSED_ARG(params); +#endif /* LWIP_HTTPD_CGI_SSI */ + } else { + hs->handle = NULL; + hs->file = NULL; + hs->left = 0; + hs->retries = 0; + } +#if LWIP_HTTPD_DYNAMIC_HEADERS + /* Determine the HTTP headers to send based on the file extension of + * the requested URI. */ + if ((hs->handle == NULL) || ((hs->handle->flags & FS_FILE_FLAGS_HEADER_INCLUDED) == 0)) { + get_http_headers(hs, uri); + } +#else /* LWIP_HTTPD_DYNAMIC_HEADERS */ + LWIP_UNUSED_ARG(uri); +#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ +#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE + if (hs->keepalive) { +#if LWIP_HTTPD_SSI + if (hs->ssi != NULL) { + hs->keepalive = 0; + } else +#endif /* LWIP_HTTPD_SSI */ + { + if ((hs->handle != NULL) && + ((hs->handle->flags & (FS_FILE_FLAGS_HEADER_INCLUDED|FS_FILE_FLAGS_HEADER_PERSISTENT)) == FS_FILE_FLAGS_HEADER_INCLUDED)) { + hs->keepalive = 0; + } + } + } +#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */ + return ERR_OK; +} + +/** + * The pcb had an error and is already deallocated. + * The argument might still be valid (if != NULL). + */ +static void +http_err(void *arg, err_t err) +{ + struct http_state *hs = (struct http_state *)arg; + LWIP_UNUSED_ARG(err); + + LWIP_DEBUGF(HTTPD_DEBUG, ("http_err: %s", lwip_strerr(err))); + + if (hs != NULL) { + http_state_free(hs); + } +} + +/** + * Data has been sent and acknowledged by the remote host. + * This means that more data can be sent. + */ +static err_t +http_sent(void *arg, struct tcp_pcb *pcb, u16_t len) +{ + struct http_state *hs = (struct http_state *)arg; + + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_sent %p\n", (void*)pcb)); + + LWIP_UNUSED_ARG(len); + + if (hs == NULL) { + return ERR_OK; + } + + hs->retries = 0; + + http_send(pcb, hs); + + return ERR_OK; +} + +/** + * The poll function is called every 2nd second. + * If there has been no data sent (which resets the retries) in 8 seconds, close. + * If the last portion of a file has not been sent in 2 seconds, close. + * + * This could be increased, but we don't want to waste resources for bad connections. + */ +static err_t +http_poll(void *arg, struct tcp_pcb *pcb) +{ + struct http_state *hs = (struct http_state *)arg; + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_poll: pcb=%p hs=%p pcb_state=%s\n", + (void*)pcb, (void*)hs, tcp_debug_state_str(pcb->state))); + + if (hs == NULL) { + err_t closed; + /* arg is null, close. */ + LWIP_DEBUGF(HTTPD_DEBUG, ("http_poll: arg is NULL, close\n")); + closed = http_close_conn(pcb, NULL); + LWIP_UNUSED_ARG(closed); +#if LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR + if (closed == ERR_MEM) { + tcp_abort(pcb); + return ERR_ABRT; + } +#endif /* LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR */ + return ERR_OK; + } else { + hs->retries++; + if (hs->retries == HTTPD_MAX_RETRIES) { + LWIP_DEBUGF(HTTPD_DEBUG, ("http_poll: too many retries, close\n")); + http_close_conn(pcb, hs); + return ERR_OK; + } + + /* If this connection has a file open, try to send some more data. If + * it has not yet received a GET request, don't do this since it will + * cause the connection to close immediately. */ + if(hs && (hs->handle)) { + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_poll: try to send more data\n")); + if(http_send(pcb, hs)) { + /* If we wrote anything to be sent, go ahead and send it now. */ + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("tcp_output\n")); + tcp_output(pcb); + } + } + } + + return ERR_OK; +} + +/** + * Data has been received on this pcb. + * For HTTP 1.0, this should normally only happen once (if the request fits in one packet). + */ +static err_t +http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) +{ + struct http_state *hs = (struct http_state *)arg; + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_recv: pcb=%p pbuf=%p err=%s\n", (void*)pcb, + (void*)p, lwip_strerr(err))); + + if ((err != ERR_OK) || (p == NULL) || (hs == NULL)) { + /* error or closed by other side? */ + if (p != NULL) { + /* Inform TCP that we have taken the data. */ + tcp_recved(pcb, p->tot_len); + pbuf_free(p); + } + if (hs == NULL) { + /* this should not happen, only to be robust */ + LWIP_DEBUGF(HTTPD_DEBUG, ("Error, http_recv: hs is NULL, close\n")); + } + http_close_conn(pcb, hs); + return ERR_OK; + } + +#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND + if (hs->no_auto_wnd) { + hs->unrecved_bytes += p->tot_len; + } else +#endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */ + { + /* Inform TCP that we have taken the data. */ + tcp_recved(pcb, p->tot_len); + } + +#if LWIP_HTTPD_SUPPORT_POST + if (hs->post_content_len_left > 0) { + /* reset idle counter when POST data is received */ + hs->retries = 0; + /* this is data for a POST, pass the complete pbuf to the application */ + http_post_rxpbuf(hs, p); + /* pbuf is passed to the application, don't free it! */ + if (hs->post_content_len_left == 0) { + /* all data received, send response or close connection */ + http_send(pcb, hs); + } + return ERR_OK; + } else +#endif /* LWIP_HTTPD_SUPPORT_POST */ + { + if (hs->handle == NULL) { + err_t parsed = http_parse_request(p, hs, pcb); + LWIP_ASSERT("http_parse_request: unexpected return value", parsed == ERR_OK + || parsed == ERR_INPROGRESS ||parsed == ERR_ARG || parsed == ERR_USE); +#if LWIP_HTTPD_SUPPORT_REQUESTLIST + if (parsed != ERR_INPROGRESS) { + /* request fully parsed or error */ + if (hs->req != NULL) { + pbuf_free(hs->req); + hs->req = NULL; + } + } +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ + pbuf_free(p); + if (parsed == ERR_OK) { +#if LWIP_HTTPD_SUPPORT_POST + if (hs->post_content_len_left == 0) +#endif /* LWIP_HTTPD_SUPPORT_POST */ + { + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_recv: data %p len %"S32_F"\n", (const void*)hs->file, hs->left)); + http_send(pcb, hs); + } + } else if (parsed == ERR_ARG) { + /* @todo: close on ERR_USE? */ + http_close_conn(pcb, hs); + } + } else { + LWIP_DEBUGF(HTTPD_DEBUG, ("http_recv: already sending data\n")); + /* already sending but still receiving data, we might want to RST here? */ + pbuf_free(p); + } + } + return ERR_OK; +} + +/** + * A new incoming connection has been accepted. + */ +static err_t +http_accept(void *arg, struct tcp_pcb *pcb, err_t err) +{ + struct http_state *hs; + LWIP_UNUSED_ARG(err); + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(HTTPD_DEBUG, ("http_accept %p / %p\n", (void*)pcb, arg)); + + if ((err != ERR_OK) || (pcb == NULL)) { + return ERR_VAL; + } + + /* Set priority */ + tcp_setprio(pcb, HTTPD_TCP_PRIO); + + /* Allocate memory for the structure that holds the state of the + connection - initialized by that function. */ + hs = http_state_alloc(); + if (hs == NULL) { + LWIP_DEBUGF(HTTPD_DEBUG, ("http_accept: Out of memory, RST\n")); + return ERR_MEM; + } + hs->pcb = pcb; + + /* Tell TCP that this is the structure we wish to be passed for our + callbacks. */ + tcp_arg(pcb, hs); + + /* Set up the various callback functions */ + tcp_recv(pcb, http_recv); + tcp_err(pcb, http_err); + tcp_poll(pcb, http_poll, HTTPD_POLL_INTERVAL); + tcp_sent(pcb, http_sent); + + return ERR_OK; +} + +/** + * @ingroup httpd + * Initialize the httpd: set up a listening PCB and bind it to the defined port + */ +void +httpd_init(void) +{ + struct tcp_pcb *pcb; + err_t err; + +#if HTTPD_USE_MEM_POOL + LWIP_MEMPOOL_INIT(HTTPD_STATE); +#if LWIP_HTTPD_SSI + LWIP_MEMPOOL_INIT(HTTPD_SSI_STATE); +#endif +#endif + LWIP_DEBUGF(HTTPD_DEBUG, ("httpd_init\n")); + + pcb = tcp_new_ip_type(IPADDR_TYPE_ANY); + LWIP_ASSERT("httpd_init: tcp_new failed", pcb != NULL); + tcp_setprio(pcb, HTTPD_TCP_PRIO); + /* set SOF_REUSEADDR here to explicitly bind httpd to multiple interfaces */ + err = tcp_bind(pcb, IP_ANY_TYPE, HTTPD_SERVER_PORT); + LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */ + LWIP_ASSERT("httpd_init: tcp_bind failed", err == ERR_OK); + pcb = tcp_listen(pcb); + LWIP_ASSERT("httpd_init: tcp_listen failed", pcb != NULL); + tcp_accept(pcb, http_accept); +} + +#if LWIP_HTTPD_SSI +/** + * Set the SSI handler function. + * + * @param ssi_handler the SSI handler function + * @param tags an array of SSI tag strings to search for in SSI-enabled files + * @param num_tags number of tags in the 'tags' array + */ +void +http_set_ssi_handler(tSSIHandler ssi_handler, const char **tags, int num_tags) +{ + LWIP_DEBUGF(HTTPD_DEBUG, ("http_set_ssi_handler\n")); + + LWIP_ASSERT("no ssi_handler given", ssi_handler != NULL); + g_pfnSSIHandler = ssi_handler; + +#if LWIP_HTTPD_SSI_RAW + LWIP_UNUSED_ARG(tags); + LWIP_UNUSED_ARG(num_tags); +#else /* LWIP_HTTPD_SSI_RAW */ + LWIP_ASSERT("no tags given", tags != NULL); + LWIP_ASSERT("invalid number of tags", num_tags > 0); + + g_ppcTags = tags; + g_iNumTags = num_tags; +#endif /* !LWIP_HTTPD_SSI_RAW */ +} +#endif /* LWIP_HTTPD_SSI */ + +#if LWIP_HTTPD_CGI +/** + * Set an array of CGI filenames/handler functions + * + * @param cgis an array of CGI filenames/handler functions + * @param num_handlers number of elements in the 'cgis' array + */ +void +http_set_cgi_handlers(const tCGI *cgis, int num_handlers) +{ + LWIP_ASSERT("no cgis given", cgis != NULL); + LWIP_ASSERT("invalid number of handlers", num_handlers > 0); + + g_pCGIs = cgis; + g_iNumCGIs = num_handlers; +} +#endif /* LWIP_HTTPD_CGI */ + +#endif /* LWIP_TCP && LWIP_CALLBACK_API */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/httpd_structs.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/httpd_structs.h new file mode 100644 index 0000000..fbd135a --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/httpd_structs.h @@ -0,0 +1,114 @@ +#ifndef LWIP_HTTPD_STRUCTS_H +#define LWIP_HTTPD_STRUCTS_H + +#include "lwip/apps/httpd.h" + +#if LWIP_HTTPD_DYNAMIC_HEADERS +/** This struct is used for a list of HTTP header strings for various + * filename extensions. */ +typedef struct +{ + const char *extension; + const char *content_type; +} tHTTPHeader; + +/** A list of strings used in HTTP headers (see RFC 1945 HTTP/1.0 and + * RFC 2616 HTTP/1.1 for header field definitions) */ +static const char * const g_psHTTPHeaderStrings[] = +{ + "HTTP/1.0 200 OK\r\n", + "HTTP/1.0 404 File not found\r\n", + "HTTP/1.0 400 Bad Request\r\n", + "HTTP/1.0 501 Not Implemented\r\n", + "HTTP/1.1 200 OK\r\n", + "HTTP/1.1 404 File not found\r\n", + "HTTP/1.1 400 Bad Request\r\n", + "HTTP/1.1 501 Not Implemented\r\n", + "Content-Length: ", + "Connection: Close\r\n", + "Connection: keep-alive\r\n", + "Connection: keep-alive\r\nContent-Length: ", + "Server: "HTTPD_SERVER_AGENT"\r\n", + "\r\n<html><body><h2>404: The requested file cannot be found.</h2></body></html>\r\n" +#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE + ,"Connection: keep-alive\r\nContent-Length: 77\r\n\r\n<html><body><h2>404: The requested file cannot be found.</h2></body></html>\r\n" +#endif +}; + +/* Indexes into the g_psHTTPHeaderStrings array */ +#define HTTP_HDR_OK 0 /* 200 OK */ +#define HTTP_HDR_NOT_FOUND 1 /* 404 File not found */ +#define HTTP_HDR_BAD_REQUEST 2 /* 400 Bad request */ +#define HTTP_HDR_NOT_IMPL 3 /* 501 Not Implemented */ +#define HTTP_HDR_OK_11 4 /* 200 OK */ +#define HTTP_HDR_NOT_FOUND_11 5 /* 404 File not found */ +#define HTTP_HDR_BAD_REQUEST_11 6 /* 400 Bad request */ +#define HTTP_HDR_NOT_IMPL_11 7 /* 501 Not Implemented */ +#define HTTP_HDR_CONTENT_LENGTH 8 /* Content-Length: (HTTP 1.0)*/ +#define HTTP_HDR_CONN_CLOSE 9 /* Connection: Close (HTTP 1.1) */ +#define HTTP_HDR_CONN_KEEPALIVE 10 /* Connection: keep-alive (HTTP 1.1) */ +#define HTTP_HDR_KEEPALIVE_LEN 11 /* Connection: keep-alive + Content-Length: (HTTP 1.1)*/ +#define HTTP_HDR_SERVER 12 /* Server: HTTPD_SERVER_AGENT */ +#define DEFAULT_404_HTML 13 /* default 404 body */ +#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE +#define DEFAULT_404_HTML_PERSISTENT 14 /* default 404 body, but including Connection: keep-alive */ +#endif + + +#define HTTP_HDR_HTML "Content-type: text/html\r\n\r\n" +#define HTTP_HDR_SSI "Content-type: text/html\r\nExpires: Fri, 10 Apr 2008 14:00:00 GMT\r\nPragma: no-cache\r\n\r\n" +#define HTTP_HDR_GIF "Content-type: image/gif\r\n\r\n" +#define HTTP_HDR_PNG "Content-type: image/png\r\n\r\n" +#define HTTP_HDR_JPG "Content-type: image/jpeg\r\n\r\n" +#define HTTP_HDR_BMP "Content-type: image/bmp\r\n\r\n" +#define HTTP_HDR_ICO "Content-type: image/x-icon\r\n\r\n" +#define HTTP_HDR_APP "Content-type: application/octet-stream\r\n\r\n" +#define HTTP_HDR_JS "Content-type: application/javascript\r\n\r\n" +#define HTTP_HDR_RA "Content-type: application/javascript\r\n\r\n" +#define HTTP_HDR_CSS "Content-type: text/css\r\n\r\n" +#define HTTP_HDR_SWF "Content-type: application/x-shockwave-flash\r\n\r\n" +#define HTTP_HDR_XML "Content-type: text/xml\r\n\r\n" +#define HTTP_HDR_PDF "Content-type: application/pdf\r\n\r\n" +#define HTTP_HDR_JSON "Content-type: application/json\r\n\r\n" + +#define HTTP_HDR_DEFAULT_TYPE "Content-type: text/plain\r\n\r\n" + +/** A list of extension-to-HTTP header strings (see outdated RFC 1700 MEDIA TYPES + * and http://www.iana.org/assignments/media-types for registered content types + * and subtypes) */ +static const tHTTPHeader g_psHTTPHeaders[] = +{ + { "html", HTTP_HDR_HTML}, + { "htm", HTTP_HDR_HTML}, + { "shtml",HTTP_HDR_SSI}, + { "shtm", HTTP_HDR_SSI}, + { "ssi", HTTP_HDR_SSI}, + { "gif", HTTP_HDR_GIF}, + { "png", HTTP_HDR_PNG}, + { "jpg", HTTP_HDR_JPG}, + { "bmp", HTTP_HDR_BMP}, + { "ico", HTTP_HDR_ICO}, + { "class",HTTP_HDR_APP}, + { "cls", HTTP_HDR_APP}, + { "js", HTTP_HDR_JS}, + { "ram", HTTP_HDR_RA}, + { "css", HTTP_HDR_CSS}, + { "swf", HTTP_HDR_SWF}, + { "xml", HTTP_HDR_XML}, + { "xsl", HTTP_HDR_XML}, + { "pdf", HTTP_HDR_PDF}, + { "json", HTTP_HDR_JSON} +}; + +#define NUM_HTTP_HEADERS (sizeof(g_psHTTPHeaders) / sizeof(tHTTPHeader)) + +#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ + +#if LWIP_HTTPD_SSI +static const char * const g_pcSSIExtensions[] = { + ".shtml", ".shtm", ".ssi", ".xml" +}; +#define NUM_SHTML_EXTENSIONS (sizeof(g_pcSSIExtensions) / sizeof(const char *)) +#endif /* LWIP_HTTPD_SSI */ + +#endif /* LWIP_HTTPD_STRUCTS_H */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/makefsdata/makefsdata b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/makefsdata/makefsdata new file mode 100644 index 0000000..37b4203 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/makefsdata/makefsdata @@ -0,0 +1,97 @@ +#!/usr/bin/perl + +open(OUTPUT, "> fsdata.c"); + +chdir("fs"); +open(FILES, "find . -type f |"); + +while($file = <FILES>) { + + # Do not include files in CVS directories nor backup files. + if($file =~ /(CVS|~)/) { + next; + } + + chop($file); + + open(HEADER, "> /tmp/header") || die $!; + if($file =~ /404/) { + print(HEADER "HTTP/1.0 404 File not found\r\n"); + } else { + print(HEADER "HTTP/1.0 200 OK\r\n"); + } + print(HEADER "Server: lwIP/pre-0.6 (http://www.sics.se/~adam/lwip/)\r\n"); + if($file =~ /\.html$/) { + print(HEADER "Content-type: text/html\r\n"); + } elsif($file =~ /\.gif$/) { + print(HEADER "Content-type: image/gif\r\n"); + } elsif($file =~ /\.png$/) { + print(HEADER "Content-type: image/png\r\n"); + } elsif($file =~ /\.jpg$/) { + print(HEADER "Content-type: image/jpeg\r\n"); + } elsif($file =~ /\.class$/) { + print(HEADER "Content-type: application/octet-stream\r\n"); + } elsif($file =~ /\.ram$/) { + print(HEADER "Content-type: audio/x-pn-realaudio\r\n"); + } else { + print(HEADER "Content-type: text/plain\r\n"); + } + print(HEADER "\r\n"); + close(HEADER); + + unless($file =~ /\.plain$/ || $file =~ /cgi/) { + system("cat /tmp/header $file > /tmp/file"); + } else { + system("cp $file /tmp/file"); + } + + open(FILE, "/tmp/file"); + unlink("/tmp/file"); + unlink("/tmp/header"); + + $file =~ s/\.//; + $fvar = $file; + $fvar =~ s-/-_-g; + $fvar =~ s-\.-_-g; + print(OUTPUT "static const unsigned char data".$fvar."[] = {\n"); + print(OUTPUT "\t/* $file */\n\t"); + for($j = 0; $j < length($file); $j++) { + printf(OUTPUT "%#02x, ", unpack("C", substr($file, $j, 1))); + } + printf(OUTPUT "0,\n"); + + + $i = 0; + while(read(FILE, $data, 1)) { + if($i == 0) { + print(OUTPUT "\t"); + } + printf(OUTPUT "%#02x, ", unpack("C", $data)); + $i++; + if($i == 10) { + print(OUTPUT "\n"); + $i = 0; + } + } + print(OUTPUT "};\n\n"); + close(FILE); + push(@fvars, $fvar); + push(@files, $file); +} + +for($i = 0; $i < @fvars; $i++) { + $file = $files[$i]; + $fvar = $fvars[$i]; + + if($i == 0) { + $prevfile = "NULL"; + } else { + $prevfile = "file" . $fvars[$i - 1]; + } + print(OUTPUT "const struct fsdata_file file".$fvar."[] = {{$prevfile, data$fvar, "); + print(OUTPUT "data$fvar + ". (length($file) + 1) .", "); + print(OUTPUT "sizeof(data$fvar) - ". (length($file) + 1) ."}};\n\n"); +} + +print(OUTPUT "#define FS_ROOT file$fvars[$i - 1]\n\n"); +print(OUTPUT "#define FS_NUMFILES $i\n"); diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/makefsdata/makefsdata.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/makefsdata/makefsdata.c new file mode 100644 index 0000000..934e721 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/makefsdata/makefsdata.c @@ -0,0 +1,1033 @@ +/** + * makefsdata: Converts a directory structure for use with the lwIP httpd. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Jim Pettinato + * Simon Goldschmidt + * + * @todo: + * - take TCP_MSS, LWIP_TCP_TIMESTAMPS and + * PAYLOAD_ALIGN_TYPE/PAYLOAD_ALIGNMENT as arguments + */ + +#include <stdio.h> +#include <stdlib.h> +#ifdef WIN32 +#define WIN32_LEAN_AND_MEAN +#include "windows.h" +#else +#include <dir.h> +#endif +#include <dos.h> +#include <string.h> +#include <time.h> +#include <sys/stat.h> + +/** Makefsdata can generate *all* files deflate-compressed (where file size shrinks). + * Since nearly all browsers support this, this is a good way to reduce ROM size. + * To compress the files, "miniz.c" must be downloaded seperately. + */ +#ifndef MAKEFS_SUPPORT_DEFLATE +#define MAKEFS_SUPPORT_DEFLATE 0 +#endif + +#define COPY_BUFSIZE (1024*1024) /* 1 MByte */ + +#if MAKEFS_SUPPORT_DEFLATE +#include "../miniz.c" + +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint; + +#define my_max(a,b) (((a) > (b)) ? (a) : (b)) +#define my_min(a,b) (((a) < (b)) ? (a) : (b)) + +/* COMP_OUT_BUF_SIZE is the size of the output buffer used during compression. + COMP_OUT_BUF_SIZE must be >= 1 and <= OUT_BUF_SIZE */ +#define COMP_OUT_BUF_SIZE COPY_BUFSIZE + +/* OUT_BUF_SIZE is the size of the output buffer used during decompression. + OUT_BUF_SIZE must be a power of 2 >= TINFL_LZ_DICT_SIZE (because the low-level decompressor not only writes, but reads from the output buffer as it decompresses) */ +#define OUT_BUF_SIZE COPY_BUFSIZE +static uint8 s_outbuf[OUT_BUF_SIZE]; +static uint8 s_checkbuf[OUT_BUF_SIZE]; + +/* tdefl_compressor contains all the state needed by the low-level compressor so it's a pretty big struct (~300k). + This example makes it a global vs. putting it on the stack, of course in real-world usage you'll probably malloc() or new it. */ +tdefl_compressor g_deflator; +tinfl_decompressor g_inflator; + +int deflate_level = 10; /* default compression level, can be changed via command line */ +#define USAGE_ARG_DEFLATE " [-defl<:compr_level>]" +#else /* MAKEFS_SUPPORT_DEFLATE */ +#define USAGE_ARG_DEFLATE "" +#endif /* MAKEFS_SUPPORT_DEFLATE */ + +/* Compatibility defines Win32 vs. DOS */ +#ifdef WIN32 + +#define FIND_T WIN32_FIND_DATAA +#define FIND_T_FILENAME(fInfo) (fInfo.cFileName) +#define FIND_T_IS_DIR(fInfo) ((fInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) +#define FIND_T_IS_FILE(fInfo) ((fInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) +#define FIND_RET_T HANDLE +#define FINDFIRST_FILE(path, result) FindFirstFileA(path, result) +#define FINDFIRST_DIR(path, result) FindFirstFileA(path, result) +#define FINDNEXT(ff_res, result) FindNextFileA(ff_res, result) +#define FINDFIRST_SUCCEEDED(ret) (ret != INVALID_HANDLE_VALUE) +#define FINDNEXT_SUCCEEDED(ret) (ret == TRUE) + +#define GETCWD(path, len) GetCurrentDirectoryA(len, path) +#define CHDIR(path) SetCurrentDirectoryA(path) +#define CHDIR_SUCCEEDED(ret) (ret == TRUE) + +#else + +#define FIND_T struct ffblk +#define FIND_T_FILENAME(fInfo) (fInfo.ff_name) +#define FIND_T_IS_DIR(fInfo) ((fInfo.ff_attrib & FA_DIREC) == FA_DIREC) +#define FIND_T_IS_FILE(fInfo) (1) +#define FIND_RET_T int +#define FINDFIRST_FILE(path, result) findfirst(path, result, FA_ARCH) +#define FINDFIRST_DIR(path, result) findfirst(path, result, FA_DIREC) +#define FINDNEXT(ff_res, result) FindNextFileA(ff_res, result) +#define FINDFIRST_SUCCEEDED(ret) (ret == 0) +#define FINDNEXT_SUCCEEDED(ret) (ret == 0) + +#define GETCWD(path, len) getcwd(path, len) +#define CHDIR(path) chdir(path) +#define CHDIR_SUCCEEDED(ret) (ret == 0) + +#endif + +#define NEWLINE "\r\n" +#define NEWLINE_LEN 2 + +/* define this to get the header variables we use to build HTTP headers */ +#define LWIP_HTTPD_DYNAMIC_HEADERS 1 +#define LWIP_HTTPD_SSI 1 +#include "lwip/init.h" +#include "../httpd_structs.h" +#include "lwip/apps/fs.h" + +#include "../core/inet_chksum.c" +#include "../core/def.c" + +/** (Your server name here) */ +const char *serverID = "Server: "HTTPD_SERVER_AGENT"\r\n"; +char serverIDBuffer[1024]; + +/* change this to suit your MEM_ALIGNMENT */ +#define PAYLOAD_ALIGNMENT 4 +/* set this to 0 to prevent aligning payload */ +#define ALIGN_PAYLOAD 1 +/* define this to a type that has the required alignment */ +#define PAYLOAD_ALIGN_TYPE "unsigned int" +static int payload_alingment_dummy_counter = 0; + +#define HEX_BYTES_PER_LINE 16 + +#define MAX_PATH_LEN 256 + +struct file_entry +{ + struct file_entry* next; + const char* filename_c; +}; + +int process_sub(FILE *data_file, FILE *struct_file); +int process_file(FILE *data_file, FILE *struct_file, const char *filename); +int file_write_http_header(FILE *data_file, const char *filename, int file_size, u16_t *http_hdr_len, + u16_t *http_hdr_chksum, u8_t provide_content_len, int is_compressed); +int file_put_ascii(FILE *file, const char *ascii_string, int len, int *i); +int s_put_ascii(char *buf, const char *ascii_string, int len, int *i); +void concat_files(const char *file1, const char *file2, const char *targetfile); +int check_path(char* path, size_t size); + +/* 5 bytes per char + 3 bytes per line */ +static char file_buffer_c[COPY_BUFSIZE * 5 + ((COPY_BUFSIZE / HEX_BYTES_PER_LINE) * 3)]; + +char curSubdir[MAX_PATH_LEN]; +char lastFileVar[MAX_PATH_LEN]; +char hdr_buf[4096]; + +unsigned char processSubs = 1; +unsigned char includeHttpHeader = 1; +unsigned char useHttp11 = 0; +unsigned char supportSsi = 1; +unsigned char precalcChksum = 0; +unsigned char includeLastModified = 0; +#if MAKEFS_SUPPORT_DEFLATE +unsigned char deflateNonSsiFiles = 0; +size_t deflatedBytesReduced = 0; +size_t overallDataBytes = 0; +#endif + +struct file_entry* first_file = NULL; +struct file_entry* last_file = NULL; + +static void print_usage(void) +{ + printf(" Usage: htmlgen [targetdir] [-s] [-e] [-i] [-11] [-nossi] [-c] [-f:<filename>] [-m] [-svr:<name>]" USAGE_ARG_DEFLATE NEWLINE NEWLINE); + printf(" targetdir: relative or absolute path to files to convert" NEWLINE); + printf(" switch -s: toggle processing of subdirectories (default is on)" NEWLINE); + printf(" switch -e: exclude HTTP header from file (header is created at runtime, default is off)" NEWLINE); + printf(" switch -11: include HTTP 1.1 header (1.0 is default)" NEWLINE); + printf(" switch -nossi: no support for SSI (cannot calculate Content-Length for SSI)" NEWLINE); + printf(" switch -c: precalculate checksums for all pages (default is off)" NEWLINE); + printf(" switch -f: target filename (default is \"fsdata.c\")" NEWLINE); + printf(" switch -m: include \"Last-Modified\" header based on file time" NEWLINE); + printf(" switch -svr: server identifier sent in HTTP response header ('Server' field)" NEWLINE); +#if MAKEFS_SUPPORT_DEFLATE + printf(" switch -defl: deflate-compress all non-SSI files (with opt. compr.-level, default=10)" NEWLINE); + printf(" ATTENTION: browser has to support \"Content-Encoding: deflate\"!" NEWLINE); +#endif + printf(" if targetdir not specified, htmlgen will attempt to" NEWLINE); + printf(" process files in subdirectory 'fs'" NEWLINE); +} + +int main(int argc, char *argv[]) +{ + char path[MAX_PATH_LEN]; + char appPath[MAX_PATH_LEN]; + FILE *data_file; + FILE *struct_file; + int filesProcessed; + int i; + char targetfile[MAX_PATH_LEN]; + strcpy(targetfile, "fsdata.c"); + + memset(path, 0, sizeof(path)); + memset(appPath, 0, sizeof(appPath)); + + printf(NEWLINE " makefsdata - HTML to C source converter" NEWLINE); + printf(" by Jim Pettinato - circa 2003 " NEWLINE); + printf(" extended by Simon Goldschmidt - 2009 " NEWLINE NEWLINE); + + strcpy(path, "fs"); + for (i = 1; i < argc; i++) { + if (argv[i] == NULL) { + continue; + } + if (argv[i][0] == '-') { + if (strstr(argv[i], "-svr:") == argv[i]) { + snprintf(serverIDBuffer, sizeof(serverIDBuffer), "Server: %s\r\n", &argv[i][5]); + serverID = serverIDBuffer; + printf("Using Server-ID: \"%s\"\n", serverID); + } else if (strstr(argv[i], "-s") == argv[i]) { + processSubs = 0; + } else if (strstr(argv[i], "-e") == argv[i]) { + includeHttpHeader = 0; + } else if (strstr(argv[i], "-11") == argv[i]) { + useHttp11 = 1; + } else if (strstr(argv[i], "-nossi") == argv[i]) { + supportSsi = 0; + } else if (strstr(argv[i], "-c") == argv[i]) { + precalcChksum = 1; + } else if (strstr(argv[i], "-f:") == argv[i]) { + strncpy(targetfile, &argv[i][3], sizeof(targetfile) - 1); + targetfile[sizeof(targetfile) - 1] = 0; + printf("Writing to file \"%s\"\n", targetfile); + } else if (strstr(argv[i], "-m") == argv[i]) { + includeLastModified = 1; + } else if (strstr(argv[i], "-defl") == argv[i]) { +#if MAKEFS_SUPPORT_DEFLATE + char* colon = strstr(argv[i], ":"); + if (colon) { + if (colon[1] != 0) { + int defl_level = atoi(&colon[1]); + if ((defl_level >= 0) && (defl_level <= 10)) { + deflate_level = defl_level; + } else { + printf("ERROR: deflate level must be [0..10]" NEWLINE); + exit(0); + } + } + } + deflateNonSsiFiles = 1; + printf("Deflating all non-SSI files with level %d (but only if size is reduced)" NEWLINE, deflate_level); +#else + printf("WARNING: Deflate support is disabled\n"); +#endif + } else if ((strstr(argv[i], "-?")) || (strstr(argv[i], "-h"))) { + print_usage(); + exit(0); + } + } else if ((argv[i][0] == '/') && (argv[i][1] == '?') && (argv[i][2] == 0)) { + print_usage(); + exit(0); + } else { + strncpy(path, argv[i], sizeof(path)-1); + path[sizeof(path)-1] = 0; + } + } + + if (!check_path(path, sizeof(path))) { + printf("Invalid path: \"%s\"." NEWLINE, path); + exit(-1); + } + + GETCWD(appPath, MAX_PATH_LEN); + /* if command line param or subdir named 'fs' not found spout usage verbiage */ + if (!CHDIR_SUCCEEDED(CHDIR(path))) { + /* if no subdir named 'fs' (or the one which was given) exists, spout usage verbiage */ + printf(" Failed to open directory \"%s\"." NEWLINE NEWLINE, path); + print_usage(); + exit(-1); + } + CHDIR(appPath); + + printf("HTTP %sheader will %s statically included." NEWLINE, + (includeHttpHeader ? (useHttp11 ? "1.1 " : "1.0 ") : ""), + (includeHttpHeader ? "be" : "not be")); + + sprintf(curSubdir, ""); /* start off in web page's root directory - relative paths */ + printf(" Processing all files in directory %s", path); + if (processSubs) { + printf(" and subdirectories..." NEWLINE NEWLINE); + } else { + printf("..." NEWLINE NEWLINE); + } + + data_file = fopen("fsdata.tmp", "wb"); + if (data_file == NULL) { + printf("Failed to create file \"fsdata.tmp\"\n"); + exit(-1); + } + struct_file = fopen("fshdr.tmp", "wb"); + if (struct_file == NULL) { + printf("Failed to create file \"fshdr.tmp\"\n"); + fclose(data_file); + exit(-1); + } + + CHDIR(path); + + fprintf(data_file, "#include \"lwip/apps/fs.h\"" NEWLINE); + fprintf(data_file, "#include \"lwip/def.h\"" NEWLINE); + fprintf(data_file, "#include \"fsdata.h\"" NEWLINE NEWLINE NEWLINE); + + fprintf(data_file, "#define file_NULL (struct fsdata_file *) NULL" NEWLINE NEWLINE NEWLINE); + /* define FS_FILE_FLAGS_HEADER_INCLUDED to 1 if not defined (compatibility with older httpd/fs) */ + fprintf(data_file, "#ifndef FS_FILE_FLAGS_HEADER_INCLUDED" NEWLINE "#define FS_FILE_FLAGS_HEADER_INCLUDED 1" NEWLINE "#endif" NEWLINE); + /* define FS_FILE_FLAGS_HEADER_PERSISTENT to 0 if not defined (compatibility with older httpd/fs: wasn't supported back then) */ + fprintf(data_file, "#ifndef FS_FILE_FLAGS_HEADER_PERSISTENT" NEWLINE "#define FS_FILE_FLAGS_HEADER_PERSISTENT 0" NEWLINE "#endif" NEWLINE); + + /* define alignment defines */ +#if ALIGN_PAYLOAD + fprintf(data_file, "/* FSDATA_FILE_ALIGNMENT: 0=off, 1=by variable, 2=by include */" NEWLINE "#ifndef FSDATA_FILE_ALIGNMENT" NEWLINE "#define FSDATA_FILE_ALIGNMENT 0" NEWLINE "#endif" NEWLINE); +#endif + fprintf(data_file, "#ifndef FSDATA_ALIGN_PRE" NEWLINE "#define FSDATA_ALIGN_PRE" NEWLINE "#endif" NEWLINE); + fprintf(data_file, "#ifndef FSDATA_ALIGN_POST" NEWLINE "#define FSDATA_ALIGN_POST" NEWLINE "#endif" NEWLINE); +#if ALIGN_PAYLOAD + fprintf(data_file, "#if FSDATA_FILE_ALIGNMENT==2" NEWLINE "#include \"fsdata_alignment.h\"" NEWLINE "#endif" NEWLINE); +#endif + + sprintf(lastFileVar, "NULL"); + + filesProcessed = process_sub(data_file, struct_file); + + /* data_file now contains all of the raw data.. now append linked list of + * file header structs to allow embedded app to search for a file name */ + fprintf(data_file, NEWLINE NEWLINE); + fprintf(struct_file, "#define FS_ROOT file_%s" NEWLINE, lastFileVar); + fprintf(struct_file, "#define FS_NUMFILES %d" NEWLINE NEWLINE, filesProcessed); + + fclose(data_file); + fclose(struct_file); + + CHDIR(appPath); + /* append struct_file to data_file */ + printf(NEWLINE "Creating target file..." NEWLINE NEWLINE); + concat_files("fsdata.tmp", "fshdr.tmp", targetfile); + + /* if succeeded, delete the temporary files */ + if (remove("fsdata.tmp") != 0) { + printf("Warning: failed to delete fsdata.tmp\n"); + } + if (remove("fshdr.tmp") != 0) { + printf("Warning: failed to delete fshdr.tmp\n"); + } + + printf(NEWLINE "Processed %d files - done." NEWLINE, filesProcessed); +#if MAKEFS_SUPPORT_DEFLATE + if (deflateNonSsiFiles) { + printf("(Deflated total byte reduction: %d bytes -> %d bytes (%.02f%%)" NEWLINE, + (int)overallDataBytes, (int)deflatedBytesReduced, (float)((deflatedBytesReduced*100.0)/overallDataBytes)); + } +#endif + printf(NEWLINE); + + while (first_file != NULL) { + struct file_entry* fe = first_file; + first_file = fe->next; + free(fe); + } + + return 0; +} + +int check_path(char* path, size_t size) +{ + size_t slen; + if (path[0] == 0) { + /* empty */ + return 0; + } + slen = strlen(path); + if (slen >= size) { + /* not NULL-terminated */ + return 0; + } + while ((slen > 0) && ((path[slen] == '\\') || (path[slen] == '/'))) { + /* path should not end with trailing backslash */ + path[slen] = 0; + slen--; + } + if (slen == 0) { + return 0; + } + return 1; +} + +static void copy_file(const char *filename_in, FILE *fout) +{ + FILE *fin; + size_t len; + void* buf; + fin = fopen(filename_in, "rb"); + if (fin == NULL) { + printf("Failed to open file \"%s\"\n", filename_in); + exit(-1); + } + buf = malloc(COPY_BUFSIZE); + while ((len = fread(buf, 1, COPY_BUFSIZE, fin)) > 0) { + fwrite(buf, 1, len, fout); + } + free(buf); + fclose(fin); +} + +void concat_files(const char *file1, const char *file2, const char *targetfile) +{ + FILE *fout; + fout = fopen(targetfile, "wb"); + if (fout == NULL) { + printf("Failed to open file \"%s\"\n", targetfile); + exit(-1); + } + copy_file(file1, fout); + copy_file(file2, fout); + fclose(fout); +} + +int process_sub(FILE *data_file, FILE *struct_file) +{ + FIND_T fInfo; + FIND_RET_T fret; + int filesProcessed = 0; + + if (processSubs) { + /* process subs recursively */ + size_t sublen = strlen(curSubdir); + size_t freelen = sizeof(curSubdir) - sublen - 1; + LWIP_ASSERT("sublen < sizeof(curSubdir)", sublen < sizeof(curSubdir)); + fret = FINDFIRST_DIR("*", &fInfo); + if (FINDFIRST_SUCCEEDED(fret)) { + do { + const char *curName = FIND_T_FILENAME(fInfo); + if ((curName[0] == '.') || (strcmp(curName, "CVS") == 0)) { + continue; + } + if (!FIND_T_IS_DIR(fInfo)) { + continue; + } + if (freelen > 0) { + CHDIR(curName); + strncat(curSubdir, "/", freelen); + strncat(curSubdir, curName, freelen - 1); + curSubdir[sizeof(curSubdir) - 1] = 0; + printf("processing subdirectory %s/..." NEWLINE, curSubdir); + filesProcessed += process_sub(data_file, struct_file); + CHDIR(".."); + curSubdir[sublen] = 0; + } else { + printf("WARNING: cannot process sub due to path length restrictions: \"%s/%s\"\n", curSubdir, curName); + } + } while (FINDNEXT_SUCCEEDED(FINDNEXT(fret, &fInfo))); + } + } + + fret = FINDFIRST_FILE("*.*", &fInfo); + if (FINDFIRST_SUCCEEDED(fret)) { + /* at least one file in directory */ + do { + if (FIND_T_IS_FILE(fInfo)) { + const char *curName = FIND_T_FILENAME(fInfo); + printf("processing %s/%s..." NEWLINE, curSubdir, curName); + if (process_file(data_file, struct_file, curName) < 0) { + printf(NEWLINE "Error... aborting" NEWLINE); + return -1; + } + filesProcessed++; + } + } while (FINDNEXT_SUCCEEDED(FINDNEXT(fret, &fInfo))); + } + return filesProcessed; +} + +u8_t* get_file_data(const char* filename, int* file_size, int can_be_compressed, int* is_compressed) +{ + FILE *inFile; + size_t fsize = 0; + u8_t* buf; + size_t r; + int rs; + inFile = fopen(filename, "rb"); + if (inFile == NULL) { + printf("Failed to open file \"%s\"\n", filename); + exit(-1); + } + fseek(inFile, 0, SEEK_END); + rs = ftell(inFile); + if (rs < 0) { + printf("ftell failed with %d\n", errno); + exit(-1); + } + fsize = (size_t)rs; + fseek(inFile, 0, SEEK_SET); + buf = (u8_t*)malloc(fsize); + LWIP_ASSERT("buf != NULL", buf != NULL); + r = fread(buf, 1, fsize, inFile); + *file_size = fsize; + *is_compressed = 0; +#if MAKEFS_SUPPORT_DEFLATE + overallDataBytes += fsize; + if (deflateNonSsiFiles) { + if (can_be_compressed) { + if (fsize < OUT_BUF_SIZE) { + u8_t* ret_buf; + tdefl_status status; + size_t in_bytes = fsize; + size_t out_bytes = OUT_BUF_SIZE; + const void *next_in = buf; + void *next_out = s_outbuf; + /* create tdefl() compatible flags (we have to compose the low-level flags ourselves, or use tdefl_create_comp_flags_from_zip_params() but that means MINIZ_NO_ZLIB_APIS can't be defined). */ + mz_uint comp_flags = s_tdefl_num_probes[MZ_MIN(10, deflate_level)] | ((deflate_level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); + if (!deflate_level) { + comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; + } + status = tdefl_init(&g_deflator, NULL, NULL, comp_flags); + if (status != TDEFL_STATUS_OKAY) { + printf("tdefl_init() failed!\n"); + exit(-1); + } + memset(s_outbuf, 0, sizeof(s_outbuf)); + status = tdefl_compress(&g_deflator, next_in, &in_bytes, next_out, &out_bytes, TDEFL_FINISH); + if (status != TDEFL_STATUS_DONE) { + printf("deflate failed: %d\n", status); + exit(-1); + } + LWIP_ASSERT("out_bytes <= COPY_BUFSIZE", out_bytes <= OUT_BUF_SIZE); + if (out_bytes < fsize) { + ret_buf = (u8_t*)malloc(out_bytes); + LWIP_ASSERT("ret_buf != NULL", ret_buf != NULL); + memcpy(ret_buf, s_outbuf, out_bytes); + { + /* sanity-check compression be inflating and comparing to the original */ + tinfl_status dec_status; + tinfl_decompressor inflator; + size_t dec_in_bytes = out_bytes; + size_t dec_out_bytes = OUT_BUF_SIZE; + next_out = s_checkbuf; + + tinfl_init(&inflator); + memset(s_checkbuf, 0, sizeof(s_checkbuf)); + dec_status = tinfl_decompress(&inflator, (const mz_uint8 *)ret_buf, &dec_in_bytes, s_checkbuf, (mz_uint8 *)next_out, &dec_out_bytes, 0); + LWIP_ASSERT("tinfl_decompress failed", dec_status == TINFL_STATUS_DONE); + LWIP_ASSERT("tinfl_decompress size mismatch", fsize == dec_out_bytes); + LWIP_ASSERT("decompressed memcmp failed", !memcmp(s_checkbuf, buf, fsize)); + } + /* free original buffer, use compressed data + size */ + free(buf); + buf = ret_buf; + *file_size = out_bytes; + printf(" - deflate: %d bytes -> %d bytes (%.02f%%)" NEWLINE, (int)fsize, (int)out_bytes, (float)((out_bytes*100.0)/fsize)); + deflatedBytesReduced += (size_t)(fsize - out_bytes); + *is_compressed = 1; + } else { + printf(" - uncompressed: (would be %d bytes larger using deflate)" NEWLINE, (int)(out_bytes - fsize)); + } + } else { + printf(" - uncompressed: (file is larger than deflate bufer)" NEWLINE); + } + } else { + printf(" - SSI file, cannot be compressed" NEWLINE); + } + } +#else + LWIP_UNUSED_ARG(can_be_compressed); +#endif + fclose(inFile); + return buf; +} + +void process_file_data(FILE* data_file, u8_t* file_data, size_t file_size) +{ + size_t written, i, src_off=0; + + size_t off = 0; + for (i = 0; i < file_size; i++) { + LWIP_ASSERT("file_buffer_c overflow", off < sizeof(file_buffer_c) - 5); + sprintf(&file_buffer_c[off], "0x%02.2x,", file_data[i]); + off += 5; + if ((++src_off % HEX_BYTES_PER_LINE) == 0) { + LWIP_ASSERT("file_buffer_c overflow", off < sizeof(file_buffer_c) - NEWLINE_LEN); + memcpy(&file_buffer_c[off], NEWLINE, NEWLINE_LEN); + off += NEWLINE_LEN; + } + if (off + 20 >= sizeof(file_buffer_c)) { + written = fwrite(file_buffer_c, 1, off, data_file); + LWIP_ASSERT("written == off", written == off); + off = 0; + } + } + written = fwrite(file_buffer_c, 1, off, data_file); + LWIP_ASSERT("written == off", written == off); +} + +int write_checksums(FILE *struct_file, const char *varname, + u16_t hdr_len, u16_t hdr_chksum, const u8_t* file_data, size_t file_size) +{ + int chunk_size = TCP_MSS; + int offset, src_offset; + size_t len; + int i = 0; +#if LWIP_TCP_TIMESTAMPS + /* when timestamps are used, usable space is 12 bytes less per segment */ + chunk_size -= 12; +#endif + + fprintf(struct_file, "#if HTTPD_PRECALCULATED_CHECKSUM" NEWLINE); + fprintf(struct_file, "const struct fsdata_chksum chksums_%s[] = {" NEWLINE, varname); + + if (hdr_len > 0) { + /* add checksum for HTTP header */ + fprintf(struct_file, "{%d, 0x%04x, %d}," NEWLINE, 0, hdr_chksum, hdr_len); + i++; + } + src_offset = 0; + for (offset = hdr_len; ; offset += len) { + unsigned short chksum; + void* data = (void*)&file_data[src_offset]; + len = LWIP_MIN(chunk_size, (int)file_size - src_offset); + if (len == 0) { + break; + } + chksum = ~inet_chksum(data, (u16_t)len); + /* add checksum for data */ + fprintf(struct_file, "{%d, 0x%04x, %d}," NEWLINE, offset, chksum, len); + i++; + } + fprintf(struct_file, "};" NEWLINE); + fprintf(struct_file, "#endif /* HTTPD_PRECALCULATED_CHECKSUM */" NEWLINE); + return i; +} + +static int is_valid_char_for_c_var(char x) +{ + if (((x >= 'A') && (x <= 'Z')) || + ((x >= 'a') && (x <= 'z')) || + ((x >= '0') && (x <= '9')) || + (x == '_')) { + return 1; + } + return 0; +} + +static void fix_filename_for_c(char* qualifiedName, size_t max_len) +{ + struct file_entry* f; + size_t len = strlen(qualifiedName); + char *new_name = (char*)malloc(len + 2); + int filename_ok; + int cnt = 0; + size_t i; + if (len + 3 == max_len) { + printf("File name too long: \"%s\"\n", qualifiedName); + exit(-1); + } + strcpy(new_name, qualifiedName); + for (i = 0; i < len; i++) { + if (!is_valid_char_for_c_var(new_name[i])) { + new_name[i] = '_'; + } + } + do { + filename_ok = 1; + for (f = first_file; f != NULL; f = f->next) { + if (!strcmp(f->filename_c, new_name)) { + filename_ok = 0; + cnt++; + /* try next unique file name */ + sprintf(&new_name[len], "%d", cnt); + break; + } + } + } while (!filename_ok && (cnt < 999)); + if (!filename_ok) { + printf("Failed to get unique file name: \"%s\"\n", qualifiedName); + exit(-1); + } + strcpy(qualifiedName, new_name); + free(new_name); +} + +static void register_filename(const char* qualifiedName) +{ + struct file_entry* fe = (struct file_entry*)malloc(sizeof(struct file_entry)); + fe->filename_c = strdup(qualifiedName); + fe->next = NULL; + if (first_file == NULL) { + first_file = last_file = fe; + } else { + last_file->next = fe; + last_file = fe; + } +} + +int is_ssi_file(const char* filename) +{ + size_t loop; + for (loop = 0; loop < NUM_SHTML_EXTENSIONS; loop++) { + if (strstr(filename, g_pcSSIExtensions[loop])) { + return 1; + } + } + return 0; +} + +int process_file(FILE *data_file, FILE *struct_file, const char *filename) +{ + char varname[MAX_PATH_LEN]; + int i = 0; + char qualifiedName[MAX_PATH_LEN]; + int file_size; + u16_t http_hdr_chksum = 0; + u16_t http_hdr_len = 0; + int chksum_count = 0; + u8_t flags = 0; + const char* flags_str; + u8_t has_content_len; + u8_t* file_data; + int is_compressed = 0; + + /* create qualified name (@todo: prepend slash or not?) */ + sprintf(qualifiedName,"%s/%s", curSubdir, filename); + /* create C variable name */ + strcpy(varname, qualifiedName); + /* convert slashes & dots to underscores */ + fix_filename_for_c(varname, MAX_PATH_LEN); + register_filename(varname); +#if ALIGN_PAYLOAD + /* to force even alignment of array, type 1 */ + fprintf(data_file, "#if FSDATA_FILE_ALIGNMENT==1" NEWLINE); + fprintf(data_file, "static const " PAYLOAD_ALIGN_TYPE " dummy_align_%s = %d;" NEWLINE, varname, payload_alingment_dummy_counter++); + fprintf(data_file, "#endif" NEWLINE); +#endif /* ALIGN_PAYLOAD */ + fprintf(data_file, "static const unsigned char FSDATA_ALIGN_PRE data_%s[] FSDATA_ALIGN_POST = {" NEWLINE, varname); + /* encode source file name (used by file system, not returned to browser) */ + fprintf(data_file, "/* %s (%d chars) */" NEWLINE, qualifiedName, strlen(qualifiedName)+1); + file_put_ascii(data_file, qualifiedName, strlen(qualifiedName)+1, &i); +#if ALIGN_PAYLOAD + /* pad to even number of bytes to assure payload is on aligned boundary */ + while(i % PAYLOAD_ALIGNMENT != 0) { + fprintf(data_file, "0x%02.2x,", 0); + i++; + } +#endif /* ALIGN_PAYLOAD */ + fprintf(data_file, NEWLINE); + + has_content_len = !is_ssi_file(filename); + file_data = get_file_data(filename, &file_size, includeHttpHeader && has_content_len, &is_compressed); + if (includeHttpHeader) { + file_write_http_header(data_file, filename, file_size, &http_hdr_len, &http_hdr_chksum, has_content_len, is_compressed); + flags = FS_FILE_FLAGS_HEADER_INCLUDED; + if (has_content_len) { + flags |= FS_FILE_FLAGS_HEADER_PERSISTENT; + } + } + if (precalcChksum) { + chksum_count = write_checksums(struct_file, varname, http_hdr_len, http_hdr_chksum, file_data, file_size); + } + + /* build declaration of struct fsdata_file in temp file */ + fprintf(struct_file, "const struct fsdata_file file_%s[] = { {" NEWLINE, varname); + fprintf(struct_file, "file_%s," NEWLINE, lastFileVar); + fprintf(struct_file, "data_%s," NEWLINE, varname); + fprintf(struct_file, "data_%s + %d," NEWLINE, varname, i); + fprintf(struct_file, "sizeof(data_%s) - %d," NEWLINE, varname, i); + switch(flags) + { + case(FS_FILE_FLAGS_HEADER_INCLUDED): + flags_str = "FS_FILE_FLAGS_HEADER_INCLUDED"; + break; + case(FS_FILE_FLAGS_HEADER_PERSISTENT): + flags_str = "FS_FILE_FLAGS_HEADER_PERSISTENT"; + break; + case(FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT): + flags_str = "FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT"; + break; + default: + flags_str = "0"; + break; + } + fprintf(struct_file, "%s," NEWLINE, flags_str); + if (precalcChksum) { + fprintf(struct_file, "#if HTTPD_PRECALCULATED_CHECKSUM" NEWLINE); + fprintf(struct_file, "%d, chksums_%s," NEWLINE, chksum_count, varname); + fprintf(struct_file, "#endif /* HTTPD_PRECALCULATED_CHECKSUM */" NEWLINE); + } + fprintf(struct_file, "}};" NEWLINE NEWLINE); + strcpy(lastFileVar, varname); + + /* write actual file contents */ + i = 0; + fprintf(data_file, NEWLINE "/* raw file data (%d bytes) */" NEWLINE, file_size); + process_file_data(data_file, file_data, file_size); + fprintf(data_file, "};" NEWLINE NEWLINE); + free(file_data); + return 0; +} + +int file_write_http_header(FILE *data_file, const char *filename, int file_size, u16_t *http_hdr_len, + u16_t *http_hdr_chksum, u8_t provide_content_len, int is_compressed) +{ + int i = 0; + int response_type = HTTP_HDR_OK; + const char* file_type; + const char *cur_string; + size_t cur_len; + int written = 0; + size_t hdr_len = 0; + u16_t acc; + const char *file_ext; + int j; + u8_t provide_last_modified = includeLastModified; + + memset(hdr_buf, 0, sizeof(hdr_buf)); + + if (useHttp11) { + response_type = HTTP_HDR_OK_11; + } + + fprintf(data_file, NEWLINE "/* HTTP header */"); + if (strstr(filename, "404") == filename) { + response_type = HTTP_HDR_NOT_FOUND; + if (useHttp11) { + response_type = HTTP_HDR_NOT_FOUND_11; + } + } else if (strstr(filename, "400") == filename) { + response_type = HTTP_HDR_BAD_REQUEST; + if (useHttp11) { + response_type = HTTP_HDR_BAD_REQUEST_11; + } + } else if (strstr(filename, "501") == filename) { + response_type = HTTP_HDR_NOT_IMPL; + if (useHttp11) { + response_type = HTTP_HDR_NOT_IMPL_11; + } + } + cur_string = g_psHTTPHeaderStrings[response_type]; + cur_len = strlen(cur_string); + fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len); + written += file_put_ascii(data_file, cur_string, cur_len, &i); + i = 0; + if (precalcChksum) { + memcpy(&hdr_buf[hdr_len], cur_string, cur_len); + hdr_len += cur_len; + } + + cur_string = serverID; + cur_len = strlen(cur_string); + fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len); + written += file_put_ascii(data_file, cur_string, cur_len, &i); + i = 0; + if (precalcChksum) { + memcpy(&hdr_buf[hdr_len], cur_string, cur_len); + hdr_len += cur_len; + } + + file_ext = filename; + if (file_ext != NULL) { + while(strstr(file_ext, ".") != NULL) { + file_ext = strstr(file_ext, "."); + file_ext++; + } + } + if ((file_ext == NULL) || (*file_ext == 0)) { + printf("failed to get extension for file \"%s\", using default.\n", filename); + file_type = HTTP_HDR_DEFAULT_TYPE; + } else { + file_type = NULL; + for (j = 0; j < NUM_HTTP_HEADERS; j++) { + if (!strcmp(file_ext, g_psHTTPHeaders[j].extension)) { + file_type = g_psHTTPHeaders[j].content_type; + break; + } + } + if (file_type == NULL) { + printf("failed to get file type for extension \"%s\", using default.\n", file_ext); + file_type = HTTP_HDR_DEFAULT_TYPE; + } + } + + /* Content-Length is used for persistent connections in HTTP/1.1 but also for + download progress in older versions + @todo: just use a big-enough buffer and let the HTTPD send spaces? */ + if (provide_content_len) { + char intbuf[MAX_PATH_LEN]; + int content_len = file_size; + memset(intbuf, 0, sizeof(intbuf)); + cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONTENT_LENGTH]; + cur_len = strlen(cur_string); + fprintf(data_file, NEWLINE "/* \"%s%d\r\n\" (%d+ bytes) */" NEWLINE, cur_string, content_len, cur_len+2); + written += file_put_ascii(data_file, cur_string, cur_len, &i); + if (precalcChksum) { + memcpy(&hdr_buf[hdr_len], cur_string, cur_len); + hdr_len += cur_len; + } + + _itoa(content_len, intbuf, 10); + strcat(intbuf, "\r\n"); + cur_len = strlen(intbuf); + written += file_put_ascii(data_file, intbuf, cur_len, &i); + i = 0; + if (precalcChksum) { + memcpy(&hdr_buf[hdr_len], intbuf, cur_len); + hdr_len += cur_len; + } + } + if (provide_last_modified) { + char modbuf[256]; + struct stat stat_data; + struct tm* t; + memset(modbuf, 0, sizeof(modbuf)); + memset(&stat_data, 0, sizeof(stat_data)); + cur_string = modbuf; + strcpy(modbuf, "Last-Modified: "); + if (stat(filename, &stat_data) != 0) { + printf("stat(%s) failed with error %d\n", filename, errno); + exit(-1); + } + t = gmtime(&stat_data.st_mtime); + if (t == NULL) { + printf("gmtime() failed with error %d\n", errno); + exit(-1); + } + strftime(&modbuf[15], sizeof(modbuf)-15, "%a, %d %b %Y %H:%M:%S GMT", t); + cur_len = strlen(cur_string); + fprintf(data_file, NEWLINE "/* \"%s\"\r\n\" (%d+ bytes) */" NEWLINE, cur_string, cur_len+2); + written += file_put_ascii(data_file, cur_string, cur_len, &i); + if (precalcChksum) { + memcpy(&hdr_buf[hdr_len], cur_string, cur_len); + hdr_len += cur_len; + } + + modbuf[0] = 0; + strcat(modbuf, "\r\n"); + cur_len = strlen(modbuf); + written += file_put_ascii(data_file, modbuf, cur_len, &i); + i = 0; + if (precalcChksum) { + memcpy(&hdr_buf[hdr_len], modbuf, cur_len); + hdr_len += cur_len; + } + } + + /* HTTP/1.1 implements persistent connections */ + if (useHttp11) { + if (provide_content_len) { + cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONN_KEEPALIVE]; + } else { + /* no Content-Length available, so a persistent connection is no possible + because the client does not know the data length */ + cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONN_CLOSE]; + } + cur_len = strlen(cur_string); + fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len); + written += file_put_ascii(data_file, cur_string, cur_len, &i); + i = 0; + if (precalcChksum) { + memcpy(&hdr_buf[hdr_len], cur_string, cur_len); + hdr_len += cur_len; + } + } + +#if MAKEFS_SUPPORT_DEFLATE + if (is_compressed) { + /* tell the client about the deflate encoding */ + LWIP_ASSERT("error", deflateNonSsiFiles); + cur_string = "Content-Encoding: deflate\r\n"; + cur_len = strlen(cur_string); + fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len); + written += file_put_ascii(data_file, cur_string, cur_len, &i); + i = 0; + } +#else + LWIP_UNUSED_ARG(is_compressed); +#endif + + /* write content-type, ATTENTION: this includes the double-CRLF! */ + cur_string = file_type; + cur_len = strlen(cur_string); + fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len); + written += file_put_ascii(data_file, cur_string, cur_len, &i); + i = 0; + + /* ATTENTION: headers are done now (double-CRLF has been written!) */ + + if (precalcChksum) { + memcpy(&hdr_buf[hdr_len], cur_string, cur_len); + hdr_len += cur_len; + + LWIP_ASSERT("hdr_len <= 0xffff", hdr_len <= 0xffff); + LWIP_ASSERT("strlen(hdr_buf) == hdr_len", strlen(hdr_buf) == hdr_len); + acc = ~inet_chksum(hdr_buf, (u16_t)hdr_len); + *http_hdr_len = (u16_t)hdr_len; + *http_hdr_chksum = acc; + } + + return written; +} + +int file_put_ascii(FILE *file, const char* ascii_string, int len, int *i) +{ + int x; + for (x = 0; x < len; x++) { + unsigned char cur = ascii_string[x]; + fprintf(file, "0x%02.2x,", cur); + if ((++(*i) % HEX_BYTES_PER_LINE) == 0) { + fprintf(file, NEWLINE); + } + } + return len; +} + +int s_put_ascii(char *buf, const char *ascii_string, int len, int *i) +{ + int x; + int idx = 0; + for (x = 0; x < len; x++) { + unsigned char cur = ascii_string[x]; + sprintf(&buf[idx], "0x%02.2x,", cur); + idx += 5; + if ((++(*i) % HEX_BYTES_PER_LINE) == 0) { + sprintf(&buf[idx], NEWLINE); + idx += NEWLINE_LEN; + } + } + return len; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/makefsdata/readme.txt b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/makefsdata/readme.txt new file mode 100644 index 0000000..3768585 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/makefsdata/readme.txt @@ -0,0 +1,13 @@ +This directory contains a script ('makefsdata') to create C code suitable for +httpd for given html pages (or other files) in a directory. + +There is also a plain C console application doing the same and extended a bit. + +Usage: htmlgen [targetdir] [-s] [-i]s + targetdir: relative or absolute path to files to convert + switch -s: toggle processing of subdirectories (default is on) + switch -e: exclude HTTP header from file (header is created at runtime, default is on) + switch -11: include HTTP 1.1 header (1.0 is default) + + if targetdir not specified, makefsdata will attempt to + process files in subdirectory 'fs'. diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/lwiperf/lwiperf.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/lwiperf/lwiperf.c new file mode 100644 index 0000000..efabe47 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/lwiperf/lwiperf.c @@ -0,0 +1,661 @@ +/** + * @file + * lwIP iPerf server implementation + */ + +/** + * @defgroup iperf Iperf server + * @ingroup apps + * + * This is a simple performance measuring server to check your bandwith using + * iPerf2 on a PC as client. + * It is currently a minimal implementation providing an IPv4 TCP server only. + * + * @todo: implement UDP mode and IPv6 + */ + +/* + * Copyright (c) 2014 Simon Goldschmidt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Simon Goldschmidt + */ + +#include "lwip/apps/lwiperf.h" + +#include "lwip/tcp.h" +#include "lwip/sys.h" + +#include <string.h> + +/* Currently, only TCP-over-IPv4 is implemented (does iperf support IPv6 anyway?) */ +#if LWIP_IPV4 && LWIP_TCP && LWIP_CALLBACK_API + +/** Specify the idle timeout (in seconds) after that the test fails */ +#ifndef LWIPERF_TCP_MAX_IDLE_SEC +#define LWIPERF_TCP_MAX_IDLE_SEC 10U +#endif +#if LWIPERF_TCP_MAX_IDLE_SEC > 255 +#error LWIPERF_TCP_MAX_IDLE_SEC must fit into an u8_t +#endif + +/* File internal memory allocation (struct lwiperf_*): this defaults to + the heap */ +#ifndef LWIPERF_ALLOC +#define LWIPERF_ALLOC(type) mem_malloc(sizeof(type)) +#define LWIPERF_FREE(type, item) mem_free(item) +#endif + +/** If this is 1, check that received data has the correct format */ +#ifndef LWIPERF_CHECK_RX_DATA +#define LWIPERF_CHECK_RX_DATA 0 +#endif + +/** This is the Iperf settings struct sent from the client */ +typedef struct _lwiperf_settings { +#define LWIPERF_FLAGS_ANSWER_TEST 0x80000000 +#define LWIPERF_FLAGS_ANSWER_NOW 0x00000001 + u32_t flags; + u32_t num_threads; /* unused for now */ + u32_t remote_port; + u32_t buffer_len; /* unused for now */ + u32_t win_band; /* TCP window / UDP rate: unused for now */ + u32_t amount; /* pos. value: bytes?; neg. values: time (unit is 10ms: 1/100 second) */ +} lwiperf_settings_t; + +/** Basic connection handle */ +struct _lwiperf_state_base; +typedef struct _lwiperf_state_base lwiperf_state_base_t; +struct _lwiperf_state_base { + /* 1=tcp, 0=udp */ + u8_t tcp; + /* 1=server, 0=client */ + u8_t server; + lwiperf_state_base_t* next; + lwiperf_state_base_t* related_server_state; +}; + +/** Connection handle for a TCP iperf session */ +typedef struct _lwiperf_state_tcp { + lwiperf_state_base_t base; + struct tcp_pcb* server_pcb; + struct tcp_pcb* conn_pcb; + u32_t time_started; + lwiperf_report_fn report_fn; + void* report_arg; + u8_t poll_count; + u8_t next_num; + u32_t bytes_transferred; + lwiperf_settings_t settings; + u8_t have_settings_buf; +} lwiperf_state_tcp_t; + +/** List of active iperf sessions */ +static lwiperf_state_base_t* lwiperf_all_connections; +/** A const buffer to send from: we want to measure sending, not copying! */ +static const u8_t lwiperf_txbuf_const[1600] = { + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', +}; + +static err_t lwiperf_tcp_poll(void *arg, struct tcp_pcb *tpcb); +static void lwiperf_tcp_err(void *arg, err_t err); + +/** Add an iperf session to the 'active' list */ +static void +lwiperf_list_add(lwiperf_state_base_t* item) +{ + if (lwiperf_all_connections == NULL) { + lwiperf_all_connections = item; + } else { + item = lwiperf_all_connections; + } +} + +/** Remove an iperf session from the 'active' list */ +static void +lwiperf_list_remove(lwiperf_state_base_t* item) +{ + lwiperf_state_base_t* prev = NULL; + lwiperf_state_base_t* iter; + for (iter = lwiperf_all_connections; iter != NULL; prev = iter, iter = iter->next) { + if (iter == item) { + if (prev == NULL) { + lwiperf_all_connections = iter->next; + } else { + prev->next = item; + } + /* @debug: ensure this item is listed only once */ + for (iter = iter->next; iter != NULL; iter = iter->next) { + LWIP_ASSERT("duplicate entry", iter != item); + } + break; + } + } +} + +/** Call the report function of an iperf tcp session */ +static void +lwip_tcp_conn_report(lwiperf_state_tcp_t* conn, enum lwiperf_report_type report_type) +{ + if ((conn != NULL) && (conn->report_fn != NULL)) { + u32_t now, duration_ms, bandwidth_kbitpsec; + now = sys_now(); + duration_ms = now - conn->time_started; + if (duration_ms == 0) { + bandwidth_kbitpsec = 0; + } else { + bandwidth_kbitpsec = (conn->bytes_transferred / duration_ms) * 8U; + } + conn->report_fn(conn->report_arg, report_type, + &conn->conn_pcb->local_ip, conn->conn_pcb->local_port, + &conn->conn_pcb->remote_ip, conn->conn_pcb->remote_port, + conn->bytes_transferred, duration_ms, bandwidth_kbitpsec); + } +} + +/** Close an iperf tcp session */ +static void +lwiperf_tcp_close(lwiperf_state_tcp_t* conn, enum lwiperf_report_type report_type) +{ + err_t err; + + lwip_tcp_conn_report(conn, report_type); + lwiperf_list_remove(&conn->base); + if (conn->conn_pcb != NULL) { + tcp_arg(conn->conn_pcb, NULL); + tcp_poll(conn->conn_pcb, NULL, 0); + tcp_sent(conn->conn_pcb, NULL); + tcp_recv(conn->conn_pcb, NULL); + tcp_err(conn->conn_pcb, NULL); + err = tcp_close(conn->conn_pcb); + if (err != ERR_OK) { + /* don't want to wait for free memory here... */ + tcp_abort(conn->conn_pcb); + } + } else { + /* no conn pcb, this is the server pcb */ + err = tcp_close(conn->server_pcb); + LWIP_ASSERT("error", err != ERR_OK); + } + LWIPERF_FREE(lwiperf_state_tcp_t, conn); +} + +/** Try to send more data on an iperf tcp session */ +static err_t +lwiperf_tcp_client_send_more(lwiperf_state_tcp_t* conn) +{ + int send_more; + err_t err; + u16_t txlen; + u16_t txlen_max; + void* txptr; + u8_t apiflags; + + LWIP_ASSERT("conn invalid", (conn != NULL) && conn->base.tcp && (conn->base.server == 0)); + + do { + send_more = 0; + if (conn->settings.amount & PP_HTONL(0x80000000)) { + /* this session is time-limited */ + u32_t now = sys_now(); + u32_t diff_ms = now - conn->time_started; + u32_t time = (u32_t)-(s32_t)lwip_htonl(conn->settings.amount); + u32_t time_ms = time * 10; + if (diff_ms >= time_ms) { + /* time specified by the client is over -> close the connection */ + lwiperf_tcp_close(conn, LWIPERF_TCP_DONE_CLIENT); + return ERR_OK; + } + } else { + /* this session is byte-limited */ + u32_t amount_bytes = lwip_htonl(conn->settings.amount); + /* @todo: this can send up to 1*MSS more than requested... */ + if (amount_bytes >= conn->bytes_transferred) { + /* all requested bytes transferred -> close the connection */ + lwiperf_tcp_close(conn, LWIPERF_TCP_DONE_CLIENT); + return ERR_OK; + } + } + + if (conn->bytes_transferred < 24) { + /* transmit the settings a first time */ + txptr = &((u8_t*)&conn->settings)[conn->bytes_transferred]; + txlen_max = (u16_t)(24 - conn->bytes_transferred); + apiflags = TCP_WRITE_FLAG_COPY; + } else if (conn->bytes_transferred < 48) { + /* transmit the settings a second time */ + txptr = &((u8_t*)&conn->settings)[conn->bytes_transferred - 24]; + txlen_max = (u16_t)(48 - conn->bytes_transferred); + apiflags = TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE; + send_more = 1; + } else { + /* transmit data */ + /* @todo: every x bytes, transmit the settings again */ + txptr = LWIP_CONST_CAST(void*, &lwiperf_txbuf_const[conn->bytes_transferred % 10]); + txlen_max = TCP_MSS; + if (conn->bytes_transferred == 48) { /* @todo: fix this for intermediate settings, too */ + txlen_max = TCP_MSS - 24; + } + apiflags = 0; /* no copying needed */ + send_more = 1; + } + txlen = txlen_max; + do { + err = tcp_write(conn->conn_pcb, txptr, txlen, apiflags); + if (err == ERR_MEM) { + txlen /= 2; + } + } while ((err == ERR_MEM) && (txlen >= (TCP_MSS/2))); + + if (err == ERR_OK) { + conn->bytes_transferred += txlen; + } else { + send_more = 0; + } + } while(send_more); + + tcp_output(conn->conn_pcb); + return ERR_OK; +} + +/** TCP sent callback, try to send more data */ +static err_t +lwiperf_tcp_client_sent(void *arg, struct tcp_pcb *tpcb, u16_t len) +{ + lwiperf_state_tcp_t* conn = (lwiperf_state_tcp_t*)arg; + /* @todo: check 'len' (e.g. to time ACK of all data)? for now, we just send more... */ + LWIP_ASSERT("invalid conn", conn->conn_pcb == tpcb); + LWIP_UNUSED_ARG(tpcb); + LWIP_UNUSED_ARG(len); + + conn->poll_count = 0; + + return lwiperf_tcp_client_send_more(conn); +} + +/** TCP connected callback (active connection), send data now */ +static err_t +lwiperf_tcp_client_connected(void *arg, struct tcp_pcb *tpcb, err_t err) +{ + lwiperf_state_tcp_t* conn = (lwiperf_state_tcp_t*)arg; + LWIP_ASSERT("invalid conn", conn->conn_pcb == tpcb); + LWIP_UNUSED_ARG(tpcb); + if (err != ERR_OK) { + lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_REMOTE); + return ERR_OK; + } + conn->poll_count = 0; + conn->time_started = sys_now(); + return lwiperf_tcp_client_send_more(conn); +} + +/** Start TCP connection back to the client (either parallel or after the + * receive test has finished. + */ +static err_t +lwiperf_tx_start(lwiperf_state_tcp_t* conn) +{ + err_t err; + lwiperf_state_tcp_t* client_conn; + struct tcp_pcb* newpcb; + ip_addr_t remote_addr; + u16_t remote_port; + + client_conn = (lwiperf_state_tcp_t*)LWIPERF_ALLOC(lwiperf_state_tcp_t); + if (client_conn == NULL) { + return ERR_MEM; + } + newpcb = tcp_new(); + if (newpcb == NULL) { + LWIPERF_FREE(lwiperf_state_tcp_t, client_conn); + return ERR_MEM; + } + + MEMCPY(client_conn, conn, sizeof(lwiperf_state_tcp_t)); + client_conn->base.server = 0; + client_conn->server_pcb = NULL; + client_conn->conn_pcb = newpcb; + client_conn->time_started = sys_now(); /* @todo: set this again on 'connected' */ + client_conn->poll_count = 0; + client_conn->next_num = 4; /* initial nr is '4' since the header has 24 byte */ + client_conn->bytes_transferred = 0; + client_conn->settings.flags = 0; /* prevent the remote side starting back as client again */ + + tcp_arg(newpcb, client_conn); + tcp_sent(newpcb, lwiperf_tcp_client_sent); + tcp_poll(newpcb, lwiperf_tcp_poll, 2U); + tcp_err(newpcb, lwiperf_tcp_err); + + ip_addr_copy(remote_addr, conn->conn_pcb->remote_ip); + remote_port = (u16_t)lwip_htonl(client_conn->settings.remote_port); + + err = tcp_connect(newpcb, &remote_addr, remote_port, lwiperf_tcp_client_connected); + if (err != ERR_OK) { + lwiperf_tcp_close(client_conn, LWIPERF_TCP_ABORTED_LOCAL); + return err; + } + lwiperf_list_add(&client_conn->base); + return ERR_OK; +} + +/** Receive data on an iperf tcp session */ +static err_t +lwiperf_tcp_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) +{ + u8_t tmp; + u16_t tot_len; + u32_t packet_idx; + struct pbuf* q; + lwiperf_state_tcp_t* conn = (lwiperf_state_tcp_t*)arg; + + LWIP_ASSERT("pcb mismatch", conn->conn_pcb == tpcb); + LWIP_UNUSED_ARG(tpcb); + + if (err != ERR_OK) { + lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_REMOTE); + return ERR_OK; + } + if (p == NULL) { + /* connection closed -> test done */ + if ((conn->settings.flags & PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST|LWIPERF_FLAGS_ANSWER_NOW)) == + PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST)) { + /* client requested transmission after end of test */ + lwiperf_tx_start(conn); + } + lwiperf_tcp_close(conn, LWIPERF_TCP_DONE_SERVER); + return ERR_OK; + } + tot_len = p->tot_len; + + conn->poll_count = 0; + + if ((!conn->have_settings_buf) || ((conn->bytes_transferred -24) % (1024*128) == 0)) { + /* wait for 24-byte header */ + if (p->tot_len < sizeof(lwiperf_settings_t)) { + lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_DATAERROR); + pbuf_free(p); + return ERR_VAL; + } + if (!conn->have_settings_buf) { + if (pbuf_copy_partial(p, &conn->settings, sizeof(lwiperf_settings_t), 0) != sizeof(lwiperf_settings_t)) { + lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL); + pbuf_free(p); + return ERR_VAL; + } + conn->have_settings_buf = 1; + if ((conn->settings.flags & PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST|LWIPERF_FLAGS_ANSWER_NOW)) == + PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST|LWIPERF_FLAGS_ANSWER_NOW)) { + /* client requested parallel transmission test */ + err_t err2 = lwiperf_tx_start(conn); + if (err2 != ERR_OK) { + lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_TXERROR); + pbuf_free(p); + return err2; + } + } + } else { + if (pbuf_memcmp(p, 0, &conn->settings, sizeof(lwiperf_settings_t)) != 0) { + lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_DATAERROR); + pbuf_free(p); + return ERR_VAL; + } + } + conn->bytes_transferred += sizeof(lwiperf_settings_t); + if (conn->bytes_transferred <= 24) { + conn->time_started = sys_now(); + tcp_recved(tpcb, p->tot_len); + pbuf_free(p); + return ERR_OK; + } + conn->next_num = 4; /* 24 bytes received... */ + tmp = pbuf_header(p, -24); + LWIP_ASSERT("pbuf_header failed", tmp == 0); + } + + packet_idx = 0; + for (q = p; q != NULL; q = q->next) { +#if LWIPERF_CHECK_RX_DATA + const u8_t* payload = (const u8_t*)q->payload; + u16_t i; + for (i = 0; i < q->len; i++) { + u8_t val = payload[i]; + u8_t num = val - '0'; + if (num == conn->next_num) { + conn->next_num++; + if (conn->next_num == 10) { + conn->next_num = 0; + } + } else { + lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_DATAERROR); + pbuf_free(p); + return ERR_VAL; + } + } +#endif + packet_idx += q->len; + } + LWIP_ASSERT("count mismatch", packet_idx == p->tot_len); + conn->bytes_transferred += packet_idx; + tcp_recved(tpcb, tot_len); + pbuf_free(p); + return ERR_OK; +} + +/** Error callback, iperf tcp session aborted */ +static void +lwiperf_tcp_err(void *arg, err_t err) +{ + lwiperf_state_tcp_t* conn = (lwiperf_state_tcp_t*)arg; + LWIP_UNUSED_ARG(err); + lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_REMOTE); +} + +/** TCP poll callback, try to send more data */ +static err_t +lwiperf_tcp_poll(void *arg, struct tcp_pcb *tpcb) +{ + lwiperf_state_tcp_t* conn = (lwiperf_state_tcp_t*)arg; + LWIP_ASSERT("pcb mismatch", conn->conn_pcb == tpcb); + LWIP_UNUSED_ARG(tpcb); + if (++conn->poll_count >= LWIPERF_TCP_MAX_IDLE_SEC) { + lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL); + return ERR_OK; /* lwiperf_tcp_close frees conn */ + } + + if (!conn->base.server) { + lwiperf_tcp_client_send_more(conn); + } + + return ERR_OK; +} + +/** This is called when a new client connects for an iperf tcp session */ +static err_t +lwiperf_tcp_accept(void *arg, struct tcp_pcb *newpcb, err_t err) +{ + lwiperf_state_tcp_t *s, *conn; + if ((err != ERR_OK) || (newpcb == NULL) || (arg == NULL)) { + return ERR_VAL; + } + + s = (lwiperf_state_tcp_t*)arg; + conn = (lwiperf_state_tcp_t*)LWIPERF_ALLOC(lwiperf_state_tcp_t); + if (conn == NULL) { + return ERR_MEM; + } + memset(conn, 0, sizeof(lwiperf_state_tcp_t)); + conn->base.tcp = 1; + conn->base.server = 1; + conn->base.related_server_state = &s->base; + conn->server_pcb = s->server_pcb; + conn->conn_pcb = newpcb; + conn->time_started = sys_now(); + conn->report_fn = s->report_fn; + conn->report_arg = s->report_arg; + + /* setup the tcp rx connection */ + tcp_arg(newpcb, conn); + tcp_recv(newpcb, lwiperf_tcp_recv); + tcp_poll(newpcb, lwiperf_tcp_poll, 2U); + tcp_err(conn->conn_pcb, lwiperf_tcp_err); + + lwiperf_list_add(&conn->base); + return ERR_OK; +} + +/** + * @ingroup iperf + * Start a TCP iperf server on the default TCP port (5001) and listen for + * incoming connections from iperf clients. + * + * @returns a connection handle that can be used to abort the server + * by calling @ref lwiperf_abort() + */ +void* +lwiperf_start_tcp_server_default(lwiperf_report_fn report_fn, void* report_arg) +{ + return lwiperf_start_tcp_server(IP_ADDR_ANY, LWIPERF_TCP_PORT_DEFAULT, + report_fn, report_arg); +} + +/** + * @ingroup iperf + * Start a TCP iperf server on a specific IP address and port and listen for + * incoming connections from iperf clients. + * + * @returns a connection handle that can be used to abort the server + * by calling @ref lwiperf_abort() + */ +void* +lwiperf_start_tcp_server(const ip_addr_t* local_addr, u16_t local_port, + lwiperf_report_fn report_fn, void* report_arg) +{ + err_t err; + struct tcp_pcb* pcb; + lwiperf_state_tcp_t* s; + + if (local_addr == NULL) { + return NULL; + } + + s = (lwiperf_state_tcp_t*)LWIPERF_ALLOC(lwiperf_state_tcp_t); + if (s == NULL) { + return NULL; + } + memset(s, 0, sizeof(lwiperf_state_tcp_t)); + s->base.tcp = 1; + s->base.server = 1; + s->report_fn = report_fn; + s->report_arg = report_arg; + + pcb = tcp_new(); + if (pcb != NULL) { + err = tcp_bind(pcb, local_addr, local_port); + if (err == ERR_OK) { + s->server_pcb = tcp_listen_with_backlog(pcb, 1); + } + } + if (s->server_pcb == NULL) { + if (pcb != NULL) { + tcp_close(pcb); + } + LWIPERF_FREE(lwiperf_state_tcp_t, s); + return NULL; + } + pcb = NULL; + + tcp_arg(s->server_pcb, s); + tcp_accept(s->server_pcb, lwiperf_tcp_accept); + + lwiperf_list_add(&s->base); + return s; +} + +/** + * @ingroup iperf + * Abort an iperf session (handle returned by lwiperf_start_tcp_server*()) + */ +void +lwiperf_abort(void* lwiperf_session) +{ + lwiperf_state_base_t* i, *dealloc, *last = NULL; + + for (i = lwiperf_all_connections; i != NULL; ) { + if ((i == lwiperf_session) || (i->related_server_state == lwiperf_session)) { + dealloc = i; + i = i->next; + if (last != NULL) { + last->next = i; + } + LWIPERF_FREE(lwiperf_state_tcp_t, dealloc); /* @todo: type? */ + } else { + last = i; + i = i->next; + } + } +} + +#endif /* LWIP_IPV4 && LWIP_TCP && LWIP_CALLBACK_API */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/mdns/mdns.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/mdns/mdns.c new file mode 100644 index 0000000..14334fc --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/mdns/mdns.c @@ -0,0 +1,2028 @@ +/** + * @file + * MDNS responder implementation + * + * @defgroup mdns MDNS + * @ingroup apps + * + * RFC 6762 - Multicast DNS\n + * RFC 6763 - DNS-Based Service Discovery\n + * + * @verbinclude mdns.txt + * + * Things left to implement: + * ------------------------- + * + * - Probing/conflict resolution + * - Sending goodbye messages (zero ttl) - shutdown, DHCP lease about to expire, DHCP turned off... + * - Checking that source address of unicast requests are on the same network + * - Limiting multicast responses to 1 per second per resource record + * - Fragmenting replies if required + * - Subscribe to netif address/link change events and act on them (currently needs to be done manually) + * - Handling multi-packet known answers + * - Individual known answer detection for all local IPv6 addresses + * - Dynamic size of outgoing packet + */ + +/* + * Copyright (c) 2015 Verisure Innovation AB + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Erik Ekman <erik@kryo.se> + * + */ + +#include "lwip/apps/mdns.h" +#include "lwip/apps/mdns_priv.h" +#include "lwip/netif.h" +#include "lwip/udp.h" +#include "lwip/ip_addr.h" +#include "lwip/mem.h" +#include "lwip/prot/dns.h" + +#include <string.h> + +#if LWIP_MDNS_RESPONDER + +#if (LWIP_IPV4 && !LWIP_IGMP) + #error "If you want to use MDNS with IPv4, you have to define LWIP_IGMP=1 in your lwipopts.h" +#endif +#if (LWIP_IPV6 && !LWIP_IPV6_MLD) +#error "If you want to use MDNS with IPv6, you have to define LWIP_IPV6_MLD=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP) + #error "If you want to use MDNS, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif + +#if LWIP_IPV4 +#include "lwip/igmp.h" +/* IPv4 multicast group 224.0.0.251 */ +static const ip_addr_t v4group = DNS_MQUERY_IPV4_GROUP_INIT; +#endif + +#if LWIP_IPV6 +#include "lwip/mld6.h" +/* IPv6 multicast group FF02::FB */ +static const ip_addr_t v6group = DNS_MQUERY_IPV6_GROUP_INIT; +#endif + +#define MDNS_PORT 5353 +#define MDNS_TTL 255 + +/* Stored offsets to beginning of domain names + * Used for compression. + */ +#define NUM_DOMAIN_OFFSETS 10 +#define DOMAIN_JUMP_SIZE 2 +#define DOMAIN_JUMP 0xc000 + +static u8_t mdns_netif_client_id; +static struct udp_pcb *mdns_pcb; + +#define NETIF_TO_HOST(netif) (struct mdns_host*)(netif_get_client_data(netif, mdns_netif_client_id)) + +#define TOPDOMAIN_LOCAL "local" + +#define REVERSE_PTR_TOPDOMAIN "arpa" +#define REVERSE_PTR_V4_DOMAIN "in-addr" +#define REVERSE_PTR_V6_DOMAIN "ip6" + +#define SRV_PRIORITY 0 +#define SRV_WEIGHT 0 + +/* Payload size allocated for each outgoing UDP packet */ +#define OUTPACKET_SIZE 500 + +/* Lookup from hostname -> IPv4 */ +#define REPLY_HOST_A 0x01 +/* Lookup from IPv4/v6 -> hostname */ +#define REPLY_HOST_PTR_V4 0x02 +/* Lookup from hostname -> IPv6 */ +#define REPLY_HOST_AAAA 0x04 +/* Lookup from hostname -> IPv6 */ +#define REPLY_HOST_PTR_V6 0x08 + +/* Lookup for service types */ +#define REPLY_SERVICE_TYPE_PTR 0x10 +/* Lookup for instances of service */ +#define REPLY_SERVICE_NAME_PTR 0x20 +/* Lookup for location of service instance */ +#define REPLY_SERVICE_SRV 0x40 +/* Lookup for text info on service instance */ +#define REPLY_SERVICE_TXT 0x80 + +static const char *dnssd_protos[] = { + "_udp", /* DNSSD_PROTO_UDP */ + "_tcp", /* DNSSD_PROTO_TCP */ +}; + +/** Description of a service */ +struct mdns_service { + /** TXT record to answer with */ + struct mdns_domain txtdata; + /** Name of service, like 'myweb' */ + char name[MDNS_LABEL_MAXLEN + 1]; + /** Type of service, like '_http' */ + char service[MDNS_LABEL_MAXLEN + 1]; + /** Callback function and userdata + * to update txtdata buffer */ + service_get_txt_fn_t txt_fn; + void *txt_userdata; + /** TTL in seconds of SRV/TXT replies */ + u32_t dns_ttl; + /** Protocol, TCP or UDP */ + u16_t proto; + /** Port of the service */ + u16_t port; +}; + +/** Description of a host/netif */ +struct mdns_host { + /** Hostname */ + char name[MDNS_LABEL_MAXLEN + 1]; + /** Pointer to services */ + struct mdns_service *services[MDNS_MAX_SERVICES]; + /** TTL in seconds of A/AAAA/PTR replies */ + u32_t dns_ttl; +}; + +/** Information about received packet */ +struct mdns_packet { + /** Sender IP/port */ + ip_addr_t source_addr; + u16_t source_port; + /** If packet was received unicast */ + u16_t recv_unicast; + /** Netif that received the packet */ + struct netif *netif; + /** Packet data */ + struct pbuf *pbuf; + /** Current parsing offset in packet */ + u16_t parse_offset; + /** Identifier. Used in legacy queries */ + u16_t tx_id; + /** Number of questions in packet, + * read from packet header */ + u16_t questions; + /** Number of unparsed questions */ + u16_t questions_left; + /** Number of answers in packet, + * (sum of normal, authorative and additional answers) + * read from packet header */ + u16_t answers; + /** Number of unparsed answers */ + u16_t answers_left; +}; + +/** Information about outgoing packet */ +struct mdns_outpacket { + /** Netif to send the packet on */ + struct netif *netif; + /** Packet data */ + struct pbuf *pbuf; + /** Current write offset in packet */ + u16_t write_offset; + /** Identifier. Used in legacy queries */ + u16_t tx_id; + /** Destination IP/port if sent unicast */ + ip_addr_t dest_addr; + u16_t dest_port; + /** Number of questions written */ + u16_t questions; + /** Number of normal answers written */ + u16_t answers; + /** Number of additional answers written */ + u16_t additional; + /** Offsets for written domain names in packet. + * Used for compression */ + u16_t domain_offsets[NUM_DOMAIN_OFFSETS]; + /** If all answers in packet should set cache_flush bit */ + u8_t cache_flush; + /** If reply should be sent unicast */ + u8_t unicast_reply; + /** If legacy query. (tx_id needed, and write + * question again in reply before answer) */ + u8_t legacy_query; + /* Reply bitmask for host information */ + u8_t host_replies; + /* Bitmask for which reverse IPv6 hosts to answer */ + u8_t host_reverse_v6_replies; + /* Reply bitmask per service */ + u8_t serv_replies[MDNS_MAX_SERVICES]; +}; + +/** Domain, type and class. + * Shared between questions and answers */ +struct mdns_rr_info { + struct mdns_domain domain; + u16_t type; + u16_t klass; +}; + +struct mdns_question { + struct mdns_rr_info info; + /** unicast reply requested */ + u16_t unicast; +}; + +struct mdns_answer { + struct mdns_rr_info info; + /** cache flush command bit */ + u16_t cache_flush; + /* Validity time in seconds */ + u32_t ttl; + /** Length of variable answer */ + u16_t rd_length; + /** Offset of start of variable answer in packet */ + u16_t rd_offset; +}; + +/** + * Add a label part to a domain + * @param domain The domain to add a label to + * @param label The label to add, like <hostname>, 'local', 'com' or '' + * @param len The length of the label + * @return ERR_OK on success, an err_t otherwise if label too long + */ +err_t +mdns_domain_add_label(struct mdns_domain *domain, const char *label, u8_t len) +{ + if (len > MDNS_LABEL_MAXLEN) { + return ERR_VAL; + } + if (len > 0 && (1 + len + domain->length >= MDNS_DOMAIN_MAXLEN)) { + return ERR_VAL; + } + /* Allow only zero marker on last byte */ + if (len == 0 && (1 + domain->length > MDNS_DOMAIN_MAXLEN)) { + return ERR_VAL; + } + domain->name[domain->length] = len; + domain->length++; + if (len) { + MEMCPY(&domain->name[domain->length], label, len); + domain->length += len; + } + return ERR_OK; +} + +/** + * Internal readname function with max 6 levels of recursion following jumps + * while decompressing name + */ +static u16_t +mdns_readname_loop(struct pbuf *p, u16_t offset, struct mdns_domain *domain, unsigned depth) +{ + u8_t c; + + do { + if (depth > 5) { + /* Too many jumps */ + return MDNS_READNAME_ERROR; + } + + c = pbuf_get_at(p, offset); + offset++; + + /* is this a compressed label? */ + if((c & 0xc0) == 0xc0) { + u16_t jumpaddr; + if (offset >= p->tot_len) { + /* Make sure both jump bytes fit in the packet */ + return MDNS_READNAME_ERROR; + } + jumpaddr = (((c & 0x3f) << 8) | (pbuf_get_at(p, offset) & 0xff)); + offset++; + if (jumpaddr >= SIZEOF_DNS_HDR && jumpaddr < p->tot_len) { + u16_t res; + /* Recursive call, maximum depth will be checked */ + res = mdns_readname_loop(p, jumpaddr, domain, depth + 1); + /* Dont return offset since new bytes were not read (jumped to somewhere in packet) */ + if (res == MDNS_READNAME_ERROR) { + return res; + } + } else { + return MDNS_READNAME_ERROR; + } + break; + } + + /* normal label */ + if (c <= MDNS_LABEL_MAXLEN) { + u8_t label[MDNS_LABEL_MAXLEN]; + err_t res; + + if (c + domain->length >= MDNS_DOMAIN_MAXLEN) { + return MDNS_READNAME_ERROR; + } + if (c != 0) { + if (pbuf_copy_partial(p, label, c, offset) != c) { + return MDNS_READNAME_ERROR; + } + offset += c; + } + res = mdns_domain_add_label(domain, (char *) label, c); + if (res != ERR_OK) { + return MDNS_READNAME_ERROR; + } + } else { + /* bad length byte */ + return MDNS_READNAME_ERROR; + } + } while (c != 0); + + return offset; +} + +/** + * Read possibly compressed domain name from packet buffer + * @param p The packet + * @param offset start position of domain name in packet + * @param domain The domain name destination + * @return The new offset after the domain, or MDNS_READNAME_ERROR + * if reading failed + */ +u16_t +mdns_readname(struct pbuf *p, u16_t offset, struct mdns_domain *domain) +{ + memset(domain, 0, sizeof(struct mdns_domain)); + return mdns_readname_loop(p, offset, domain, 0); +} + +/** + * Print domain name to debug output + * @param domain The domain name + */ +static void +mdns_domain_debug_print(struct mdns_domain *domain) +{ + u8_t *src = domain->name; + u8_t i; + + while (*src) { + u8_t label_len = *src; + src++; + for (i = 0; i < label_len; i++) { + LWIP_DEBUGF(MDNS_DEBUG, ("%c", src[i])); + } + src += label_len; + LWIP_DEBUGF(MDNS_DEBUG, (".")); + } +} + +/** + * Return 1 if contents of domains match (case-insensitive) + * @param a Domain name to compare 1 + * @param b Domain name to compare 2 + * @return 1 if domains are equal ignoring case, 0 otherwise + */ +int +mdns_domain_eq(struct mdns_domain *a, struct mdns_domain *b) +{ + u8_t *ptra, *ptrb; + u8_t len; + int res; + + if (a->length != b->length) { + return 0; + } + + ptra = a->name; + ptrb = b->name; + while (*ptra && *ptrb && ptra < &a->name[a->length]) { + if (*ptra != *ptrb) { + return 0; + } + len = *ptra; + ptra++; + ptrb++; + res = lwip_strnicmp((char *) ptra, (char *) ptrb, len); + if (res != 0) { + return 0; + } + ptra += len; + ptrb += len; + } + if (*ptra != *ptrb && ptra < &a->name[a->length]) { + return 0; + } + return 1; +} + +/** + * Call user supplied function to setup TXT data + * @param service The service to build TXT record for + */ +static void +mdns_prepare_txtdata(struct mdns_service *service) +{ + memset(&service->txtdata, 0, sizeof(struct mdns_domain)); + if (service->txt_fn) { + service->txt_fn(service, service->txt_userdata); + } +} + +#if LWIP_IPV4 +/** + * Build domain for reverse lookup of IPv4 address + * like 12.0.168.192.in-addr.arpa. for 192.168.0.12 + * @param domain Where to write the domain name + * @param addr Pointer to an IPv4 address to encode + * @return ERR_OK if domain was written, an err_t otherwise + */ +static err_t +mdns_build_reverse_v4_domain(struct mdns_domain *domain, const ip4_addr_t *addr) +{ + int i; + err_t res; + const u8_t *ptr; + if (!domain || !addr) { + return ERR_ARG; + } + memset(domain, 0, sizeof(struct mdns_domain)); + ptr = (const u8_t *) addr; + for (i = sizeof(ip4_addr_t) - 1; i >= 0; i--) { + char buf[4]; + u8_t val = ptr[i]; + + lwip_itoa(buf, sizeof(buf), val); + res = mdns_domain_add_label(domain, buf, (u8_t)strlen(buf)); + LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res); + } + res = mdns_domain_add_label(domain, REVERSE_PTR_V4_DOMAIN, (u8_t)(sizeof(REVERSE_PTR_V4_DOMAIN)-1)); + LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res); + res = mdns_domain_add_label(domain, REVERSE_PTR_TOPDOMAIN, (u8_t)(sizeof(REVERSE_PTR_TOPDOMAIN)-1)); + LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res); + res = mdns_domain_add_label(domain, NULL, 0); + LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res); + + return ERR_OK; +} +#endif + +#if LWIP_IPV6 +/** + * Build domain for reverse lookup of IP address + * like b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa. for 2001:db8::567:89ab + * @param domain Where to write the domain name + * @param addr Pointer to an IPv6 address to encode + * @return ERR_OK if domain was written, an err_t otherwise + */ +static err_t +mdns_build_reverse_v6_domain(struct mdns_domain *domain, const ip6_addr_t *addr) +{ + int i; + err_t res; + const u8_t *ptr; + if (!domain || !addr) { + return ERR_ARG; + } + memset(domain, 0, sizeof(struct mdns_domain)); + ptr = (const u8_t *) addr; + for (i = sizeof(ip6_addr_t) - 1; i >= 0; i--) { + char buf; + u8_t byte = ptr[i]; + int j; + for (j = 0; j < 2; j++) { + if ((byte & 0x0F) < 0xA) { + buf = '0' + (byte & 0x0F); + } else { + buf = 'a' + (byte & 0x0F) - 0xA; + } + res = mdns_domain_add_label(domain, &buf, sizeof(buf)); + LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res); + byte >>= 4; + } + } + res = mdns_domain_add_label(domain, REVERSE_PTR_V6_DOMAIN, (u8_t)(sizeof(REVERSE_PTR_V6_DOMAIN)-1)); + LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res); + res = mdns_domain_add_label(domain, REVERSE_PTR_TOPDOMAIN, (u8_t)(sizeof(REVERSE_PTR_TOPDOMAIN)-1)); + LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res); + res = mdns_domain_add_label(domain, NULL, 0); + LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res); + + return ERR_OK; +} +#endif + +/* Add .local. to domain */ +static err_t +mdns_add_dotlocal(struct mdns_domain *domain) +{ + err_t res = mdns_domain_add_label(domain, TOPDOMAIN_LOCAL, (u8_t)(sizeof(TOPDOMAIN_LOCAL)-1)); + LWIP_ERROR("mdns_add_dotlocal: Failed to add label", (res == ERR_OK), return res); + return mdns_domain_add_label(domain, NULL, 0); +} + +/** + * Build the <hostname>.local. domain name + * @param domain Where to write the domain name + * @param mdns TMDNS netif descriptor. + * @return ERR_OK if domain <hostname>.local. was written, an err_t otherwise + */ +static err_t +mdns_build_host_domain(struct mdns_domain *domain, struct mdns_host *mdns) +{ + err_t res; + memset(domain, 0, sizeof(struct mdns_domain)); + LWIP_ERROR("mdns_build_host_domain: mdns != NULL", (mdns != NULL), return ERR_VAL); + res = mdns_domain_add_label(domain, mdns->name, (u8_t)strlen(mdns->name)); + LWIP_ERROR("mdns_build_host_domain: Failed to add label", (res == ERR_OK), return res); + return mdns_add_dotlocal(domain); +} + +/** + * Build the lookup-all-services special DNS-SD domain name + * @param domain Where to write the domain name + * @return ERR_OK if domain _services._dns-sd._udp.local. was written, an err_t otherwise + */ +static err_t +mdns_build_dnssd_domain(struct mdns_domain *domain) +{ + err_t res; + memset(domain, 0, sizeof(struct mdns_domain)); + res = mdns_domain_add_label(domain, "_services", (u8_t)(sizeof("_services")-1)); + LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res); + res = mdns_domain_add_label(domain, "_dns-sd", (u8_t)(sizeof("_dns-sd")-1)); + LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res); + res = mdns_domain_add_label(domain, dnssd_protos[DNSSD_PROTO_UDP], (u8_t)strlen(dnssd_protos[DNSSD_PROTO_UDP])); + LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res); + return mdns_add_dotlocal(domain); +} + +/** + * Build domain name for a service + * @param domain Where to write the domain name + * @param service The service struct, containing service name, type and protocol + * @param include_name Whether to include the service name in the domain + * @return ERR_OK if domain was written. If service name is included, + * <name>.<type>.<proto>.local. will be written, otherwise <type>.<proto>.local. + * An err_t is returned on error. + */ +static err_t +mdns_build_service_domain(struct mdns_domain *domain, struct mdns_service *service, int include_name) +{ + err_t res; + memset(domain, 0, sizeof(struct mdns_domain)); + if (include_name) { + res = mdns_domain_add_label(domain, service->name, (u8_t)strlen(service->name)); + LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res); + } + res = mdns_domain_add_label(domain, service->service, (u8_t)strlen(service->service)); + LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res); + res = mdns_domain_add_label(domain, dnssd_protos[service->proto], (u8_t)strlen(dnssd_protos[service->proto])); + LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res); + return mdns_add_dotlocal(domain); +} + +/** + * Check which replies we should send for a host/netif based on question + * @param netif The network interface that received the question + * @param rr Domain/type/class from a question + * @param reverse_v6_reply Bitmask of which IPv6 addresses to send reverse PTRs for + * if reply bit has REPLY_HOST_PTR_V6 set + * @return Bitmask of which replies to send + */ +static int +check_host(struct netif *netif, struct mdns_rr_info *rr, u8_t *reverse_v6_reply) +{ + err_t res; + int replies = 0; + struct mdns_domain mydomain; + + LWIP_UNUSED_ARG(reverse_v6_reply); /* if ipv6 is disabled */ + + if (rr->klass != DNS_RRCLASS_IN && rr->klass != DNS_RRCLASS_ANY) { + /* Invalid class */ + return replies; + } + + /* Handle PTR for our addresses */ + if (rr->type == DNS_RRTYPE_PTR || rr->type == DNS_RRTYPE_ANY) { +#if LWIP_IPV6 + int i; + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) { + res = mdns_build_reverse_v6_domain(&mydomain, netif_ip6_addr(netif, i)); + if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) { + replies |= REPLY_HOST_PTR_V6; + /* Mark which addresses where requested */ + if (reverse_v6_reply) { + *reverse_v6_reply |= (1 << i); + } + } + } + } +#endif +#if LWIP_IPV4 + if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) { + res = mdns_build_reverse_v4_domain(&mydomain, netif_ip4_addr(netif)); + if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) { + replies |= REPLY_HOST_PTR_V4; + } + } +#endif + } + + res = mdns_build_host_domain(&mydomain, NETIF_TO_HOST(netif)); + /* Handle requests for our hostname */ + if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) { + /* TODO return NSEC if unsupported protocol requested */ +#if LWIP_IPV4 + if (!ip4_addr_isany_val(*netif_ip4_addr(netif)) + && (rr->type == DNS_RRTYPE_A || rr->type == DNS_RRTYPE_ANY)) { + replies |= REPLY_HOST_A; + } +#endif +#if LWIP_IPV6 + if (rr->type == DNS_RRTYPE_AAAA || rr->type == DNS_RRTYPE_ANY) { + replies |= REPLY_HOST_AAAA; + } +#endif + } + + return replies; +} + +/** + * Check which replies we should send for a service based on question + * @param service A registered MDNS service + * @param rr Domain/type/class from a question + * @return Bitmask of which replies to send + */ +static int +check_service(struct mdns_service *service, struct mdns_rr_info *rr) +{ + err_t res; + int replies = 0; + struct mdns_domain mydomain; + + if (rr->klass != DNS_RRCLASS_IN && rr->klass != DNS_RRCLASS_ANY) { + /* Invalid class */ + return 0; + } + + res = mdns_build_dnssd_domain(&mydomain); + if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain) && + (rr->type == DNS_RRTYPE_PTR || rr->type == DNS_RRTYPE_ANY)) { + /* Request for all service types */ + replies |= REPLY_SERVICE_TYPE_PTR; + } + + res = mdns_build_service_domain(&mydomain, service, 0); + if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain) && + (rr->type == DNS_RRTYPE_PTR || rr->type == DNS_RRTYPE_ANY)) { + /* Request for the instance of my service */ + replies |= REPLY_SERVICE_NAME_PTR; + } + + res = mdns_build_service_domain(&mydomain, service, 1); + if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) { + /* Request for info about my service */ + if (rr->type == DNS_RRTYPE_SRV || rr->type == DNS_RRTYPE_ANY) { + replies |= REPLY_SERVICE_SRV; + } + if (rr->type == DNS_RRTYPE_TXT || rr->type == DNS_RRTYPE_ANY) { + replies |= REPLY_SERVICE_TXT; + } + } + + return replies; +} + +/** + * Return bytes needed to write before jump for best result of compressing supplied domain + * against domain in outpacket starting at specified offset. + * If a match is found, offset is updated to where to jump to + * @param pbuf Pointer to pbuf with the partially constructed DNS packet + * @param offset Start position of a domain written earlier. If this location is suitable + * for compression, the pointer is updated to where in the domain to jump to. + * @param domain The domain to write + * @return Number of bytes to write of the new domain before writing a jump to the offset. + * If compression can not be done against this previous domain name, the full new + * domain length is returned. + */ +u16_t +mdns_compress_domain(struct pbuf *pbuf, u16_t *offset, struct mdns_domain *domain) +{ + struct mdns_domain target; + u16_t target_end; + u8_t target_len; + u8_t writelen = 0; + u8_t *ptr; + if (pbuf == NULL) { + return domain->length; + } + target_end = mdns_readname(pbuf, *offset, &target); + if (target_end == MDNS_READNAME_ERROR) { + return domain->length; + } + target_len = (u8_t)(target_end - *offset); + ptr = domain->name; + while (writelen < domain->length) { + u8_t domainlen = (u8_t)(domain->length - writelen); + u8_t labellen; + if (domainlen <= target.length && domainlen > DOMAIN_JUMP_SIZE) { + /* Compare domains if target is long enough, and we have enough left of the domain */ + u8_t targetpos = (u8_t)(target.length - domainlen); + if ((targetpos + DOMAIN_JUMP_SIZE) >= target_len) { + /* We are checking at or beyond a jump in the original, stop looking */ + break; + } + if (target.length >= domainlen && + memcmp(&domain->name[writelen], &target.name[targetpos], domainlen) == 0) { + *offset += targetpos; + return writelen; + } + } + /* Skip to next label in domain */ + labellen = *ptr; + writelen += 1 + labellen; + ptr += 1 + labellen; + } + /* Nothing found */ + return domain->length; +} + +/** + * Write domain to outpacket. Compression will be attempted, + * unless domain->skip_compression is set. + * @param outpkt The outpacket to write to + * @param domain The domain name to write + * @return ERR_OK on success, an err_t otherwise + */ +static err_t +mdns_write_domain(struct mdns_outpacket *outpkt, struct mdns_domain *domain) +{ + int i; + err_t res; + u16_t writelen = domain->length; + u16_t jump_offset = 0; + u16_t jump; + + if (!domain->skip_compression) { + for (i = 0; i < NUM_DOMAIN_OFFSETS; ++i) { + u16_t offset = outpkt->domain_offsets[i]; + if (offset) { + u16_t len = mdns_compress_domain(outpkt->pbuf, &offset, domain); + if (len < writelen) { + writelen = len; + jump_offset = offset; + } + } + } + } + + if (writelen) { + /* Write uncompressed part of name */ + res = pbuf_take_at(outpkt->pbuf, domain->name, writelen, outpkt->write_offset); + if (res != ERR_OK) { + return res; + } + + /* Store offset of this new domain */ + for (i = 0; i < NUM_DOMAIN_OFFSETS; ++i) { + if (outpkt->domain_offsets[i] == 0) { + outpkt->domain_offsets[i] = outpkt->write_offset; + break; + } + } + + outpkt->write_offset += writelen; + } + if (jump_offset) { + /* Write jump */ + jump = lwip_htons(DOMAIN_JUMP | jump_offset); + res = pbuf_take_at(outpkt->pbuf, &jump, DOMAIN_JUMP_SIZE, outpkt->write_offset); + if (res != ERR_OK) { + return res; + } + outpkt->write_offset += DOMAIN_JUMP_SIZE; + } + return ERR_OK; +} + +/** + * Write a question to an outpacket + * A question contains domain, type and class. Since an answer also starts with these fields this function is also + * called from mdns_add_answer(). + * @param outpkt The outpacket to write to + * @param domain The domain name the answer is for + * @param type The DNS type of the answer (like 'AAAA', 'SRV') + * @param klass The DNS type of the answer (like 'IN') + * @param unicast If highest bit in class should be set, to instruct the responder to + * reply with a unicast packet + * @return ERR_OK on success, an err_t otherwise + */ +static err_t +mdns_add_question(struct mdns_outpacket *outpkt, struct mdns_domain *domain, u16_t type, u16_t klass, u16_t unicast) +{ + u16_t question_len; + u16_t field16; + err_t res; + + if (!outpkt->pbuf) { + /* If no pbuf is active, allocate one */ + outpkt->pbuf = pbuf_alloc(PBUF_TRANSPORT, OUTPACKET_SIZE, PBUF_RAM); + if (!outpkt->pbuf) { + return ERR_MEM; + } + outpkt->write_offset = SIZEOF_DNS_HDR; + } + + /* Worst case calculation. Domain string might be compressed */ + question_len = domain->length + sizeof(type) + sizeof(klass); + if (outpkt->write_offset + question_len > outpkt->pbuf->tot_len) { + /* No space */ + return ERR_MEM; + } + + /* Write name */ + res = mdns_write_domain(outpkt, domain); + if (res != ERR_OK) { + return res; + } + + /* Write type */ + field16 = lwip_htons(type); + res = pbuf_take_at(outpkt->pbuf, &field16, sizeof(field16), outpkt->write_offset); + if (res != ERR_OK) { + return res; + } + outpkt->write_offset += sizeof(field16); + + /* Write class */ + if (unicast) { + klass |= 0x8000; + } + field16 = lwip_htons(klass); + res = pbuf_take_at(outpkt->pbuf, &field16, sizeof(field16), outpkt->write_offset); + if (res != ERR_OK) { + return res; + } + outpkt->write_offset += sizeof(field16); + + return ERR_OK; +} + +/** + * Write answer to reply packet. + * buf or answer_domain can be null. The rd_length written will be buf_length + + * size of (compressed) domain. Most uses will need either buf or answer_domain, + * special case is SRV that starts with 3 u16 and then a domain name. + * @param reply The outpacket to write to + * @param domain The domain name the answer is for + * @param type The DNS type of the answer (like 'AAAA', 'SRV') + * @param klass The DNS type of the answer (like 'IN') + * @param cache_flush If highest bit in class should be set, to instruct receiver that + * this reply replaces any earlier answer for this domain/type/class + * @param ttl Validity time in seconds to send out for IP address data in DNS replies + * @param buf Pointer to buffer of answer data + * @param buf_length Length of variable data + * @param answer_domain A domain to write after any buffer data as answer + * @return ERR_OK on success, an err_t otherwise + */ +static err_t +mdns_add_answer(struct mdns_outpacket *reply, struct mdns_domain *domain, u16_t type, u16_t klass, u16_t cache_flush, + u32_t ttl, const u8_t *buf, size_t buf_length, struct mdns_domain *answer_domain) +{ + u16_t answer_len; + u16_t field16; + u16_t rdlen_offset; + u16_t answer_offset; + u32_t field32; + err_t res; + + if (!reply->pbuf) { + /* If no pbuf is active, allocate one */ + reply->pbuf = pbuf_alloc(PBUF_TRANSPORT, OUTPACKET_SIZE, PBUF_RAM); + if (!reply->pbuf) { + return ERR_MEM; + } + reply->write_offset = SIZEOF_DNS_HDR; + } + + /* Worst case calculation. Domain strings might be compressed */ + answer_len = domain->length + sizeof(type) + sizeof(klass) + sizeof(ttl) + sizeof(field16)/*rd_length*/; + if (buf) { + answer_len += (u16_t)buf_length; + } + if (answer_domain) { + answer_len += answer_domain->length; + } + if (reply->write_offset + answer_len > reply->pbuf->tot_len) { + /* No space */ + return ERR_MEM; + } + + /* Answer starts with same data as question, then more fields */ + mdns_add_question(reply, domain, type, klass, cache_flush); + + /* Write TTL */ + field32 = lwip_htonl(ttl); + res = pbuf_take_at(reply->pbuf, &field32, sizeof(field32), reply->write_offset); + if (res != ERR_OK) { + return res; + } + reply->write_offset += sizeof(field32); + + /* Store offsets and skip forward to the data */ + rdlen_offset = reply->write_offset; + reply->write_offset += sizeof(field16); + answer_offset = reply->write_offset; + + if (buf) { + /* Write static data */ + res = pbuf_take_at(reply->pbuf, buf, (u16_t)buf_length, reply->write_offset); + if (res != ERR_OK) { + return res; + } + reply->write_offset += (u16_t)buf_length; + } + + if (answer_domain) { + /* Write name answer (compressed if possible) */ + res = mdns_write_domain(reply, answer_domain); + if (res != ERR_OK) { + return res; + } + } + + /* Write rd_length after when we know the answer size */ + field16 = lwip_htons(reply->write_offset - answer_offset); + res = pbuf_take_at(reply->pbuf, &field16, sizeof(field16), rdlen_offset); + + return res; +} + +/** + * Helper function for mdns_read_question/mdns_read_answer + * Reads a domain, type and class from the packet + * @param pkt The MDNS packet to read from. The parse_offset field will be + * incremented to point to the next unparsed byte. + * @param info The struct to fill with domain, type and class + * @return ERR_OK on success, an err_t otherwise + */ +static err_t +mdns_read_rr_info(struct mdns_packet *pkt, struct mdns_rr_info *info) +{ + u16_t field16, copied; + pkt->parse_offset = mdns_readname(pkt->pbuf, pkt->parse_offset, &info->domain); + if (pkt->parse_offset == MDNS_READNAME_ERROR) { + return ERR_VAL; + } + + copied = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), pkt->parse_offset); + if (copied != sizeof(field16)) { + return ERR_VAL; + } + pkt->parse_offset += copied; + info->type = lwip_ntohs(field16); + + copied = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), pkt->parse_offset); + if (copied != sizeof(field16)) { + return ERR_VAL; + } + pkt->parse_offset += copied; + info->klass = lwip_ntohs(field16); + + return ERR_OK; +} + +/** + * Read a question from the packet. + * All questions have to be read before the answers. + * @param pkt The MDNS packet to read from. The questions_left field will be decremented + * and the parse_offset will be updated. + * @param question The struct to fill with question data + * @return ERR_OK on success, an err_t otherwise + */ +static err_t +mdns_read_question(struct mdns_packet *pkt, struct mdns_question *question) +{ + /* Safety check */ + if (pkt->pbuf->tot_len < pkt->parse_offset) { + return ERR_VAL; + } + + if (pkt->questions_left) { + err_t res; + pkt->questions_left--; + + memset(question, 0, sizeof(struct mdns_question)); + res = mdns_read_rr_info(pkt, &question->info); + if (res != ERR_OK) { + return res; + } + + /* Extract unicast flag from class field */ + question->unicast = question->info.klass & 0x8000; + question->info.klass &= 0x7FFF; + + return ERR_OK; + } + return ERR_VAL; +} + +/** + * Read an answer from the packet + * The variable length reply is not copied, its pbuf offset and length is stored instead. + * @param pkt The MDNS packet to read. The answers_left field will be decremented and + * the parse_offset will be updated. + * @param answer The struct to fill with answer data + * @return ERR_OK on success, an err_t otherwise + */ +static err_t +mdns_read_answer(struct mdns_packet *pkt, struct mdns_answer *answer) +{ + /* Read questions first */ + if (pkt->questions_left) { + return ERR_VAL; + } + + /* Safety check */ + if (pkt->pbuf->tot_len < pkt->parse_offset) { + return ERR_VAL; + } + + if (pkt->answers_left) { + u16_t copied, field16; + u32_t ttl; + err_t res; + pkt->answers_left--; + + memset(answer, 0, sizeof(struct mdns_answer)); + res = mdns_read_rr_info(pkt, &answer->info); + if (res != ERR_OK) { + return res; + } + + /* Extract cache_flush flag from class field */ + answer->cache_flush = answer->info.klass & 0x8000; + answer->info.klass &= 0x7FFF; + + copied = pbuf_copy_partial(pkt->pbuf, &ttl, sizeof(ttl), pkt->parse_offset); + if (copied != sizeof(ttl)) { + return ERR_VAL; + } + pkt->parse_offset += copied; + answer->ttl = lwip_ntohl(ttl); + + copied = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), pkt->parse_offset); + if (copied != sizeof(field16)) { + return ERR_VAL; + } + pkt->parse_offset += copied; + answer->rd_length = lwip_ntohs(field16); + + answer->rd_offset = pkt->parse_offset; + pkt->parse_offset += answer->rd_length; + + return ERR_OK; + } + return ERR_VAL; +} + +#if LWIP_IPV4 +/** Write an IPv4 address (A) RR to outpacket */ +static err_t +mdns_add_a_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif) +{ + struct mdns_domain host; + mdns_build_host_domain(&host, NETIF_TO_HOST(netif)); + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with A record\n")); + return mdns_add_answer(reply, &host, DNS_RRTYPE_A, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, (const u8_t *) netif_ip4_addr(netif), sizeof(ip4_addr_t), NULL); +} + +/** Write a 4.3.2.1.in-addr.arpa -> hostname.local PTR RR to outpacket */ +static err_t +mdns_add_hostv4_ptr_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif) +{ + struct mdns_domain host, revhost; + mdns_build_host_domain(&host, NETIF_TO_HOST(netif)); + mdns_build_reverse_v4_domain(&revhost, netif_ip4_addr(netif)); + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with v4 PTR record\n")); + return mdns_add_answer(reply, &revhost, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, NULL, 0, &host); +} +#endif + +#if LWIP_IPV6 +/** Write an IPv6 address (AAAA) RR to outpacket */ +static err_t +mdns_add_aaaa_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif, int addrindex) +{ + struct mdns_domain host; + mdns_build_host_domain(&host, NETIF_TO_HOST(netif)); + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with AAAA record\n")); + return mdns_add_answer(reply, &host, DNS_RRTYPE_AAAA, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, (const u8_t *) netif_ip6_addr(netif, addrindex), sizeof(ip6_addr_t), NULL); +} + +/** Write a x.y.z.ip6.arpa -> hostname.local PTR RR to outpacket */ +static err_t +mdns_add_hostv6_ptr_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif, int addrindex) +{ + struct mdns_domain host, revhost; + mdns_build_host_domain(&host, NETIF_TO_HOST(netif)); + mdns_build_reverse_v6_domain(&revhost, netif_ip6_addr(netif, addrindex)); + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with v6 PTR record\n")); + return mdns_add_answer(reply, &revhost, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, NULL, 0, &host); +} +#endif + +/** Write an all-services -> servicetype PTR RR to outpacket */ +static err_t +mdns_add_servicetype_ptr_answer(struct mdns_outpacket *reply, struct mdns_service *service) +{ + struct mdns_domain service_type, service_dnssd; + mdns_build_service_domain(&service_type, service, 0); + mdns_build_dnssd_domain(&service_dnssd); + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with service type PTR record\n")); + return mdns_add_answer(reply, &service_dnssd, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, 0, service->dns_ttl, NULL, 0, &service_type); +} + +/** Write a servicetype -> servicename PTR RR to outpacket */ +static err_t +mdns_add_servicename_ptr_answer(struct mdns_outpacket *reply, struct mdns_service *service) +{ + struct mdns_domain service_type, service_instance; + mdns_build_service_domain(&service_type, service, 0); + mdns_build_service_domain(&service_instance, service, 1); + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with service name PTR record\n")); + return mdns_add_answer(reply, &service_type, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, 0, service->dns_ttl, NULL, 0, &service_instance); +} + +/** Write a SRV RR to outpacket */ +static err_t +mdns_add_srv_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct mdns_host *mdns, struct mdns_service *service) +{ + struct mdns_domain service_instance, srvhost; + u16_t srvdata[3]; + mdns_build_service_domain(&service_instance, service, 1); + mdns_build_host_domain(&srvhost, mdns); + if (reply->legacy_query) { + /* RFC 6762 section 18.14: + * In legacy unicast responses generated to answer legacy queries, + * name compression MUST NOT be performed on SRV records. + */ + srvhost.skip_compression = 1; + } + srvdata[0] = lwip_htons(SRV_PRIORITY); + srvdata[1] = lwip_htons(SRV_WEIGHT); + srvdata[2] = lwip_htons(service->port); + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with SRV record\n")); + return mdns_add_answer(reply, &service_instance, DNS_RRTYPE_SRV, DNS_RRCLASS_IN, cache_flush, service->dns_ttl, + (const u8_t *) &srvdata, sizeof(srvdata), &srvhost); +} + +/** Write a TXT RR to outpacket */ +static err_t +mdns_add_txt_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct mdns_service *service) +{ + struct mdns_domain service_instance; + mdns_build_service_domain(&service_instance, service, 1); + mdns_prepare_txtdata(service); + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with TXT record\n")); + return mdns_add_answer(reply, &service_instance, DNS_RRTYPE_TXT, DNS_RRCLASS_IN, cache_flush, service->dns_ttl, + (u8_t *) &service->txtdata.name, service->txtdata.length, NULL); +} + +/** + * Setup outpacket as a reply to the incoming packet + */ +static void +mdns_init_outpacket(struct mdns_outpacket *out, struct mdns_packet *in) +{ + memset(out, 0, sizeof(struct mdns_outpacket)); + out->cache_flush = 1; + out->netif = in->netif; + + /* Copy source IP/port to use when responding unicast, or to choose + * which pcb to use for multicast (IPv4/IPv6) + */ + SMEMCPY(&out->dest_addr, &in->source_addr, sizeof(ip_addr_t)); + out->dest_port = in->source_port; + + if (in->source_port != MDNS_PORT) { + out->unicast_reply = 1; + out->cache_flush = 0; + if (in->questions == 1) { + out->legacy_query = 1; + out->tx_id = in->tx_id; + } + } + + if (in->recv_unicast) { + out->unicast_reply = 1; + } +} + +/** + * Send chosen answers as a reply + * + * Add all selected answers (first write will allocate pbuf) + * Add additional answers based on the selected answers + * Send the packet + */ +static void +mdns_send_outpacket(struct mdns_outpacket *outpkt) +{ + struct mdns_service *service; + err_t res; + int i; + struct mdns_host* mdns = NETIF_TO_HOST(outpkt->netif); + + /* Write answers to host questions */ +#if LWIP_IPV4 + if (outpkt->host_replies & REPLY_HOST_A) { + res = mdns_add_a_answer(outpkt, outpkt->cache_flush, outpkt->netif); + if (res != ERR_OK) { + goto cleanup; + } + outpkt->answers++; + } + if (outpkt->host_replies & REPLY_HOST_PTR_V4) { + res = mdns_add_hostv4_ptr_answer(outpkt, outpkt->cache_flush, outpkt->netif); + if (res != ERR_OK) { + goto cleanup; + } + outpkt->answers++; + } +#endif +#if LWIP_IPV6 + if (outpkt->host_replies & REPLY_HOST_AAAA) { + int addrindex; + for (addrindex = 0; addrindex < LWIP_IPV6_NUM_ADDRESSES; ++addrindex) { + if (ip6_addr_isvalid(netif_ip6_addr_state(outpkt->netif, addrindex))) { + res = mdns_add_aaaa_answer(outpkt, outpkt->cache_flush, outpkt->netif, addrindex); + if (res != ERR_OK) { + goto cleanup; + } + outpkt->answers++; + } + } + } + if (outpkt->host_replies & REPLY_HOST_PTR_V6) { + u8_t rev_addrs = outpkt->host_reverse_v6_replies; + int addrindex = 0; + while (rev_addrs) { + if (rev_addrs & 1) { + res = mdns_add_hostv6_ptr_answer(outpkt, outpkt->cache_flush, outpkt->netif, addrindex); + if (res != ERR_OK) { + goto cleanup; + } + outpkt->answers++; + } + addrindex++; + rev_addrs >>= 1; + } + } +#endif + + /* Write answers to service questions */ + for (i = 0; i < MDNS_MAX_SERVICES; ++i) { + service = mdns->services[i]; + if (!service) { + continue; + } + + if (outpkt->serv_replies[i] & REPLY_SERVICE_TYPE_PTR) { + res = mdns_add_servicetype_ptr_answer(outpkt, service); + if (res != ERR_OK) { + goto cleanup; + } + outpkt->answers++; + } + + if (outpkt->serv_replies[i] & REPLY_SERVICE_NAME_PTR) { + res = mdns_add_servicename_ptr_answer(outpkt, service); + if (res != ERR_OK) { + goto cleanup; + } + outpkt->answers++; + } + + if (outpkt->serv_replies[i] & REPLY_SERVICE_SRV) { + res = mdns_add_srv_answer(outpkt, outpkt->cache_flush, mdns, service); + if (res != ERR_OK) { + goto cleanup; + } + outpkt->answers++; + } + + if (outpkt->serv_replies[i] & REPLY_SERVICE_TXT) { + res = mdns_add_txt_answer(outpkt, outpkt->cache_flush, service); + if (res != ERR_OK) { + goto cleanup; + } + outpkt->answers++; + } + } + + /* All answers written, add additional RRs */ + for (i = 0; i < MDNS_MAX_SERVICES; ++i) { + service = mdns->services[i]; + if (!service) { + continue; + } + + if (outpkt->serv_replies[i] & REPLY_SERVICE_NAME_PTR) { + /* Our service instance requested, include SRV & TXT + * if they are already not requested. */ + if (!(outpkt->serv_replies[i] & REPLY_SERVICE_SRV)) { + res = mdns_add_srv_answer(outpkt, outpkt->cache_flush, mdns, service); + if (res != ERR_OK) { + goto cleanup; + } + outpkt->additional++; + } + + if (!(outpkt->serv_replies[i] & REPLY_SERVICE_TXT)) { + res = mdns_add_txt_answer(outpkt, outpkt->cache_flush, service); + if (res != ERR_OK) { + goto cleanup; + } + outpkt->additional++; + } + } + + /* If service instance, SRV, record or an IP address is requested, + * supply all addresses for the host + */ + if ((outpkt->serv_replies[i] & (REPLY_SERVICE_NAME_PTR | REPLY_SERVICE_SRV)) || + (outpkt->host_replies & (REPLY_HOST_A | REPLY_HOST_AAAA))) { +#if LWIP_IPV6 + if (!(outpkt->host_replies & REPLY_HOST_AAAA)) { + int addrindex; + for (addrindex = 0; addrindex < LWIP_IPV6_NUM_ADDRESSES; ++addrindex) { + if (ip6_addr_isvalid(netif_ip6_addr_state(outpkt->netif, addrindex))) { + res = mdns_add_aaaa_answer(outpkt, outpkt->cache_flush, outpkt->netif, addrindex); + if (res != ERR_OK) { + goto cleanup; + } + outpkt->additional++; + } + } + } +#endif +#if LWIP_IPV4 + if (!(outpkt->host_replies & REPLY_HOST_A)) { + res = mdns_add_a_answer(outpkt, outpkt->cache_flush, outpkt->netif); + if (res != ERR_OK) { + goto cleanup; + } + outpkt->additional++; + } +#endif + } + } + + if (outpkt->pbuf) { + const ip_addr_t *mcast_destaddr; + struct dns_hdr hdr; + + /* Write header */ + memset(&hdr, 0, sizeof(hdr)); + hdr.flags1 = DNS_FLAG1_RESPONSE | DNS_FLAG1_AUTHORATIVE; + hdr.numanswers = lwip_htons(outpkt->answers); + hdr.numextrarr = lwip_htons(outpkt->additional); + if (outpkt->legacy_query) { + hdr.numquestions = lwip_htons(1); + hdr.id = lwip_htons(outpkt->tx_id); + } + pbuf_take(outpkt->pbuf, &hdr, sizeof(hdr)); + + /* Shrink packet */ + pbuf_realloc(outpkt->pbuf, outpkt->write_offset); + + if (IP_IS_V6_VAL(outpkt->dest_addr)) { +#if LWIP_IPV6 + mcast_destaddr = &v6group; +#endif + } else { +#if LWIP_IPV4 + mcast_destaddr = &v4group; +#endif + } + /* Send created packet */ + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Sending packet, len=%d, unicast=%d\n", outpkt->write_offset, outpkt->unicast_reply)); + if (outpkt->unicast_reply) { + udp_sendto_if(mdns_pcb, outpkt->pbuf, &outpkt->dest_addr, outpkt->dest_port, outpkt->netif); + } else { + udp_sendto_if(mdns_pcb, outpkt->pbuf, mcast_destaddr, MDNS_PORT, outpkt->netif); + } + } + +cleanup: + if (outpkt->pbuf) { + pbuf_free(outpkt->pbuf); + outpkt->pbuf = NULL; + } +} + +/** + * Send unsolicited answer containing all our known data + * @param netif The network interface to send on + * @param destination The target address to send to (usually multicast address) + */ +static void +mdns_announce(struct netif *netif, const ip_addr_t *destination) +{ + struct mdns_outpacket announce; + int i; + struct mdns_host* mdns = NETIF_TO_HOST(netif); + + memset(&announce, 0, sizeof(announce)); + announce.netif = netif; + announce.cache_flush = 1; +#if LWIP_IPV4 + if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) + announce.host_replies = REPLY_HOST_A | REPLY_HOST_PTR_V4; +#endif +#if LWIP_IPV6 + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) { + announce.host_replies |= REPLY_HOST_AAAA | REPLY_HOST_PTR_V6; + announce.host_reverse_v6_replies |= (1 << i); + } + } +#endif + + for (i = 0; i < MDNS_MAX_SERVICES; i++) { + struct mdns_service *serv = mdns->services[i]; + if (serv) { + announce.serv_replies[i] = REPLY_SERVICE_TYPE_PTR | REPLY_SERVICE_NAME_PTR | + REPLY_SERVICE_SRV | REPLY_SERVICE_TXT; + } + } + + announce.dest_port = MDNS_PORT; + SMEMCPY(&announce.dest_addr, destination, sizeof(announce.dest_addr)); + mdns_send_outpacket(&announce); +} + +/** + * Handle question MDNS packet + * 1. Parse all questions and set bits what answers to send + * 2. Clear pending answers if known answers are supplied + * 3. Put chosen answers in new packet and send as reply + */ +static void +mdns_handle_question(struct mdns_packet *pkt) +{ + struct mdns_service *service; + struct mdns_outpacket reply; + int replies = 0; + int i; + err_t res; + struct mdns_host* mdns = NETIF_TO_HOST(pkt->netif); + + mdns_init_outpacket(&reply, pkt); + + while (pkt->questions_left) { + struct mdns_question q; + + res = mdns_read_question(pkt, &q); + if (res != ERR_OK) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse question, skipping query packet\n")); + return; + } + + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Query for domain ")); + mdns_domain_debug_print(&q.info.domain); + LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", q.info.type, q.info.klass)); + + if (q.unicast) { + /* Reply unicast if any question is unicast */ + reply.unicast_reply = 1; + } + + reply.host_replies |= check_host(pkt->netif, &q.info, &reply.host_reverse_v6_replies); + replies |= reply.host_replies; + + for (i = 0; i < MDNS_MAX_SERVICES; ++i) { + service = mdns->services[i]; + if (!service) { + continue; + } + reply.serv_replies[i] |= check_service(service, &q.info); + replies |= reply.serv_replies[i]; + } + + if (replies && reply.legacy_query) { + /* Add question to reply packet (legacy packet only has 1 question) */ + res = mdns_add_question(&reply, &q.info.domain, q.info.type, q.info.klass, 0); + if (res != ERR_OK) { + goto cleanup; + } + } + } + + /* Handle known answers */ + while (pkt->answers_left) { + struct mdns_answer ans; + u8_t rev_v6; + int match; + + res = mdns_read_answer(pkt, &ans); + if (res != ERR_OK) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse answer, skipping query packet\n")); + goto cleanup; + } + + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Known answer for domain ")); + mdns_domain_debug_print(&ans.info.domain); + LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", ans.info.type, ans.info.klass)); + + + if (ans.info.type == DNS_RRTYPE_ANY || ans.info.klass == DNS_RRCLASS_ANY) { + /* Skip known answers for ANY type & class */ + continue; + } + + rev_v6 = 0; + match = reply.host_replies & check_host(pkt->netif, &ans.info, &rev_v6); + if (match && (ans.ttl > (mdns->dns_ttl / 2))) { + /* The RR in the known answer matches an RR we are planning to send, + * and the TTL is less than half gone. + * If the payload matches we should not send that answer. + */ + if (ans.info.type == DNS_RRTYPE_PTR) { + /* Read domain and compare */ + struct mdns_domain known_ans, my_ans; + u16_t len; + len = mdns_readname(pkt->pbuf, ans.rd_offset, &known_ans); + res = mdns_build_host_domain(&my_ans, mdns); + if (len != MDNS_READNAME_ERROR && res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) { +#if LWIP_IPV4 + if (match & REPLY_HOST_PTR_V4) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: v4 PTR\n")); + reply.host_replies &= ~REPLY_HOST_PTR_V4; + } +#endif +#if LWIP_IPV6 + if (match & REPLY_HOST_PTR_V6) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: v6 PTR\n")); + reply.host_reverse_v6_replies &= ~rev_v6; + if (reply.host_reverse_v6_replies == 0) { + reply.host_replies &= ~REPLY_HOST_PTR_V6; + } + } +#endif + } + } else if (match & REPLY_HOST_A) { +#if LWIP_IPV4 + if (ans.rd_length == sizeof(ip4_addr_t) && + pbuf_memcmp(pkt->pbuf, ans.rd_offset, netif_ip4_addr(pkt->netif), ans.rd_length) == 0) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: A\n")); + reply.host_replies &= ~REPLY_HOST_A; + } +#endif + } else if (match & REPLY_HOST_AAAA) { +#if LWIP_IPV6 + if (ans.rd_length == sizeof(ip6_addr_t) && + /* TODO this clears all AAAA responses if first addr is set as known */ + pbuf_memcmp(pkt->pbuf, ans.rd_offset, netif_ip6_addr(pkt->netif, 0), ans.rd_length) == 0) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: AAAA\n")); + reply.host_replies &= ~REPLY_HOST_AAAA; + } +#endif + } + } + + for (i = 0; i < MDNS_MAX_SERVICES; ++i) { + service = mdns->services[i]; + if (!service) { + continue; + } + match = reply.serv_replies[i] & check_service(service, &ans.info); + if (match && (ans.ttl > (service->dns_ttl / 2))) { + /* The RR in the known answer matches an RR we are planning to send, + * and the TTL is less than half gone. + * If the payload matches we should not send that answer. + */ + if (ans.info.type == DNS_RRTYPE_PTR) { + /* Read domain and compare */ + struct mdns_domain known_ans, my_ans; + u16_t len; + len = mdns_readname(pkt->pbuf, ans.rd_offset, &known_ans); + if (len != MDNS_READNAME_ERROR) { + if (match & REPLY_SERVICE_TYPE_PTR) { + res = mdns_build_service_domain(&my_ans, service, 0); + if (res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: service type PTR\n")); + reply.serv_replies[i] &= ~REPLY_SERVICE_TYPE_PTR; + } + } + if (match & REPLY_SERVICE_NAME_PTR) { + res = mdns_build_service_domain(&my_ans, service, 1); + if (res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: service name PTR\n")); + reply.serv_replies[i] &= ~REPLY_SERVICE_NAME_PTR; + } + } + } + } else if (match & REPLY_SERVICE_SRV) { + /* Read and compare to my SRV record */ + u16_t field16, len, read_pos; + struct mdns_domain known_ans, my_ans; + read_pos = ans.rd_offset; + do { + /* Check priority field */ + len = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), read_pos); + if (len != sizeof(field16) || lwip_ntohs(field16) != SRV_PRIORITY) { + break; + } + read_pos += len; + /* Check weight field */ + len = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), read_pos); + if (len != sizeof(field16) || lwip_ntohs(field16) != SRV_WEIGHT) { + break; + } + read_pos += len; + /* Check port field */ + len = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), read_pos); + if (len != sizeof(field16) || lwip_ntohs(field16) != service->port) { + break; + } + read_pos += len; + /* Check host field */ + len = mdns_readname(pkt->pbuf, read_pos, &known_ans); + mdns_build_host_domain(&my_ans, mdns); + if (len == MDNS_READNAME_ERROR || !mdns_domain_eq(&known_ans, &my_ans)) { + break; + } + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: SRV\n")); + reply.serv_replies[i] &= ~REPLY_SERVICE_SRV; + } while (0); + } else if (match & REPLY_SERVICE_TXT) { + mdns_prepare_txtdata(service); + if (service->txtdata.length == ans.rd_length && + pbuf_memcmp(pkt->pbuf, ans.rd_offset, service->txtdata.name, ans.rd_length) == 0) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: TXT\n")); + reply.serv_replies[i] &= ~REPLY_SERVICE_TXT; + } + } + } + } + } + + mdns_send_outpacket(&reply); + +cleanup: + if (reply.pbuf) { + /* This should only happen if we fail to alloc/write question for legacy query */ + pbuf_free(reply.pbuf); + reply.pbuf = NULL; + } +} + +/** + * Handle response MDNS packet + * Only prints debug for now. Will need more code to do conflict resolution. + */ +static void +mdns_handle_response(struct mdns_packet *pkt) +{ + /* Ignore all questions */ + while (pkt->questions_left) { + struct mdns_question q; + err_t res; + + res = mdns_read_question(pkt, &q); + if (res != ERR_OK) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse question, skipping response packet\n")); + return; + } + } + + while (pkt->answers_left) { + struct mdns_answer ans; + err_t res; + + res = mdns_read_answer(pkt, &ans); + if (res != ERR_OK) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse answer, skipping response packet\n")); + return; + } + + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Answer for domain ")); + mdns_domain_debug_print(&ans.info.domain); + LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", ans.info.type, ans.info.klass)); + } +} + +/** + * Receive input function for MDNS packets. + * Handles both IPv4 and IPv6 UDP pcbs. + */ +static void +mdns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) +{ + struct dns_hdr hdr; + struct mdns_packet packet; + struct netif *recv_netif = ip_current_input_netif(); + u16_t offset = 0; + + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(pcb); + + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Received IPv%d MDNS packet, len %d\n", IP_IS_V6(addr)? 6 : 4, p->tot_len)); + + if (NETIF_TO_HOST(recv_netif) == NULL) { + /* From netif not configured for MDNS */ + goto dealloc; + } + + if (pbuf_copy_partial(p, &hdr, SIZEOF_DNS_HDR, offset) < SIZEOF_DNS_HDR) { + /* Too small */ + goto dealloc; + } + offset += SIZEOF_DNS_HDR; + + if (DNS_HDR_GET_OPCODE(&hdr)) { + /* Ignore non-standard queries in multicast packets (RFC 6762, section 18.3) */ + goto dealloc; + } + + memset(&packet, 0, sizeof(packet)); + SMEMCPY(&packet.source_addr, addr, sizeof(packet.source_addr)); + packet.source_port = port; + packet.netif = recv_netif; + packet.pbuf = p; + packet.parse_offset = offset; + packet.tx_id = lwip_ntohs(hdr.id); + packet.questions = packet.questions_left = lwip_ntohs(hdr.numquestions); + packet.answers = packet.answers_left = lwip_ntohs(hdr.numanswers) + lwip_ntohs(hdr.numauthrr) + lwip_ntohs(hdr.numextrarr); + +#if LWIP_IPV6 + if (IP_IS_V6(ip_current_dest_addr())) { + if (!ip_addr_cmp(ip_current_dest_addr(), &v6group)) { + packet.recv_unicast = 1; + } + } +#endif +#if LWIP_IPV4 + if (!IP_IS_V6(ip_current_dest_addr())) { + if (!ip_addr_cmp(ip_current_dest_addr(), &v4group)) { + packet.recv_unicast = 1; + } + } +#endif + + if (hdr.flags1 & DNS_FLAG1_RESPONSE) { + mdns_handle_response(&packet); + } else { + mdns_handle_question(&packet); + } + +dealloc: + pbuf_free(p); +} + +/** + * @ingroup mdns + * Initiate MDNS responder. Will open UDP sockets on port 5353 + */ +void +mdns_resp_init(void) +{ + err_t res; + + mdns_pcb = udp_new_ip_type(IPADDR_TYPE_ANY); + LWIP_ASSERT("Failed to allocate pcb", mdns_pcb != NULL); +#if LWIP_MULTICAST_TX_OPTIONS + udp_set_multicast_ttl(mdns_pcb, MDNS_TTL); +#else + mdns_pcb->ttl = MDNS_TTL; +#endif + res = udp_bind(mdns_pcb, IP_ANY_TYPE, MDNS_PORT); + LWIP_UNUSED_ARG(res); /* in case of LWIP_NOASSERT */ + LWIP_ASSERT("Failed to bind pcb", res == ERR_OK); + udp_recv(mdns_pcb, mdns_recv, NULL); + + mdns_netif_client_id = netif_alloc_client_data_id(); +} + +/** + * @ingroup mdns + * Announce IP settings have changed on netif. + * Call this in your callback registered by netif_set_status_callback(). + * This function may go away in the future when netif supports registering + * multiple callback functions. + * @param netif The network interface where settings have changed. + */ +void +mdns_resp_netif_settings_changed(struct netif *netif) +{ + LWIP_ERROR("mdns_resp_netif_ip_changed: netif != NULL", (netif != NULL), return); + + if (NETIF_TO_HOST(netif) == NULL) { + return; + } + + /* Announce on IPv6 and IPv4 */ +#if LWIP_IPV6 + mdns_announce(netif, IP6_ADDR_ANY); +#endif +#if LWIP_IPV4 + mdns_announce(netif, IP4_ADDR_ANY); +#endif +} + +/** + * @ingroup mdns + * Activate MDNS responder for a network interface and send announce packets. + * @param netif The network interface to activate. + * @param hostname Name to use. Queries for <hostname>.local will be answered + * with the IP addresses of the netif. The hostname will be copied, the + * given pointer can be on the stack. + * @param dns_ttl Validity time in seconds to send out for IP address data in DNS replies + * @return ERR_OK if netif was added, an err_t otherwise + */ +err_t +mdns_resp_add_netif(struct netif *netif, const char *hostname, u32_t dns_ttl) +{ + err_t res; + struct mdns_host* mdns; + + LWIP_ERROR("mdns_resp_add_netif: netif != NULL", (netif != NULL), return ERR_VAL); + LWIP_ERROR("mdns_resp_add_netif: Hostname too long", (strlen(hostname) <= MDNS_LABEL_MAXLEN), return ERR_VAL); + + LWIP_ASSERT("mdns_resp_add_netif: Double add", NETIF_TO_HOST(netif) == NULL); + mdns = (struct mdns_host *) mem_malloc(sizeof(struct mdns_host)); + LWIP_ERROR("mdns_resp_add_netif: Alloc failed", (mdns != NULL), return ERR_MEM); + + netif_set_client_data(netif, mdns_netif_client_id, mdns); + + memset(mdns, 0, sizeof(struct mdns_host)); + MEMCPY(&mdns->name, hostname, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(hostname))); + mdns->dns_ttl = dns_ttl; + + /* Join multicast groups */ +#if LWIP_IPV4 + res = igmp_joingroup_netif(netif, ip_2_ip4(&v4group)); + if (res != ERR_OK) { + goto cleanup; + } +#endif +#if LWIP_IPV6 + res = mld6_joingroup_netif(netif, ip_2_ip6(&v6group)); + if (res != ERR_OK) { + goto cleanup; + } +#endif + + mdns_resp_netif_settings_changed(netif); + return ERR_OK; + +cleanup: + mem_free(mdns); + netif_set_client_data(netif, mdns_netif_client_id, NULL); + return res; +} + +/** + * @ingroup mdns + * Stop responding to MDNS queries on this interface, leave multicast groups, + * and free the helper structure and any of its services. + * @param netif The network interface to remove. + * @return ERR_OK if netif was removed, an err_t otherwise + */ +err_t +mdns_resp_remove_netif(struct netif *netif) +{ + int i; + struct mdns_host* mdns; + + LWIP_ASSERT("mdns_resp_remove_netif: Null pointer", netif); + mdns = NETIF_TO_HOST(netif); + LWIP_ERROR("mdns_resp_remove_netif: Not an active netif", (mdns != NULL), return ERR_VAL); + + for (i = 0; i < MDNS_MAX_SERVICES; i++) { + struct mdns_service *service = mdns->services[i]; + if (service) { + mem_free(service); + } + } + + /* Leave multicast groups */ +#if LWIP_IPV4 + igmp_leavegroup_netif(netif, ip_2_ip4(&v4group)); +#endif +#if LWIP_IPV6 + mld6_leavegroup_netif(netif, ip_2_ip6(&v6group)); +#endif + + mem_free(mdns); + netif_set_client_data(netif, mdns_netif_client_id, NULL); + return ERR_OK; +} + +/** + * @ingroup mdns + * Add a service to the selected network interface. + * @param netif The network interface to publish this service on + * @param name The name of the service + * @param service The service type, like "_http" + * @param proto The service protocol, DNSSD_PROTO_TCP for TCP ("_tcp") and DNSSD_PROTO_UDP + * for others ("_udp") + * @param port The port the service listens to + * @param dns_ttl Validity time in seconds to send out for service data in DNS replies + * @param txt_fn Callback to get TXT data. Will be called each time a TXT reply is created to + * allow dynamic replies. + * @param txt_data Userdata pointer for txt_fn + * @return ERR_OK if the service was added to the netif, an err_t otherwise + */ +err_t +mdns_resp_add_service(struct netif *netif, const char *name, const char *service, enum mdns_sd_proto proto, u16_t port, u32_t dns_ttl, service_get_txt_fn_t txt_fn, void *txt_data) +{ + int i; + int slot = -1; + struct mdns_service *srv; + struct mdns_host* mdns; + + LWIP_ASSERT("mdns_resp_add_service: netif != NULL", netif); + mdns = NETIF_TO_HOST(netif); + LWIP_ERROR("mdns_resp_add_service: Not an mdns netif", (mdns != NULL), return ERR_VAL); + + LWIP_ERROR("mdns_resp_add_service: Name too long", (strlen(name) <= MDNS_LABEL_MAXLEN), return ERR_VAL); + LWIP_ERROR("mdns_resp_add_service: Service too long", (strlen(service) <= MDNS_LABEL_MAXLEN), return ERR_VAL); + LWIP_ERROR("mdns_resp_add_service: Bad proto (need TCP or UDP)", (proto == DNSSD_PROTO_TCP || proto == DNSSD_PROTO_UDP), return ERR_VAL); + + for (i = 0; i < MDNS_MAX_SERVICES; i++) { + if (mdns->services[i] == NULL) { + slot = i; + break; + } + } + LWIP_ERROR("mdns_resp_add_service: Service list full (increase MDNS_MAX_SERVICES)", (slot >= 0), return ERR_MEM); + + srv = (struct mdns_service*)mem_malloc(sizeof(struct mdns_service)); + LWIP_ERROR("mdns_resp_add_service: Alloc failed", (srv != NULL), return ERR_MEM); + + memset(srv, 0, sizeof(struct mdns_service)); + + MEMCPY(&srv->name, name, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(name))); + MEMCPY(&srv->service, service, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(service))); + srv->txt_fn = txt_fn; + srv->txt_userdata = txt_data; + srv->proto = (u16_t)proto; + srv->port = port; + srv->dns_ttl = dns_ttl; + + mdns->services[slot] = srv; + + /* Announce on IPv6 and IPv4 */ +#if LWIP_IPV6 + mdns_announce(netif, IP6_ADDR_ANY); +#endif +#if LWIP_IPV4 + mdns_announce(netif, IP4_ADDR_ANY); +#endif + + return ERR_OK; +} + +/** + * @ingroup mdns + * Call this function from inside the service_get_txt_fn_t callback to add text data. + * Buffer for TXT data is 256 bytes, and each field is prefixed with a length byte. + * @param service The service provided to the get_txt callback + * @param txt String to add to the TXT field. + * @param txt_len Length of string + * @return ERR_OK if the string was added to the reply, an err_t otherwise + */ +err_t +mdns_resp_add_service_txtitem(struct mdns_service *service, const char *txt, u8_t txt_len) +{ + LWIP_ASSERT("mdns_resp_add_service_txtitem: service != NULL", service); + + /* Use a mdns_domain struct to store txt chunks since it is the same encoding */ + return mdns_domain_add_label(&service->txtdata, txt, txt_len); +} + +#endif /* LWIP_MDNS_RESPONDER */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/mqtt/mqtt.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/mqtt/mqtt.c new file mode 100644 index 0000000..a0e77b9 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/mqtt/mqtt.c @@ -0,0 +1,1341 @@ +/** + * @file + * MQTT client + * + * @defgroup mqtt MQTT client + * @ingroup apps + * @verbinclude mqtt_client.txt + */ + +/* + * Copyright (c) 2016 Erik Andersson <erian747@gmail.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack + * + * Author: Erik Andersson <erian747@gmail.com> + * + * + * @todo: + * - Handle large outgoing payloads for PUBLISH messages + * - Fix restriction of a single topic in each (UN)SUBSCRIBE message (protocol has support for multiple topics) + * - Add support for legacy MQTT protocol version + * + * Please coordinate changes and requests with Erik Andersson + * Erik Andersson <erian747@gmail.com> + * + */ +#include "lwip/apps/mqtt.h" +#include "lwip/timeouts.h" +#include "lwip/ip_addr.h" +#include "lwip/mem.h" +#include "lwip/err.h" +#include "lwip/pbuf.h" +#include "lwip/tcp.h" +#include <string.h> + +#if LWIP_TCP && LWIP_CALLBACK_API + +/** + * MQTT_DEBUG: Default is off. + */ +#if !defined MQTT_DEBUG || defined __DOXYGEN__ +#define MQTT_DEBUG LWIP_DBG_OFF +#endif + +#define MQTT_DEBUG_TRACE (MQTT_DEBUG | LWIP_DBG_TRACE) +#define MQTT_DEBUG_STATE (MQTT_DEBUG | LWIP_DBG_STATE) +#define MQTT_DEBUG_WARN (MQTT_DEBUG | LWIP_DBG_LEVEL_WARNING) +#define MQTT_DEBUG_WARN_STATE (MQTT_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE) +#define MQTT_DEBUG_SERIOUS (MQTT_DEBUG | LWIP_DBG_LEVEL_SERIOUS) + +static void mqtt_cyclic_timer(void *arg); + +/** + * MQTT client connection states + */ +enum { + TCP_DISCONNECTED, + TCP_CONNECTING, + MQTT_CONNECTING, + MQTT_CONNECTED +}; + +/** + * MQTT control message types + */ +enum mqtt_message_type { + MQTT_MSG_TYPE_CONNECT = 1, + MQTT_MSG_TYPE_CONNACK = 2, + MQTT_MSG_TYPE_PUBLISH = 3, + MQTT_MSG_TYPE_PUBACK = 4, + MQTT_MSG_TYPE_PUBREC = 5, + MQTT_MSG_TYPE_PUBREL = 6, + MQTT_MSG_TYPE_PUBCOMP = 7, + MQTT_MSG_TYPE_SUBSCRIBE = 8, + MQTT_MSG_TYPE_SUBACK = 9, + MQTT_MSG_TYPE_UNSUBSCRIBE = 10, + MQTT_MSG_TYPE_UNSUBACK = 11, + MQTT_MSG_TYPE_PINGREQ = 12, + MQTT_MSG_TYPE_PINGRESP = 13, + MQTT_MSG_TYPE_DISCONNECT = 14 +}; + +/** Helpers to extract control packet type and qos from first byte in fixed header */ +#define MQTT_CTL_PACKET_TYPE(fixed_hdr_byte0) ((fixed_hdr_byte0 & 0xf0) >> 4) +#define MQTT_CTL_PACKET_QOS(fixed_hdr_byte0) ((fixed_hdr_byte0 & 0x6) >> 1) + +/** + * MQTT connect flags, only used in CONNECT message + */ +enum mqtt_connect_flag { + MQTT_CONNECT_FLAG_USERNAME = 1 << 7, + MQTT_CONNECT_FLAG_PASSWORD = 1 << 6, + MQTT_CONNECT_FLAG_WILL_RETAIN = 1 << 5, + MQTT_CONNECT_FLAG_WILL = 1 << 2, + MQTT_CONNECT_FLAG_CLEAN_SESSION = 1 << 1 +}; + + +#if defined(LWIP_DEBUG) +static const char * const mqtt_message_type_str[15] = +{ + "UNDEFINED", + "CONNECT", + "CONNACK", + "PUBLISH", + "PUBACK", + "PUBREC", + "PUBREL", + "PUBCOMP", + "SUBSCRIBE", + "SUBACK", + "UNSUBSCRIBE", + "UNSUBACK", + "PINGREQ", + "PINGRESP", + "DISCONNECT" +}; + +/** + * Message type value to string + * @param msg_type see enum mqtt_message_type + * + * @return Control message type text string + */ +static const char * +mqtt_msg_type_to_str(u8_t msg_type) +{ + if (msg_type >= LWIP_ARRAYSIZE(mqtt_message_type_str)) { + msg_type = 0; + } + return mqtt_message_type_str[msg_type]; +} + +#endif + + +/** + * Generate MQTT packet identifier + * @param client MQTT client + * @return New packet identifier, range 1 to 65535 + */ +static u16_t +msg_generate_packet_id(mqtt_client_t *client) +{ + client->pkt_id_seq++; + if (client->pkt_id_seq == 0) { + client->pkt_id_seq++; + } + return client->pkt_id_seq; +} + +/*--------------------------------------------------------------------------------------------------------------------- */ +/* Output ring buffer */ + + +#define MQTT_RINGBUF_IDX_MASK ((MQTT_OUTPUT_RINGBUF_SIZE) - 1) + +/** Add single item to ring buffer */ +#define mqtt_ringbuf_put(rb, item) ((rb)->buf)[(rb)->put++ & MQTT_RINGBUF_IDX_MASK] = (item) + +/** Return number of bytes in ring buffer */ +#define mqtt_ringbuf_len(rb) ((u16_t)((rb)->put - (rb)->get)) + +/** Return number of bytes free in ring buffer */ +#define mqtt_ringbuf_free(rb) (MQTT_OUTPUT_RINGBUF_SIZE - mqtt_ringbuf_len(rb)) + +/** Return number of bytes possible to read without wrapping around */ +#define mqtt_ringbuf_linear_read_length(rb) LWIP_MIN(mqtt_ringbuf_len(rb), (MQTT_OUTPUT_RINGBUF_SIZE - ((rb)->get & MQTT_RINGBUF_IDX_MASK))) + +/** Return pointer to ring buffer get position */ +#define mqtt_ringbuf_get_ptr(rb) (&(rb)->buf[(rb)->get & MQTT_RINGBUF_IDX_MASK]) + +#define mqtt_ringbuf_advance_get_idx(rb, len) ((rb)->get += (len)) + + +/** + * Try send as many bytes as possible from output ring buffer + * @param rb Output ring buffer + * @param tpcb TCP connection handle + */ +static void +mqtt_output_send(struct mqtt_ringbuf_t *rb, struct tcp_pcb *tpcb) +{ + err_t err; + u8_t wrap = 0; + u16_t ringbuf_lin_len = mqtt_ringbuf_linear_read_length(rb); + u16_t send_len = tcp_sndbuf(tpcb); + LWIP_ASSERT("mqtt_output_send: tpcb != NULL", tpcb != NULL); + + if (send_len == 0 || ringbuf_lin_len == 0) { + return; + } + + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_output_send: tcp_sndbuf: %d bytes, ringbuf_linear_available: %d, get %d, put %d\n", + send_len, ringbuf_lin_len, ((rb)->get & MQTT_RINGBUF_IDX_MASK), ((rb)->put & MQTT_RINGBUF_IDX_MASK))); + + if (send_len > ringbuf_lin_len) { + /* Space in TCP output buffer is larger than available in ring buffer linear portion */ + send_len = ringbuf_lin_len; + /* Wrap around if more data in ring buffer after linear portion */ + wrap = (mqtt_ringbuf_len(rb) > ringbuf_lin_len); + } + err = tcp_write(tpcb, mqtt_ringbuf_get_ptr(rb), send_len, TCP_WRITE_FLAG_COPY | (wrap ? TCP_WRITE_FLAG_MORE : 0)); + if ((err == ERR_OK) && wrap) { + mqtt_ringbuf_advance_get_idx(rb, send_len); + /* Use the lesser one of ring buffer linear length and TCP send buffer size */ + send_len = LWIP_MIN(tcp_sndbuf(tpcb), mqtt_ringbuf_linear_read_length(rb)); + err = tcp_write(tpcb, mqtt_ringbuf_get_ptr(rb), send_len, TCP_WRITE_FLAG_COPY); + } + + if (err == ERR_OK) { + mqtt_ringbuf_advance_get_idx(rb, send_len); + /* Flush */ + tcp_output(tpcb); + } else { + LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_output_send: Send failed with err %d (\"%s\")\n", err, lwip_strerr(err))); + } +} + + + +/*--------------------------------------------------------------------------------------------------------------------- */ +/* Request queue */ + +/** + * Create request item + * @param r_objs Pointer to request objects + * @param pkt_id Packet identifier of request + * @param cb Packet callback to call when requests lifetime ends + * @param arg Parameter following callback + * @return Request or NULL if failed to create + */ +static struct mqtt_request_t * +mqtt_create_request(struct mqtt_request_t *r_objs, u16_t pkt_id, mqtt_request_cb_t cb, void *arg) +{ + struct mqtt_request_t *r = NULL; + u8_t n; + LWIP_ASSERT("mqtt_create_request: r_objs != NULL", r_objs != NULL); + for (n = 0; n < MQTT_REQ_MAX_IN_FLIGHT; n++) { + /* Item point to itself if not in use */ + if (r_objs[n].next == &r_objs[n]) { + r = &r_objs[n]; + r->next = NULL; + r->cb = cb; + r->arg = arg; + r->pkt_id = pkt_id; + break; + } + } + return r; +} + + +/** + * Append request to pending request queue + * @param tail Pointer to request queue tail pointer + * @param r Request to append + */ +static void +mqtt_append_request(struct mqtt_request_t **tail, struct mqtt_request_t *r) +{ + struct mqtt_request_t *head = NULL; + s16_t time_before = 0; + struct mqtt_request_t *iter; + + LWIP_ASSERT("mqtt_append_request: tail != NULL", tail != NULL); + + /* Iterate trough queue to find head, and count total timeout time */ + for (iter = *tail; iter != NULL; iter = iter->next) { + time_before += iter->timeout_diff; + head = iter; + } + + LWIP_ASSERT("mqtt_append_request: time_before <= MQTT_REQ_TIMEOUT", time_before <= MQTT_REQ_TIMEOUT); + r->timeout_diff = MQTT_REQ_TIMEOUT - time_before; + if (head == NULL) { + *tail = r; + } else { + head->next = r; + } +} + + +/** + * Delete request item + * @param r Request item to delete + */ +static void +mqtt_delete_request(struct mqtt_request_t *r) +{ + if (r != NULL) { + r->next = r; + } +} + +/** + * Remove a request item with a specific packet identifier from request queue + * @param tail Pointer to request queue tail pointer + * @param pkt_id Packet identifier of request to take + * @return Request item if found, NULL if not + */ +static struct mqtt_request_t * +mqtt_take_request(struct mqtt_request_t **tail, u16_t pkt_id) +{ + struct mqtt_request_t *iter = NULL, *prev = NULL; + LWIP_ASSERT("mqtt_take_request: tail != NULL", tail != NULL); + /* Search all request for pkt_id */ + for (iter = *tail; iter != NULL; iter = iter->next) { + if (iter->pkt_id == pkt_id) { + break; + } + prev = iter; + } + + /* If request was found */ + if (iter != NULL) { + /* unchain */ + if (prev == NULL) { + *tail= iter->next; + } else { + prev->next = iter->next; + } + /* If exists, add remaining timeout time for the request to next */ + if (iter->next != NULL) { + iter->next->timeout_diff += iter->timeout_diff; + } + iter->next = NULL; + } + return iter; +} + +/** + * Handle requests timeout + * @param tail Pointer to request queue tail pointer + * @param t Time since last call in seconds + */ +static void +mqtt_request_time_elapsed(struct mqtt_request_t **tail, u8_t t) +{ + struct mqtt_request_t *r = *tail; + LWIP_ASSERT("mqtt_request_time_elapsed: tail != NULL", tail != NULL); + while (t > 0 && r != NULL) { + if (t >= r->timeout_diff) { + t -= (u8_t)r->timeout_diff; + /* Unchain */ + *tail = r->next; + /* Notify upper layer about timeout */ + if (r->cb != NULL) { + r->cb(r->arg, ERR_TIMEOUT); + } + mqtt_delete_request(r); + /* Tail might be be modified in callback, so re-read it in every iteration */ + r = *(struct mqtt_request_t * const volatile *)tail; + } else { + r->timeout_diff -= t; + t = 0; + } + } +} + +/** + * Free all request items + * @param tail Pointer to request queue tail pointer + */ +static void +mqtt_clear_requests(struct mqtt_request_t **tail) +{ + struct mqtt_request_t *iter, *next; + LWIP_ASSERT("mqtt_clear_requests: tail != NULL", tail != NULL); + for (iter = *tail; iter != NULL; iter = next) { + next = iter->next; + mqtt_delete_request(iter); + } + *tail = NULL; +} +/** + * Initialize all request items + * @param r_objs Pointer to request objects + */ +static void +mqtt_init_requests(struct mqtt_request_t *r_objs) +{ + u8_t n; + LWIP_ASSERT("mqtt_init_requests: r_objs != NULL", r_objs != NULL); + for (n = 0; n < MQTT_REQ_MAX_IN_FLIGHT; n++) { + /* Item pointing to itself indicates unused */ + r_objs[n].next = &r_objs[n]; + } +} + +/*--------------------------------------------------------------------------------------------------------------------- */ +/* Output message build helpers */ + + +static void +mqtt_output_append_u8(struct mqtt_ringbuf_t *rb, u8_t value) +{ + mqtt_ringbuf_put(rb, value); +} + +static +void mqtt_output_append_u16(struct mqtt_ringbuf_t *rb, u16_t value) +{ + mqtt_ringbuf_put(rb, value >> 8); + mqtt_ringbuf_put(rb, value & 0xff); +} + +static void +mqtt_output_append_buf(struct mqtt_ringbuf_t *rb, const void *data, u16_t length) +{ + u16_t n; + for (n = 0; n < length; n++) { + mqtt_ringbuf_put(rb, ((const u8_t *)data)[n]); + } +} + +static void +mqtt_output_append_string(struct mqtt_ringbuf_t *rb, const char *str, u16_t length) +{ + u16_t n; + mqtt_ringbuf_put(rb, length >> 8); + mqtt_ringbuf_put(rb, length & 0xff); + for (n = 0; n < length; n++) { + mqtt_ringbuf_put(rb, str[n]); + } +} + +/** + * Append fixed header + * @param rb Output ring buffer + * @param msg_type see enum mqtt_message_type + * @param dup MQTT DUP flag + * @param qos MQTT QoS field + * @param retain MQTT retain flag + * @param r_length Remaining length after fixed header + */ + +static void +mqtt_output_append_fixed_header(struct mqtt_ringbuf_t *rb, u8_t msg_type, u8_t dup, + u8_t qos, u8_t retain, u16_t r_length) +{ + /* Start with control byte */ + mqtt_output_append_u8(rb, (((msg_type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1))); + /* Encode remaining length field */ + do { + mqtt_output_append_u8(rb, (r_length & 0x7f) | (r_length >= 128 ? 0x80 : 0)); + r_length >>= 7; + } while (r_length > 0); +} + + +/** + * Check output buffer space + * @param rb Output ring buffer + * @param r_length Remaining length after fixed header + * @return 1 if message will fit, 0 if not enough buffer space + */ +static u8_t +mqtt_output_check_space(struct mqtt_ringbuf_t *rb, u16_t r_length) +{ + /* Start with length of type byte + remaining length */ + u16_t total_len = 1 + r_length; + + LWIP_ASSERT("mqtt_output_check_space: rb != NULL", rb != NULL); + + /* Calculate number of required bytes to contain the remaining bytes field and add to total*/ + do { + total_len++; + r_length >>= 7; + } while (r_length > 0); + + return (total_len <= mqtt_ringbuf_free(rb)); +} + + +/** + * Close connection to server + * @param client MQTT client + * @param reason Reason for disconnection + */ +static void +mqtt_close(mqtt_client_t *client, mqtt_connection_status_t reason) +{ + LWIP_ASSERT("mqtt_close: client != NULL", client != NULL); + + /* Bring down TCP connection if not already done */ + if (client->conn != NULL) { + err_t res; + tcp_recv(client->conn, NULL); + tcp_err(client->conn, NULL); + tcp_sent(client->conn, NULL); + res = tcp_close(client->conn); + if (res != ERR_OK) { + tcp_abort(client->conn); + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_close: Close err=%s\n", lwip_strerr(res))); + } + client->conn = NULL; + } + + /* Remove all pending requests */ + mqtt_clear_requests(&client->pend_req_queue); + /* Stop cyclic timer */ + sys_untimeout(mqtt_cyclic_timer, client); + + /* Notify upper layer of disconnection if changed state */ + if (client->conn_state != TCP_DISCONNECTED) { + + client->conn_state = TCP_DISCONNECTED; + if (client->connect_cb != NULL) { + client->connect_cb(client, client->connect_arg, reason); + } + } +} + + +/** + * Interval timer, called every MQTT_CYCLIC_TIMER_INTERVAL seconds in MQTT_CONNECTING and MQTT_CONNECTED states + * @param arg MQTT client + */ +static void +mqtt_cyclic_timer(void *arg) +{ + u8_t restart_timer = 1; + mqtt_client_t *client = (mqtt_client_t *)arg; + LWIP_ASSERT("mqtt_cyclic_timer: client != NULL", client != NULL); + + if (client->conn_state == MQTT_CONNECTING) { + client->cyclic_tick++; + if ((client->cyclic_tick * MQTT_CYCLIC_TIMER_INTERVAL) >= MQTT_CONNECT_TIMOUT) { + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_cyclic_timer: CONNECT attempt to server timed out\n")); + /* Disconnect TCP */ + mqtt_close(client, MQTT_CONNECT_TIMEOUT); + restart_timer = 0; + } + } else if (client->conn_state == MQTT_CONNECTED) { + /* Handle timeout for pending requests */ + mqtt_request_time_elapsed(&client->pend_req_queue, MQTT_CYCLIC_TIMER_INTERVAL); + + /* keep_alive > 0 means keep alive functionality shall be used */ + if (client->keep_alive > 0) { + + client->server_watchdog++; + /* If reception from server has been idle for 1.5*keep_alive time, server is considered unresponsive */ + if ((client->server_watchdog * MQTT_CYCLIC_TIMER_INTERVAL) > (client->keep_alive + client->keep_alive/2)) { + LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_cyclic_timer: Server incoming keep-alive timeout\n")); + mqtt_close(client, MQTT_CONNECT_TIMEOUT); + restart_timer = 0; + } + + /* If time for a keep alive message to be sent, transmission has been idle for keep_alive time */ + if ((client->cyclic_tick * MQTT_CYCLIC_TIMER_INTERVAL) >= client->keep_alive) { + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_cyclic_timer: Sending keep-alive message to server\n")); + if (mqtt_output_check_space(&client->output, 0) != 0) { + mqtt_output_append_fixed_header(&client->output, MQTT_MSG_TYPE_PINGREQ, 0, 0, 0, 0); + client->cyclic_tick = 0; + } + } else { + client->cyclic_tick++; + } + } + } else { + LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_cyclic_timer: Timer should not be running in state %d\n", client->conn_state)); + restart_timer = 0; + } + if (restart_timer) { + sys_timeout(MQTT_CYCLIC_TIMER_INTERVAL*1000, mqtt_cyclic_timer, arg); + } +} + + +/** + * Send PUBACK, PUBREC or PUBREL response message + * @param client MQTT client + * @param msg PUBACK, PUBREC or PUBREL + * @param pkt_id Packet identifier + * @param qos QoS value + * @return ERR_OK if successful, ERR_MEM if out of memory + */ +static err_t +pub_ack_rec_rel_response(mqtt_client_t *client, u8_t msg, u16_t pkt_id, u8_t qos) +{ + err_t err = ERR_OK; + if (mqtt_output_check_space(&client->output, 2)) { + mqtt_output_append_fixed_header(&client->output, msg, 0, qos, 0, 2); + mqtt_output_append_u16(&client->output, pkt_id); + mqtt_output_send(&client->output, client->conn); + } else { + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("pub_ack_rec_rel_response: OOM creating response: %s with pkt_id: %d\n", + mqtt_msg_type_to_str(msg), pkt_id)); + err = ERR_MEM; + } + return err; +} + +/** + * Subscribe response from server + * @param r Matching request + * @param result Result code from server + */ +static void +mqtt_incomming_suback(struct mqtt_request_t *r, u8_t result) +{ + if (r->cb != NULL) { + r->cb(r->arg, result < 3 ? ERR_OK : ERR_ABRT); + } +} + + +/** + * Complete MQTT message received or buffer full + * @param client MQTT client + * @param fixed_hdr_idx header index + * @param length length received part + * @param remaining_length Remaining length of complete message + */ +static mqtt_connection_status_t + mqtt_message_received(mqtt_client_t *client, u8_t fixed_hdr_idx, u16_t length, u32_t remaining_length) +{ + mqtt_connection_status_t res = MQTT_CONNECT_ACCEPTED; + + u8_t *var_hdr_payload = client->rx_buffer + fixed_hdr_idx; + + /* Control packet type */ + u8_t pkt_type = MQTT_CTL_PACKET_TYPE(client->rx_buffer[0]); + u16_t pkt_id = 0; + + if (pkt_type == MQTT_MSG_TYPE_CONNACK) { + if (client->conn_state == MQTT_CONNECTING) { + /* Get result code from CONNACK */ + res = (mqtt_connection_status_t)var_hdr_payload[1]; + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_message_received: Connect response code %d\n", res)); + if (res == MQTT_CONNECT_ACCEPTED) { + /* Reset cyclic_tick when changing to connected state */ + client->cyclic_tick = 0; + client->conn_state = MQTT_CONNECTED; + /* Notify upper layer */ + if (client->connect_cb != 0) { + client->connect_cb(client, client->connect_arg, res); + } + } + } else { + LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_message_received: Received CONNACK in connected state\n")); + } + } else if (pkt_type == MQTT_MSG_TYPE_PINGRESP) { + LWIP_DEBUGF(MQTT_DEBUG_TRACE,( "mqtt_message_received: Received PINGRESP from server\n")); + + } else if (pkt_type == MQTT_MSG_TYPE_PUBLISH) { + u16_t payload_offset = 0; + u16_t payload_length = length; + u8_t qos = MQTT_CTL_PACKET_QOS(client->rx_buffer[0]); + + if (client->msg_idx <= MQTT_VAR_HEADER_BUFFER_LEN) { + /* Should have topic and pkt id*/ + uint8_t *topic; + uint16_t after_topic; + u8_t bkp; + u16_t topic_len = var_hdr_payload[0]; + topic_len = (topic_len << 8) + (u16_t)(var_hdr_payload[1]); + + topic = var_hdr_payload + 2; + after_topic = 2 + topic_len; + /* Check length, add one byte even for QoS 0 so that zero termination will fit */ + if ((after_topic + (qos? 2 : 1)) > length) { + LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_message_received: Receive buffer can not fit topic + pkt_id\n")); + goto out_disconnect; + } + + /* id for QoS 1 and 2 */ + if (qos > 0) { + client->inpub_pkt_id = ((u16_t)var_hdr_payload[after_topic] << 8) + (u16_t)var_hdr_payload[after_topic + 1]; + after_topic += 2; + } else { + client->inpub_pkt_id = 0; + } + /* Take backup of byte after topic */ + bkp = topic[topic_len]; + /* Zero terminate string */ + topic[topic_len] = 0; + /* Payload data remaining in receive buffer */ + payload_length = length - after_topic; + payload_offset = after_topic; + + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_incomming_publish: Received message with QoS %d at topic: %s, payload length %d\n", + qos, topic, remaining_length + payload_length)); + if (client->pub_cb != NULL) { + client->pub_cb(client->inpub_arg, (const char *)topic, remaining_length + payload_length); + } + /* Restore byte after topic */ + topic[topic_len] = bkp; + } + if (payload_length > 0 || remaining_length == 0) { + client->data_cb(client->inpub_arg, var_hdr_payload + payload_offset, payload_length, remaining_length == 0 ? MQTT_DATA_FLAG_LAST : 0); + /* Reply if QoS > 0 */ + if (remaining_length == 0 && qos > 0) { + /* Send PUBACK for QoS 1 or PUBREC for QoS 2 */ + u8_t resp_msg = (qos == 1) ? MQTT_MSG_TYPE_PUBACK : MQTT_MSG_TYPE_PUBREC; + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_incomming_publish: Sending publish response: %s with pkt_id: %d\n", + mqtt_msg_type_to_str(resp_msg), client->inpub_pkt_id)); + pub_ack_rec_rel_response(client, resp_msg, client->inpub_pkt_id, 0); + } + } + } else { + /* Get packet identifier */ + pkt_id = (u16_t)var_hdr_payload[0] << 8; + pkt_id |= (u16_t)var_hdr_payload[1]; + if (pkt_id == 0) { + LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_message_received: Got message with illegal packet identifier: 0\n")); + goto out_disconnect; + } + if (pkt_type == MQTT_MSG_TYPE_PUBREC) { + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_message_received: PUBREC, sending PUBREL with pkt_id: %d\n",pkt_id)); + pub_ack_rec_rel_response(client, MQTT_MSG_TYPE_PUBREL, pkt_id, 1); + + } else if (pkt_type == MQTT_MSG_TYPE_PUBREL) { + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_message_received: PUBREL, sending PUBCOMP response with pkt_id: %d\n",pkt_id)); + pub_ack_rec_rel_response(client, MQTT_MSG_TYPE_PUBCOMP, pkt_id, 0); + + } else if (pkt_type == MQTT_MSG_TYPE_SUBACK || pkt_type == MQTT_MSG_TYPE_UNSUBACK || + pkt_type == MQTT_MSG_TYPE_PUBCOMP || pkt_type == MQTT_MSG_TYPE_PUBACK) { + struct mqtt_request_t *r = mqtt_take_request(&client->pend_req_queue, pkt_id); + if (r != NULL) { + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_message_received: %s response with id %d\n", mqtt_msg_type_to_str(pkt_type), pkt_id)); + if (pkt_type == MQTT_MSG_TYPE_SUBACK) { + if (length < 3) { + LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_message_received: To small SUBACK packet\n")); + goto out_disconnect; + } else { + mqtt_incomming_suback(r, var_hdr_payload[2]); + } + } else if (r->cb != NULL) { + r->cb(r->arg, ERR_OK); + } + mqtt_delete_request(r); + } else { + LWIP_DEBUGF(MQTT_DEBUG_WARN,( "mqtt_message_received: Received %s reply, with wrong pkt_id: %d\n", mqtt_msg_type_to_str(pkt_type), pkt_id)); + } + } else { + LWIP_DEBUGF(MQTT_DEBUG_WARN,( "mqtt_message_received: Received unknown message type: %d\n", pkt_type)); + goto out_disconnect; + } + } + return res; +out_disconnect: + return MQTT_CONNECT_DISCONNECTED; +} + + +/** + * MQTT incoming message parser + * @param client MQTT client + * @param p PBUF chain of received data + * @return Connection status + */ +static mqtt_connection_status_t +mqtt_parse_incoming(mqtt_client_t *client, struct pbuf *p) +{ + u16_t in_offset = 0; + u32_t msg_rem_len = 0; + u8_t fixed_hdr_idx = 0; + u8_t b = 0; + + while (p->tot_len > in_offset) { + if ((fixed_hdr_idx < 2) || ((b & 0x80) != 0)) { + + if (fixed_hdr_idx < client->msg_idx) { + b = client->rx_buffer[fixed_hdr_idx]; + } else { + b = pbuf_get_at(p, in_offset++); + client->rx_buffer[client->msg_idx++] = b; + } + fixed_hdr_idx++; + + if (fixed_hdr_idx >= 2) { + msg_rem_len |= (u32_t)(b & 0x7f) << ((fixed_hdr_idx - 2) * 7); + if ((b & 0x80) == 0) { + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_parse_incoming: Remaining length after fixed header: %d\n", msg_rem_len)); + if (msg_rem_len == 0) { + /* Complete message with no extra headers of payload received */ + mqtt_message_received(client, fixed_hdr_idx, 0, 0); + client->msg_idx = 0; + fixed_hdr_idx = 0; + } else { + /* Bytes remaining in message */ + msg_rem_len = (msg_rem_len + fixed_hdr_idx) - client->msg_idx; + } + } + } + } else { + u16_t cpy_len, cpy_start, buffer_space; + + cpy_start = (client->msg_idx - fixed_hdr_idx) % (MQTT_VAR_HEADER_BUFFER_LEN - fixed_hdr_idx) + fixed_hdr_idx; + + /* Allow to copy the lesser one of available length in input data or bytes remaining in message */ + cpy_len = (u16_t)LWIP_MIN((u16_t)(p->tot_len - in_offset), msg_rem_len); + + /* Limit to available space in buffer */ + buffer_space = MQTT_VAR_HEADER_BUFFER_LEN - cpy_start; + if (cpy_len > buffer_space) { + cpy_len = buffer_space; + } + pbuf_copy_partial(p, client->rx_buffer+cpy_start, cpy_len, in_offset); + + /* Advance get and put indexes */ + client->msg_idx += cpy_len; + in_offset += cpy_len; + msg_rem_len -= cpy_len; + + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_parse_incoming: msg_idx: %d, cpy_len: %d, remaining %d\n", client->msg_idx, cpy_len, msg_rem_len)); + if (msg_rem_len == 0 || cpy_len == buffer_space) { + /* Whole message received or buffer is full */ + mqtt_connection_status_t res = mqtt_message_received(client, fixed_hdr_idx, (cpy_start + cpy_len) - fixed_hdr_idx, msg_rem_len); + if (res != MQTT_CONNECT_ACCEPTED) { + return res; + } + if (msg_rem_len == 0) { + /* Reset parser state */ + client->msg_idx = 0; + /* msg_tot_len = 0; */ + fixed_hdr_idx = 0; + } + } + } + } + return MQTT_CONNECT_ACCEPTED; +} + + +/** + * TCP received callback function. @see tcp_recv_fn + * @param arg MQTT client + * @param p PBUF chain of received data + * @param err Passed as return value if not ERR_OK + * @return ERR_OK or err passed into callback + */ +static err_t +mqtt_tcp_recv_cb(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) +{ + mqtt_client_t *client = (mqtt_client_t *)arg; + LWIP_ASSERT("mqtt_tcp_recv_cb: client != NULL", client != NULL); + LWIP_ASSERT("mqtt_tcp_recv_cb: client->conn == pcb", client->conn == pcb); + + if (p == NULL) { + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_tcp_recv_cb: Recv pbuf=NULL, remote has closed connection\n")); + mqtt_close(client, MQTT_CONNECT_DISCONNECTED); + } else { + mqtt_connection_status_t res; + if (err != ERR_OK) { + LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_tcp_recv_cb: Recv err=%d\n", err)); + pbuf_free(p); + return err; + } + + /* Tell remote that data has been received */ + tcp_recved(pcb, p->tot_len); + res = mqtt_parse_incoming(client, p); + pbuf_free(p); + + if (res != MQTT_CONNECT_ACCEPTED) { + mqtt_close(client, res); + } + /* If keep alive functionality is used */ + if (client->keep_alive != 0) { + /* Reset server alive watchdog */ + client->server_watchdog = 0; + } + + } + return ERR_OK; +} + + +/** + * TCP data sent callback function. @see tcp_sent_fn + * @param arg MQTT client + * @param tpcb TCP connection handle + * @param len Number of bytes sent + * @return ERR_OK + */ +static err_t +mqtt_tcp_sent_cb(void *arg, struct tcp_pcb *tpcb, u16_t len) +{ + mqtt_client_t *client = (mqtt_client_t *)arg; + + LWIP_UNUSED_ARG(tpcb); + LWIP_UNUSED_ARG(len); + + if (client->conn_state == MQTT_CONNECTED) { + struct mqtt_request_t *r; + + /* Reset keep-alive send timer and server watchdog */ + client->cyclic_tick = 0; + client->server_watchdog = 0; + /* QoS 0 publish has no response from server, so call its callbacks here */ + while ((r = mqtt_take_request(&client->pend_req_queue, 0)) != NULL) { + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_tcp_sent_cb: Calling QoS 0 publish complete callback\n")); + if (r->cb != NULL) { + r->cb(r->arg, ERR_OK); + } + mqtt_delete_request(r); + } + /* Try send any remaining buffers from output queue */ + mqtt_output_send(&client->output, client->conn); + } + return ERR_OK; +} + +/** + * TCP error callback function. @see tcp_err_fn + * @param arg MQTT client + * @param err Error encountered + */ +static void +mqtt_tcp_err_cb(void *arg, err_t err) +{ + mqtt_client_t *client = (mqtt_client_t *)arg; + LWIP_UNUSED_ARG(err); /* only used for debug output */ + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_tcp_err_cb: TCP error callback: error %d, arg: %p\n", err, arg)); + LWIP_ASSERT("mqtt_tcp_err_cb: client != NULL", client != NULL); + /* Set conn to null before calling close as pcb is already deallocated*/ + client->conn = 0; + mqtt_close(client, MQTT_CONNECT_DISCONNECTED); +} + +/** + * TCP poll callback function. @see tcp_poll_fn + * @param arg MQTT client + * @param tpcb TCP connection handle + * @return err ERR_OK + */ +static err_t +mqtt_tcp_poll_cb(void *arg, struct tcp_pcb *tpcb) +{ + mqtt_client_t *client = (mqtt_client_t *)arg; + if (client->conn_state == MQTT_CONNECTED) { + /* Try send any remaining buffers from output queue */ + mqtt_output_send(&client->output, tpcb); + } + return ERR_OK; +} + +/** + * TCP connect callback function. @see tcp_connected_fn + * @param arg MQTT client + * @param err Always ERR_OK, mqtt_tcp_err_cb is called in case of error + * @return ERR_OK + */ +static err_t +mqtt_tcp_connect_cb(void *arg, struct tcp_pcb *tpcb, err_t err) +{ + mqtt_client_t* client = (mqtt_client_t *)arg; + + if (err != ERR_OK) { + LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_tcp_connect_cb: TCP connect error %d\n", err)); + return err; + } + + /* Initiate receiver state */ + client->msg_idx = 0; + + /* Setup TCP callbacks */ + tcp_recv(tpcb, mqtt_tcp_recv_cb); + tcp_sent(tpcb, mqtt_tcp_sent_cb); + tcp_poll(tpcb, mqtt_tcp_poll_cb, 2); + + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_tcp_connect_cb: TCP connection established to server\n")); + /* Enter MQTT connect state */ + client->conn_state = MQTT_CONNECTING; + + /* Start cyclic timer */ + sys_timeout(MQTT_CYCLIC_TIMER_INTERVAL*1000, mqtt_cyclic_timer, client); + client->cyclic_tick = 0; + + /* Start transmission from output queue, connect message is the first one out*/ + mqtt_output_send(&client->output, client->conn); + + return ERR_OK; +} + + + +/*---------------------------------------------------------------------------------------------------- */ +/* Public API */ + + +/** + * @ingroup mqtt + * MQTT publish function. + * @param client MQTT client + * @param topic Publish topic string + * @param payload Data to publish (NULL is allowed) + * @param payload_length: Length of payload (0 is allowed) + * @param qos Quality of service, 0 1 or 2 + * @param retain MQTT retain flag + * @param cb Callback to call when publish is complete or has timed out + * @param arg User supplied argument to publish callback + * @return ERR_OK if successful + * ERR_CONN if client is disconnected + * ERR_MEM if short on memory + */ +err_t +mqtt_publish(mqtt_client_t *client, const char *topic, const void *payload, u16_t payload_length, u8_t qos, u8_t retain, + mqtt_request_cb_t cb, void *arg) +{ + struct mqtt_request_t *r; + u16_t pkt_id; + size_t topic_strlen; + size_t total_len; + u16_t topic_len; + u16_t remaining_length; + + LWIP_ASSERT("mqtt_publish: client != NULL", client); + LWIP_ASSERT("mqtt_publish: topic != NULL", topic); + LWIP_ERROR("mqtt_publish: TCP disconnected", (client->conn_state != TCP_DISCONNECTED), return ERR_CONN); + + topic_strlen = strlen(topic); + LWIP_ERROR("mqtt_publish: topic length overflow", (topic_strlen <= (0xFFFF - 2)), return ERR_ARG); + topic_len = (u16_t)topic_strlen; + total_len = 2 + topic_len + payload_length; + LWIP_ERROR("mqtt_publish: total length overflow", (total_len <= 0xFFFF), return ERR_ARG); + remaining_length = (u16_t)total_len; + + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_publish: Publish with payload length %d to topic \"%s\"\n", payload_length, topic)); + + if (qos > 0) { + remaining_length += 2; + /* Generate pkt_id id for QoS1 and 2 */ + pkt_id = msg_generate_packet_id(client); + } else { + /* Use reserved value pkt_id 0 for QoS 0 in request handle */ + pkt_id = 0; + } + + r = mqtt_create_request(client->req_list, pkt_id, cb, arg); + if (r == NULL) { + return ERR_MEM; + } + + if (mqtt_output_check_space(&client->output, remaining_length) == 0) { + mqtt_delete_request(r); + return ERR_MEM; + } + /* Append fixed header */ + mqtt_output_append_fixed_header(&client->output, MQTT_MSG_TYPE_PUBLISH, 0, qos, retain, remaining_length); + + /* Append Topic */ + mqtt_output_append_string(&client->output, topic, topic_len); + + /* Append packet if for QoS 1 and 2*/ + if (qos > 0) { + mqtt_output_append_u16(&client->output, pkt_id); + } + + /* Append optional publish payload */ + if ((payload != NULL) && (payload_length > 0)) { + mqtt_output_append_buf(&client->output, payload, payload_length); + } + + mqtt_append_request(&client->pend_req_queue, r); + mqtt_output_send(&client->output, client->conn); + return ERR_OK; +} + + +/** + * @ingroup mqtt + * MQTT subscribe/unsubscribe function. + * @param client MQTT client + * @param topic topic to subscribe to + * @param qos Quality of service, 0 1 or 2 (only used for subscribe) + * @param cb Callback to call when subscribe/unsubscribe reponse is received + * @param arg User supplied argument to publish callback + * @param sub 1 for subscribe, 0 for unsubscribe + * @return ERR_OK if successful, @see err_t enum for other results + */ +err_t +mqtt_sub_unsub(mqtt_client_t *client, const char *topic, u8_t qos, mqtt_request_cb_t cb, void *arg, u8_t sub) +{ + size_t topic_strlen; + size_t total_len; + u16_t topic_len; + u16_t remaining_length; + u16_t pkt_id; + struct mqtt_request_t *r; + + LWIP_ASSERT("mqtt_sub_unsub: client != NULL", client); + LWIP_ASSERT("mqtt_sub_unsub: topic != NULL", topic); + + topic_strlen = strlen(topic); + LWIP_ERROR("mqtt_sub_unsub: topic length overflow", (topic_strlen <= (0xFFFF - 2)), return ERR_ARG); + topic_len = (u16_t)topic_strlen; + /* Topic string, pkt_id, qos for subscribe */ + total_len = topic_len + 2 + 2 + (sub != 0); + LWIP_ERROR("mqtt_sub_unsub: total length overflow", (total_len <= 0xFFFF), return ERR_ARG); + remaining_length = (u16_t)total_len; + + LWIP_ASSERT("mqtt_sub_unsub: qos < 3", qos < 3); + if (client->conn_state == TCP_DISCONNECTED) { + LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_sub_unsub: Can not (un)subscribe in disconnected state\n")); + return ERR_CONN; + } + + pkt_id = msg_generate_packet_id(client); + r = mqtt_create_request(client->req_list, pkt_id, cb, arg); + if (r == NULL) { + return ERR_MEM; + } + + if (mqtt_output_check_space(&client->output, remaining_length) == 0) { + mqtt_delete_request(r); + return ERR_MEM; + } + + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_sub_unsub: Client (un)subscribe to topic \"%s\", id: %d\n", topic, pkt_id)); + + mqtt_output_append_fixed_header(&client->output, sub ? MQTT_MSG_TYPE_SUBSCRIBE : MQTT_MSG_TYPE_UNSUBSCRIBE, 0, 1, 0, remaining_length); + /* Packet id */ + mqtt_output_append_u16(&client->output, pkt_id); + /* Topic */ + mqtt_output_append_string(&client->output, topic, topic_len); + /* QoS */ + if (sub != 0) { + mqtt_output_append_u8(&client->output, LWIP_MIN(qos, 2)); + } + + mqtt_append_request(&client->pend_req_queue, r); + mqtt_output_send(&client->output, client->conn); + return ERR_OK; +} + + +/** + * @ingroup mqtt + * Set callback to handle incoming publish requests from server + * @param client MQTT client + * @param pub_cb Callback invoked when publish starts, contain topic and total length of payload + * @param data_cb Callback for each fragment of payload that arrives + * @param arg User supplied argument to both callbacks + */ +void +mqtt_set_inpub_callback(mqtt_client_t *client, mqtt_incoming_publish_cb_t pub_cb, + mqtt_incoming_data_cb_t data_cb, void *arg) +{ + LWIP_ASSERT("mqtt_set_inpub_callback: client != NULL", client != NULL); + client->data_cb = data_cb; + client->pub_cb = pub_cb; + client->inpub_arg = arg; +} + +/** + * @ingroup mqtt + * Create a new MQTT client instance + * @return Pointer to instance on success, NULL otherwise + */ +mqtt_client_t * +mqtt_client_new(void) +{ + mqtt_client_t *client = (mqtt_client_t *)mem_malloc(sizeof(mqtt_client_t)); + if (client != NULL) { + memset(client, 0, sizeof(mqtt_client_t)); + } + return client; +} + + +/** + * @ingroup mqtt + * Connect to MQTT server + * @param client MQTT client + * @param ip_addr Server IP + * @param port Server port + * @param cb Connection state change callback + * @param arg User supplied argument to connection callback + * @param client_info Client identification and connection options + * @return ERR_OK if successful, @see err_t enum for other results + */ +err_t +mqtt_client_connect(mqtt_client_t *client, const ip_addr_t *ip_addr, u16_t port, mqtt_connection_cb_t cb, void *arg, + const struct mqtt_connect_client_info_t *client_info) +{ + err_t err; + size_t len; + u16_t client_id_length; + /* Length is the sum of 2+"MQTT", protocol level, flags and keep alive */ + u16_t remaining_length = 2 + 4 + 1 + 1 + 2; + u8_t flags = 0, will_topic_len = 0, will_msg_len = 0; + + LWIP_ASSERT("mqtt_client_connect: client != NULL", client != NULL); + LWIP_ASSERT("mqtt_client_connect: ip_addr != NULL", ip_addr != NULL); + LWIP_ASSERT("mqtt_client_connect: client_info != NULL", client_info != NULL); + LWIP_ASSERT("mqtt_client_connect: client_info->client_id != NULL", client_info->client_id != NULL); + + if (client->conn_state != TCP_DISCONNECTED) { + LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_client_connect: Already connected\n")); + return ERR_ISCONN; + } + + /* Wipe clean */ + memset(client, 0, sizeof(mqtt_client_t)); + client->connect_arg = arg; + client->connect_cb = cb; + client->keep_alive = client_info->keep_alive; + mqtt_init_requests(client->req_list); + + /* Build connect message */ + if (client_info->will_topic != NULL && client_info->will_msg != NULL) { + flags |= MQTT_CONNECT_FLAG_WILL; + flags |= (client_info->will_qos & 3) << 3; + if (client_info->will_retain) { + flags |= MQTT_CONNECT_FLAG_WILL_RETAIN; + } + len = strlen(client_info->will_topic); + LWIP_ERROR("mqtt_client_connect: client_info->will_topic length overflow", len <= 0xFF, return ERR_VAL); + LWIP_ERROR("mqtt_client_connect: client_info->will_topic length must be > 0", len > 0, return ERR_VAL); + will_topic_len = (u8_t)len; + len = strlen(client_info->will_msg); + LWIP_ERROR("mqtt_client_connect: client_info->will_msg length overflow", len <= 0xFF, return ERR_VAL); + will_msg_len = (u8_t)len; + len = remaining_length + 2 + will_topic_len + 2 + will_msg_len; + LWIP_ERROR("mqtt_client_connect: remaining_length overflow", len <= 0xFFFF, return ERR_VAL); + remaining_length = (u16_t)len; + } + + /* Don't complicate things, always connect using clean session */ + flags |= MQTT_CONNECT_FLAG_CLEAN_SESSION; + + len = strlen(client_info->client_id); + LWIP_ERROR("mqtt_client_connect: client_info->client_id length overflow", len <= 0xFFFF, return ERR_VAL); + client_id_length = (u16_t)len; + len = remaining_length + 2 + client_id_length; + LWIP_ERROR("mqtt_client_connect: remaining_length overflow", len <= 0xFFFF, return ERR_VAL); + remaining_length = (u16_t)len; + + if (mqtt_output_check_space(&client->output, remaining_length) == 0) { + return ERR_MEM; + } + + client->conn = tcp_new(); + if (client->conn == NULL) { + return ERR_MEM; + } + + /* Set arg pointer for callbacks */ + tcp_arg(client->conn, client); + /* Any local address, pick random local port number */ + err = tcp_bind(client->conn, IP_ADDR_ANY, 0); + if (err != ERR_OK) { + LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_client_connect: Error binding to local ip/port, %d\n", err)); + goto tcp_fail; + } + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_client_connect: Connecting to host: %s at port:%"U16_F"\n", ipaddr_ntoa(ip_addr), port)); + + /* Connect to server */ + err = tcp_connect(client->conn, ip_addr, port, mqtt_tcp_connect_cb); + if (err != ERR_OK) { + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_client_connect: Error connecting to remote ip/port, %d\n", err)); + goto tcp_fail; + } + /* Set error callback */ + tcp_err(client->conn, mqtt_tcp_err_cb); + client->conn_state = TCP_CONNECTING; + + /* Append fixed header */ + mqtt_output_append_fixed_header(&client->output, MQTT_MSG_TYPE_CONNECT, 0, 0, 0, remaining_length); + /* Append Protocol string */ + mqtt_output_append_string(&client->output, "MQTT", 4); + /* Append Protocol level */ + mqtt_output_append_u8(&client->output, 4); + /* Append connect flags */ + mqtt_output_append_u8(&client->output, flags); + /* Append keep-alive */ + mqtt_output_append_u16(&client->output, client_info->keep_alive); + /* Append client id */ + mqtt_output_append_string(&client->output, client_info->client_id, client_id_length); + /* Append will message if used */ + if ((flags & MQTT_CONNECT_FLAG_WILL) != 0) { + mqtt_output_append_string(&client->output, client_info->will_topic, will_topic_len); + mqtt_output_append_string(&client->output, client_info->will_msg, will_msg_len); + } + return ERR_OK; + +tcp_fail: + tcp_abort(client->conn); + client->conn = NULL; + return err; +} + + +/** + * @ingroup mqtt + * Disconnect from MQTT server + * @param client MQTT client + */ +void +mqtt_disconnect(mqtt_client_t *client) +{ + LWIP_ASSERT("mqtt_disconnect: client != NULL", client); + /* If connection in not already closed */ + if (client->conn_state != TCP_DISCONNECTED) { + /* Set conn_state before calling mqtt_close to prevent callback from being called */ + client->conn_state = TCP_DISCONNECTED; + mqtt_close(client, (mqtt_connection_status_t)0); + } +} + +/** + * @ingroup mqtt + * Check connection with server + * @param client MQTT client + * @return 1 if connected to server, 0 otherwise + */ +u8_t +mqtt_client_is_connected(mqtt_client_t *client) +{ + LWIP_ASSERT("mqtt_client_is_connected: client != NULL", client); + return client->conn_state == MQTT_CONNECTED; +} + +#endif /* LWIP_TCP && LWIP_CALLBACK_API */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/netbiosns/netbiosns.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/netbiosns/netbiosns.c new file mode 100644 index 0000000..2dfbe65 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/netbiosns/netbiosns.c @@ -0,0 +1,367 @@ +/** + * @file + * NetBIOS name service responder + */ + +/** + * @defgroup netbiosns NETBIOS responder + * @ingroup apps + * + * This is an example implementation of a NetBIOS name server. + * It responds to name queries for a configurable name. + * Name resolving is not supported. + * + * Note that the device doesn't broadcast it's own name so can't + * detect duplicate names! + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#include "lwip/apps/netbiosns.h" + +#if LWIP_IPV4 && LWIP_UDP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/udp.h" +#include "lwip/netif.h" + +#include <string.h> + +/** default port number for "NetBIOS Name service */ +#define NETBIOS_PORT 137 + +/** size of a NetBIOS name */ +#define NETBIOS_NAME_LEN 16 + +/** The Time-To-Live for NetBIOS name responds (in seconds) + * Default is 300000 seconds (3 days, 11 hours, 20 minutes) */ +#define NETBIOS_NAME_TTL 300000u + +/** NetBIOS header flags */ +#define NETB_HFLAG_RESPONSE 0x8000U +#define NETB_HFLAG_OPCODE 0x7800U +#define NETB_HFLAG_OPCODE_NAME_QUERY 0x0000U +#define NETB_HFLAG_AUTHORATIVE 0x0400U +#define NETB_HFLAG_TRUNCATED 0x0200U +#define NETB_HFLAG_RECURS_DESIRED 0x0100U +#define NETB_HFLAG_RECURS_AVAILABLE 0x0080U +#define NETB_HFLAG_BROADCAST 0x0010U +#define NETB_HFLAG_REPLYCODE 0x0008U +#define NETB_HFLAG_REPLYCODE_NOERROR 0x0000U + +/** NetBIOS name flags */ +#define NETB_NFLAG_UNIQUE 0x8000U +#define NETB_NFLAG_NODETYPE 0x6000U +#define NETB_NFLAG_NODETYPE_HNODE 0x6000U +#define NETB_NFLAG_NODETYPE_MNODE 0x4000U +#define NETB_NFLAG_NODETYPE_PNODE 0x2000U +#define NETB_NFLAG_NODETYPE_BNODE 0x0000U + +/** NetBIOS message header */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct netbios_hdr { + PACK_STRUCT_FIELD(u16_t trans_id); + PACK_STRUCT_FIELD(u16_t flags); + PACK_STRUCT_FIELD(u16_t questions); + PACK_STRUCT_FIELD(u16_t answerRRs); + PACK_STRUCT_FIELD(u16_t authorityRRs); + PACK_STRUCT_FIELD(u16_t additionalRRs); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** NetBIOS message name part */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct netbios_name_hdr { + PACK_STRUCT_FLD_8(u8_t nametype); + PACK_STRUCT_FLD_8(u8_t encname[(NETBIOS_NAME_LEN*2)+1]); + PACK_STRUCT_FIELD(u16_t type); + PACK_STRUCT_FIELD(u16_t cls); + PACK_STRUCT_FIELD(u32_t ttl); + PACK_STRUCT_FIELD(u16_t datalen); + PACK_STRUCT_FIELD(u16_t flags); + PACK_STRUCT_FLD_S(ip4_addr_p_t addr); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** NetBIOS message */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct netbios_resp +{ + struct netbios_hdr resp_hdr; + struct netbios_name_hdr resp_name; +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#ifdef NETBIOS_LWIP_NAME +#define NETBIOS_LOCAL_NAME NETBIOS_LWIP_NAME +#else +static char netbiosns_local_name[NETBIOS_NAME_LEN]; +#define NETBIOS_LOCAL_NAME netbiosns_local_name +#endif + +struct udp_pcb *netbiosns_pcb; + +/** Decode a NetBIOS name (from packet to string) */ +static int +netbiosns_name_decode(char *name_enc, char *name_dec, int name_dec_len) +{ + char *pname; + char cname; + char cnbname; + int idx = 0; + + LWIP_UNUSED_ARG(name_dec_len); + + /* Start decoding netbios name. */ + pname = name_enc; + for (;;) { + /* Every two characters of the first level-encoded name + * turn into one character in the decoded name. */ + cname = *pname; + if (cname == '\0') + break; /* no more characters */ + if (cname == '.') + break; /* scope ID follows */ + if (cname < 'A' || cname > 'Z') { + /* Not legal. */ + return -1; + } + cname -= 'A'; + cnbname = cname << 4; + pname++; + + cname = *pname; + if (cname == '\0' || cname == '.') { + /* No more characters in the name - but we're in + * the middle of a pair. Not legal. */ + return -1; + } + if (cname < 'A' || cname > 'Z') { + /* Not legal. */ + return -1; + } + cname -= 'A'; + cnbname |= cname; + pname++; + + /* Do we have room to store the character? */ + if (idx < NETBIOS_NAME_LEN) { + /* Yes - store the character. */ + name_dec[idx++] = (cnbname!=' '?cnbname:'\0'); + } + } + + return 0; +} + +#if 0 /* function currently unused */ +/** Encode a NetBIOS name (from string to packet) - currently unused because + we don't ask for names. */ +static int +netbiosns_name_encode(char *name_enc, char *name_dec, int name_dec_len) +{ + char *pname; + char cname; + unsigned char ucname; + int idx = 0; + + /* Start encoding netbios name. */ + pname = name_enc; + + for (;;) { + /* Every two characters of the first level-encoded name + * turn into one character in the decoded name. */ + cname = *pname; + if (cname == '\0') + break; /* no more characters */ + if (cname == '.') + break; /* scope ID follows */ + if ((cname < 'A' || cname > 'Z') && (cname < '0' || cname > '9')) { + /* Not legal. */ + return -1; + } + + /* Do we have room to store the character? */ + if (idx >= name_dec_len) { + return -1; + } + + /* Yes - store the character. */ + ucname = cname; + name_dec[idx++] = ('A'+((ucname>>4) & 0x0F)); + name_dec[idx++] = ('A'+( ucname & 0x0F)); + pname++; + } + + /* Fill with "space" coding */ + for (;idx < name_dec_len - 1;) { + name_dec[idx++] = 'C'; + name_dec[idx++] = 'A'; + } + + /* Terminate string */ + name_dec[idx] = '\0'; + + return 0; +} +#endif /* 0 */ + +/** NetBIOS Name service recv callback */ +static void +netbiosns_recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) +{ + LWIP_UNUSED_ARG(arg); + + /* if packet is valid */ + if (p != NULL) { + char netbios_name[NETBIOS_NAME_LEN+1]; + struct netbios_hdr* netbios_hdr = (struct netbios_hdr*)p->payload; + struct netbios_name_hdr* netbios_name_hdr = (struct netbios_name_hdr*)(netbios_hdr+1); + + /* we only answer if we got a default interface */ + if (netif_default != NULL) { + /* @todo: do we need to check answerRRs/authorityRRs/additionalRRs? */ + /* if the packet is a NetBIOS name query question */ + if (((netbios_hdr->flags & PP_NTOHS(NETB_HFLAG_OPCODE)) == PP_NTOHS(NETB_HFLAG_OPCODE_NAME_QUERY)) && + ((netbios_hdr->flags & PP_NTOHS(NETB_HFLAG_RESPONSE)) == 0) && + (netbios_hdr->questions == PP_NTOHS(1))) { + /* decode the NetBIOS name */ + netbiosns_name_decode((char*)(netbios_name_hdr->encname), netbios_name, sizeof(netbios_name)); + /* if the packet is for us */ + if (lwip_strnicmp(netbios_name, NETBIOS_LOCAL_NAME, sizeof(NETBIOS_LOCAL_NAME)) == 0) { + struct pbuf *q; + struct netbios_resp *resp; + + q = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct netbios_resp), PBUF_RAM); + if (q != NULL) { + resp = (struct netbios_resp*)q->payload; + + /* prepare NetBIOS header response */ + resp->resp_hdr.trans_id = netbios_hdr->trans_id; + resp->resp_hdr.flags = PP_HTONS(NETB_HFLAG_RESPONSE | + NETB_HFLAG_OPCODE_NAME_QUERY | + NETB_HFLAG_AUTHORATIVE | + NETB_HFLAG_RECURS_DESIRED); + resp->resp_hdr.questions = 0; + resp->resp_hdr.answerRRs = PP_HTONS(1); + resp->resp_hdr.authorityRRs = 0; + resp->resp_hdr.additionalRRs = 0; + + /* prepare NetBIOS header datas */ + MEMCPY( resp->resp_name.encname, netbios_name_hdr->encname, sizeof(netbios_name_hdr->encname)); + resp->resp_name.nametype = netbios_name_hdr->nametype; + resp->resp_name.type = netbios_name_hdr->type; + resp->resp_name.cls = netbios_name_hdr->cls; + resp->resp_name.ttl = PP_HTONL(NETBIOS_NAME_TTL); + resp->resp_name.datalen = PP_HTONS(sizeof(resp->resp_name.flags)+sizeof(resp->resp_name.addr)); + resp->resp_name.flags = PP_HTONS(NETB_NFLAG_NODETYPE_BNODE); + ip4_addr_copy(resp->resp_name.addr, *netif_ip4_addr(netif_default)); + + /* send the NetBIOS response */ + udp_sendto(upcb, q, addr, port); + + /* free the "reference" pbuf */ + pbuf_free(q); + } + } + } + } + /* free the pbuf */ + pbuf_free(p); + } +} + +/** + * @ingroup netbiosns + * Init netbios responder + */ +void +netbiosns_init(void) +{ +#ifdef NETBIOS_LWIP_NAME + LWIP_ASSERT("NetBIOS name is too long!", strlen(NETBIOS_LWIP_NAME) < NETBIOS_NAME_LEN); +#endif + + netbiosns_pcb = udp_new_ip_type(IPADDR_TYPE_ANY); + if (netbiosns_pcb != NULL) { + /* we have to be allowed to send broadcast packets! */ + ip_set_option(netbiosns_pcb, SOF_BROADCAST); + udp_bind(netbiosns_pcb, IP_ANY_TYPE, NETBIOS_PORT); + udp_recv(netbiosns_pcb, netbiosns_recv, netbiosns_pcb); + } +} + +#ifndef NETBIOS_LWIP_NAME +/** + * @ingroup netbiosns + * Set netbios name. ATTENTION: the hostname must be less than 15 characters! + */ +void +netbiosns_set_name(const char* hostname) +{ + size_t copy_len = strlen(hostname); + LWIP_ASSERT("NetBIOS name is too long!", copy_len < NETBIOS_NAME_LEN); + if (copy_len >= NETBIOS_NAME_LEN) { + copy_len = NETBIOS_NAME_LEN - 1; + } + MEMCPY(netbiosns_local_name, hostname, copy_len + 1); +} +#endif + +/** + * @ingroup netbiosns + * Stop netbios responder + */ +void +netbiosns_stop(void) +{ + if (netbiosns_pcb != NULL) { + udp_remove(netbiosns_pcb); + netbiosns_pcb = NULL; + } +} + +#endif /* LWIP_IPV4 && LWIP_UDP */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_asn1.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_asn1.c new file mode 100644 index 0000000..f35b760 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_asn1.c @@ -0,0 +1,749 @@ +/** + * @file + * Abstract Syntax Notation One (ISO 8824, 8825) encoding + * + * @todo not optimised (yet), favor correctness over speed, favor speed over size + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons <christiaan.simons@axon.tv> + * Martin Hentschel <info@cl-soft.de> + */ + +#include "lwip/apps/snmp_opts.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "snmp_asn1.h" + +#define PBUF_OP_EXEC(code) \ + if ((code) != ERR_OK) { \ + return ERR_BUF; \ + } + +/** + * Encodes a TLV into a pbuf stream. + * + * @param pbuf_stream points to a pbuf stream + * @param tlv TLV to encode + * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode + */ +err_t +snmp_ans1_enc_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv) +{ + u8_t data; + u8_t length_bytes_required; + + /* write type */ + if ((tlv->type & SNMP_ASN1_DATATYPE_MASK) == SNMP_ASN1_DATATYPE_EXTENDED) { + /* extended format is not used by SNMP so we do not accept those values */ + return ERR_ARG; + } + if (tlv->type_len != 0) { + /* any other value as auto is not accepted for type (we always use one byte because extended syntax is prohibited) */ + return ERR_ARG; + } + + PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, tlv->type)); + tlv->type_len = 1; + + /* write length */ + if (tlv->value_len <= 127) { + length_bytes_required = 1; + } else if (tlv->value_len <= 255) { + length_bytes_required = 2; + } else { + length_bytes_required = 3; + } + + /* check for forced min length */ + if (tlv->length_len > 0) { + if (tlv->length_len < length_bytes_required) { + /* unable to code requested length in requested number of bytes */ + return ERR_ARG; + } + + length_bytes_required = tlv->length_len; + } else { + tlv->length_len = length_bytes_required; + } + + if (length_bytes_required > 1) { + /* multi byte representation required */ + length_bytes_required--; + data = 0x80 | length_bytes_required; /* extended length definition, 1 length byte follows */ + + PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data)); + + while (length_bytes_required > 1) { + if (length_bytes_required == 2) { + /* append high byte */ + data = (u8_t)(tlv->value_len >> 8); + } else { + /* append leading 0x00 */ + data = 0x00; + } + + PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data)); + length_bytes_required--; + } + } + + /* append low byte */ + data = (u8_t)(tlv->value_len & 0xFF); + PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data)); + + return ERR_OK; +} + +/** + * Encodes raw data (octet string, opaque) into a pbuf chained ASN1 msg. + * + * @param pbuf_stream points to a pbuf stream + * @param raw_len raw data length + * @param raw points raw data + * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode + */ +err_t +snmp_asn1_enc_raw(struct snmp_pbuf_stream* pbuf_stream, const u8_t *raw, u16_t raw_len) +{ + PBUF_OP_EXEC(snmp_pbuf_stream_writebuf(pbuf_stream, raw, raw_len)); + + return ERR_OK; +} + +/** + * Encodes u32_t (counter, gauge, timeticks) into a pbuf chained ASN1 msg. + * + * @param pbuf_stream points to a pbuf stream + * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt()) + * @param value is the host order u32_t value to be encoded + * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode + * + * @see snmp_asn1_enc_u32t_cnt() + */ +err_t +snmp_asn1_enc_u32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, u32_t value) +{ + if (octets_needed > 5) { + return ERR_ARG; + } + if (octets_needed == 5) { + /* not enough bits in 'value' add leading 0x00 */ + PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00)); + octets_needed--; + } + + while (octets_needed > 1) { + octets_needed--; + PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3)))); + } + + /* (only) one least significant octet */ + PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value)); + + return ERR_OK; +} + +/** + * Encodes u64_t (counter64) into a pbuf chained ASN1 msg. + * + * @param pbuf_stream points to a pbuf stream + * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt()) + * @param value is the host order u32_t value to be encoded + * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode + * + * @see snmp_asn1_enc_u64t_cnt() + */ +err_t +snmp_asn1_enc_u64t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, const u32_t* value) +{ + if (octets_needed > 9) { + return ERR_ARG; + } + if (octets_needed == 9) { + /* not enough bits in 'value' add leading 0x00 */ + PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00)); + octets_needed--; + } + + while (octets_needed > 4) { + octets_needed--; + PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value >> ((octets_needed-4) << 3)))); + } + + /* skip to low u32 */ + value++; + + while (octets_needed > 1) { + octets_needed--; + PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value >> (octets_needed << 3)))); + } + + /* always write at least one octet (also in case of value == 0) */ + PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value))); + + return ERR_OK; +} + +/** + * Encodes s32_t integer into a pbuf chained ASN1 msg. + * + * @param pbuf_stream points to a pbuf stream + * @param octets_needed encoding length (from snmp_asn1_enc_s32t_cnt()) + * @param value is the host order s32_t value to be encoded + * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode + * + * @see snmp_asn1_enc_s32t_cnt() + */ +err_t +snmp_asn1_enc_s32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, s32_t value) +{ + while (octets_needed > 1) { + octets_needed--; + + PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3)))); + } + + /* (only) one least significant octet */ + PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value)); + + return ERR_OK; +} + +/** + * Encodes object identifier into a pbuf chained ASN1 msg. + * + * @param pbuf_stream points to a pbuf stream + * @param oid points to object identifier array + * @param oid_len object identifier array length + * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode + */ +err_t +snmp_asn1_enc_oid(struct snmp_pbuf_stream* pbuf_stream, const u32_t *oid, u16_t oid_len) +{ + if (oid_len > 1) { + /* write compressed first two sub id's */ + u32_t compressed_byte = ((oid[0] * 40) + oid[1]); + PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)compressed_byte)); + oid_len -= 2; + oid += 2; + } else { + /* @bug: allow empty varbinds for symmetry (we must decode them for getnext), allow partial compression?? */ + /* ident_len <= 1, at least we need zeroDotZero (0.0) (ident_len == 2) */ + return ERR_ARG; + } + + while (oid_len > 0) { + u32_t sub_id; + u8_t shift, tail; + + oid_len--; + sub_id = *oid; + tail = 0; + shift = 28; + while (shift > 0) { + u8_t code; + + code = (u8_t)(sub_id >> shift); + if ((code != 0) || (tail != 0)) { + tail = 1; + PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, code | 0x80)); + } + shift -= 7; + } + PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)sub_id & 0x7F)); + + /* proceed to next sub-identifier */ + oid++; + } + return ERR_OK; +} + +/** + * Returns octet count for length. + * + * @param length parameter length + * @param octets_needed points to the return value + */ +void +snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed) +{ + if (length < 0x80U) { + *octets_needed = 1; + } else if (length < 0x100U) { + *octets_needed = 2; + } else { + *octets_needed = 3; + } +} + +/** + * Returns octet count for an u32_t. + * + * @param value value to be encoded + * @param octets_needed points to the return value + * + * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded + * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value + * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!! + */ +void +snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed) +{ + if (value < 0x80UL) { + *octets_needed = 1; + } else if (value < 0x8000UL) { + *octets_needed = 2; + } else if (value < 0x800000UL) { + *octets_needed = 3; + } else if (value < 0x80000000UL) { + *octets_needed = 4; + } else { + *octets_needed = 5; + } +} + +/** + * Returns octet count for an u64_t. + * + * @param value value to be encoded + * @param octets_needed points to the return value + * + * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded + * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value + * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!! + */ +void +snmp_asn1_enc_u64t_cnt(const u32_t *value, u16_t *octets_needed) +{ + /* check if high u32 is 0 */ + if (*value == 0x00) { + /* only low u32 is important */ + value++; + snmp_asn1_enc_u32t_cnt(*value, octets_needed); + } else { + /* low u32 does not matter for length determination */ + snmp_asn1_enc_u32t_cnt(*value, octets_needed); + *octets_needed = *octets_needed + 4; /* add the 4 bytes of low u32 */ + } +} + +/** + * Returns octet count for an s32_t. + * + * @param value value to be encoded + * @param octets_needed points to the return value + * + * @note ASN coded integers are _always_ signed. + */ +void +snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed) +{ + if (value < 0) { + value = ~value; + } + if (value < 0x80L) { + *octets_needed = 1; + } else if (value < 0x8000L) { + *octets_needed = 2; + } else if (value < 0x800000L) { + *octets_needed = 3; + } else { + *octets_needed = 4; + } +} + +/** + * Returns octet count for an object identifier. + * + * @param oid points to object identifier array + * @param oid_len object identifier array length + * @param octets_needed points to the return value + */ +void +snmp_asn1_enc_oid_cnt(const u32_t *oid, u16_t oid_len, u16_t *octets_needed) +{ + u32_t sub_id; + + *octets_needed = 0; + if (oid_len > 1) { + /* compressed prefix in one octet */ + (*octets_needed)++; + oid_len -= 2; + oid += 2; + } + while (oid_len > 0) { + oid_len--; + sub_id = *oid; + + sub_id >>= 7; + (*octets_needed)++; + while (sub_id > 0) { + sub_id >>= 7; + (*octets_needed)++; + } + oid++; + } +} + +/** + * Decodes a TLV from a pbuf stream. + * + * @param pbuf_stream points to a pbuf stream + * @param tlv returns decoded TLV + * @return ERR_OK if successful, ERR_VAL if we can't decode + */ +err_t +snmp_asn1_dec_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv) +{ + u8_t data; + + /* decode type first */ + PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); + tlv->type = data; + + if ((tlv->type & SNMP_ASN1_DATATYPE_MASK) == SNMP_ASN1_DATATYPE_EXTENDED) { + /* extended format is not used by SNMP so we do not accept those values */ + return ERR_VAL; + } + tlv->type_len = 1; + + /* now, decode length */ + PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); + + if (data < 0x80) { /* short form */ + tlv->length_len = 1; + tlv->value_len = data; + } else if (data > 0x80) { /* long form */ + u8_t length_bytes = data - 0x80; + tlv->length_len = length_bytes + 1; /* this byte + defined number of length bytes following */ + tlv->value_len = 0; + + while (length_bytes > 0) { + /* we only support up to u16.maxvalue-1 (2 bytes) but have to accept leading zero bytes */ + if (tlv->value_len > 0xFF) { + return ERR_VAL; + } + PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); + tlv->value_len <<= 8; + tlv->value_len |= data; + + /* take care for special value used for indefinite length */ + if (tlv->value_len == 0xFFFF) { + return ERR_VAL; + } + + length_bytes--; + } + } else { /* data == 0x80 indefinite length form */ + /* (not allowed for SNMP; RFC 1157, 3.2.2) */ + return ERR_VAL; + } + + return ERR_OK; +} + +/** + * Decodes positive integer (counter, gauge, timeticks) into u32_t. + * + * @param pbuf_stream points to a pbuf stream + * @param len length of the coded integer field + * @param value return host order integer + * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode + * + * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded + * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value + * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!! + */ +err_t +snmp_asn1_dec_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value) +{ + u8_t data; + + if ((len > 0) && (len <= 5)) { + PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); + + /* expecting sign bit to be zero, only unsigned please! */ + if (((len == 5) && (data == 0x00)) || ((len < 5) && ((data & 0x80) == 0))) { + *value = data; + len--; + + while (len > 0) { + PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); + len--; + + *value <<= 8; + *value |= data; + } + + return ERR_OK; + } + } + + return ERR_VAL; +} + +/** + * Decodes large positive integer (counter64) into 2x u32_t. + * + * @param pbuf_stream points to a pbuf stream + * @param len length of the coded integer field + * @param value return host order integer + * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode + * + * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded + * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value + * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!! + */ +err_t +snmp_asn1_dec_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value) +{ + u8_t data; + + if (len <= 4) { + /* high u32 is 0 */ + *value = 0; + /* directly skip to low u32 */ + value++; + } + + if ((len > 0) && (len <= 9)) { + PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); + + /* expecting sign bit to be zero, only unsigned please! */ + if (((len == 9) && (data == 0x00)) || ((len < 9) && ((data & 0x80) == 0))) { + *value = data; + len--; + + while (len > 0) { + PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); + + if (len == 4) { + /* skip to low u32 */ + value++; + *value = 0; + } else { + *value <<= 8; + } + + *value |= data; + len--; + } + + return ERR_OK; + } + } + + return ERR_VAL; +} + +/** + * Decodes integer into s32_t. + * + * @param pbuf_stream points to a pbuf stream + * @param len length of the coded integer field + * @param value return host order integer + * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode + * + * @note ASN coded integers are _always_ signed! + */ +err_t +snmp_asn1_dec_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, s32_t *value) +{ +#if BYTE_ORDER == LITTLE_ENDIAN + u8_t *lsb_ptr = (u8_t*)value; +#endif +#if BYTE_ORDER == BIG_ENDIAN + u8_t *lsb_ptr = (u8_t*)value + sizeof(s32_t) - 1; +#endif + u8_t sign; + u8_t data; + + if ((len > 0) && (len < 5)) { + PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); + len--; + + if (data & 0x80) { + /* negative, start from -1 */ + *value = -1; + sign = 1; + *lsb_ptr &= data; + } else { + /* positive, start from 0 */ + *value = 0; + sign = 0; + *lsb_ptr |= data; + } + + /* OR/AND octets with value */ + while (len > 0) { + PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); + len--; + +#if BYTE_ORDER == LITTLE_ENDIAN + *value <<= 8; +#endif +#if BYTE_ORDER == BIG_ENDIAN + *value >>= 8; +#endif + + if (sign) { + *lsb_ptr |= 255; + *lsb_ptr &= data; + } else { + *lsb_ptr |= data; + } + } + + return ERR_OK; + } + + return ERR_VAL; +} + +/** + * Decodes object identifier from incoming message into array of u32_t. + * + * @param pbuf_stream points to a pbuf stream + * @param len length of the coded object identifier + * @param oid return decoded object identifier + * @param oid_len return decoded object identifier length + * @param oid_max_len size of oid buffer + * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode + */ +err_t +snmp_asn1_dec_oid(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t* oid, u8_t* oid_len, u8_t oid_max_len) +{ + u32_t *oid_ptr; + u8_t data; + + *oid_len = 0; + oid_ptr = oid; + if (len > 0) { + if (oid_max_len < 2) { + return ERR_MEM; + } + + PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); + len--; + + /* first compressed octet */ + if (data == 0x2B) { + /* (most) common case 1.3 (iso.org) */ + *oid_ptr = 1; + oid_ptr++; + *oid_ptr = 3; + oid_ptr++; + } else if (data < 40) { + *oid_ptr = 0; + oid_ptr++; + *oid_ptr = data; + oid_ptr++; + } else if (data < 80) { + *oid_ptr = 1; + oid_ptr++; + *oid_ptr = data - 40; + oid_ptr++; + } else { + *oid_ptr = 2; + oid_ptr++; + *oid_ptr = data - 80; + oid_ptr++; + } + *oid_len = 2; + } else { + /* accepting zero length identifiers e.g. for getnext operation. uncommon but valid */ + return ERR_OK; + } + + while ((len > 0) && (*oid_len < oid_max_len)) { + PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); + len--; + + if ((data & 0x80) == 0x00) { + /* sub-identifier uses single octet */ + *oid_ptr = data; + } else { + /* sub-identifier uses multiple octets */ + u32_t sub_id = (data & ~0x80); + while ((len > 0) && ((data & 0x80) != 0)) { + PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); + len--; + + sub_id = (sub_id << 7) + (data & ~0x80); + } + + if ((data & 0x80) != 0) { + /* "more bytes following" bit still set at end of len */ + return ERR_VAL; + } + *oid_ptr = sub_id; + } + oid_ptr++; + (*oid_len)++; + } + + if (len > 0) { + /* OID to long to fit in our buffer */ + return ERR_MEM; + } + + return ERR_OK; +} + +/** + * Decodes (copies) raw data (ip-addresses, octet strings, opaque encoding) + * from incoming message into array. + * + * @param pbuf_stream points to a pbuf stream + * @param len length of the coded raw data (zero is valid, e.g. empty string!) + * @param buf return raw bytes + * @param buf_len returns length of the raw return value + * @param buf_max_len buffer size + * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode + */ +err_t +snmp_asn1_dec_raw(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u8_t *buf, u16_t* buf_len, u16_t buf_max_len) +{ + if (len > buf_max_len) { + /* not enough dst space */ + return ERR_MEM; + } + *buf_len = len; + + while (len > 0) { + PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, buf)); + buf++; + len--; + } + + return ERR_OK; +} + +#endif /* LWIP_SNMP */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_asn1.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_asn1.h new file mode 100644 index 0000000..ec50d8c --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_asn1.h @@ -0,0 +1,108 @@ +/** + * @file + * Abstract Syntax Notation One (ISO 8824, 8825) codec. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * Copyright (c) 2016 Elias Oenal. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons <christiaan.simons@axon.tv> + * Martin Hentschel <info@cl-soft.de> + * Elias Oenal <lwip@eliasoenal.com> + */ + +#ifndef LWIP_HDR_APPS_SNMP_ASN1_H +#define LWIP_HDR_APPS_SNMP_ASN1_H + +#include "lwip/apps/snmp_opts.h" + +#if LWIP_SNMP + +#include "lwip/err.h" +#include "lwip/apps/snmp_core.h" +#include "snmp_pbuf_stream.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SNMP_ASN1_TLV_INDEFINITE_LENGTH 0x80 + +#define SNMP_ASN1_CLASS_MASK 0xC0 +#define SNMP_ASN1_CONTENTTYPE_MASK 0x20 +#define SNMP_ASN1_DATATYPE_MASK 0x1F +#define SNMP_ASN1_DATATYPE_EXTENDED 0x1F /* DataType indicating that datatype is encoded in following bytes */ + +/* context specific (SNMP) tags (from SNMP spec. RFC1157) */ +#define SNMP_ASN1_CONTEXT_PDU_GET_REQ 0 +#define SNMP_ASN1_CONTEXT_PDU_GET_NEXT_REQ 1 +#define SNMP_ASN1_CONTEXT_PDU_GET_RESP 2 +#define SNMP_ASN1_CONTEXT_PDU_SET_REQ 3 +#define SNMP_ASN1_CONTEXT_PDU_TRAP 4 +#define SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ 5 + +#define SNMP_ASN1_CONTEXT_VARBIND_NO_SUCH_OBJECT 0 +#define SNMP_ASN1_CONTEXT_VARBIND_END_OF_MIB_VIEW 2 + +struct snmp_asn1_tlv +{ + u8_t type; /* only U8 because extended types are not specified by SNMP */ + u8_t type_len; /* encoded length of 'type' field (normally 1) */ + u8_t length_len; /* indicates how many bytes are required to encode the 'value_len' field */ + u16_t value_len; /* encoded length of the value */ +}; +#define SNMP_ASN1_TLV_HDR_LENGTH(tlv) ((tlv).type_len + (tlv).length_len) +#define SNMP_ASN1_TLV_LENGTH(tlv) ((tlv).type_len + (tlv).length_len + (tlv).value_len) +#define SNMP_ASN1_SET_TLV_PARAMS(tlv, type_, length_len_, value_len_) do { (tlv).type = (type_); (tlv).type_len = 0; (tlv).length_len = (length_len_); (tlv).value_len = (value_len_); } while (0); + +err_t snmp_asn1_dec_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv); +err_t snmp_asn1_dec_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value); +err_t snmp_asn1_dec_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value); +err_t snmp_asn1_dec_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, s32_t *value); +err_t snmp_asn1_dec_oid(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t* oid, u8_t* oid_len, u8_t oid_max_len); +err_t snmp_asn1_dec_raw(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u8_t *buf, u16_t* buf_len, u16_t buf_max_len); + +err_t snmp_ans1_enc_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv); + +void snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed); +void snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed); +void snmp_asn1_enc_u64t_cnt(const u32_t *value, u16_t *octets_needed); +void snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed); +void snmp_asn1_enc_oid_cnt(const u32_t *oid, u16_t oid_len, u16_t *octets_needed); +err_t snmp_asn1_enc_oid(struct snmp_pbuf_stream* pbuf_stream, const u32_t *oid, u16_t oid_len); +err_t snmp_asn1_enc_s32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, s32_t value); +err_t snmp_asn1_enc_u32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, u32_t value); +err_t snmp_asn1_enc_u64t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, const u32_t* value); +err_t snmp_asn1_enc_raw(struct snmp_pbuf_stream* pbuf_stream, const u8_t *raw, u16_t raw_len); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_SNMP */ + +#endif /* LWIP_HDR_APPS_SNMP_ASN1_H */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_core.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_core.c new file mode 100644 index 0000000..c041833 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_core.c @@ -0,0 +1,1349 @@ +/** + * @file + * MIB tree access/construction functions. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons <christiaan.simons@axon.tv> + * Martin Hentschel <info@cl-soft.de> +*/ + +/** + * @defgroup snmp SNMPv2c agent + * @ingroup apps + * SNMPv2c compatible agent\n + * There is also a MIB compiler and a MIB viewer in lwIP contrib repository + * (lwip-contrib/apps/LwipMibCompiler).\n + * The agent implements the most important MIB2 MIBs including IPv6 support + * (interfaces, UDP, TCP, SNMP, ICMP, SYSTEM). IP MIB is an older version + * whithout IPv6 statistics (TODO).\n + * Rewritten by Martin Hentschel <info@cl-soft.de> and + * Dirk Ziegelmeier <dziegel@gmx.de>\n + * Work on SNMPv3 has started, but is not finished.\n + * + * 0 Agent Capabilities + * ==================== + * + * Features: + * --------- + * - SNMPv2c support. + * - Low RAM usage - no memory pools, stack only. + * - MIB2 implementation is separated from SNMP stack. + * - Support for multiple MIBs (snmp_set_mibs() call) - e.g. for private MIB. + * - Simple and generic API for MIB implementation. + * - Comfortable node types and helper functions for scalar arrays and tables. + * - Counter64, bit and truthvalue datatype support. + * - Callbacks for SNMP writes e.g. to implement persistency. + * - Runs on two APIs: RAW and netconn. + * - Async API is gone - the stack now supports netconn API instead, + * so blocking operations can be done in MIB calls. + * SNMP runs in a worker thread when netconn API is used. + * - Simplified thread sync support for MIBs - useful when MIBs + * need to access variables shared with other threads where no locking is + * possible. Used in MIB2 to access lwIP stats from lwIP thread. + * + * MIB compiler (code generator): + * ------------------------------ + * - Provided in lwIP contrib repository. + * - Written in C#. MIB viewer used Windows Forms. + * - Developed on Windows with Visual Studio 2010. + * - Can be compiled and used on all platforms with http://www.monodevelop.com/. + * - Based on a heavily modified version of of SharpSnmpLib (a4bd05c6afb4) + * (https://sharpsnmplib.codeplex.com/SourceControl/network/forks/Nemo157/MIBParserUpdate). + * - MIB parser, C file generation framework and LWIP code generation are cleanly + * separated, which means the code may be useful as a base for code generation + * of other SNMP agents. + * + * Notes: + * ------ + * - Stack and MIB compiler were used to implement a Profinet device. + * Compiled/implemented MIBs: LLDP-MIB, LLDP-EXT-DOT3-MIB, LLDP-EXT-PNO-MIB. + * + * SNMPv1 per RFC1157 and SNMPv2c per RFC 3416 + * ------------------------------------------- + * Note the S in SNMP stands for "Simple". Note that "Simple" is + * relative. SNMP is simple compared to the complex ISO network + * management protocols CMIP (Common Management Information Protocol) + * and CMOT (CMip Over Tcp). + * + * MIB II + * ------ + * The standard lwIP stack management information base. + * This is a required MIB, so this is always enabled. + * The groups EGP, CMOT and transmission are disabled by default. + * + * Most mib-2 objects are not writable except: + * sysName, sysLocation, sysContact, snmpEnableAuthenTraps. + * Writing to or changing the ARP and IP address and route + * tables is not possible. + * + * Note lwIP has a very limited notion of IP routing. It currently + * doen't have a route table and doesn't have a notion of the U,G,H flags. + * Instead lwIP uses the interface list with only one default interface + * acting as a single gateway interface (G) for the default route. + * + * The agent returns a "virtual table" with the default route 0.0.0.0 + * for the default interface and network routes (no H) for each + * network interface in the netif_list. + * All routes are considered to be up (U). + * + * Loading additional MIBs + * ----------------------- + * MIBs can only be added in compile-time, not in run-time. + * + * + * 1 Building the Agent + * ==================== + * First of all you'll need to add the following define + * to your local lwipopts.h: + * \#define LWIP_SNMP 1 + * + * and add the source files your makefile. + * + * Note you'll might need to adapt you network driver to update + * the mib2 variables for your interface. + * + * 2 Running the Agent + * =================== + * The following function calls must be made in your program to + * actually get the SNMP agent running. + * + * Before starting the agent you should supply pointers + * for sysContact, sysLocation, and snmpEnableAuthenTraps. + * You can do this by calling + * + * - snmp_mib2_set_syscontact() + * - snmp_mib2_set_syslocation() + * - snmp_set_auth_traps_enabled() + * + * You can register a callback which is called on successful write access: + * snmp_set_write_callback(). + * + * Additionally you may want to set + * + * - snmp_mib2_set_sysdescr() + * - snmp_set_device_enterprise_oid() + * - snmp_mib2_set_sysname() + * + * Also before starting the agent you need to setup + * one or more trap destinations using these calls: + * + * - snmp_trap_dst_enable() + * - snmp_trap_dst_ip_set() + * + * If you need more than MIB2, set the MIBs you want to use + * by snmp_set_mibs(). + * + * Finally, enable the agent by calling snmp_init() + * + * @defgroup snmp_core Core + * @ingroup snmp + * + * @defgroup snmp_traps Traps + * @ingroup snmp + */ + +#include "lwip/apps/snmp_opts.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/apps/snmp.h" +#include "lwip/apps/snmp_core.h" +#include "snmp_core_priv.h" +#include "lwip/netif.h" +#include <string.h> + + +#if (LWIP_SNMP && (SNMP_TRAP_DESTINATIONS<=0)) + #error "If you want to use SNMP, you have to define SNMP_TRAP_DESTINATIONS>=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_SNMP) + #error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif + +struct snmp_statistics snmp_stats; +static const struct snmp_obj_id snmp_device_enterprise_oid_default = {SNMP_DEVICE_ENTERPRISE_OID_LEN, SNMP_DEVICE_ENTERPRISE_OID}; +static const struct snmp_obj_id* snmp_device_enterprise_oid = &snmp_device_enterprise_oid_default; + +const u32_t snmp_zero_dot_zero_values[] = { 0, 0 }; +const struct snmp_obj_id_const_ref snmp_zero_dot_zero = { LWIP_ARRAYSIZE(snmp_zero_dot_zero_values), snmp_zero_dot_zero_values }; + + +#if SNMP_LWIP_MIB2 +#include "lwip/apps/snmp_mib2.h" +static const struct snmp_mib* const default_mibs[] = { &mib2 }; +static u8_t snmp_num_mibs = 1; +#else +static const struct snmp_mib* const default_mibs[] = { NULL }; +static u8_t snmp_num_mibs = 0; +#endif + +/* List of known mibs */ +static struct snmp_mib const * const *snmp_mibs = default_mibs; + +/** + * @ingroup snmp_core + * Sets the MIBs to use. + * Example: call snmp_set_mibs() as follows: + * static const struct snmp_mib *my_snmp_mibs[] = { + * &mib2, + * &private_mib + * }; + * snmp_set_mibs(my_snmp_mibs, LWIP_ARRAYSIZE(my_snmp_mibs)); + */ +void +snmp_set_mibs(const struct snmp_mib **mibs, u8_t num_mibs) +{ + LWIP_ASSERT("mibs pointer must be != NULL", (mibs != NULL)); + LWIP_ASSERT("num_mibs pointer must be != 0", (num_mibs != 0)); + snmp_mibs = mibs; + snmp_num_mibs = num_mibs; +} + +/** + * @ingroup snmp_core + * 'device enterprise oid' is used for 'device OID' field in trap PDU's (for identification of generating device) + * as well as for value returned by MIB-2 'sysObjectID' field (if internal MIB2 implementation is used). + * The 'device enterprise oid' shall point to an OID located under 'private-enterprises' branch (1.3.6.1.4.1.XXX). If a vendor + * wants to provide a custom object there, he has to get its own enterprise oid from IANA (http://www.iana.org). It + * is not allowed to use LWIP enterprise ID! + * In order to identify a specific device it is recommended to create a dedicated OID for each device type under its own + * enterprise oid. + * e.g. + * device a > 1.3.6.1.4.1.XXX(ent-oid).1(devices).1(device a) + * device b > 1.3.6.1.4.1.XXX(ent-oid).1(devices).2(device b) + * for more details see description of 'sysObjectID' field in RFC1213-MIB + */ +void snmp_set_device_enterprise_oid(const struct snmp_obj_id* device_enterprise_oid) +{ + if (device_enterprise_oid == NULL) { + snmp_device_enterprise_oid = &snmp_device_enterprise_oid_default; + } else { + snmp_device_enterprise_oid = device_enterprise_oid; + } +} + +/** + * @ingroup snmp_core + * Get 'device enterprise oid' + */ +const struct snmp_obj_id* snmp_get_device_enterprise_oid(void) +{ + return snmp_device_enterprise_oid; +} + +#if LWIP_IPV4 +/** + * Conversion from InetAddressIPv4 oid to lwIP ip4_addr + * @param oid points to u32_t ident[4] input + * @param ip points to output struct + */ +u8_t +snmp_oid_to_ip4(const u32_t *oid, ip4_addr_t *ip) +{ + if ((oid[0] > 0xFF) || + (oid[1] > 0xFF) || + (oid[2] > 0xFF) || + (oid[3] > 0xFF)) { + ip4_addr_copy(*ip, *IP4_ADDR_ANY4); + return 0; + } + + IP4_ADDR(ip, oid[0], oid[1], oid[2], oid[3]); + return 1; +} + +/** + * Convert ip4_addr to InetAddressIPv4 (no InetAddressType) + * @param ip points to input struct + * @param oid points to u32_t ident[4] output + */ +void +snmp_ip4_to_oid(const ip4_addr_t *ip, u32_t *oid) +{ + oid[0] = ip4_addr1(ip); + oid[1] = ip4_addr2(ip); + oid[2] = ip4_addr3(ip); + oid[3] = ip4_addr4(ip); +} +#endif /* LWIP_IPV4 */ + +#if LWIP_IPV6 +/** + * Conversion from InetAddressIPv6 oid to lwIP ip6_addr + * @param oid points to u32_t oid[16] input + * @param ip points to output struct + */ +u8_t +snmp_oid_to_ip6(const u32_t *oid, ip6_addr_t *ip) +{ + if ((oid[0] > 0xFF) || + (oid[1] > 0xFF) || + (oid[2] > 0xFF) || + (oid[3] > 0xFF) || + (oid[4] > 0xFF) || + (oid[5] > 0xFF) || + (oid[6] > 0xFF) || + (oid[7] > 0xFF) || + (oid[8] > 0xFF) || + (oid[9] > 0xFF) || + (oid[10] > 0xFF) || + (oid[11] > 0xFF) || + (oid[12] > 0xFF) || + (oid[13] > 0xFF) || + (oid[14] > 0xFF) || + (oid[15] > 0xFF)) { + ip6_addr_set_any(ip); + return 0; + } + + ip->addr[0] = (oid[0] << 24) | (oid[1] << 16) | (oid[2] << 8) | (oid[3] << 0); + ip->addr[1] = (oid[4] << 24) | (oid[5] << 16) | (oid[6] << 8) | (oid[7] << 0); + ip->addr[2] = (oid[8] << 24) | (oid[9] << 16) | (oid[10] << 8) | (oid[11] << 0); + ip->addr[3] = (oid[12] << 24) | (oid[13] << 16) | (oid[14] << 8) | (oid[15] << 0); + return 1; +} + +/** + * Convert ip6_addr to InetAddressIPv6 (no InetAddressType) + * @param ip points to input struct + * @param oid points to u32_t ident[16] output + */ +void +snmp_ip6_to_oid(const ip6_addr_t *ip, u32_t *oid) +{ + oid[0] = (ip->addr[0] & 0xFF000000) >> 24; + oid[1] = (ip->addr[0] & 0x00FF0000) >> 16; + oid[2] = (ip->addr[0] & 0x0000FF00) >> 8; + oid[3] = (ip->addr[0] & 0x000000FF) >> 0; + oid[4] = (ip->addr[1] & 0xFF000000) >> 24; + oid[5] = (ip->addr[1] & 0x00FF0000) >> 16; + oid[6] = (ip->addr[1] & 0x0000FF00) >> 8; + oid[7] = (ip->addr[1] & 0x000000FF) >> 0; + oid[8] = (ip->addr[2] & 0xFF000000) >> 24; + oid[9] = (ip->addr[2] & 0x00FF0000) >> 16; + oid[10] = (ip->addr[2] & 0x0000FF00) >> 8; + oid[11] = (ip->addr[2] & 0x000000FF) >> 0; + oid[12] = (ip->addr[3] & 0xFF000000) >> 24; + oid[13] = (ip->addr[3] & 0x00FF0000) >> 16; + oid[14] = (ip->addr[3] & 0x0000FF00) >> 8; + oid[15] = (ip->addr[3] & 0x000000FF) >> 0; +} +#endif /* LWIP_IPV6 */ + +#if LWIP_IPV4 || LWIP_IPV6 +/** + * Convert to InetAddressType+InetAddress+InetPortNumber + * @param ip IP address + * @param port Port + * @param oid OID + * @return OID length + */ +u8_t +snmp_ip_port_to_oid(const ip_addr_t *ip, u16_t port, u32_t *oid) +{ + u8_t idx; + + idx = snmp_ip_to_oid(ip, oid); + oid[idx] = port; + idx++; + + return idx; +} + +/** + * Convert to InetAddressType+InetAddress + * @param ip IP address + * @param oid OID + * @return OID length + */ +u8_t +snmp_ip_to_oid(const ip_addr_t *ip, u32_t *oid) +{ + if (IP_IS_ANY_TYPE_VAL(*ip)) { + oid[0] = 0; /* any */ + oid[1] = 0; /* no IP OIDs follow */ + return 2; + } else if (IP_IS_V6(ip)) { +#if LWIP_IPV6 + oid[0] = 2; /* ipv6 */ + oid[1] = 16; /* 16 InetAddressIPv6 OIDs follow */ + snmp_ip6_to_oid(ip_2_ip6(ip), &oid[2]); + return 18; +#else /* LWIP_IPV6 */ + return 0; +#endif /* LWIP_IPV6 */ + } else { +#if LWIP_IPV4 + oid[0] = 1; /* ipv4 */ + oid[1] = 4; /* 4 InetAddressIPv4 OIDs follow */ + snmp_ip4_to_oid(ip_2_ip4(ip), &oid[2]); + return 6; +#else /* LWIP_IPV4 */ + return 0; +#endif /* LWIP_IPV4 */ + } +} + +/** + * Convert from InetAddressType+InetAddress to ip_addr_t + * @param oid OID + * @param oid_len OID length + * @param ip IP address + * @return Parsed OID length + */ +u8_t +snmp_oid_to_ip(const u32_t *oid, u8_t oid_len, ip_addr_t *ip) +{ + /* InetAddressType */ + if (oid_len < 1) { + return 0; + } + + if (oid[0] == 0) { /* any */ + /* 1x InetAddressType, 1x OID len */ + if (oid_len < 2) { + return 0; + } + if (oid[1] != 0) { + return 0; + } + + memset(ip, 0, sizeof(*ip)); + IP_SET_TYPE(ip, IPADDR_TYPE_ANY); + + return 2; + } else if (oid[0] == 1) { /* ipv4 */ +#if LWIP_IPV4 + /* 1x InetAddressType, 1x OID len, 4x InetAddressIPv4 */ + if (oid_len < 6) { + return 0; + } + + /* 4x ipv4 OID */ + if (oid[1] != 4) { + return 0; + } + + IP_SET_TYPE(ip, IPADDR_TYPE_V4); + if (!snmp_oid_to_ip4(&oid[2], ip_2_ip4(ip))) { + return 0; + } + + return 6; +#else /* LWIP_IPV4 */ + return 0; +#endif /* LWIP_IPV4 */ + } else if (oid[0] == 2) { /* ipv6 */ +#if LWIP_IPV6 + /* 1x InetAddressType, 1x OID len, 16x InetAddressIPv6 */ + if (oid_len < 18) { + return 0; + } + + /* 16x ipv6 OID */ + if (oid[1] != 16) { + return 0; + } + + IP_SET_TYPE(ip, IPADDR_TYPE_V6); + if (!snmp_oid_to_ip6(&oid[2], ip_2_ip6(ip))) { + return 0; + } + + return 18; +#else /* LWIP_IPV6 */ + return 0; +#endif /* LWIP_IPV6 */ + } else { /* unsupported InetAddressType */ + return 0; + } +} + +/** + * Convert from InetAddressType+InetAddress+InetPortNumber to ip_addr_t and u16_t + * @param oid OID + * @param oid_len OID length + * @param ip IP address + * @param port Port + * @return Parsed OID length + */ +u8_t +snmp_oid_to_ip_port(const u32_t *oid, u8_t oid_len, ip_addr_t *ip, u16_t *port) +{ + u8_t idx = 0; + + /* InetAddressType + InetAddress */ + idx += snmp_oid_to_ip(&oid[idx], oid_len-idx, ip); + if (idx == 0) { + return 0; + } + + /* InetPortNumber */ + if (oid_len < (idx+1)) { + return 0; + } + if (oid[idx] > 0xffff) { + return 0; + } + *port = (u16_t)oid[idx]; + idx++; + + return idx; +} + +#endif /* LWIP_IPV4 || LWIP_IPV6 */ + +/** + * Assign an OID to struct snmp_obj_id + * @param target Assignment target + * @param oid OID + * @param oid_len OID length + */ +void +snmp_oid_assign(struct snmp_obj_id* target, const u32_t *oid, u8_t oid_len) +{ + LWIP_ASSERT("oid_len <= LWIP_SNMP_OBJ_ID_LEN", oid_len <= SNMP_MAX_OBJ_ID_LEN); + + target->len = oid_len; + + if (oid_len > 0) { + MEMCPY(target->id, oid, oid_len * sizeof(u32_t)); + } +} + +/** + * Prefix an OID to OID in struct snmp_obj_id + * @param target Assignment target to prefix + * @param oid OID + * @param oid_len OID length + */ +void +snmp_oid_prefix(struct snmp_obj_id* target, const u32_t *oid, u8_t oid_len) +{ + LWIP_ASSERT("target->len + oid_len <= LWIP_SNMP_OBJ_ID_LEN", (target->len + oid_len) <= SNMP_MAX_OBJ_ID_LEN); + + if (oid_len > 0) { + /* move existing OID to make room at the beginning for OID to insert */ + int i; + for (i = target->len-1; i>=0; i--) { + target->id[i + oid_len] = target->id[i]; + } + + /* paste oid at the beginning */ + MEMCPY(target->id, oid, oid_len * sizeof(u32_t)); + } +} + +/** + * Combine two OIDs into struct snmp_obj_id + * @param target Assignmet target + * @param oid1 OID 1 + * @param oid1_len OID 1 length + * @param oid2 OID 2 + * @param oid2_len OID 2 length + */ +void +snmp_oid_combine(struct snmp_obj_id* target, const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len) +{ + snmp_oid_assign(target, oid1, oid1_len); + snmp_oid_append(target, oid2, oid2_len); +} + +/** + * Append OIDs to struct snmp_obj_id + * @param target Assignment target to append to + * @param oid OID + * @param oid_len OID length + */ +void +snmp_oid_append(struct snmp_obj_id* target, const u32_t *oid, u8_t oid_len) +{ + LWIP_ASSERT("offset + oid_len <= LWIP_SNMP_OBJ_ID_LEN", (target->len + oid_len) <= SNMP_MAX_OBJ_ID_LEN); + + if (oid_len > 0) { + MEMCPY(&target->id[target->len], oid, oid_len * sizeof(u32_t)); + target->len += oid_len; + } +} + +/** + * Compare two OIDs + * @param oid1 OID 1 + * @param oid1_len OID 1 length + * @param oid2 OID 2 + * @param oid2_len OID 2 length + * @return -1: OID1<OID2 1: OID1 >OID2 0: equal + */ +s8_t +snmp_oid_compare(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len) +{ + u8_t level = 0; + LWIP_ASSERT("'oid1' param must not be NULL or 'oid1_len' param be 0!", (oid1 != NULL) || (oid1_len == 0)); + LWIP_ASSERT("'oid2' param must not be NULL or 'oid2_len' param be 0!", (oid2 != NULL) || (oid2_len == 0)); + + while ((level < oid1_len) && (level < oid2_len)) { + if (*oid1 < *oid2) { + return -1; + } + if (*oid1 > *oid2) { + return 1; + } + + level++; + oid1++; + oid2++; + } + + /* common part of both OID's is equal, compare length */ + if (oid1_len < oid2_len) { + return -1; + } + if (oid1_len > oid2_len) { + return 1; + } + + /* they are equal */ + return 0; +} + + +/** + * Check of two OIDs are equal + * @param oid1 OID 1 + * @param oid1_len OID 1 length + * @param oid2 OID 2 + * @param oid2_len OID 2 length + * @return 1: equal 0: non-equal + */ +u8_t +snmp_oid_equal(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len) +{ + return (snmp_oid_compare(oid1, oid1_len, oid2, oid2_len) == 0)? 1 : 0; +} + +/** + * Convert netif to interface index + * @param netif netif + * @return index + */ +u8_t +netif_to_num(const struct netif *netif) +{ + u8_t result = 0; + struct netif *netif_iterator = netif_list; + + while (netif_iterator != NULL) { + result++; + + if (netif_iterator == netif) { + return result; + } + + netif_iterator = netif_iterator->next; + } + + LWIP_ASSERT("netif not found in netif_list", 0); + return 0; +} + +static const struct snmp_mib* +snmp_get_mib_from_oid(const u32_t *oid, u8_t oid_len) +{ + const u32_t* list_oid; + const u32_t* searched_oid; + u8_t i, l; + + u8_t max_match_len = 0; + const struct snmp_mib* matched_mib = NULL; + + LWIP_ASSERT("'oid' param must not be NULL!", (oid != NULL)); + + if (oid_len == 0) { + return NULL; + } + + for (i = 0; i < snmp_num_mibs; i++) { + LWIP_ASSERT("MIB array not initialized correctly", (snmp_mibs[i] != NULL)); + LWIP_ASSERT("MIB array not initialized correctly - base OID is NULL", (snmp_mibs[i]->base_oid != NULL)); + + if (oid_len >= snmp_mibs[i]->base_oid_len) { + l = snmp_mibs[i]->base_oid_len; + list_oid = snmp_mibs[i]->base_oid; + searched_oid = oid; + + while (l > 0) { + if (*list_oid != *searched_oid) { + break; + } + + l--; + list_oid++; + searched_oid++; + } + + if ((l == 0) && (snmp_mibs[i]->base_oid_len > max_match_len)) { + max_match_len = snmp_mibs[i]->base_oid_len; + matched_mib = snmp_mibs[i]; + } + } + } + + return matched_mib; +} + +static const struct snmp_mib* +snmp_get_next_mib(const u32_t *oid, u8_t oid_len) +{ + u8_t i; + const struct snmp_mib* next_mib = NULL; + + LWIP_ASSERT("'oid' param must not be NULL!", (oid != NULL)); + + if (oid_len == 0) { + return NULL; + } + + for (i = 0; i < snmp_num_mibs; i++) { + if (snmp_mibs[i]->base_oid != NULL) { + /* check if mib is located behind starting point */ + if (snmp_oid_compare(snmp_mibs[i]->base_oid, snmp_mibs[i]->base_oid_len, oid, oid_len) > 0) { + if ((next_mib == NULL) || + (snmp_oid_compare(snmp_mibs[i]->base_oid, snmp_mibs[i]->base_oid_len, + next_mib->base_oid, next_mib->base_oid_len) < 0)) { + next_mib = snmp_mibs[i]; + } + } + } + } + + return next_mib; +} + +static const struct snmp_mib* +snmp_get_mib_between(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len) +{ + const struct snmp_mib* next_mib = snmp_get_next_mib(oid1, oid1_len); + + LWIP_ASSERT("'oid2' param must not be NULL!", (oid2 != NULL)); + LWIP_ASSERT("'oid2_len' param must be greater than 0!", (oid2_len > 0)); + + if (next_mib != NULL) { + if (snmp_oid_compare(next_mib->base_oid, next_mib->base_oid_len, oid2, oid2_len) < 0) { + return next_mib; + } + } + + return NULL; +} + +u8_t +snmp_get_node_instance_from_oid(const u32_t *oid, u8_t oid_len, struct snmp_node_instance* node_instance) +{ + u8_t result = SNMP_ERR_NOSUCHOBJECT; + const struct snmp_mib *mib; + const struct snmp_node *mn = NULL; + + mib = snmp_get_mib_from_oid(oid, oid_len); + if (mib != NULL) { + u8_t oid_instance_len; + + mn = snmp_mib_tree_resolve_exact(mib, oid, oid_len, &oid_instance_len); + if ((mn != NULL) && (mn->node_type != SNMP_NODE_TREE)) { + /* get instance */ + const struct snmp_leaf_node* leaf_node = (const struct snmp_leaf_node*)(const void*)mn; + + node_instance->node = mn; + snmp_oid_assign(&node_instance->instance_oid, oid + (oid_len - oid_instance_len), oid_instance_len); + + result = leaf_node->get_instance( + oid, + oid_len - oid_instance_len, + node_instance); + +#ifdef LWIP_DEBUG + if (result == SNMP_ERR_NOERROR) { + if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_READ) != 0) && (node_instance->get_value == NULL)) { + LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is readable but no get_value function is specified\n")); + } + if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_WRITE) != 0) && (node_instance->set_value == NULL)) { + LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is writable but no set_value and/or set_test function is specified\n")); + } + } +#endif + } + } + + return result; +} + +u8_t +snmp_get_next_node_instance_from_oid(const u32_t *oid, u8_t oid_len, snmp_validate_node_instance_method validate_node_instance_method, void* validate_node_instance_arg, struct snmp_obj_id* node_oid, struct snmp_node_instance* node_instance) +{ + const struct snmp_mib *mib; + const struct snmp_node *mn = NULL; + const u32_t* start_oid = NULL; + u8_t start_oid_len = 0; + + /* resolve target MIB from passed OID */ + mib = snmp_get_mib_from_oid(oid, oid_len); + if (mib == NULL) { + /* passed OID does not reference any known MIB, start at the next closest MIB */ + mib = snmp_get_next_mib(oid, oid_len); + + if (mib != NULL) { + start_oid = mib->base_oid; + start_oid_len = mib->base_oid_len; + } + } else { + start_oid = oid; + start_oid_len = oid_len; + } + + /* resolve target node from MIB, skip to next MIB if no suitable node is found in current MIB */ + while ((mib != NULL) && (mn == NULL)) { + u8_t oid_instance_len; + + /* check if OID directly references a node inside current MIB, in this case we have to ask this node for the next instance */ + mn = snmp_mib_tree_resolve_exact(mib, start_oid, start_oid_len, &oid_instance_len); + if (mn != NULL) { + snmp_oid_assign(node_oid, start_oid, start_oid_len - oid_instance_len); /* set oid to node */ + snmp_oid_assign(&node_instance->instance_oid, start_oid + (start_oid_len - oid_instance_len), oid_instance_len); /* set (relative) instance oid */ + } else { + /* OID does not reference a node, search for the next closest node inside MIB; set instance_oid.len to zero because we want the first instance of this node */ + mn = snmp_mib_tree_resolve_next(mib, start_oid, start_oid_len, node_oid); + node_instance->instance_oid.len = 0; + } + + /* validate the node; if the node has no further instance or the returned instance is invalid, search for the next in MIB and validate again */ + node_instance->node = mn; + while (mn != NULL) { + u8_t result; + + /* clear fields which may have values from previous loops */ + node_instance->asn1_type = 0; + node_instance->access = SNMP_NODE_INSTANCE_NOT_ACCESSIBLE; + node_instance->get_value = NULL; + node_instance->set_test = NULL; + node_instance->set_value = NULL; + node_instance->release_instance = NULL; + node_instance->reference.ptr = NULL; + node_instance->reference_len = 0; + + result = ((const struct snmp_leaf_node*)(const void*)mn)->get_next_instance( + node_oid->id, + node_oid->len, + node_instance); + + if (result == SNMP_ERR_NOERROR) { +#ifdef LWIP_DEBUG + if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_READ) != 0) && (node_instance->get_value == NULL)) { + LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is readable but no get_value function is specified\n")); + } + if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_WRITE) != 0) && (node_instance->set_value == NULL)) { + LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is writable but no set_value function is specified\n")); + } +#endif + + /* validate node because the node may be not accessible for example (but let the caller decide what is valid */ + if ((validate_node_instance_method == NULL) || + (validate_node_instance_method(node_instance, validate_node_instance_arg) == SNMP_ERR_NOERROR)) { + /* node_oid "returns" the full result OID (including the instance part) */ + snmp_oid_append(node_oid, node_instance->instance_oid.id, node_instance->instance_oid.len); + break; + } + + if (node_instance->release_instance != NULL) { + node_instance->release_instance(node_instance); + } + /* + the instance itself is not valid, ask for next instance from same node. + we don't have to change any variables because node_instance->instance_oid is used as input (starting point) + as well as output (resulting next OID), so we have to simply call get_next_instance method again + */ + } else { + if (node_instance->release_instance != NULL) { + node_instance->release_instance(node_instance); + } + + /* the node has no further instance, skip to next node */ + mn = snmp_mib_tree_resolve_next(mib, node_oid->id, node_oid->len, &node_instance->instance_oid); /* misuse node_instance->instance_oid as tmp buffer */ + if (mn != NULL) { + /* prepare for next loop */ + snmp_oid_assign(node_oid, node_instance->instance_oid.id, node_instance->instance_oid.len); + node_instance->instance_oid.len = 0; + node_instance->node = mn; + } + } + } + + if (mn != NULL) { + /* + we found a suitable next node, + now we have to check if a inner MIB is located between the searched OID and the resulting OID. + this is possible because MIB's may be located anywhere in the global tree, that means also in + the subtree of another MIB (e.g. if searched OID is .2 and resulting OID is .4, then another + MIB having .3 as root node may exist) + */ + const struct snmp_mib *intermediate_mib; + intermediate_mib = snmp_get_mib_between(start_oid, start_oid_len, node_oid->id, node_oid->len); + + if (intermediate_mib != NULL) { + /* search for first node inside intermediate mib in next loop */ + if (node_instance->release_instance != NULL) { + node_instance->release_instance(node_instance); + } + + mn = NULL; + mib = intermediate_mib; + start_oid = mib->base_oid; + start_oid_len = mib->base_oid_len; + } + /* else { we found out target node } */ + } else { + /* + there is no further (suitable) node inside this MIB, search for the next MIB with following priority + 1. search for inner MIB's (whose root is located inside tree of current MIB) + 2. search for surrouding MIB's (where the current MIB is the inner MIB) and continue there if any + 3. take the next closest MIB (not being related to the current MIB) + */ + const struct snmp_mib *next_mib; + next_mib = snmp_get_next_mib(start_oid, start_oid_len); /* returns MIB's related to point 1 and 3 */ + + /* is the found MIB an inner MIB? (point 1) */ + if ((next_mib != NULL) && (next_mib->base_oid_len > mib->base_oid_len) && + (snmp_oid_compare(next_mib->base_oid, mib->base_oid_len, mib->base_oid, mib->base_oid_len) == 0)) { + /* yes it is -> continue at inner MIB */ + mib = next_mib; + start_oid = mib->base_oid; + start_oid_len = mib->base_oid_len; + } else { + /* check if there is a surrounding mib where to continue (point 2) (only possible if OID length > 1) */ + if (mib->base_oid_len > 1) { + mib = snmp_get_mib_from_oid(mib->base_oid, mib->base_oid_len - 1); + + if (mib == NULL) { + /* no surrounding mib, use next mib encountered above (point 3) */ + mib = next_mib; + + if (mib != NULL) { + start_oid = mib->base_oid; + start_oid_len = mib->base_oid_len; + } + } + /* else { start_oid stays the same because we want to continue from current offset in surrounding mib (point 2) } */ + } + } + } + } + + if (mib == NULL) { + /* loop is only left when mib == null (error) or mib_node != NULL (success) */ + return SNMP_ERR_ENDOFMIBVIEW; + } + + return SNMP_ERR_NOERROR; +} + +/** + * Searches tree for the supplied object identifier. + * + */ +const struct snmp_node * +snmp_mib_tree_resolve_exact(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, u8_t* oid_instance_len) +{ + const struct snmp_node* const* node = &mib->root_node; + u8_t oid_offset = mib->base_oid_len; + + while ((oid_offset < oid_len) && ((*node)->node_type == SNMP_NODE_TREE)) { + /* search for matching sub node */ + u32_t subnode_oid = *(oid + oid_offset); + + u32_t i = (*(const struct snmp_tree_node* const*)node)->subnode_count; + node = (*(const struct snmp_tree_node* const*)node)->subnodes; + while ((i > 0) && ((*node)->oid != subnode_oid)) { + node++; + i--; + } + + if (i == 0) { + /* no matching subnode found */ + return NULL; + } + + oid_offset++; + } + + if ((*node)->node_type != SNMP_NODE_TREE) { + /* we found a leaf node */ + *oid_instance_len = oid_len - oid_offset; + return (*node); + } + + return NULL; +} + +const struct snmp_node* +snmp_mib_tree_resolve_next(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, struct snmp_obj_id* oidret) +{ + u8_t oid_offset = mib->base_oid_len; + const struct snmp_node* const* node; + const struct snmp_tree_node* node_stack[SNMP_MAX_OBJ_ID_LEN]; + s32_t nsi = 0; /* NodeStackIndex */ + u32_t subnode_oid; + + if (mib->root_node->node_type != SNMP_NODE_TREE) { + /* a next operation on a mib with only a leaf node will always return NULL because there is no other node */ + return NULL; + } + + /* first build node stack related to passed oid (as far as possible), then go backwards to determine the next node */ + node_stack[nsi] = (const struct snmp_tree_node*)(const void*)mib->root_node; + while (oid_offset < oid_len) { + /* search for matching sub node */ + u32_t i = node_stack[nsi]->subnode_count; + node = node_stack[nsi]->subnodes; + + subnode_oid = *(oid + oid_offset); + + while ((i > 0) && ((*node)->oid != subnode_oid)) { + node++; + i--; + } + + if ((i == 0) || ((*node)->node_type != SNMP_NODE_TREE)) { + /* no (matching) tree-subnode found */ + break; + } + nsi++; + node_stack[nsi] = (const struct snmp_tree_node*)(const void*)(*node); + + oid_offset++; + } + + + if (oid_offset >= oid_len) { + /* passed oid references a tree node -> return first useable sub node of it */ + subnode_oid = 0; + } else { + subnode_oid = *(oid + oid_offset) + 1; + } + + while (nsi >= 0) { + const struct snmp_node* subnode = NULL; + + /* find next node on current level */ + s32_t i = node_stack[nsi]->subnode_count; + node = node_stack[nsi]->subnodes; + while (i > 0) { + if ((*node)->oid == subnode_oid) { + subnode = *node; + break; + } else if (((*node)->oid > subnode_oid) && ((subnode == NULL) || ((*node)->oid < subnode->oid))) { + subnode = *node; + } + + node++; + i--; + } + + if (subnode == NULL) { + /* no further node found on this level, go one level up and start searching with index of current node*/ + subnode_oid = node_stack[nsi]->node.oid + 1; + nsi--; + } else { + if (subnode->node_type == SNMP_NODE_TREE) { + /* next is a tree node, go into it and start searching */ + nsi++; + node_stack[nsi] = (const struct snmp_tree_node*)(const void*)subnode; + subnode_oid = 0; + } else { + /* we found a leaf node -> fill oidret and return it */ + snmp_oid_assign(oidret, mib->base_oid, mib->base_oid_len); + i = 1; + while (i <= nsi) { + oidret->id[oidret->len] = node_stack[i]->node.oid; + oidret->len++; + i++; + } + + oidret->id[oidret->len] = subnode->oid; + oidret->len++; + + return subnode; + } + } + } + + return NULL; +} + +/** initialize struct next_oid_state using this function before passing it to next_oid_check */ +void +snmp_next_oid_init(struct snmp_next_oid_state *state, + const u32_t *start_oid, u8_t start_oid_len, + u32_t *next_oid_buf, u8_t next_oid_max_len) +{ + state->start_oid = start_oid; + state->start_oid_len = start_oid_len; + state->next_oid = next_oid_buf; + state->next_oid_len = 0; + state->next_oid_max_len = next_oid_max_len; + state->status = SNMP_NEXT_OID_STATUS_NO_MATCH; +} + +/** checks if the passed incomplete OID may be a possible candidate for snmp_next_oid_check(); +this methid is intended if the complete OID is not yet known but it is very expensive to build it up, +so it is possible to test the starting part before building up the complete oid and pass it to snmp_next_oid_check()*/ +u8_t +snmp_next_oid_precheck(struct snmp_next_oid_state *state, const u32_t *oid, const u8_t oid_len) +{ + if (state->status != SNMP_NEXT_OID_STATUS_BUF_TO_SMALL) { + u8_t start_oid_len = (oid_len < state->start_oid_len) ? oid_len : state->start_oid_len; + + /* check passed OID is located behind start offset */ + if (snmp_oid_compare(oid, oid_len, state->start_oid, start_oid_len) >= 0) { + /* check if new oid is located closer to start oid than current closest oid */ + if ((state->status == SNMP_NEXT_OID_STATUS_NO_MATCH) || + (snmp_oid_compare(oid, oid_len, state->next_oid, state->next_oid_len) < 0)) { + return 1; + } + } + } + + return 0; +} + +/** checks the passed OID if it is a candidate to be the next one (get_next); returns !=0 if passed oid is currently closest, otherwise 0 */ +u8_t +snmp_next_oid_check(struct snmp_next_oid_state *state, const u32_t *oid, const u8_t oid_len, void* reference) +{ + /* do not overwrite a fail result */ + if (state->status != SNMP_NEXT_OID_STATUS_BUF_TO_SMALL) { + /* check passed OID is located behind start offset */ + if (snmp_oid_compare(oid, oid_len, state->start_oid, state->start_oid_len) > 0) { + /* check if new oid is located closer to start oid than current closest oid */ + if ((state->status == SNMP_NEXT_OID_STATUS_NO_MATCH) || + (snmp_oid_compare(oid, oid_len, state->next_oid, state->next_oid_len) < 0)) { + if (oid_len <= state->next_oid_max_len) { + MEMCPY(state->next_oid, oid, oid_len * sizeof(u32_t)); + state->next_oid_len = oid_len; + state->status = SNMP_NEXT_OID_STATUS_SUCCESS; + state->reference = reference; + return 1; + } else { + state->status = SNMP_NEXT_OID_STATUS_BUF_TO_SMALL; + } + } + } + } + + return 0; +} + +u8_t +snmp_oid_in_range(const u32_t *oid_in, u8_t oid_len, const struct snmp_oid_range *oid_ranges, u8_t oid_ranges_len) +{ + u8_t i; + + if (oid_len != oid_ranges_len) { + return 0; + } + + for (i = 0; i < oid_ranges_len; i++) { + if ((oid_in[i] < oid_ranges[i].min) || (oid_in[i] > oid_ranges[i].max)) { + return 0; + } + } + + return 1; +} + +snmp_err_t +snmp_set_test_ok(struct snmp_node_instance* instance, u16_t value_len, void* value) +{ + LWIP_UNUSED_ARG(instance); + LWIP_UNUSED_ARG(value_len); + LWIP_UNUSED_ARG(value); + + return SNMP_ERR_NOERROR; +} + +/** + * Decodes BITS pseudotype value from ASN.1 OctetString. + * + * @note Because BITS pseudo type is encoded as OCTET STRING, it cannot directly + * be encoded/decoded by the agent. Instead call this function as required from + * get/test/set methods. + * + * @param buf points to a buffer holding the ASN1 octet string + * @param buf_len length of octet string + * @param bit_value decoded Bit value with Bit0 == LSB + * @return ERR_OK if successful, ERR_ARG if bit value contains more than 32 bit + */ +err_t +snmp_decode_bits(const u8_t *buf, u32_t buf_len, u32_t *bit_value) +{ + u8_t b; + u8_t bits_processed = 0; + *bit_value = 0; + + while (buf_len > 0) { + /* any bit set in this byte? */ + if (*buf != 0x00) { + if (bits_processed >= 32) { + /* accept more than 4 bytes, but only when no bits are set */ + return ERR_VAL; + } + + b = *buf; + do { + if (b & 0x80) { + *bit_value |= (1 << bits_processed); + } + bits_processed++; + b <<= 1; + } + while ((bits_processed & 0x07) != 0); /* &0x07 -> % 8 */ + } else { + bits_processed += 8; + } + + buf_len--; + buf++; + } + + return ERR_OK; +} + +err_t +snmp_decode_truthvalue(const s32_t *asn1_value, u8_t *bool_value) +{ + /* defined by RFC1443: + TruthValue ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "Represents a boolean value." + SYNTAX INTEGER { true(1), false(2) } + */ + + if ((asn1_value == NULL) || (bool_value == NULL)) { + return ERR_ARG; + } + + if (*asn1_value == 1) { + *bool_value = 1; + } else if (*asn1_value == 2) { + *bool_value = 0; + } else { + return ERR_VAL; + } + + return ERR_OK; +} + +/** + * Encodes BITS pseudotype value into ASN.1 OctetString. + * + * @note Because BITS pseudo type is encoded as OCTET STRING, it cannot directly + * be encoded/decoded by the agent. Instead call this function as required from + * get/test/set methods. + * + * @param buf points to a buffer where the resulting ASN1 octet string is stored to + * @param buf_len max length of the bufffer + * @param bit_value Bit value to encode with Bit0 == LSB + * @param bit_count Number of possible bits for the bit value (according to rfc we have to send all bits independant from their truth value) + * @return number of bytes used from buffer to store the resulting OctetString + */ +u8_t +snmp_encode_bits(u8_t *buf, u32_t buf_len, u32_t bit_value, u8_t bit_count) +{ + u8_t len = 0; + u8_t min_bytes = (bit_count + 7) >> 3; /* >>3 -> / 8 */ + + while ((buf_len > 0) && (bit_value != 0x00)) { + s8_t i = 7; + *buf = 0x00; + while (i >= 0) { + if (bit_value & 0x01) { + *buf |= 0x01; + } + + if (i > 0) { + *buf <<= 1; + } + + bit_value >>= 1; + i--; + } + + buf++; + buf_len--; + len++; + } + + if (len < min_bytes) { + buf += len; + buf_len -= len; + + while ((len < min_bytes) && (buf_len > 0)) { + *buf = 0x00; + buf++; + buf_len--; + len++; + } + } + + return len; +} + +u8_t +snmp_encode_truthvalue(s32_t *asn1_value, u32_t bool_value) +{ + /* defined by RFC1443: + TruthValue ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "Represents a boolean value." + SYNTAX INTEGER { true(1), false(2) } + */ + + if (asn1_value == NULL) { + return 0; + } + + if (bool_value) { + *asn1_value = 1; /* defined by RFC1443 */ + } else { + *asn1_value = 2; /* defined by RFC1443 */ + } + + return sizeof(s32_t); +} + +#endif /* LWIP_SNMP */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_core_priv.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_core_priv.h new file mode 100644 index 0000000..5552177 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_core_priv.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel <info@cl-soft.de> + * + */ + +#ifndef LWIP_HDR_APPS_SNMP_CORE_PRIV_H +#define LWIP_HDR_APPS_SNMP_CORE_PRIV_H + +#include "lwip/apps/snmp_opts.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/apps/snmp_core.h" +#include "snmp_asn1.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* (outdated) SNMPv1 error codes + * shall not be used by MIBS anymore, nevertheless required from core for properly answering a v1 request + */ +#define SNMP_ERR_NOSUCHNAME 2 +#define SNMP_ERR_BADVALUE 3 +#define SNMP_ERR_READONLY 4 +/* error codes which are internal and shall not be used by MIBS + * shall not be used by MIBS anymore, nevertheless required from core for properly answering a v1 request + */ +#define SNMP_ERR_TOOBIG 1 +#define SNMP_ERR_AUTHORIZATIONERROR 16 +#define SNMP_ERR_NOSUCHOBJECT SNMP_VARBIND_EXCEPTION_OFFSET + SNMP_ASN1_CONTEXT_VARBIND_NO_SUCH_OBJECT +#define SNMP_ERR_ENDOFMIBVIEW SNMP_VARBIND_EXCEPTION_OFFSET + SNMP_ASN1_CONTEXT_VARBIND_END_OF_MIB_VIEW + + +const struct snmp_node* snmp_mib_tree_resolve_exact(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, u8_t* oid_instance_len); +const struct snmp_node* snmp_mib_tree_resolve_next(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, struct snmp_obj_id* oidret); + +typedef u8_t (*snmp_validate_node_instance_method)(struct snmp_node_instance*, void*); + +u8_t snmp_get_node_instance_from_oid(const u32_t *oid, u8_t oid_len, struct snmp_node_instance* node_instance); +u8_t snmp_get_next_node_instance_from_oid(const u32_t *oid, u8_t oid_len, snmp_validate_node_instance_method validate_node_instance_method, void* validate_node_instance_arg, struct snmp_obj_id* node_oid, struct snmp_node_instance* node_instance); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_SNMP */ + +#endif /* LWIP_HDR_APPS_SNMP_CORE_PRIV_H */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2.c new file mode 100644 index 0000000..9d8c43c --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2.c @@ -0,0 +1,116 @@ +/** + * @file + * Management Information Base II (RFC1213) objects and functions. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Dirk Ziegelmeier <dziegel@gmx.de> + * Christiaan Simons <christiaan.simons@axon.tv> + */ + +/** + * @defgroup snmp_mib2 MIB2 + * @ingroup snmp + */ + +#include "lwip/apps/snmp_opts.h" + +#if LWIP_SNMP && SNMP_LWIP_MIB2 /* don't build if not configured for use in lwipopts.h */ + +#if !LWIP_STATS +#error LWIP_SNMP MIB2 needs LWIP_STATS (for MIB2) +#endif +#if !MIB2_STATS +#error LWIP_SNMP MIB2 needs MIB2_STATS (for MIB2) +#endif + +#include "lwip/snmp.h" +#include "lwip/apps/snmp.h" +#include "lwip/apps/snmp_core.h" +#include "lwip/apps/snmp_mib2.h" +#include "lwip/apps/snmp_scalar.h" + +#if SNMP_USE_NETCONN +#include "lwip/tcpip.h" +#include "lwip/priv/tcpip_priv.h" +void +snmp_mib2_lwip_synchronizer(snmp_threadsync_called_fn fn, void* arg) +{ +#if LWIP_TCPIP_CORE_LOCKING + LOCK_TCPIP_CORE(); + fn(arg); + UNLOCK_TCPIP_CORE(); +#else + tcpip_callback(fn, arg); +#endif +} + +struct snmp_threadsync_instance snmp_mib2_lwip_locks; +#endif + +/* dot3 and EtherLike MIB not planned. (transmission .1.3.6.1.2.1.10) */ +/* historical (some say hysterical). (cmot .1.3.6.1.2.1.9) */ +/* lwIP has no EGP, thus may not implement it. (egp .1.3.6.1.2.1.8) */ + +/* --- mib-2 .1.3.6.1.2.1 ----------------------------------------------------- */ +extern const struct snmp_scalar_array_node snmp_mib2_snmp_root; +extern const struct snmp_tree_node snmp_mib2_udp_root; +extern const struct snmp_tree_node snmp_mib2_tcp_root; +extern const struct snmp_scalar_array_node snmp_mib2_icmp_root; +extern const struct snmp_tree_node snmp_mib2_interface_root; +extern const struct snmp_scalar_array_node snmp_mib2_system_node; +extern const struct snmp_tree_node snmp_mib2_at_root; +extern const struct snmp_tree_node snmp_mib2_ip_root; + +static const struct snmp_node* const mib2_nodes[] = { + &snmp_mib2_system_node.node.node, + &snmp_mib2_interface_root.node, +#if LWIP_ARP && LWIP_IPV4 + &snmp_mib2_at_root.node, +#endif /* LWIP_ARP && LWIP_IPV4 */ +#if LWIP_IPV4 + &snmp_mib2_ip_root.node, +#endif /* LWIP_IPV4 */ +#if LWIP_ICMP + &snmp_mib2_icmp_root.node.node, +#endif /* LWIP_ICMP */ +#if LWIP_TCP + &snmp_mib2_tcp_root.node, +#endif /* LWIP_TCP */ +#if LWIP_UDP + &snmp_mib2_udp_root.node, +#endif /* LWIP_UDP */ + &snmp_mib2_snmp_root.node.node +}; + +static const struct snmp_tree_node mib2_root = SNMP_CREATE_TREE_NODE(1, mib2_nodes); + +static const u32_t mib2_base_oid_arr[] = { 1,3,6,1,2,1 }; +const struct snmp_mib mib2 = SNMP_MIB_CREATE(mib2_base_oid_arr, &mib2_root.node); + +#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_icmp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_icmp.c new file mode 100644 index 0000000..995bd32 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_icmp.c @@ -0,0 +1,182 @@ +/** + * @file + * Management Information Base II (RFC1213) ICMP objects and functions. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Dirk Ziegelmeier <dziegel@gmx.de> + * Christiaan Simons <christiaan.simons@axon.tv> + */ + +#include "lwip/snmp.h" +#include "lwip/apps/snmp.h" +#include "lwip/apps/snmp_core.h" +#include "lwip/apps/snmp_mib2.h" +#include "lwip/apps/snmp_table.h" +#include "lwip/apps/snmp_scalar.h" +#include "lwip/icmp.h" +#include "lwip/stats.h" + +#if LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_ICMP + +#if SNMP_USE_NETCONN +#define SYNC_NODE_NAME(node_name) node_name ## _synced +#define CREATE_LWIP_SYNC_NODE(oid, node_name) \ + static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks); +#else +#define SYNC_NODE_NAME(node_name) node_name +#define CREATE_LWIP_SYNC_NODE(oid, node_name) +#endif + +/* --- icmp .1.3.6.1.2.1.5 ----------------------------------------------------- */ + +static s16_t +icmp_get_value(const struct snmp_scalar_array_node_def *node, void *value) +{ + u32_t *uint_ptr = (u32_t*)value; + + switch (node->oid) { + case 1: /* icmpInMsgs */ + *uint_ptr = STATS_GET(mib2.icmpinmsgs); + return sizeof(*uint_ptr); + case 2: /* icmpInErrors */ + *uint_ptr = STATS_GET(mib2.icmpinerrors); + return sizeof(*uint_ptr); + case 3: /* icmpInDestUnreachs */ + *uint_ptr = STATS_GET(mib2.icmpindestunreachs); + return sizeof(*uint_ptr); + case 4: /* icmpInTimeExcds */ + *uint_ptr = STATS_GET(mib2.icmpintimeexcds); + return sizeof(*uint_ptr); + case 5: /* icmpInParmProbs */ + *uint_ptr = STATS_GET(mib2.icmpinparmprobs); + return sizeof(*uint_ptr); + case 6: /* icmpInSrcQuenchs */ + *uint_ptr = STATS_GET(mib2.icmpinsrcquenchs); + return sizeof(*uint_ptr); + case 7: /* icmpInRedirects */ + *uint_ptr = STATS_GET(mib2.icmpinredirects); + return sizeof(*uint_ptr); + case 8: /* icmpInEchos */ + *uint_ptr = STATS_GET(mib2.icmpinechos); + return sizeof(*uint_ptr); + case 9: /* icmpInEchoReps */ + *uint_ptr = STATS_GET(mib2.icmpinechoreps); + return sizeof(*uint_ptr); + case 10: /* icmpInTimestamps */ + *uint_ptr = STATS_GET(mib2.icmpintimestamps); + return sizeof(*uint_ptr); + case 11: /* icmpInTimestampReps */ + *uint_ptr = STATS_GET(mib2.icmpintimestampreps); + return sizeof(*uint_ptr); + case 12: /* icmpInAddrMasks */ + *uint_ptr = STATS_GET(mib2.icmpinaddrmasks); + return sizeof(*uint_ptr); + case 13: /* icmpInAddrMaskReps */ + *uint_ptr = STATS_GET(mib2.icmpinaddrmaskreps); + return sizeof(*uint_ptr); + case 14: /* icmpOutMsgs */ + *uint_ptr = STATS_GET(mib2.icmpoutmsgs); + return sizeof(*uint_ptr); + case 15: /* icmpOutErrors */ + *uint_ptr = STATS_GET(mib2.icmpouterrors); + return sizeof(*uint_ptr); + case 16: /* icmpOutDestUnreachs */ + *uint_ptr = STATS_GET(mib2.icmpoutdestunreachs); + return sizeof(*uint_ptr); + case 17: /* icmpOutTimeExcds */ + *uint_ptr = STATS_GET(mib2.icmpouttimeexcds); + return sizeof(*uint_ptr); + case 18: /* icmpOutParmProbs: not supported -> always 0 */ + *uint_ptr = 0; + return sizeof(*uint_ptr); + case 19: /* icmpOutSrcQuenchs: not supported -> always 0 */ + *uint_ptr = 0; + return sizeof(*uint_ptr); + case 20: /* icmpOutRedirects: not supported -> always 0 */ + *uint_ptr = 0; + return sizeof(*uint_ptr); + case 21: /* icmpOutEchos */ + *uint_ptr = STATS_GET(mib2.icmpoutechos); + return sizeof(*uint_ptr); + case 22: /* icmpOutEchoReps */ + *uint_ptr = STATS_GET(mib2.icmpoutechoreps); + return sizeof(*uint_ptr); + case 23: /* icmpOutTimestamps: not supported -> always 0 */ + *uint_ptr = 0; + return sizeof(*uint_ptr); + case 24: /* icmpOutTimestampReps: not supported -> always 0 */ + *uint_ptr = 0; + return sizeof(*uint_ptr); + case 25: /* icmpOutAddrMasks: not supported -> always 0 */ + *uint_ptr = 0; + return sizeof(*uint_ptr); + case 26: /* icmpOutAddrMaskReps: not supported -> always 0 */ + *uint_ptr = 0; + return sizeof(*uint_ptr); + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("icmp_get_value(): unknown id: %"S32_F"\n", node->oid)); + break; + } + + return 0; +} + + +static const struct snmp_scalar_array_node_def icmp_nodes[] = { + { 1, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + { 2, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + { 3, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + { 4, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + { 5, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + { 6, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + { 7, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + { 8, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + { 9, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + {10, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + {11, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + {12, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + {13, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + {14, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + {15, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + {16, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + {17, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + {18, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + {19, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + {20, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + {21, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + {22, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + {23, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + {24, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + {25, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + {26, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY} +}; + +const struct snmp_scalar_array_node snmp_mib2_icmp_root = SNMP_SCALAR_CREATE_ARRAY_NODE(5, icmp_nodes, icmp_get_value, NULL, NULL); + +#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_ICMP */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_interfaces.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_interfaces.c new file mode 100644 index 0000000..979b507 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_interfaces.c @@ -0,0 +1,375 @@ +/** + * @file + * Management Information Base II (RFC1213) INTERFACES objects and functions. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Dirk Ziegelmeier <dziegel@gmx.de> + * Christiaan Simons <christiaan.simons@axon.tv> + */ + +#include "lwip/snmp.h" +#include "lwip/apps/snmp.h" +#include "lwip/apps/snmp_core.h" +#include "lwip/apps/snmp_mib2.h" +#include "lwip/apps/snmp_table.h" +#include "lwip/apps/snmp_scalar.h" +#include "lwip/netif.h" +#include "lwip/stats.h" + +#include <string.h> + +#if LWIP_SNMP && SNMP_LWIP_MIB2 + +#if SNMP_USE_NETCONN +#define SYNC_NODE_NAME(node_name) node_name ## _synced +#define CREATE_LWIP_SYNC_NODE(oid, node_name) \ + static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks); +#else +#define SYNC_NODE_NAME(node_name) node_name +#define CREATE_LWIP_SYNC_NODE(oid, node_name) +#endif + + +/* --- interfaces .1.3.6.1.2.1.2 ----------------------------------------------------- */ + +static s16_t +interfaces_get_value(struct snmp_node_instance* instance, void* value) +{ + if (instance->node->oid == 1) { + s32_t *sint_ptr = (s32_t*)value; + s32_t num_netifs = 0; + + struct netif *netif = netif_list; + while (netif != NULL) { + num_netifs++; + netif = netif->next; + } + + *sint_ptr = num_netifs; + return sizeof(*sint_ptr); + } + + return 0; +} + +/* list of allowed value ranges for incoming OID */ +static const struct snmp_oid_range interfaces_Table_oid_ranges[] = { + { 1, 0xff } /* netif->num is u8_t */ +}; + +static const u8_t iftable_ifOutQLen = 0; + +static const u8_t iftable_ifOperStatus_up = 1; +static const u8_t iftable_ifOperStatus_down = 2; + +static const u8_t iftable_ifAdminStatus_up = 1; +static const u8_t iftable_ifAdminStatus_lowerLayerDown = 7; +static const u8_t iftable_ifAdminStatus_down = 2; + +static snmp_err_t +interfaces_Table_get_cell_instance(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, struct snmp_node_instance* cell_instance) +{ + u32_t ifIndex; + struct netif *netif; + + LWIP_UNUSED_ARG(column); + + /* check if incoming OID length and if values are in plausible range */ + if (!snmp_oid_in_range(row_oid, row_oid_len, interfaces_Table_oid_ranges, LWIP_ARRAYSIZE(interfaces_Table_oid_ranges))) { + return SNMP_ERR_NOSUCHINSTANCE; + } + + /* get netif index from incoming OID */ + ifIndex = row_oid[0]; + + /* find netif with index */ + netif = netif_list; + while (netif != NULL) { + if (netif_to_num(netif) == ifIndex) { + /* store netif pointer for subsequent operations (get/test/set) */ + cell_instance->reference.ptr = netif; + return SNMP_ERR_NOERROR; + } + netif = netif->next; + } + + /* not found */ + return SNMP_ERR_NOSUCHINSTANCE; +} + +static snmp_err_t +interfaces_Table_get_next_cell_instance(const u32_t* column, struct snmp_obj_id* row_oid, struct snmp_node_instance* cell_instance) +{ + struct netif *netif; + struct snmp_next_oid_state state; + u32_t result_temp[LWIP_ARRAYSIZE(interfaces_Table_oid_ranges)]; + + LWIP_UNUSED_ARG(column); + + /* init struct to search next oid */ + snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(interfaces_Table_oid_ranges)); + + /* iterate over all possible OIDs to find the next one */ + netif = netif_list; + while (netif != NULL) { + u32_t test_oid[LWIP_ARRAYSIZE(interfaces_Table_oid_ranges)]; + test_oid[0] = netif_to_num(netif); + + /* check generated OID: is it a candidate for the next one? */ + snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(interfaces_Table_oid_ranges), netif); + + netif = netif->next; + } + + /* did we find a next one? */ + if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) { + snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len); + /* store netif pointer for subsequent operations (get/test/set) */ + cell_instance->reference.ptr = /* (struct netif*) */state.reference; + return SNMP_ERR_NOERROR; + } + + /* not found */ + return SNMP_ERR_NOSUCHINSTANCE; +} + +static s16_t +interfaces_Table_get_value(struct snmp_node_instance* instance, void* value) +{ + struct netif *netif = (struct netif*)instance->reference.ptr; + u32_t* value_u32 = (u32_t*)value; + s32_t* value_s32 = (s32_t*)value; + u16_t value_len; + + switch (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id)) + { + case 1: /* ifIndex */ + *value_s32 = netif_to_num(netif); + value_len = sizeof(*value_s32); + break; + case 2: /* ifDescr */ + value_len = sizeof(netif->name); + MEMCPY(value, netif->name, value_len); + break; + case 3: /* ifType */ + *value_s32 = netif->link_type; + value_len = sizeof(*value_s32); + break; + case 4: /* ifMtu */ + *value_s32 = netif->mtu; + value_len = sizeof(*value_s32); + break; + case 5: /* ifSpeed */ + *value_u32 = netif->link_speed; + value_len = sizeof(*value_u32); + break; + case 6: /* ifPhysAddress */ + value_len = sizeof(netif->hwaddr); + MEMCPY(value, &netif->hwaddr, value_len); + break; + case 7: /* ifAdminStatus */ + if (netif_is_up(netif)) { + *value_s32 = iftable_ifOperStatus_up; + } else { + *value_s32 = iftable_ifOperStatus_down; + } + value_len = sizeof(*value_s32); + break; + case 8: /* ifOperStatus */ + if (netif_is_up(netif)) { + if (netif_is_link_up(netif)) { + *value_s32 = iftable_ifAdminStatus_up; + } else { + *value_s32 = iftable_ifAdminStatus_lowerLayerDown; + } + } else { + *value_s32 = iftable_ifAdminStatus_down; + } + value_len = sizeof(*value_s32); + break; + case 9: /* ifLastChange */ + *value_u32 = netif->ts; + value_len = sizeof(*value_u32); + break; + case 10: /* ifInOctets */ + *value_u32 = netif->mib2_counters.ifinoctets; + value_len = sizeof(*value_u32); + break; + case 11: /* ifInUcastPkts */ + *value_u32 = netif->mib2_counters.ifinucastpkts; + value_len = sizeof(*value_u32); + break; + case 12: /* ifInNUcastPkts */ + *value_u32 = netif->mib2_counters.ifinnucastpkts; + value_len = sizeof(*value_u32); + break; + case 13: /* ifInDiscards */ + *value_u32 = netif->mib2_counters.ifindiscards; + value_len = sizeof(*value_u32); + break; + case 14: /* ifInErrors */ + *value_u32 = netif->mib2_counters.ifinerrors; + value_len = sizeof(*value_u32); + break; + case 15: /* ifInUnkownProtos */ + *value_u32 = netif->mib2_counters.ifinunknownprotos; + value_len = sizeof(*value_u32); + break; + case 16: /* ifOutOctets */ + *value_u32 = netif->mib2_counters.ifoutoctets; + value_len = sizeof(*value_u32); + break; + case 17: /* ifOutUcastPkts */ + *value_u32 = netif->mib2_counters.ifoutucastpkts; + value_len = sizeof(*value_u32); + break; + case 18: /* ifOutNUcastPkts */ + *value_u32 = netif->mib2_counters.ifoutnucastpkts; + value_len = sizeof(*value_u32); + break; + case 19: /* ifOutDiscarts */ + *value_u32 = netif->mib2_counters.ifoutdiscards; + value_len = sizeof(*value_u32); + break; + case 20: /* ifOutErrors */ + *value_u32 = netif->mib2_counters.ifouterrors; + value_len = sizeof(*value_u32); + break; + case 21: /* ifOutQLen */ + *value_u32 = iftable_ifOutQLen; + value_len = sizeof(*value_u32); + break; + /** @note returning zeroDotZero (0.0) no media specific MIB support */ + case 22: /* ifSpecific */ + value_len = snmp_zero_dot_zero.len * sizeof(u32_t); + MEMCPY(value, snmp_zero_dot_zero.id, value_len); + break; + default: + return 0; + } + + return value_len; +} + +#if !SNMP_SAFE_REQUESTS + +static snmp_err_t +interfaces_Table_set_test(struct snmp_node_instance* instance, u16_t len, void *value) +{ + s32_t *sint_ptr = (s32_t*)value; + + /* stack should never call this method for another column, + because all other columns are set to readonly */ + LWIP_ASSERT("Invalid column", (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id) == 7)); + LWIP_UNUSED_ARG(len); + + if (*sint_ptr == 1 || *sint_ptr == 2) { + return SNMP_ERR_NOERROR; + } + + return SNMP_ERR_WRONGVALUE; +} + +static snmp_err_t +interfaces_Table_set_value(struct snmp_node_instance* instance, u16_t len, void *value) +{ + struct netif *netif = (struct netif*)instance->reference.ptr; + s32_t *sint_ptr = (s32_t*)value; + + /* stack should never call this method for another column, + because all other columns are set to readonly */ + LWIP_ASSERT("Invalid column", (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id) == 7)); + LWIP_UNUSED_ARG(len); + + if (*sint_ptr == 1) { + netif_set_up(netif); + } else if (*sint_ptr == 2) { + netif_set_down(netif); + } + + return SNMP_ERR_NOERROR; +} + +#endif /* SNMP_SAFE_REQUESTS */ + +static const struct snmp_scalar_node interfaces_Number = SNMP_SCALAR_CREATE_NODE_READONLY(1, SNMP_ASN1_TYPE_INTEGER, interfaces_get_value); + +static const struct snmp_table_col_def interfaces_Table_columns[] = { + { 1, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifIndex */ + { 2, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifDescr */ + { 3, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifType */ + { 4, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifMtu */ + { 5, SNMP_ASN1_TYPE_GAUGE, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifSpeed */ + { 6, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifPhysAddress */ +#if !SNMP_SAFE_REQUESTS + { 7, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_WRITE }, /* ifAdminStatus */ +#else + { 7, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifAdminStatus */ +#endif + { 8, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOperStatus */ + { 9, SNMP_ASN1_TYPE_TIMETICKS, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifLastChange */ + { 10, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInOctets */ + { 11, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInUcastPkts */ + { 12, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInNUcastPkts */ + { 13, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInDiscarts */ + { 14, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInErrors */ + { 15, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInUnkownProtos */ + { 16, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutOctets */ + { 17, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutUcastPkts */ + { 18, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutNUcastPkts */ + { 19, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutDiscarts */ + { 20, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutErrors */ + { 21, SNMP_ASN1_TYPE_GAUGE, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutQLen */ + { 22, SNMP_ASN1_TYPE_OBJECT_ID, SNMP_NODE_INSTANCE_READ_ONLY } /* ifSpecific */ +}; + +#if !SNMP_SAFE_REQUESTS +static const struct snmp_table_node interfaces_Table = SNMP_TABLE_CREATE( + 2, interfaces_Table_columns, + interfaces_Table_get_cell_instance, interfaces_Table_get_next_cell_instance, + interfaces_Table_get_value, interfaces_Table_set_test, interfaces_Table_set_value); +#else +static const struct snmp_table_node interfaces_Table = SNMP_TABLE_CREATE( + 2, interfaces_Table_columns, + interfaces_Table_get_cell_instance, interfaces_Table_get_next_cell_instance, + interfaces_Table_get_value, NULL, NULL); +#endif + +/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */ +CREATE_LWIP_SYNC_NODE(1, interfaces_Number) +CREATE_LWIP_SYNC_NODE(2, interfaces_Table) + +static const struct snmp_node* const interface_nodes[] = { + &SYNC_NODE_NAME(interfaces_Number).node.node, + &SYNC_NODE_NAME(interfaces_Table).node.node +}; + +const struct snmp_tree_node snmp_mib2_interface_root = SNMP_CREATE_TREE_NODE(2, interface_nodes); + +#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_ip.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_ip.c new file mode 100644 index 0000000..4f05180 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_ip.c @@ -0,0 +1,743 @@ +/** + * @file + * Management Information Base II (RFC1213) IP objects and functions. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Dirk Ziegelmeier <dziegel@gmx.de> + * Christiaan Simons <christiaan.simons@axon.tv> + */ + +#include "lwip/snmp.h" +#include "lwip/apps/snmp.h" +#include "lwip/apps/snmp_core.h" +#include "lwip/apps/snmp_mib2.h" +#include "lwip/apps/snmp_table.h" +#include "lwip/apps/snmp_scalar.h" +#include "lwip/stats.h" +#include "lwip/netif.h" +#include "lwip/ip.h" +#include "lwip/etharp.h" + +#if LWIP_SNMP && SNMP_LWIP_MIB2 + +#if SNMP_USE_NETCONN +#define SYNC_NODE_NAME(node_name) node_name ## _synced +#define CREATE_LWIP_SYNC_NODE(oid, node_name) \ + static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks); +#else +#define SYNC_NODE_NAME(node_name) node_name +#define CREATE_LWIP_SYNC_NODE(oid, node_name) +#endif + +#if LWIP_IPV4 +/* --- ip .1.3.6.1.2.1.4 ----------------------------------------------------- */ + +static s16_t +ip_get_value(struct snmp_node_instance* instance, void* value) +{ + s32_t* sint_ptr = (s32_t*)value; + u32_t* uint_ptr = (u32_t*)value; + + switch (instance->node->oid) { + case 1: /* ipForwarding */ +#if IP_FORWARD + /* forwarding */ + *sint_ptr = 1; +#else + /* not-forwarding */ + *sint_ptr = 2; +#endif + return sizeof(*sint_ptr); + case 2: /* ipDefaultTTL */ + *sint_ptr = IP_DEFAULT_TTL; + return sizeof(*sint_ptr); + case 3: /* ipInReceives */ + *uint_ptr = STATS_GET(mib2.ipinreceives); + return sizeof(*uint_ptr); + case 4: /* ipInHdrErrors */ + *uint_ptr = STATS_GET(mib2.ipinhdrerrors); + return sizeof(*uint_ptr); + case 5: /* ipInAddrErrors */ + *uint_ptr = STATS_GET(mib2.ipinaddrerrors); + return sizeof(*uint_ptr); + case 6: /* ipForwDatagrams */ + *uint_ptr = STATS_GET(mib2.ipforwdatagrams); + return sizeof(*uint_ptr); + case 7: /* ipInUnknownProtos */ + *uint_ptr = STATS_GET(mib2.ipinunknownprotos); + return sizeof(*uint_ptr); + case 8: /* ipInDiscards */ + *uint_ptr = STATS_GET(mib2.ipindiscards); + return sizeof(*uint_ptr); + case 9: /* ipInDelivers */ + *uint_ptr = STATS_GET(mib2.ipindelivers); + return sizeof(*uint_ptr); + case 10: /* ipOutRequests */ + *uint_ptr = STATS_GET(mib2.ipoutrequests); + return sizeof(*uint_ptr); + case 11: /* ipOutDiscards */ + *uint_ptr = STATS_GET(mib2.ipoutdiscards); + return sizeof(*uint_ptr); + case 12: /* ipOutNoRoutes */ + *uint_ptr = STATS_GET(mib2.ipoutnoroutes); + return sizeof(*uint_ptr); + case 13: /* ipReasmTimeout */ +#if IP_REASSEMBLY + *sint_ptr = IP_REASS_MAXAGE; +#else + *sint_ptr = 0; +#endif + return sizeof(*sint_ptr); + case 14: /* ipReasmReqds */ + *uint_ptr = STATS_GET(mib2.ipreasmreqds); + return sizeof(*uint_ptr); + case 15: /* ipReasmOKs */ + *uint_ptr = STATS_GET(mib2.ipreasmoks); + return sizeof(*uint_ptr); + case 16: /* ipReasmFails */ + *uint_ptr = STATS_GET(mib2.ipreasmfails); + return sizeof(*uint_ptr); + case 17: /* ipFragOKs */ + *uint_ptr = STATS_GET(mib2.ipfragoks); + return sizeof(*uint_ptr); + case 18: /* ipFragFails */ + *uint_ptr = STATS_GET(mib2.ipfragfails); + return sizeof(*uint_ptr); + case 19: /* ipFragCreates */ + *uint_ptr = STATS_GET(mib2.ipfragcreates); + return sizeof(*uint_ptr); + case 23: /* ipRoutingDiscards: not supported -> always 0 */ + *uint_ptr = 0; + return sizeof(*uint_ptr); + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_get_value(): unknown id: %"S32_F"\n", instance->node->oid)); + break; + } + + return 0; +} + +/** + * Test ip object value before setting. + * + * @param instance node instance + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value from. + * + * @note we allow set if the value matches the hardwired value, + * otherwise return badvalue. + */ +static snmp_err_t +ip_set_test(struct snmp_node_instance* instance, u16_t len, void *value) +{ + snmp_err_t ret = SNMP_ERR_WRONGVALUE; + s32_t *sint_ptr = (s32_t*)value; + + LWIP_UNUSED_ARG(len); + switch (instance->node->oid) { + case 1: /* ipForwarding */ +#if IP_FORWARD + /* forwarding */ + if (*sint_ptr == 1) +#else + /* not-forwarding */ + if (*sint_ptr == 2) +#endif + { + ret = SNMP_ERR_NOERROR; + } + break; + case 2: /* ipDefaultTTL */ + if (*sint_ptr == IP_DEFAULT_TTL) { + ret = SNMP_ERR_NOERROR; + } + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_set_test(): unknown id: %"S32_F"\n", instance->node->oid)); + break; + } + + return ret; +} + +static snmp_err_t +ip_set_value(struct snmp_node_instance* instance, u16_t len, void *value) +{ + LWIP_UNUSED_ARG(instance); + LWIP_UNUSED_ARG(len); + LWIP_UNUSED_ARG(value); + /* nothing to do here because in set_test we only accept values being the same as our own stored value -> no need to store anything */ + return SNMP_ERR_NOERROR; +} + +/* --- ipAddrTable --- */ + +/* list of allowed value ranges for incoming OID */ +static const struct snmp_oid_range ip_AddrTable_oid_ranges[] = { + { 0, 0xff }, /* IP A */ + { 0, 0xff }, /* IP B */ + { 0, 0xff }, /* IP C */ + { 0, 0xff } /* IP D */ +}; + +static snmp_err_t +ip_AddrTable_get_cell_value_core(struct netif *netif, const u32_t* column, union snmp_variant_value* value, u32_t* value_len) +{ + LWIP_UNUSED_ARG(value_len); + + switch (*column) { + case 1: /* ipAdEntAddr */ + value->u32 = netif_ip4_addr(netif)->addr; + break; + case 2: /* ipAdEntIfIndex */ + value->u32 = netif_to_num(netif); + break; + case 3: /* ipAdEntNetMask */ + value->u32 = netif_ip4_netmask(netif)->addr; + break; + case 4: /* ipAdEntBcastAddr */ + /* lwIP oddity, there's no broadcast + address in the netif we can rely on */ + value->u32 = IPADDR_BROADCAST & 1; + break; + case 5: /* ipAdEntReasmMaxSize */ +#if IP_REASSEMBLY + /* @todo The theoretical maximum is IP_REASS_MAX_PBUFS * size of the pbufs, + * but only if receiving one fragmented packet at a time. + * The current solution is to calculate for 2 simultaneous packets... + */ + value->u32 = (IP_HLEN + ((IP_REASS_MAX_PBUFS/2) * + (PBUF_POOL_BUFSIZE - PBUF_LINK_ENCAPSULATION_HLEN - PBUF_LINK_HLEN - IP_HLEN))); +#else + /** @todo returning MTU would be a bad thing and + returning a wild guess like '576' isn't good either */ + value->u32 = 0; +#endif + break; + default: + return SNMP_ERR_NOSUCHINSTANCE; + } + + return SNMP_ERR_NOERROR; +} + +static snmp_err_t +ip_AddrTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len) +{ + ip4_addr_t ip; + struct netif *netif; + + /* check if incoming OID length and if values are in plausible range */ + if (!snmp_oid_in_range(row_oid, row_oid_len, ip_AddrTable_oid_ranges, LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges))) { + return SNMP_ERR_NOSUCHINSTANCE; + } + + /* get IP from incoming OID */ + snmp_oid_to_ip4(&row_oid[0], &ip); /* we know it succeeds because of oid_in_range check above */ + + /* find netif with requested ip */ + netif = netif_list; + while (netif != NULL) { + if (ip4_addr_cmp(&ip, netif_ip4_addr(netif))) { + /* fill in object properties */ + return ip_AddrTable_get_cell_value_core(netif, column, value, value_len); + } + + netif = netif->next; + } + + /* not found */ + return SNMP_ERR_NOSUCHINSTANCE; +} + +static snmp_err_t +ip_AddrTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len) +{ + struct netif *netif; + struct snmp_next_oid_state state; + u32_t result_temp[LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges)]; + + /* init struct to search next oid */ + snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges)); + + /* iterate over all possible OIDs to find the next one */ + netif = netif_list; + while (netif != NULL) { + u32_t test_oid[LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges)]; + snmp_ip4_to_oid(netif_ip4_addr(netif), &test_oid[0]); + + /* check generated OID: is it a candidate for the next one? */ + snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges), netif); + + netif = netif->next; + } + + /* did we find a next one? */ + if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) { + snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len); + /* fill in object properties */ + return ip_AddrTable_get_cell_value_core((struct netif*)state.reference, column, value, value_len); + } + + /* not found */ + return SNMP_ERR_NOSUCHINSTANCE; +} + +/* --- ipRouteTable --- */ + +/* list of allowed value ranges for incoming OID */ +static const struct snmp_oid_range ip_RouteTable_oid_ranges[] = { + { 0, 0xff }, /* IP A */ + { 0, 0xff }, /* IP B */ + { 0, 0xff }, /* IP C */ + { 0, 0xff }, /* IP D */ +}; + +static snmp_err_t +ip_RouteTable_get_cell_value_core(struct netif *netif, u8_t default_route, const u32_t* column, union snmp_variant_value* value, u32_t* value_len) +{ + switch (*column) { + case 1: /* ipRouteDest */ + if (default_route) { + /* default rte has 0.0.0.0 dest */ + value->u32 = IP4_ADDR_ANY4->addr; + } else { + /* netifs have netaddress dest */ + ip4_addr_t tmp; + ip4_addr_get_network(&tmp, netif_ip4_addr(netif), netif_ip4_netmask(netif)); + value->u32 = tmp.addr; + } + break; + case 2: /* ipRouteIfIndex */ + value->u32 = netif_to_num(netif); + break; + case 3: /* ipRouteMetric1 */ + if (default_route) { + value->s32 = 1; /* default */ + } else { + value->s32 = 0; /* normal */ + } + break; + case 4: /* ipRouteMetric2 */ + case 5: /* ipRouteMetric3 */ + case 6: /* ipRouteMetric4 */ + value->s32 = -1; /* none */ + break; + case 7: /* ipRouteNextHop */ + if (default_route) { + /* default rte: gateway */ + value->u32 = netif_ip4_gw(netif)->addr; + } else { + /* other rtes: netif ip_addr */ + value->u32 = netif_ip4_addr(netif)->addr; + } + break; + case 8: /* ipRouteType */ + if (default_route) { + /* default rte is indirect */ + value->u32 = 4; /* indirect */ + } else { + /* other rtes are direct */ + value->u32 = 3; /* direct */ + } + break; + case 9: /* ipRouteProto */ + /* locally defined routes */ + value->u32 = 2; /* local */ + break; + case 10: /* ipRouteAge */ + /* @todo (sysuptime - timestamp last change) / 100 */ + value->u32 = 0; + break; + case 11: /* ipRouteMask */ + if (default_route) { + /* default rte use 0.0.0.0 mask */ + value->u32 = IP4_ADDR_ANY4->addr; + } else { + /* other rtes use netmask */ + value->u32 = netif_ip4_netmask(netif)->addr; + } + break; + case 12: /* ipRouteMetric5 */ + value->s32 = -1; /* none */ + break; + case 13: /* ipRouteInfo */ + value->const_ptr = snmp_zero_dot_zero.id; + *value_len = snmp_zero_dot_zero.len * sizeof(u32_t); + break; + default: + return SNMP_ERR_NOSUCHINSTANCE; + } + + return SNMP_ERR_NOERROR; +} + +static snmp_err_t +ip_RouteTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len) +{ + ip4_addr_t test_ip; + struct netif *netif; + + /* check if incoming OID length and if values are in plausible range */ + if (!snmp_oid_in_range(row_oid, row_oid_len, ip_RouteTable_oid_ranges, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges))) { + return SNMP_ERR_NOSUCHINSTANCE; + } + + /* get IP and port from incoming OID */ + snmp_oid_to_ip4(&row_oid[0], &test_ip); /* we know it succeeds because of oid_in_range check above */ + + /* default route is on default netif */ + if (ip4_addr_isany_val(test_ip) && (netif_default != NULL)) { + /* fill in object properties */ + return ip_RouteTable_get_cell_value_core(netif_default, 1, column, value, value_len); + } + + /* find netif with requested route */ + netif = netif_list; + while (netif != NULL) { + ip4_addr_t dst; + ip4_addr_get_network(&dst, netif_ip4_addr(netif), netif_ip4_netmask(netif)); + + if (ip4_addr_cmp(&dst, &test_ip)) { + /* fill in object properties */ + return ip_RouteTable_get_cell_value_core(netif, 0, column, value, value_len); + } + + netif = netif->next; + } + + /* not found */ + return SNMP_ERR_NOSUCHINSTANCE; +} + +static snmp_err_t +ip_RouteTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len) +{ + struct netif *netif; + struct snmp_next_oid_state state; + u32_t result_temp[LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges)]; + u32_t test_oid[LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges)]; + + /* init struct to search next oid */ + snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges)); + + /* check default route */ + if (netif_default != NULL) { + snmp_ip4_to_oid(IP4_ADDR_ANY4, &test_oid[0]); + snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges), netif_default); + } + + /* iterate over all possible OIDs to find the next one */ + netif = netif_list; + while (netif != NULL) { + ip4_addr_t dst; + ip4_addr_get_network(&dst, netif_ip4_addr(netif), netif_ip4_netmask(netif)); + + /* check generated OID: is it a candidate for the next one? */ + if (!ip4_addr_isany_val(dst)) { + snmp_ip4_to_oid(&dst, &test_oid[0]); + snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges), netif); + } + + netif = netif->next; + } + + /* did we find a next one? */ + if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) { + ip4_addr_t dst; + snmp_oid_to_ip4(&result_temp[0], &dst); + snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len); + /* fill in object properties */ + return ip_RouteTable_get_cell_value_core((struct netif*)state.reference, ip4_addr_isany_val(dst), column, value, value_len); + } else { + /* not found */ + return SNMP_ERR_NOSUCHINSTANCE; + } +} + +#if LWIP_ARP && LWIP_IPV4 +/* --- ipNetToMediaTable --- */ + +/* list of allowed value ranges for incoming OID */ +static const struct snmp_oid_range ip_NetToMediaTable_oid_ranges[] = { + { 1, 0xff }, /* IfIndex */ + { 0, 0xff }, /* IP A */ + { 0, 0xff }, /* IP B */ + { 0, 0xff }, /* IP C */ + { 0, 0xff } /* IP D */ +}; + +static snmp_err_t +ip_NetToMediaTable_get_cell_value_core(u8_t arp_table_index, const u32_t* column, union snmp_variant_value* value, u32_t* value_len) +{ + ip4_addr_t *ip; + struct netif *netif; + struct eth_addr *ethaddr; + + etharp_get_entry(arp_table_index, &ip, &netif, ðaddr); + + /* value */ + switch (*column) { + case 1: /* atIfIndex / ipNetToMediaIfIndex */ + value->u32 = netif_to_num(netif); + break; + case 2: /* atPhysAddress / ipNetToMediaPhysAddress */ + value->ptr = ethaddr; + *value_len = sizeof(*ethaddr); + break; + case 3: /* atNetAddress / ipNetToMediaNetAddress */ + value->u32 = ip->addr; + break; + case 4: /* ipNetToMediaType */ + value->u32 = 3; /* dynamic*/ + break; + default: + return SNMP_ERR_NOSUCHINSTANCE; + } + + return SNMP_ERR_NOERROR; +} + +static snmp_err_t +ip_NetToMediaTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len) +{ + ip4_addr_t ip_in; + u8_t netif_index; + u8_t i; + + /* check if incoming OID length and if values are in plausible range */ + if (!snmp_oid_in_range(row_oid, row_oid_len, ip_NetToMediaTable_oid_ranges, LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges))) { + return SNMP_ERR_NOSUCHINSTANCE; + } + + /* get IP from incoming OID */ + netif_index = (u8_t)row_oid[0]; + snmp_oid_to_ip4(&row_oid[1], &ip_in); /* we know it succeeds because of oid_in_range check above */ + + /* find requested entry */ + for (i=0; i<ARP_TABLE_SIZE; i++) { + ip4_addr_t *ip; + struct netif *netif; + struct eth_addr *ethaddr; + + if (etharp_get_entry(i, &ip, &netif, ðaddr)) { + if ((netif_index == netif_to_num(netif)) && ip4_addr_cmp(&ip_in, ip)) { + /* fill in object properties */ + return ip_NetToMediaTable_get_cell_value_core(i, column, value, value_len); + } + } + } + + /* not found */ + return SNMP_ERR_NOSUCHINSTANCE; +} + +static snmp_err_t +ip_NetToMediaTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len) +{ + u8_t i; + struct snmp_next_oid_state state; + u32_t result_temp[LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges)]; + + /* init struct to search next oid */ + snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges)); + + /* iterate over all possible OIDs to find the next one */ + for (i=0; i<ARP_TABLE_SIZE; i++) { + ip4_addr_t *ip; + struct netif *netif; + struct eth_addr *ethaddr; + + if (etharp_get_entry(i, &ip, &netif, ðaddr)) { + u32_t test_oid[LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges)]; + + test_oid[0] = netif_to_num(netif); + snmp_ip4_to_oid(ip, &test_oid[1]); + + /* check generated OID: is it a candidate for the next one? */ + snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges), LWIP_PTR_NUMERIC_CAST(void*, i)); + } + } + + /* did we find a next one? */ + if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) { + snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len); + /* fill in object properties */ + return ip_NetToMediaTable_get_cell_value_core(LWIP_PTR_NUMERIC_CAST(u8_t, state.reference), column, value, value_len); + } + + /* not found */ + return SNMP_ERR_NOSUCHINSTANCE; +} + +#endif /* LWIP_ARP && LWIP_IPV4 */ + +static const struct snmp_scalar_node ip_Forwarding = SNMP_SCALAR_CREATE_NODE(1, SNMP_NODE_INSTANCE_READ_WRITE, SNMP_ASN1_TYPE_INTEGER, ip_get_value, ip_set_test, ip_set_value); +static const struct snmp_scalar_node ip_DefaultTTL = SNMP_SCALAR_CREATE_NODE(2, SNMP_NODE_INSTANCE_READ_WRITE, SNMP_ASN1_TYPE_INTEGER, ip_get_value, ip_set_test, ip_set_value); +static const struct snmp_scalar_node ip_InReceives = SNMP_SCALAR_CREATE_NODE_READONLY(3, SNMP_ASN1_TYPE_COUNTER, ip_get_value); +static const struct snmp_scalar_node ip_InHdrErrors = SNMP_SCALAR_CREATE_NODE_READONLY(4, SNMP_ASN1_TYPE_COUNTER, ip_get_value); +static const struct snmp_scalar_node ip_InAddrErrors = SNMP_SCALAR_CREATE_NODE_READONLY(5, SNMP_ASN1_TYPE_COUNTER, ip_get_value); +static const struct snmp_scalar_node ip_ForwDatagrams = SNMP_SCALAR_CREATE_NODE_READONLY(6, SNMP_ASN1_TYPE_COUNTER, ip_get_value); +static const struct snmp_scalar_node ip_InUnknownProtos = SNMP_SCALAR_CREATE_NODE_READONLY(7, SNMP_ASN1_TYPE_COUNTER, ip_get_value); +static const struct snmp_scalar_node ip_InDiscards = SNMP_SCALAR_CREATE_NODE_READONLY(8, SNMP_ASN1_TYPE_COUNTER, ip_get_value); +static const struct snmp_scalar_node ip_InDelivers = SNMP_SCALAR_CREATE_NODE_READONLY(9, SNMP_ASN1_TYPE_COUNTER, ip_get_value); +static const struct snmp_scalar_node ip_OutRequests = SNMP_SCALAR_CREATE_NODE_READONLY(10, SNMP_ASN1_TYPE_COUNTER, ip_get_value); +static const struct snmp_scalar_node ip_OutDiscards = SNMP_SCALAR_CREATE_NODE_READONLY(11, SNMP_ASN1_TYPE_COUNTER, ip_get_value); +static const struct snmp_scalar_node ip_OutNoRoutes = SNMP_SCALAR_CREATE_NODE_READONLY(12, SNMP_ASN1_TYPE_COUNTER, ip_get_value); +static const struct snmp_scalar_node ip_ReasmTimeout = SNMP_SCALAR_CREATE_NODE_READONLY(13, SNMP_ASN1_TYPE_INTEGER, ip_get_value); +static const struct snmp_scalar_node ip_ReasmReqds = SNMP_SCALAR_CREATE_NODE_READONLY(14, SNMP_ASN1_TYPE_COUNTER, ip_get_value); +static const struct snmp_scalar_node ip_ReasmOKs = SNMP_SCALAR_CREATE_NODE_READONLY(15, SNMP_ASN1_TYPE_COUNTER, ip_get_value); +static const struct snmp_scalar_node ip_ReasmFails = SNMP_SCALAR_CREATE_NODE_READONLY(16, SNMP_ASN1_TYPE_COUNTER, ip_get_value); +static const struct snmp_scalar_node ip_FragOKs = SNMP_SCALAR_CREATE_NODE_READONLY(17, SNMP_ASN1_TYPE_COUNTER, ip_get_value); +static const struct snmp_scalar_node ip_FragFails = SNMP_SCALAR_CREATE_NODE_READONLY(18, SNMP_ASN1_TYPE_COUNTER, ip_get_value); +static const struct snmp_scalar_node ip_FragCreates = SNMP_SCALAR_CREATE_NODE_READONLY(19, SNMP_ASN1_TYPE_COUNTER, ip_get_value); +static const struct snmp_scalar_node ip_RoutingDiscards = SNMP_SCALAR_CREATE_NODE_READONLY(23, SNMP_ASN1_TYPE_COUNTER, ip_get_value); + +static const struct snmp_table_simple_col_def ip_AddrTable_columns[] = { + { 1, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipAdEntAddr */ + { 2, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipAdEntIfIndex */ + { 3, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipAdEntNetMask */ + { 4, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipAdEntBcastAddr */ + { 5, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 } /* ipAdEntReasmMaxSize */ +}; + +static const struct snmp_table_simple_node ip_AddrTable = SNMP_TABLE_CREATE_SIMPLE(20, ip_AddrTable_columns, ip_AddrTable_get_cell_value, ip_AddrTable_get_next_cell_instance_and_value); + +static const struct snmp_table_simple_col_def ip_RouteTable_columns[] = { + { 1, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteDest */ + { 2, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteIfIndex */ + { 3, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric1 */ + { 4, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric2 */ + { 5, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric3 */ + { 6, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric4 */ + { 7, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteNextHop */ + { 8, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteType */ + { 9, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteProto */ + { 10, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteAge */ + { 11, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteMask */ + { 12, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric5 */ + { 13, SNMP_ASN1_TYPE_OBJECT_ID, SNMP_VARIANT_VALUE_TYPE_PTR } /* ipRouteInfo */ +}; + +static const struct snmp_table_simple_node ip_RouteTable = SNMP_TABLE_CREATE_SIMPLE(21, ip_RouteTable_columns, ip_RouteTable_get_cell_value, ip_RouteTable_get_next_cell_instance_and_value); +#endif /* LWIP_IPV4 */ + +#if LWIP_ARP && LWIP_IPV4 +static const struct snmp_table_simple_col_def ip_NetToMediaTable_columns[] = { + { 1, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipNetToMediaIfIndex */ + { 2, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_VARIANT_VALUE_TYPE_PTR }, /* ipNetToMediaPhysAddress */ + { 3, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipNetToMediaNetAddress */ + { 4, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 } /* ipNetToMediaType */ +}; + +static const struct snmp_table_simple_node ip_NetToMediaTable = SNMP_TABLE_CREATE_SIMPLE(22, ip_NetToMediaTable_columns, ip_NetToMediaTable_get_cell_value, ip_NetToMediaTable_get_next_cell_instance_and_value); +#endif /* LWIP_ARP && LWIP_IPV4 */ + +#if LWIP_IPV4 +/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */ +CREATE_LWIP_SYNC_NODE( 1, ip_Forwarding) +CREATE_LWIP_SYNC_NODE( 2, ip_DefaultTTL) +CREATE_LWIP_SYNC_NODE( 3, ip_InReceives) +CREATE_LWIP_SYNC_NODE( 4, ip_InHdrErrors) +CREATE_LWIP_SYNC_NODE( 5, ip_InAddrErrors) +CREATE_LWIP_SYNC_NODE( 6, ip_ForwDatagrams) +CREATE_LWIP_SYNC_NODE( 7, ip_InUnknownProtos) +CREATE_LWIP_SYNC_NODE( 8, ip_InDiscards) +CREATE_LWIP_SYNC_NODE( 9, ip_InDelivers) +CREATE_LWIP_SYNC_NODE(10, ip_OutRequests) +CREATE_LWIP_SYNC_NODE(11, ip_OutDiscards) +CREATE_LWIP_SYNC_NODE(12, ip_OutNoRoutes) +CREATE_LWIP_SYNC_NODE(13, ip_ReasmTimeout) +CREATE_LWIP_SYNC_NODE(14, ip_ReasmReqds) +CREATE_LWIP_SYNC_NODE(15, ip_ReasmOKs) +CREATE_LWIP_SYNC_NODE(15, ip_ReasmFails) +CREATE_LWIP_SYNC_NODE(17, ip_FragOKs) +CREATE_LWIP_SYNC_NODE(18, ip_FragFails) +CREATE_LWIP_SYNC_NODE(19, ip_FragCreates) +CREATE_LWIP_SYNC_NODE(20, ip_AddrTable) +CREATE_LWIP_SYNC_NODE(21, ip_RouteTable) +#if LWIP_ARP +CREATE_LWIP_SYNC_NODE(22, ip_NetToMediaTable) +#endif /* LWIP_ARP */ +CREATE_LWIP_SYNC_NODE(23, ip_RoutingDiscards) + +static const struct snmp_node* const ip_nodes[] = { + &SYNC_NODE_NAME(ip_Forwarding).node.node, + &SYNC_NODE_NAME(ip_DefaultTTL).node.node, + &SYNC_NODE_NAME(ip_InReceives).node.node, + &SYNC_NODE_NAME(ip_InHdrErrors).node.node, + &SYNC_NODE_NAME(ip_InAddrErrors).node.node, + &SYNC_NODE_NAME(ip_ForwDatagrams).node.node, + &SYNC_NODE_NAME(ip_InUnknownProtos).node.node, + &SYNC_NODE_NAME(ip_InDiscards).node.node, + &SYNC_NODE_NAME(ip_InDelivers).node.node, + &SYNC_NODE_NAME(ip_OutRequests).node.node, + &SYNC_NODE_NAME(ip_OutDiscards).node.node, + &SYNC_NODE_NAME(ip_OutNoRoutes).node.node, + &SYNC_NODE_NAME(ip_ReasmTimeout).node.node, + &SYNC_NODE_NAME(ip_ReasmReqds).node.node, + &SYNC_NODE_NAME(ip_ReasmOKs).node.node, + &SYNC_NODE_NAME(ip_ReasmFails).node.node, + &SYNC_NODE_NAME(ip_FragOKs).node.node, + &SYNC_NODE_NAME(ip_FragFails).node.node, + &SYNC_NODE_NAME(ip_FragCreates).node.node, + &SYNC_NODE_NAME(ip_AddrTable).node.node, + &SYNC_NODE_NAME(ip_RouteTable).node.node, +#if LWIP_ARP + &SYNC_NODE_NAME(ip_NetToMediaTable).node.node, +#endif /* LWIP_ARP */ + &SYNC_NODE_NAME(ip_RoutingDiscards).node.node +}; + +const struct snmp_tree_node snmp_mib2_ip_root = SNMP_CREATE_TREE_NODE(4, ip_nodes); +#endif /* LWIP_IPV4 */ + +/* --- at .1.3.6.1.2.1.3 ----------------------------------------------------- */ + +#if LWIP_ARP && LWIP_IPV4 +/* at node table is a subset of ip_nettomedia table (same rows but less columns) */ +static const struct snmp_table_simple_col_def at_Table_columns[] = { + { 1, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* atIfIndex */ + { 2, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_VARIANT_VALUE_TYPE_PTR }, /* atPhysAddress */ + { 3, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 } /* atNetAddress */ +}; + +static const struct snmp_table_simple_node at_Table = SNMP_TABLE_CREATE_SIMPLE(1, at_Table_columns, ip_NetToMediaTable_get_cell_value, ip_NetToMediaTable_get_next_cell_instance_and_value); + +/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */ +CREATE_LWIP_SYNC_NODE(1, at_Table) + +static const struct snmp_node* const at_nodes[] = { + &SYNC_NODE_NAME(at_Table).node.node +}; + +const struct snmp_tree_node snmp_mib2_at_root = SNMP_CREATE_TREE_NODE(3, at_nodes); +#endif /* LWIP_ARP && LWIP_IPV4 */ + +#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_snmp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_snmp.c new file mode 100644 index 0000000..8a36d61 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_snmp.c @@ -0,0 +1,227 @@ +/** + * @file + * Management Information Base II (RFC1213) SNMP objects and functions. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Dirk Ziegelmeier <dziegel@gmx.de> + * Christiaan Simons <christiaan.simons@axon.tv> + */ + +#include "lwip/snmp.h" +#include "lwip/apps/snmp.h" +#include "lwip/apps/snmp_core.h" +#include "lwip/apps/snmp_mib2.h" +#include "lwip/apps/snmp_scalar.h" + +#if LWIP_SNMP && SNMP_LWIP_MIB2 + +#define MIB2_AUTH_TRAPS_ENABLED 1 +#define MIB2_AUTH_TRAPS_DISABLED 2 + +/* --- snmp .1.3.6.1.2.1.11 ----------------------------------------------------- */ +static s16_t +snmp_get_value(const struct snmp_scalar_array_node_def *node, void *value) +{ + u32_t *uint_ptr = (u32_t*)value; + switch (node->oid) { + case 1: /* snmpInPkts */ + *uint_ptr = snmp_stats.inpkts; + break; + case 2: /* snmpOutPkts */ + *uint_ptr = snmp_stats.outpkts; + break; + case 3: /* snmpInBadVersions */ + *uint_ptr = snmp_stats.inbadversions; + break; + case 4: /* snmpInBadCommunityNames */ + *uint_ptr = snmp_stats.inbadcommunitynames; + break; + case 5: /* snmpInBadCommunityUses */ + *uint_ptr = snmp_stats.inbadcommunityuses; + break; + case 6: /* snmpInASNParseErrs */ + *uint_ptr = snmp_stats.inasnparseerrs; + break; + case 8: /* snmpInTooBigs */ + *uint_ptr = snmp_stats.intoobigs; + break; + case 9: /* snmpInNoSuchNames */ + *uint_ptr = snmp_stats.innosuchnames; + break; + case 10: /* snmpInBadValues */ + *uint_ptr = snmp_stats.inbadvalues; + break; + case 11: /* snmpInReadOnlys */ + *uint_ptr = snmp_stats.inreadonlys; + break; + case 12: /* snmpInGenErrs */ + *uint_ptr = snmp_stats.ingenerrs; + break; + case 13: /* snmpInTotalReqVars */ + *uint_ptr = snmp_stats.intotalreqvars; + break; + case 14: /* snmpInTotalSetVars */ + *uint_ptr = snmp_stats.intotalsetvars; + break; + case 15: /* snmpInGetRequests */ + *uint_ptr = snmp_stats.ingetrequests; + break; + case 16: /* snmpInGetNexts */ + *uint_ptr = snmp_stats.ingetnexts; + break; + case 17: /* snmpInSetRequests */ + *uint_ptr = snmp_stats.insetrequests; + break; + case 18: /* snmpInGetResponses */ + *uint_ptr = snmp_stats.ingetresponses; + break; + case 19: /* snmpInTraps */ + *uint_ptr = snmp_stats.intraps; + break; + case 20: /* snmpOutTooBigs */ + *uint_ptr = snmp_stats.outtoobigs; + break; + case 21: /* snmpOutNoSuchNames */ + *uint_ptr = snmp_stats.outnosuchnames; + break; + case 22: /* snmpOutBadValues */ + *uint_ptr = snmp_stats.outbadvalues; + break; + case 24: /* snmpOutGenErrs */ + *uint_ptr = snmp_stats.outgenerrs; + break; + case 25: /* snmpOutGetRequests */ + *uint_ptr = snmp_stats.outgetrequests; + break; + case 26: /* snmpOutGetNexts */ + *uint_ptr = snmp_stats.outgetnexts; + break; + case 27: /* snmpOutSetRequests */ + *uint_ptr = snmp_stats.outsetrequests; + break; + case 28: /* snmpOutGetResponses */ + *uint_ptr = snmp_stats.outgetresponses; + break; + case 29: /* snmpOutTraps */ + *uint_ptr = snmp_stats.outtraps; + break; + case 30: /* snmpEnableAuthenTraps */ + if (snmp_get_auth_traps_enabled() == SNMP_AUTH_TRAPS_DISABLED) { + *uint_ptr = MIB2_AUTH_TRAPS_DISABLED; + } else { + *uint_ptr = MIB2_AUTH_TRAPS_ENABLED; + } + break; + case 31: /* snmpSilentDrops */ + *uint_ptr = 0; /* not supported */ + break; + case 32: /* snmpProxyDrops */ + *uint_ptr = 0; /* not supported */ + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_get_value(): unknown id: %"S32_F"\n", node->oid)); + return 0; + } + + return sizeof(*uint_ptr); +} + +static snmp_err_t +snmp_set_test(const struct snmp_scalar_array_node_def *node, u16_t len, void *value) +{ + snmp_err_t ret = SNMP_ERR_WRONGVALUE; + LWIP_UNUSED_ARG(len); + + if (node->oid == 30) { + /* snmpEnableAuthenTraps */ + s32_t *sint_ptr = (s32_t*)value; + + /* we should have writable non-volatile mem here */ + if ((*sint_ptr == MIB2_AUTH_TRAPS_DISABLED) || (*sint_ptr == MIB2_AUTH_TRAPS_ENABLED)) { + ret = SNMP_ERR_NOERROR; + } + } + return ret; +} + +static snmp_err_t +snmp_set_value(const struct snmp_scalar_array_node_def *node, u16_t len, void *value) +{ + LWIP_UNUSED_ARG(len); + + if (node->oid == 30) { + /* snmpEnableAuthenTraps */ + s32_t *sint_ptr = (s32_t*)value; + if (*sint_ptr == MIB2_AUTH_TRAPS_DISABLED) { + snmp_set_auth_traps_enabled(SNMP_AUTH_TRAPS_DISABLED); + } else { + snmp_set_auth_traps_enabled(SNMP_AUTH_TRAPS_ENABLED); + } + } + + return SNMP_ERR_NOERROR; +} + +/* the following nodes access variables in SNMP stack (snmp_stats) from SNMP worker thread -> OK, no sync needed */ +static const struct snmp_scalar_array_node_def snmp_nodes[] = { + { 1, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInPkts */ + { 2, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutPkts */ + { 3, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInBadVersions */ + { 4, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInBadCommunityNames */ + { 5, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInBadCommunityUses */ + { 6, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInASNParseErrs */ + { 8, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInTooBigs */ + { 9, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInNoSuchNames */ + {10, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInBadValues */ + {11, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInReadOnlys */ + {12, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInGenErrs */ + {13, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInTotalReqVars */ + {14, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInTotalSetVars */ + {15, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInGetRequests */ + {16, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInGetNexts */ + {17, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInSetRequests */ + {18, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInGetResponses */ + {19, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInTraps */ + {20, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutTooBigs */ + {21, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutNoSuchNames */ + {22, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutBadValues */ + {24, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutGenErrs */ + {25, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutGetRequests */ + {26, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutGetNexts */ + {27, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutSetRequests */ + {28, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutGetResponses */ + {29, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutTraps */ + {30, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_WRITE}, /* snmpEnableAuthenTraps */ + {31, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpSilentDrops */ + {32, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY} /* snmpProxyDrops */ +}; + +const struct snmp_scalar_array_node snmp_mib2_snmp_root = SNMP_SCALAR_CREATE_ARRAY_NODE(11, snmp_nodes, snmp_get_value, snmp_set_test, snmp_set_value); + +#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_system.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_system.c new file mode 100644 index 0000000..90e5780 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_system.c @@ -0,0 +1,377 @@ +/** + * @file + * Management Information Base II (RFC1213) SYSTEM objects and functions. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Dirk Ziegelmeier <dziegel@gmx.de> + * Christiaan Simons <christiaan.simons@axon.tv> + */ + +#include "lwip/snmp.h" +#include "lwip/apps/snmp.h" +#include "lwip/apps/snmp_core.h" +#include "lwip/apps/snmp_mib2.h" +#include "lwip/apps/snmp_table.h" +#include "lwip/apps/snmp_scalar.h" +#include "lwip/sys.h" + +#include <string.h> + +#if LWIP_SNMP && SNMP_LWIP_MIB2 + +#if SNMP_USE_NETCONN +#define SYNC_NODE_NAME(node_name) node_name ## _synced +#define CREATE_LWIP_SYNC_NODE(oid, node_name) \ + static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks); +#else +#define SYNC_NODE_NAME(node_name) node_name +#define CREATE_LWIP_SYNC_NODE(oid, node_name) +#endif + +/* --- system .1.3.6.1.2.1.1 ----------------------------------------------------- */ + +/** mib-2.system.sysDescr */ +static const u8_t sysdescr_default[] = SNMP_LWIP_MIB2_SYSDESC; +static const u8_t* sysdescr = sysdescr_default; +static const u16_t* sysdescr_len = NULL; /* use strlen for determining len */ + +/** mib-2.system.sysContact */ +static const u8_t syscontact_default[] = SNMP_LWIP_MIB2_SYSCONTACT; +static const u8_t* syscontact = syscontact_default; +static const u16_t* syscontact_len = NULL; /* use strlen for determining len */ +static u8_t* syscontact_wr = NULL; /* if writable, points to the same buffer as syscontact (required for correct constness) */ +static u16_t* syscontact_wr_len = NULL; /* if writable, points to the same buffer as syscontact_len (required for correct constness) */ +static u16_t syscontact_bufsize = 0; /* 0=not writable */ + +/** mib-2.system.sysName */ +static const u8_t sysname_default[] = SNMP_LWIP_MIB2_SYSNAME; +static const u8_t* sysname = sysname_default; +static const u16_t* sysname_len = NULL; /* use strlen for determining len */ +static u8_t* sysname_wr = NULL; /* if writable, points to the same buffer as sysname (required for correct constness) */ +static u16_t* sysname_wr_len = NULL; /* if writable, points to the same buffer as sysname_len (required for correct constness) */ +static u16_t sysname_bufsize = 0; /* 0=not writable */ + +/** mib-2.system.sysLocation */ +static const u8_t syslocation_default[] = SNMP_LWIP_MIB2_SYSLOCATION; +static const u8_t* syslocation = syslocation_default; +static const u16_t* syslocation_len = NULL; /* use strlen for determining len */ +static u8_t* syslocation_wr = NULL; /* if writable, points to the same buffer as syslocation (required for correct constness) */ +static u16_t* syslocation_wr_len = NULL; /* if writable, points to the same buffer as syslocation_len (required for correct constness) */ +static u16_t syslocation_bufsize = 0; /* 0=not writable */ + +/** + * @ingroup snmp_mib2 + * Initializes sysDescr pointers. + * + * @param str if non-NULL then copy str pointer + * @param len points to string length, excluding zero terminator + */ +void +snmp_mib2_set_sysdescr(const u8_t *str, const u16_t *len) +{ + if (str != NULL) { + sysdescr = str; + sysdescr_len = len; + } +} + +/** + * @ingroup snmp_mib2 + * Initializes sysContact pointers + * + * @param ocstr if non-NULL then copy str pointer + * @param ocstrlen points to string length, excluding zero terminator. + * if set to NULL it is assumed that ocstr is NULL-terminated. + * @param bufsize size of the buffer in bytes. + * (this is required because the buffer can be overwritten by snmp-set) + * if ocstrlen is NULL buffer needs space for terminating 0 byte. + * otherwise complete buffer is used for string. + * if bufsize is set to 0, the value is regarded as read-only. + */ +void +snmp_mib2_set_syscontact(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize) +{ + if (ocstr != NULL) { + syscontact = ocstr; + syscontact_wr = ocstr; + syscontact_len = ocstrlen; + syscontact_wr_len = ocstrlen; + syscontact_bufsize = bufsize; + } +} + +/** + * @ingroup snmp_mib2 + * see \ref snmp_mib2_set_syscontact but set pointer to readonly memory + */ +void +snmp_mib2_set_syscontact_readonly(const u8_t *ocstr, const u16_t *ocstrlen) +{ + if (ocstr != NULL) { + syscontact = ocstr; + syscontact_len = ocstrlen; + syscontact_wr = NULL; + syscontact_wr_len = NULL; + syscontact_bufsize = 0; + } +} + + +/** + * @ingroup snmp_mib2 + * Initializes sysName pointers + * + * @param ocstr if non-NULL then copy str pointer + * @param ocstrlen points to string length, excluding zero terminator. + * if set to NULL it is assumed that ocstr is NULL-terminated. + * @param bufsize size of the buffer in bytes. + * (this is required because the buffer can be overwritten by snmp-set) + * if ocstrlen is NULL buffer needs space for terminating 0 byte. + * otherwise complete buffer is used for string. + * if bufsize is set to 0, the value is regarded as read-only. + */ +void +snmp_mib2_set_sysname(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize) +{ + if (ocstr != NULL) { + sysname = ocstr; + sysname_wr = ocstr; + sysname_len = ocstrlen; + sysname_wr_len = ocstrlen; + sysname_bufsize = bufsize; + } +} + +/** + * @ingroup snmp_mib2 + * see \ref snmp_mib2_set_sysname but set pointer to readonly memory + */ +void +snmp_mib2_set_sysname_readonly(const u8_t *ocstr, const u16_t *ocstrlen) +{ + if (ocstr != NULL) { + sysname = ocstr; + sysname_len = ocstrlen; + sysname_wr = NULL; + sysname_wr_len = NULL; + sysname_bufsize = 0; + } +} + +/** + * @ingroup snmp_mib2 + * Initializes sysLocation pointers + * + * @param ocstr if non-NULL then copy str pointer + * @param ocstrlen points to string length, excluding zero terminator. + * if set to NULL it is assumed that ocstr is NULL-terminated. + * @param bufsize size of the buffer in bytes. + * (this is required because the buffer can be overwritten by snmp-set) + * if ocstrlen is NULL buffer needs space for terminating 0 byte. + * otherwise complete buffer is used for string. + * if bufsize is set to 0, the value is regarded as read-only. + */ +void +snmp_mib2_set_syslocation(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize) +{ + if (ocstr != NULL) { + syslocation = ocstr; + syslocation_wr = ocstr; + syslocation_len = ocstrlen; + syslocation_wr_len = ocstrlen; + syslocation_bufsize = bufsize; + } +} + +/** + * @ingroup snmp_mib2 + * see \ref snmp_mib2_set_syslocation but set pointer to readonly memory + */ +void +snmp_mib2_set_syslocation_readonly(const u8_t *ocstr, const u16_t *ocstrlen) +{ + if (ocstr != NULL) { + syslocation = ocstr; + syslocation_len = ocstrlen; + syslocation_wr = NULL; + syslocation_wr_len = NULL; + syslocation_bufsize = 0; + } +} + + +static s16_t +system_get_value(const struct snmp_scalar_array_node_def *node, void *value) +{ + const u8_t* var = NULL; + const s16_t* var_len; + u16_t result; + + switch (node->oid) { + case 1: /* sysDescr */ + var = sysdescr; + var_len = (const s16_t*)sysdescr_len; + break; + case 2: /* sysObjectID */ + { + const struct snmp_obj_id* dev_enterprise_oid = snmp_get_device_enterprise_oid(); + MEMCPY(value, dev_enterprise_oid->id, dev_enterprise_oid->len * sizeof(u32_t)); + return dev_enterprise_oid->len * sizeof(u32_t); + } + case 3: /* sysUpTime */ + MIB2_COPY_SYSUPTIME_TO((u32_t*)value); + return sizeof(u32_t); + case 4: /* sysContact */ + var = syscontact; + var_len = (const s16_t*)syscontact_len; + break; + case 5: /* sysName */ + var = sysname; + var_len = (const s16_t*)sysname_len; + break; + case 6: /* sysLocation */ + var = syslocation; + var_len = (const s16_t*)syslocation_len; + break; + case 7: /* sysServices */ + *(s32_t*)value = SNMP_SYSSERVICES; + return sizeof(s32_t); + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_get_value(): unknown id: %"S32_F"\n", node->oid)); + return 0; + } + + /* handle string values (OID 1,4,5 and 6) */ + LWIP_ASSERT("", (value != NULL)); + if (var_len == NULL) { + result = (s16_t)strlen((const char*)var); + } else { + result = *var_len; + } + MEMCPY(value, var, result); + return result; +} + +static snmp_err_t +system_set_test(const struct snmp_scalar_array_node_def *node, u16_t len, void *value) +{ + snmp_err_t ret = SNMP_ERR_WRONGVALUE; + const u16_t* var_bufsize = NULL; + const u16_t* var_wr_len; + + LWIP_UNUSED_ARG(value); + + switch (node->oid) { + case 4: /* sysContact */ + var_bufsize = &syscontact_bufsize; + var_wr_len = syscontact_wr_len; + break; + case 5: /* sysName */ + var_bufsize = &sysname_bufsize; + var_wr_len = sysname_wr_len; + break; + case 6: /* sysLocation */ + var_bufsize = &syslocation_bufsize; + var_wr_len = syslocation_wr_len; + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_set_test(): unknown id: %"S32_F"\n", node->oid)); + return ret; + } + + /* check if value is writable at all */ + if (*var_bufsize > 0) { + if (var_wr_len == NULL) { + /* we have to take the terminating 0 into account */ + if (len < *var_bufsize) { + ret = SNMP_ERR_NOERROR; + } + } else { + if (len <= *var_bufsize) { + ret = SNMP_ERR_NOERROR; + } + } + } else { + ret = SNMP_ERR_NOTWRITABLE; + } + + return ret; +} + +static snmp_err_t +system_set_value(const struct snmp_scalar_array_node_def *node, u16_t len, void *value) +{ + u8_t* var_wr = NULL; + u16_t* var_wr_len; + + switch (node->oid) { + case 4: /* sysContact */ + var_wr = syscontact_wr; + var_wr_len = syscontact_wr_len; + break; + case 5: /* sysName */ + var_wr = sysname_wr; + var_wr_len = sysname_wr_len; + break; + case 6: /* sysLocation */ + var_wr = syslocation_wr; + var_wr_len = syslocation_wr_len; + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_set_value(): unknown id: %"S32_F"\n", node->oid)); + return SNMP_ERR_GENERROR; + } + + /* no need to check size of target buffer, this was already done in set_test method */ + LWIP_ASSERT("", var_wr != NULL); + MEMCPY(var_wr, value, len); + + if (var_wr_len == NULL) { + /* add terminating 0 */ + var_wr[len] = 0; + } else { + *var_wr_len = len; + } + + return SNMP_ERR_NOERROR; +} + +static const struct snmp_scalar_array_node_def system_nodes[] = { + {1, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* sysDescr */ + {2, SNMP_ASN1_TYPE_OBJECT_ID, SNMP_NODE_INSTANCE_READ_ONLY}, /* sysObjectID */ + {3, SNMP_ASN1_TYPE_TIMETICKS, SNMP_NODE_INSTANCE_READ_ONLY}, /* sysUpTime */ + {4, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_WRITE}, /* sysContact */ + {5, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_WRITE}, /* sysName */ + {6, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_WRITE}, /* sysLocation */ + {7, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY} /* sysServices */ +}; + +const struct snmp_scalar_array_node snmp_mib2_system_node = SNMP_SCALAR_CREATE_ARRAY_NODE(1, system_nodes, system_get_value, system_set_test, system_set_value); + +#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_tcp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_tcp.c new file mode 100644 index 0000000..21f6965 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_tcp.c @@ -0,0 +1,594 @@ +/** + * @file + * Management Information Base II (RFC1213) TCP objects and functions. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Dirk Ziegelmeier <dziegel@gmx.de> + * Christiaan Simons <christiaan.simons@axon.tv> + */ + +#include "lwip/snmp.h" +#include "lwip/apps/snmp.h" +#include "lwip/apps/snmp_core.h" +#include "lwip/apps/snmp_mib2.h" +#include "lwip/apps/snmp_table.h" +#include "lwip/apps/snmp_scalar.h" +#include "lwip/tcp.h" +#include "lwip/priv/tcp_priv.h" +#include "lwip/stats.h" + +#include <string.h> + +#if LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_TCP + +#if SNMP_USE_NETCONN +#define SYNC_NODE_NAME(node_name) node_name ## _synced +#define CREATE_LWIP_SYNC_NODE(oid, node_name) \ + static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks); +#else +#define SYNC_NODE_NAME(node_name) node_name +#define CREATE_LWIP_SYNC_NODE(oid, node_name) +#endif + +/* --- tcp .1.3.6.1.2.1.6 ----------------------------------------------------- */ + +static s16_t +tcp_get_value(struct snmp_node_instance* instance, void* value) +{ + u32_t *uint_ptr = (u32_t*)value; + s32_t *sint_ptr = (s32_t*)value; + + switch (instance->node->oid) { + case 1: /* tcpRtoAlgorithm, vanj(4) */ + *sint_ptr = 4; + return sizeof(*sint_ptr); + case 2: /* tcpRtoMin */ + /* @todo not the actual value, a guess, + needs to be calculated */ + *sint_ptr = 1000; + return sizeof(*sint_ptr); + case 3: /* tcpRtoMax */ + /* @todo not the actual value, a guess, + needs to be calculated */ + *sint_ptr = 60000; + return sizeof(*sint_ptr); + case 4: /* tcpMaxConn */ + *sint_ptr = MEMP_NUM_TCP_PCB; + return sizeof(*sint_ptr); + case 5: /* tcpActiveOpens */ + *uint_ptr = STATS_GET(mib2.tcpactiveopens); + return sizeof(*uint_ptr); + case 6: /* tcpPassiveOpens */ + *uint_ptr = STATS_GET(mib2.tcppassiveopens); + return sizeof(*uint_ptr); + case 7: /* tcpAttemptFails */ + *uint_ptr = STATS_GET(mib2.tcpattemptfails); + return sizeof(*uint_ptr); + case 8: /* tcpEstabResets */ + *uint_ptr = STATS_GET(mib2.tcpestabresets); + return sizeof(*uint_ptr); + case 9: /* tcpCurrEstab */ + { + u16_t tcpcurrestab = 0; + struct tcp_pcb *pcb = tcp_active_pcbs; + while (pcb != NULL) { + if ((pcb->state == ESTABLISHED) || + (pcb->state == CLOSE_WAIT)) { + tcpcurrestab++; + } + pcb = pcb->next; + } + *uint_ptr = tcpcurrestab; + } + return sizeof(*uint_ptr); + case 10: /* tcpInSegs */ + *uint_ptr = STATS_GET(mib2.tcpinsegs); + return sizeof(*uint_ptr); + case 11: /* tcpOutSegs */ + *uint_ptr = STATS_GET(mib2.tcpoutsegs); + return sizeof(*uint_ptr); + case 12: /* tcpRetransSegs */ + *uint_ptr = STATS_GET(mib2.tcpretranssegs); + return sizeof(*uint_ptr); + case 14: /* tcpInErrs */ + *uint_ptr = STATS_GET(mib2.tcpinerrs); + return sizeof(*uint_ptr); + case 15: /* tcpOutRsts */ + *uint_ptr = STATS_GET(mib2.tcpoutrsts); + return sizeof(*uint_ptr); + case 17: /* tcpHCInSegs */ + memset(value, 0, 2*sizeof(u32_t)); /* not supported */ + return 2*sizeof(u32_t); + case 18: /* tcpHCOutSegs */ + memset(value, 0, 2*sizeof(u32_t)); /* not supported */ + return 2*sizeof(u32_t); + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcp_get_value(): unknown id: %"S32_F"\n", instance->node->oid)); + break; + } + + return 0; +} + +/* --- tcpConnTable --- */ + +#if LWIP_IPV4 + +/* list of allowed value ranges for incoming OID */ +static const struct snmp_oid_range tcp_ConnTable_oid_ranges[] = { + { 0, 0xff }, /* IP A */ + { 0, 0xff }, /* IP B */ + { 0, 0xff }, /* IP C */ + { 0, 0xff }, /* IP D */ + { 0, 0xffff }, /* Port */ + { 0, 0xff }, /* IP A */ + { 0, 0xff }, /* IP B */ + { 0, 0xff }, /* IP C */ + { 0, 0xff }, /* IP D */ + { 0, 0xffff } /* Port */ +}; + +static snmp_err_t +tcp_ConnTable_get_cell_value_core(struct tcp_pcb *pcb, const u32_t* column, union snmp_variant_value* value, u32_t* value_len) +{ + LWIP_UNUSED_ARG(value_len); + + /* value */ + switch (*column) { + case 1: /* tcpConnState */ + value->u32 = pcb->state + 1; + break; + case 2: /* tcpConnLocalAddress */ + value->u32 = ip_2_ip4(&pcb->local_ip)->addr; + break; + case 3: /* tcpConnLocalPort */ + value->u32 = pcb->local_port; + break; + case 4: /* tcpConnRemAddress */ + if (pcb->state == LISTEN) { + value->u32 = IP4_ADDR_ANY4->addr; + } else { + value->u32 = ip_2_ip4(&pcb->remote_ip)->addr; + } + break; + case 5: /* tcpConnRemPort */ + if (pcb->state == LISTEN) { + value->u32 = 0; + } else { + value->u32 = pcb->remote_port; + } + break; + default: + LWIP_ASSERT("invalid id", 0); + return SNMP_ERR_NOSUCHINSTANCE; + } + + return SNMP_ERR_NOERROR; +} + +static snmp_err_t +tcp_ConnTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len) +{ + u8_t i; + ip4_addr_t local_ip; + ip4_addr_t remote_ip; + u16_t local_port; + u16_t remote_port; + struct tcp_pcb *pcb; + + /* check if incoming OID length and if values are in plausible range */ + if (!snmp_oid_in_range(row_oid, row_oid_len, tcp_ConnTable_oid_ranges, LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges))) { + return SNMP_ERR_NOSUCHINSTANCE; + } + + /* get IPs and ports from incoming OID */ + snmp_oid_to_ip4(&row_oid[0], &local_ip); /* we know it succeeds because of oid_in_range check above */ + local_port = (u16_t)row_oid[4]; + snmp_oid_to_ip4(&row_oid[5], &remote_ip); /* we know it succeeds because of oid_in_range check above */ + remote_port = (u16_t)row_oid[9]; + + /* find tcp_pcb with requested ips and ports */ + for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_lists); i++) { + pcb = *tcp_pcb_lists[i]; + + while (pcb != NULL) { + /* do local IP and local port match? */ + if (IP_IS_V4_VAL(pcb->local_ip) && + ip4_addr_cmp(&local_ip, ip_2_ip4(&pcb->local_ip)) && (local_port == pcb->local_port)) { + + /* PCBs in state LISTEN are not connected and have no remote_ip or remote_port */ + if (pcb->state == LISTEN) { + if (ip4_addr_cmp(&remote_ip, IP4_ADDR_ANY4) && (remote_port == 0)) { + /* fill in object properties */ + return tcp_ConnTable_get_cell_value_core(pcb, column, value, value_len); + } + } else { + if (IP_IS_V4_VAL(pcb->remote_ip) && + ip4_addr_cmp(&remote_ip, ip_2_ip4(&pcb->remote_ip)) && (remote_port == pcb->remote_port)) { + /* fill in object properties */ + return tcp_ConnTable_get_cell_value_core(pcb, column, value, value_len); + } + } + } + + pcb = pcb->next; + } + } + + /* not found */ + return SNMP_ERR_NOSUCHINSTANCE; +} + +static snmp_err_t +tcp_ConnTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len) +{ + u8_t i; + struct tcp_pcb *pcb; + struct snmp_next_oid_state state; + u32_t result_temp[LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges)]; + + /* init struct to search next oid */ + snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges)); + + /* iterate over all possible OIDs to find the next one */ + for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_lists); i++) { + pcb = *tcp_pcb_lists[i]; + while (pcb != NULL) { + u32_t test_oid[LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges)]; + + if (IP_IS_V4_VAL(pcb->local_ip)) { + snmp_ip4_to_oid(ip_2_ip4(&pcb->local_ip), &test_oid[0]); + test_oid[4] = pcb->local_port; + + /* PCBs in state LISTEN are not connected and have no remote_ip or remote_port */ + if (pcb->state == LISTEN) { + snmp_ip4_to_oid(IP4_ADDR_ANY4, &test_oid[5]); + test_oid[9] = 0; + } else { + if (IP_IS_V6_VAL(pcb->remote_ip)) { /* should never happen */ + continue; + } + snmp_ip4_to_oid(ip_2_ip4(&pcb->remote_ip), &test_oid[5]); + test_oid[9] = pcb->remote_port; + } + + /* check generated OID: is it a candidate for the next one? */ + snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges), pcb); + } + + pcb = pcb->next; + } + } + + /* did we find a next one? */ + if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) { + snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len); + /* fill in object properties */ + return tcp_ConnTable_get_cell_value_core((struct tcp_pcb*)state.reference, column, value, value_len); + } + + /* not found */ + return SNMP_ERR_NOSUCHINSTANCE; +} + +#endif /* LWIP_IPV4 */ + +/* --- tcpConnectionTable --- */ + +static snmp_err_t +tcp_ConnectionTable_get_cell_value_core(const u32_t* column, struct tcp_pcb *pcb, union snmp_variant_value* value) +{ + /* all items except tcpConnectionState and tcpConnectionProcess are declared as not-accessible */ + switch (*column) { + case 7: /* tcpConnectionState */ + value->u32 = pcb->state + 1; + break; + case 8: /* tcpConnectionProcess */ + value->u32 = 0; /* not supported */ + break; + default: + return SNMP_ERR_NOSUCHINSTANCE; + } + + return SNMP_ERR_NOERROR; +} + +static snmp_err_t +tcp_ConnectionTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len) +{ + ip_addr_t local_ip, remote_ip; + u16_t local_port, remote_port; + struct tcp_pcb *pcb; + u8_t idx = 0; + u8_t i; + struct tcp_pcb ** const tcp_pcb_nonlisten_lists[] = {&tcp_bound_pcbs, &tcp_active_pcbs, &tcp_tw_pcbs}; + + LWIP_UNUSED_ARG(value_len); + + /* tcpConnectionLocalAddressType + tcpConnectionLocalAddress + tcpConnectionLocalPort */ + idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len-idx, &local_ip, &local_port); + if (idx == 0) { + return SNMP_ERR_NOSUCHINSTANCE; + } + + /* tcpConnectionRemAddressType + tcpConnectionRemAddress + tcpConnectionRemPort */ + idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len-idx, &remote_ip, &remote_port); + if (idx == 0) { + return SNMP_ERR_NOSUCHINSTANCE; + } + + /* find tcp_pcb with requested ip and port*/ + for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_nonlisten_lists); i++) { + pcb = *tcp_pcb_nonlisten_lists[i]; + + while (pcb != NULL) { + if (ip_addr_cmp(&local_ip, &pcb->local_ip) && + (local_port == pcb->local_port) && + ip_addr_cmp(&remote_ip, &pcb->remote_ip) && + (remote_port == pcb->remote_port)) { + /* fill in object properties */ + return tcp_ConnectionTable_get_cell_value_core(column, pcb, value); + } + pcb = pcb->next; + } + } + + /* not found */ + return SNMP_ERR_NOSUCHINSTANCE; +} + +static snmp_err_t +tcp_ConnectionTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len) +{ + struct tcp_pcb *pcb; + struct snmp_next_oid_state state; + /* 1x tcpConnectionLocalAddressType + 1x OID len + 16x tcpConnectionLocalAddress + 1x tcpConnectionLocalPort + * 1x tcpConnectionRemAddressType + 1x OID len + 16x tcpConnectionRemAddress + 1x tcpConnectionRemPort */ + u32_t result_temp[38]; + u8_t i; + struct tcp_pcb ** const tcp_pcb_nonlisten_lists[] = {&tcp_bound_pcbs, &tcp_active_pcbs, &tcp_tw_pcbs}; + + LWIP_UNUSED_ARG(value_len); + + /* init struct to search next oid */ + snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(result_temp)); + + /* iterate over all possible OIDs to find the next one */ + for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_nonlisten_lists); i++) { + pcb = *tcp_pcb_nonlisten_lists[i]; + + while (pcb != NULL) { + u8_t idx = 0; + u32_t test_oid[LWIP_ARRAYSIZE(result_temp)]; + + /* tcpConnectionLocalAddressType + tcpConnectionLocalAddress + tcpConnectionLocalPort */ + idx += snmp_ip_port_to_oid(&pcb->local_ip, pcb->local_port, &test_oid[idx]); + + /* tcpConnectionRemAddressType + tcpConnectionRemAddress + tcpConnectionRemPort */ + idx += snmp_ip_port_to_oid(&pcb->remote_ip, pcb->remote_port, &test_oid[idx]); + + /* check generated OID: is it a candidate for the next one? */ + snmp_next_oid_check(&state, test_oid, idx, pcb); + + pcb = pcb->next; + } + } + + /* did we find a next one? */ + if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) { + snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len); + /* fill in object properties */ + return tcp_ConnectionTable_get_cell_value_core(column, (struct tcp_pcb*)state.reference, value); + } else { + /* not found */ + return SNMP_ERR_NOSUCHINSTANCE; + } +} + +/* --- tcpListenerTable --- */ + +static snmp_err_t +tcp_ListenerTable_get_cell_value_core(const u32_t* column, union snmp_variant_value* value) +{ + /* all items except tcpListenerProcess are declared as not-accessible */ + switch (*column) { + case 4: /* tcpListenerProcess */ + value->u32 = 0; /* not supported */ + break; + default: + return SNMP_ERR_NOSUCHINSTANCE; + } + + return SNMP_ERR_NOERROR; +} + +static snmp_err_t +tcp_ListenerTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len) +{ + ip_addr_t local_ip; + u16_t local_port; + struct tcp_pcb_listen *pcb; + u8_t idx = 0; + + LWIP_UNUSED_ARG(value_len); + + /* tcpListenerLocalAddressType + tcpListenerLocalAddress + tcpListenerLocalPort */ + idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len-idx, &local_ip, &local_port); + if (idx == 0) { + return SNMP_ERR_NOSUCHINSTANCE; + } + + /* find tcp_pcb with requested ip and port*/ + pcb = tcp_listen_pcbs.listen_pcbs; + while (pcb != NULL) { + if (ip_addr_cmp(&local_ip, &pcb->local_ip) && + (local_port == pcb->local_port)) { + /* fill in object properties */ + return tcp_ListenerTable_get_cell_value_core(column, value); + } + pcb = pcb->next; + } + + /* not found */ + return SNMP_ERR_NOSUCHINSTANCE; +} + +static snmp_err_t +tcp_ListenerTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len) +{ + struct tcp_pcb_listen *pcb; + struct snmp_next_oid_state state; + /* 1x tcpListenerLocalAddressType + 1x OID len + 16x tcpListenerLocalAddress + 1x tcpListenerLocalPort */ + u32_t result_temp[19]; + + LWIP_UNUSED_ARG(value_len); + + /* init struct to search next oid */ + snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(result_temp)); + + /* iterate over all possible OIDs to find the next one */ + pcb = tcp_listen_pcbs.listen_pcbs; + while (pcb != NULL) { + u8_t idx = 0; + u32_t test_oid[LWIP_ARRAYSIZE(result_temp)]; + + /* tcpListenerLocalAddressType + tcpListenerLocalAddress + tcpListenerLocalPort */ + idx += snmp_ip_port_to_oid(&pcb->local_ip, pcb->local_port, &test_oid[idx]); + + /* check generated OID: is it a candidate for the next one? */ + snmp_next_oid_check(&state, test_oid, idx, NULL); + + pcb = pcb->next; + } + + /* did we find a next one? */ + if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) { + snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len); + /* fill in object properties */ + return tcp_ListenerTable_get_cell_value_core(column, value); + } else { + /* not found */ + return SNMP_ERR_NOSUCHINSTANCE; + } +} + +static const struct snmp_scalar_node tcp_RtoAlgorithm = SNMP_SCALAR_CREATE_NODE_READONLY(1, SNMP_ASN1_TYPE_INTEGER, tcp_get_value); +static const struct snmp_scalar_node tcp_RtoMin = SNMP_SCALAR_CREATE_NODE_READONLY(2, SNMP_ASN1_TYPE_INTEGER, tcp_get_value); +static const struct snmp_scalar_node tcp_RtoMax = SNMP_SCALAR_CREATE_NODE_READONLY(3, SNMP_ASN1_TYPE_INTEGER, tcp_get_value); +static const struct snmp_scalar_node tcp_MaxConn = SNMP_SCALAR_CREATE_NODE_READONLY(4, SNMP_ASN1_TYPE_INTEGER, tcp_get_value); +static const struct snmp_scalar_node tcp_ActiveOpens = SNMP_SCALAR_CREATE_NODE_READONLY(5, SNMP_ASN1_TYPE_COUNTER, tcp_get_value); +static const struct snmp_scalar_node tcp_PassiveOpens = SNMP_SCALAR_CREATE_NODE_READONLY(6, SNMP_ASN1_TYPE_COUNTER, tcp_get_value); +static const struct snmp_scalar_node tcp_AttemptFails = SNMP_SCALAR_CREATE_NODE_READONLY(7, SNMP_ASN1_TYPE_COUNTER, tcp_get_value); +static const struct snmp_scalar_node tcp_EstabResets = SNMP_SCALAR_CREATE_NODE_READONLY(8, SNMP_ASN1_TYPE_COUNTER, tcp_get_value); +static const struct snmp_scalar_node tcp_CurrEstab = SNMP_SCALAR_CREATE_NODE_READONLY(9, SNMP_ASN1_TYPE_GAUGE, tcp_get_value); +static const struct snmp_scalar_node tcp_InSegs = SNMP_SCALAR_CREATE_NODE_READONLY(10, SNMP_ASN1_TYPE_COUNTER, tcp_get_value); +static const struct snmp_scalar_node tcp_OutSegs = SNMP_SCALAR_CREATE_NODE_READONLY(11, SNMP_ASN1_TYPE_COUNTER, tcp_get_value); +static const struct snmp_scalar_node tcp_RetransSegs = SNMP_SCALAR_CREATE_NODE_READONLY(12, SNMP_ASN1_TYPE_COUNTER, tcp_get_value); +static const struct snmp_scalar_node tcp_InErrs = SNMP_SCALAR_CREATE_NODE_READONLY(14, SNMP_ASN1_TYPE_COUNTER, tcp_get_value); +static const struct snmp_scalar_node tcp_OutRsts = SNMP_SCALAR_CREATE_NODE_READONLY(15, SNMP_ASN1_TYPE_COUNTER, tcp_get_value); +static const struct snmp_scalar_node tcp_HCInSegs = SNMP_SCALAR_CREATE_NODE_READONLY(17, SNMP_ASN1_TYPE_COUNTER64, tcp_get_value); +static const struct snmp_scalar_node tcp_HCOutSegs = SNMP_SCALAR_CREATE_NODE_READONLY(18, SNMP_ASN1_TYPE_COUNTER64, tcp_get_value); + +#if LWIP_IPV4 +static const struct snmp_table_simple_col_def tcp_ConnTable_columns[] = { + { 1, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnState */ + { 2, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnLocalAddress */ + { 3, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnLocalPort */ + { 4, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnRemAddress */ + { 5, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 } /* tcpConnRemPort */ +}; + +static const struct snmp_table_simple_node tcp_ConnTable = SNMP_TABLE_CREATE_SIMPLE(13, tcp_ConnTable_columns, tcp_ConnTable_get_cell_value, tcp_ConnTable_get_next_cell_instance_and_value); +#endif /* LWIP_IPV4 */ + +static const struct snmp_table_simple_col_def tcp_ConnectionTable_columns[] = { + /* all items except tcpConnectionState and tcpConnectionProcess are declared as not-accessible */ + { 7, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnectionState */ + { 8, SNMP_ASN1_TYPE_UNSIGNED32, SNMP_VARIANT_VALUE_TYPE_U32 } /* tcpConnectionProcess */ +}; + +static const struct snmp_table_simple_node tcp_ConnectionTable = SNMP_TABLE_CREATE_SIMPLE(19, tcp_ConnectionTable_columns, tcp_ConnectionTable_get_cell_value, tcp_ConnectionTable_get_next_cell_instance_and_value); + + +static const struct snmp_table_simple_col_def tcp_ListenerTable_columns[] = { + /* all items except tcpListenerProcess are declared as not-accessible */ + { 4, SNMP_ASN1_TYPE_UNSIGNED32, SNMP_VARIANT_VALUE_TYPE_U32 } /* tcpListenerProcess */ +}; + +static const struct snmp_table_simple_node tcp_ListenerTable = SNMP_TABLE_CREATE_SIMPLE(20, tcp_ListenerTable_columns, tcp_ListenerTable_get_cell_value, tcp_ListenerTable_get_next_cell_instance_and_value); + +/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */ +CREATE_LWIP_SYNC_NODE( 1, tcp_RtoAlgorithm) +CREATE_LWIP_SYNC_NODE( 2, tcp_RtoMin) +CREATE_LWIP_SYNC_NODE( 3, tcp_RtoMax) +CREATE_LWIP_SYNC_NODE( 4, tcp_MaxConn) +CREATE_LWIP_SYNC_NODE( 5, tcp_ActiveOpens) +CREATE_LWIP_SYNC_NODE( 6, tcp_PassiveOpens) +CREATE_LWIP_SYNC_NODE( 7, tcp_AttemptFails) +CREATE_LWIP_SYNC_NODE( 8, tcp_EstabResets) +CREATE_LWIP_SYNC_NODE( 9, tcp_CurrEstab) +CREATE_LWIP_SYNC_NODE(10, tcp_InSegs) +CREATE_LWIP_SYNC_NODE(11, tcp_OutSegs) +CREATE_LWIP_SYNC_NODE(12, tcp_RetransSegs) +#if LWIP_IPV4 +CREATE_LWIP_SYNC_NODE(13, tcp_ConnTable) +#endif /* LWIP_IPV4 */ +CREATE_LWIP_SYNC_NODE(14, tcp_InErrs) +CREATE_LWIP_SYNC_NODE(15, tcp_OutRsts) +CREATE_LWIP_SYNC_NODE(17, tcp_HCInSegs) +CREATE_LWIP_SYNC_NODE(18, tcp_HCOutSegs) +CREATE_LWIP_SYNC_NODE(19, tcp_ConnectionTable) +CREATE_LWIP_SYNC_NODE(20, tcp_ListenerTable) + +static const struct snmp_node* const tcp_nodes[] = { + &SYNC_NODE_NAME(tcp_RtoAlgorithm).node.node, + &SYNC_NODE_NAME(tcp_RtoMin).node.node, + &SYNC_NODE_NAME(tcp_RtoMax).node.node, + &SYNC_NODE_NAME(tcp_MaxConn).node.node, + &SYNC_NODE_NAME(tcp_ActiveOpens).node.node, + &SYNC_NODE_NAME(tcp_PassiveOpens).node.node, + &SYNC_NODE_NAME(tcp_AttemptFails).node.node, + &SYNC_NODE_NAME(tcp_EstabResets).node.node, + &SYNC_NODE_NAME(tcp_CurrEstab).node.node, + &SYNC_NODE_NAME(tcp_InSegs).node.node, + &SYNC_NODE_NAME(tcp_OutSegs).node.node, + &SYNC_NODE_NAME(tcp_RetransSegs).node.node, +#if LWIP_IPV4 + &SYNC_NODE_NAME(tcp_ConnTable).node.node, +#endif /* LWIP_IPV4 */ + &SYNC_NODE_NAME(tcp_InErrs).node.node, + &SYNC_NODE_NAME(tcp_OutRsts).node.node, + &SYNC_NODE_NAME(tcp_HCInSegs).node.node, + &SYNC_NODE_NAME(tcp_HCOutSegs).node.node, + &SYNC_NODE_NAME(tcp_ConnectionTable).node.node, + &SYNC_NODE_NAME(tcp_ListenerTable).node.node +}; + +const struct snmp_tree_node snmp_mib2_tcp_root = SNMP_CREATE_TREE_NODE(6, tcp_nodes); +#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_TCP */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_udp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_udp.c new file mode 100644 index 0000000..6a983df --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_udp.c @@ -0,0 +1,357 @@ +/** + * @file + * Management Information Base II (RFC1213) UDP objects and functions. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Dirk Ziegelmeier <dziegel@gmx.de> + * Christiaan Simons <christiaan.simons@axon.tv> + */ + +#include "lwip/snmp.h" +#include "lwip/apps/snmp.h" +#include "lwip/apps/snmp_core.h" +#include "lwip/apps/snmp_mib2.h" +#include "lwip/apps/snmp_table.h" +#include "lwip/apps/snmp_scalar.h" +#include "lwip/udp.h" +#include "lwip/stats.h" + +#include <string.h> + +#if LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_UDP + +#if SNMP_USE_NETCONN +#define SYNC_NODE_NAME(node_name) node_name ## _synced +#define CREATE_LWIP_SYNC_NODE(oid, node_name) \ + static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks); +#else +#define SYNC_NODE_NAME(node_name) node_name +#define CREATE_LWIP_SYNC_NODE(oid, node_name) +#endif + +/* --- udp .1.3.6.1.2.1.7 ----------------------------------------------------- */ + +static s16_t +udp_get_value(struct snmp_node_instance* instance, void* value) +{ + u32_t *uint_ptr = (u32_t*)value; + + switch (instance->node->oid) { + case 1: /* udpInDatagrams */ + *uint_ptr = STATS_GET(mib2.udpindatagrams); + return sizeof(*uint_ptr); + case 2: /* udpNoPorts */ + *uint_ptr = STATS_GET(mib2.udpnoports); + return sizeof(*uint_ptr); + case 3: /* udpInErrors */ + *uint_ptr = STATS_GET(mib2.udpinerrors); + return sizeof(*uint_ptr); + case 4: /* udpOutDatagrams */ + *uint_ptr = STATS_GET(mib2.udpoutdatagrams); + return sizeof(*uint_ptr); + case 8: /* udpHCInDatagrams */ + memset(value, 0, 2*sizeof(u32_t)); /* not supported */ + return 2*sizeof(u32_t); + case 9: /* udpHCOutDatagrams */ + memset(value, 0, 2*sizeof(u32_t)); /* not supported */ + return 2*sizeof(u32_t); + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("udp_get_value(): unknown id: %"S32_F"\n", instance->node->oid)); + break; + } + + return 0; +} + +/* --- udpEndpointTable --- */ + +static snmp_err_t +udp_endpointTable_get_cell_value_core(const u32_t* column, union snmp_variant_value* value) +{ + /* all items except udpEndpointProcess are declared as not-accessible */ + switch (*column) { + case 8: /* udpEndpointProcess */ + value->u32 = 0; /* not supported */ + break; + default: + return SNMP_ERR_NOSUCHINSTANCE; + } + + return SNMP_ERR_NOERROR; +} + +static snmp_err_t +udp_endpointTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len) +{ + ip_addr_t local_ip, remote_ip; + u16_t local_port, remote_port; + struct udp_pcb *pcb; + u8_t idx = 0; + + LWIP_UNUSED_ARG(value_len); + + /* udpEndpointLocalAddressType + udpEndpointLocalAddress + udpEndpointLocalPort */ + idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len-idx, &local_ip, &local_port); + if (idx == 0) { + return SNMP_ERR_NOSUCHINSTANCE; + } + + /* udpEndpointRemoteAddressType + udpEndpointRemoteAddress + udpEndpointRemotePort */ + idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len-idx, &remote_ip, &remote_port); + if (idx == 0) { + return SNMP_ERR_NOSUCHINSTANCE; + } + + /* udpEndpointInstance */ + if (row_oid_len < (idx+1)) { + return SNMP_ERR_NOSUCHINSTANCE; + } + if (row_oid[idx] != 0) { + return SNMP_ERR_NOSUCHINSTANCE; + } + + /* find udp_pcb with requested ip and port*/ + pcb = udp_pcbs; + while (pcb != NULL) { + if (ip_addr_cmp(&local_ip, &pcb->local_ip) && + (local_port == pcb->local_port) && + ip_addr_cmp(&remote_ip, &pcb->remote_ip) && + (remote_port == pcb->remote_port)) { + /* fill in object properties */ + return udp_endpointTable_get_cell_value_core(column, value); + } + pcb = pcb->next; + } + + /* not found */ + return SNMP_ERR_NOSUCHINSTANCE; +} + +static snmp_err_t +udp_endpointTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len) +{ + struct udp_pcb *pcb; + struct snmp_next_oid_state state; + /* 1x udpEndpointLocalAddressType + 1x OID len + 16x udpEndpointLocalAddress + 1x udpEndpointLocalPort + + * 1x udpEndpointRemoteAddressType + 1x OID len + 16x udpEndpointRemoteAddress + 1x udpEndpointRemotePort + + * 1x udpEndpointInstance = 39 + */ + u32_t result_temp[39]; + + LWIP_UNUSED_ARG(value_len); + + /* init struct to search next oid */ + snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(result_temp)); + + /* iterate over all possible OIDs to find the next one */ + pcb = udp_pcbs; + while (pcb != NULL) { + u32_t test_oid[LWIP_ARRAYSIZE(result_temp)]; + u8_t idx = 0; + + /* udpEndpointLocalAddressType + udpEndpointLocalAddress + udpEndpointLocalPort */ + idx += snmp_ip_port_to_oid(&pcb->local_ip, pcb->local_port, &test_oid[idx]); + + /* udpEndpointRemoteAddressType + udpEndpointRemoteAddress + udpEndpointRemotePort */ + idx += snmp_ip_port_to_oid(&pcb->remote_ip, pcb->remote_port, &test_oid[idx]); + + test_oid[idx] = 0; /* udpEndpointInstance */ + idx++; + + /* check generated OID: is it a candidate for the next one? */ + snmp_next_oid_check(&state, test_oid, idx, NULL); + + pcb = pcb->next; + } + + /* did we find a next one? */ + if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) { + snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len); + /* fill in object properties */ + return udp_endpointTable_get_cell_value_core(column, value); + } else { + /* not found */ + return SNMP_ERR_NOSUCHINSTANCE; + } +} + +/* --- udpTable --- */ + +#if LWIP_IPV4 + +/* list of allowed value ranges for incoming OID */ +static const struct snmp_oid_range udp_Table_oid_ranges[] = { + { 0, 0xff }, /* IP A */ + { 0, 0xff }, /* IP B */ + { 0, 0xff }, /* IP C */ + { 0, 0xff }, /* IP D */ + { 1, 0xffff } /* Port */ +}; + +static snmp_err_t +udp_Table_get_cell_value_core(struct udp_pcb *pcb, const u32_t* column, union snmp_variant_value* value, u32_t* value_len) +{ + LWIP_UNUSED_ARG(value_len); + + switch (*column) { + case 1: /* udpLocalAddress */ + /* set reference to PCB local IP and return a generic node that copies IP4 addresses */ + value->u32 = ip_2_ip4(&pcb->local_ip)->addr; + break; + case 2: /* udpLocalPort */ + /* set reference to PCB local port and return a generic node that copies u16_t values */ + value->u32 = pcb->local_port; + break; + default: + return SNMP_ERR_NOSUCHINSTANCE; + } + + return SNMP_ERR_NOERROR; +} + +static snmp_err_t +udp_Table_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len) +{ + ip4_addr_t ip; + u16_t port; + struct udp_pcb *pcb; + + /* check if incoming OID length and if values are in plausible range */ + if (!snmp_oid_in_range(row_oid, row_oid_len, udp_Table_oid_ranges, LWIP_ARRAYSIZE(udp_Table_oid_ranges))) { + return SNMP_ERR_NOSUCHINSTANCE; + } + + /* get IP and port from incoming OID */ + snmp_oid_to_ip4(&row_oid[0], &ip); /* we know it succeeds because of oid_in_range check above */ + port = (u16_t)row_oid[4]; + + /* find udp_pcb with requested ip and port*/ + pcb = udp_pcbs; + while (pcb != NULL) { + if (IP_IS_V4_VAL(pcb->local_ip)) { + if (ip4_addr_cmp(&ip, ip_2_ip4(&pcb->local_ip)) && (port == pcb->local_port)) { + /* fill in object properties */ + return udp_Table_get_cell_value_core(pcb, column, value, value_len); + } + } + pcb = pcb->next; + } + + /* not found */ + return SNMP_ERR_NOSUCHINSTANCE; +} + +static snmp_err_t +udp_Table_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len) +{ + struct udp_pcb *pcb; + struct snmp_next_oid_state state; + u32_t result_temp[LWIP_ARRAYSIZE(udp_Table_oid_ranges)]; + + /* init struct to search next oid */ + snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(udp_Table_oid_ranges)); + + /* iterate over all possible OIDs to find the next one */ + pcb = udp_pcbs; + while (pcb != NULL) { + u32_t test_oid[LWIP_ARRAYSIZE(udp_Table_oid_ranges)]; + + if (IP_IS_V4_VAL(pcb->local_ip)) { + snmp_ip4_to_oid(ip_2_ip4(&pcb->local_ip), &test_oid[0]); + test_oid[4] = pcb->local_port; + + /* check generated OID: is it a candidate for the next one? */ + snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(udp_Table_oid_ranges), pcb); + } + + pcb = pcb->next; + } + + /* did we find a next one? */ + if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) { + snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len); + /* fill in object properties */ + return udp_Table_get_cell_value_core((struct udp_pcb*)state.reference, column, value, value_len); + } else { + /* not found */ + return SNMP_ERR_NOSUCHINSTANCE; + } +} + +#endif /* LWIP_IPV4 */ + +static const struct snmp_scalar_node udp_inDatagrams = SNMP_SCALAR_CREATE_NODE_READONLY(1, SNMP_ASN1_TYPE_COUNTER, udp_get_value); +static const struct snmp_scalar_node udp_noPorts = SNMP_SCALAR_CREATE_NODE_READONLY(2, SNMP_ASN1_TYPE_COUNTER, udp_get_value); +static const struct snmp_scalar_node udp_inErrors = SNMP_SCALAR_CREATE_NODE_READONLY(3, SNMP_ASN1_TYPE_COUNTER, udp_get_value); +static const struct snmp_scalar_node udp_outDatagrams = SNMP_SCALAR_CREATE_NODE_READONLY(4, SNMP_ASN1_TYPE_COUNTER, udp_get_value); +static const struct snmp_scalar_node udp_HCInDatagrams = SNMP_SCALAR_CREATE_NODE_READONLY(8, SNMP_ASN1_TYPE_COUNTER64, udp_get_value); +static const struct snmp_scalar_node udp_HCOutDatagrams = SNMP_SCALAR_CREATE_NODE_READONLY(9, SNMP_ASN1_TYPE_COUNTER64, udp_get_value); + +#if LWIP_IPV4 +static const struct snmp_table_simple_col_def udp_Table_columns[] = { + { 1, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* udpLocalAddress */ + { 2, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 } /* udpLocalPort */ +}; +static const struct snmp_table_simple_node udp_Table = SNMP_TABLE_CREATE_SIMPLE(5, udp_Table_columns, udp_Table_get_cell_value, udp_Table_get_next_cell_instance_and_value); +#endif /* LWIP_IPV4 */ + +static const struct snmp_table_simple_col_def udp_endpointTable_columns[] = { + /* all items except udpEndpointProcess are declared as not-accessible */ + { 8, SNMP_ASN1_TYPE_UNSIGNED32, SNMP_VARIANT_VALUE_TYPE_U32 } /* udpEndpointProcess */ +}; + +static const struct snmp_table_simple_node udp_endpointTable = SNMP_TABLE_CREATE_SIMPLE(7, udp_endpointTable_columns, udp_endpointTable_get_cell_value, udp_endpointTable_get_next_cell_instance_and_value); + +/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */ +CREATE_LWIP_SYNC_NODE(1, udp_inDatagrams) +CREATE_LWIP_SYNC_NODE(2, udp_noPorts) +CREATE_LWIP_SYNC_NODE(3, udp_inErrors) +CREATE_LWIP_SYNC_NODE(4, udp_outDatagrams) +#if LWIP_IPV4 +CREATE_LWIP_SYNC_NODE(5, udp_Table) +#endif /* LWIP_IPV4 */ +CREATE_LWIP_SYNC_NODE(7, udp_endpointTable) +CREATE_LWIP_SYNC_NODE(8, udp_HCInDatagrams) +CREATE_LWIP_SYNC_NODE(9, udp_HCOutDatagrams) + +static const struct snmp_node* const udp_nodes[] = { + &SYNC_NODE_NAME(udp_inDatagrams).node.node, + &SYNC_NODE_NAME(udp_noPorts).node.node, + &SYNC_NODE_NAME(udp_inErrors).node.node, + &SYNC_NODE_NAME(udp_outDatagrams).node.node, +#if LWIP_IPV4 + &SYNC_NODE_NAME(udp_Table).node.node, +#endif /* LWIP_IPV4 */ + &SYNC_NODE_NAME(udp_endpointTable).node.node, + &SYNC_NODE_NAME(udp_HCInDatagrams).node.node, + &SYNC_NODE_NAME(udp_HCOutDatagrams).node.node +}; + +const struct snmp_tree_node snmp_mib2_udp_root = SNMP_CREATE_TREE_NODE(7, udp_nodes); +#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_UDP */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_msg.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_msg.c new file mode 100644 index 0000000..0cb7ca9 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_msg.c @@ -0,0 +1,1668 @@ +/** + * @file + * SNMP message processing (RFC1157). + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * Copyright (c) 2016 Elias Oenal. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons <christiaan.simons@axon.tv> + * Martin Hentschel <info@cl-soft.de> + * Elias Oenal <lwip@eliasoenal.com> + */ + +#include "lwip/apps/snmp_opts.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "snmp_msg.h" +#include "snmp_asn1.h" +#include "snmp_core_priv.h" +#include "lwip/ip_addr.h" +#include "lwip/stats.h" + +#if LWIP_SNMP_V3 +#include "lwip/apps/snmpv3.h" +#include "snmpv3_priv.h" +#ifdef LWIP_SNMPV3_INCLUDE_ENGINE +#include LWIP_SNMPV3_INCLUDE_ENGINE +#endif +#endif + +#include <string.h> + +/* public (non-static) constants */ +/** SNMP community string */ +const char *snmp_community = SNMP_COMMUNITY; +/** SNMP community string for write access */ +const char *snmp_community_write = SNMP_COMMUNITY_WRITE; +/** SNMP community string for sending traps */ +const char *snmp_community_trap = SNMP_COMMUNITY_TRAP; + +snmp_write_callback_fct snmp_write_callback = NULL; +void* snmp_write_callback_arg = NULL; + +/** + * @ingroup snmp_core + * Returns current SNMP community string. + * @return current SNMP community string + */ +const char * +snmp_get_community(void) +{ + return snmp_community; +} + +/** + * @ingroup snmp_core + * Sets SNMP community string. + * The string itself (its storage) must be valid throughout the whole life of + * program (or until it is changed to sth else). + * + * @param community is a pointer to new community string + */ +void +snmp_set_community(const char * const community) +{ + LWIP_ASSERT("community string is too long!", strlen(community) <= SNMP_MAX_COMMUNITY_STR_LEN); + snmp_community = community; +} + +/** + * @ingroup snmp_core + * Returns current SNMP write-access community string. + * @return current SNMP write-access community string + */ +const char * +snmp_get_community_write(void) +{ + return snmp_community_write; +} + +/** + * @ingroup snmp_traps + * Returns current SNMP community string used for sending traps. + * @return current SNMP community string used for sending traps + */ +const char * +snmp_get_community_trap(void) +{ + return snmp_community_trap; +} + +/** + * @ingroup snmp_core + * Sets SNMP community string for write-access. + * The string itself (its storage) must be valid throughout the whole life of + * program (or until it is changed to sth else). + * + * @param community is a pointer to new write-access community string + */ +void +snmp_set_community_write(const char * const community) +{ + LWIP_ASSERT("community string must not be NULL", community != NULL); + LWIP_ASSERT("community string is too long!", strlen(community) <= SNMP_MAX_COMMUNITY_STR_LEN); + snmp_community_write = community; +} + +/** + * @ingroup snmp_traps + * Sets SNMP community string used for sending traps. + * The string itself (its storage) must be valid throughout the whole life of + * program (or until it is changed to sth else). + * + * @param community is a pointer to new trap community string + */ +void +snmp_set_community_trap(const char * const community) +{ + LWIP_ASSERT("community string is too long!", strlen(community) <= SNMP_MAX_COMMUNITY_STR_LEN); + snmp_community_trap = community; +} + +/** + * @ingroup snmp_core + * Callback fired on every successful write access + */ +void +snmp_set_write_callback(snmp_write_callback_fct write_callback, void* callback_arg) +{ + snmp_write_callback = write_callback; + snmp_write_callback_arg = callback_arg; +} + +/* ----------------------------------------------------------------------- */ +/* forward declarations */ +/* ----------------------------------------------------------------------- */ + +static err_t snmp_process_get_request(struct snmp_request *request); +static err_t snmp_process_getnext_request(struct snmp_request *request); +static err_t snmp_process_getbulk_request(struct snmp_request *request); +static err_t snmp_process_set_request(struct snmp_request *request); + +static err_t snmp_parse_inbound_frame(struct snmp_request *request); +static err_t snmp_prepare_outbound_frame(struct snmp_request *request); +static err_t snmp_complete_outbound_frame(struct snmp_request *request); +static void snmp_execute_write_callbacks(struct snmp_request *request); + + +/* ----------------------------------------------------------------------- */ +/* implementation */ +/* ----------------------------------------------------------------------- */ + +void +snmp_receive(void *handle, struct pbuf *p, const ip_addr_t *source_ip, u16_t port) +{ + err_t err; + struct snmp_request request; + + memset(&request, 0, sizeof(request)); + request.handle = handle; + request.source_ip = source_ip; + request.source_port = port; + request.inbound_pbuf = p; + + snmp_stats.inpkts++; + + err = snmp_parse_inbound_frame(&request); + if (err == ERR_OK) { + err = snmp_prepare_outbound_frame(&request); + if (err == ERR_OK) { + + if (request.error_status == SNMP_ERR_NOERROR) { + /* only process frame if we do not already have an error to return (e.g. all readonly) */ + if (request.request_type == SNMP_ASN1_CONTEXT_PDU_GET_REQ) { + err = snmp_process_get_request(&request); + } else if (request.request_type == SNMP_ASN1_CONTEXT_PDU_GET_NEXT_REQ) { + err = snmp_process_getnext_request(&request); + } else if (request.request_type == SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ) { + err = snmp_process_getbulk_request(&request); + } else if (request.request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ) { + err = snmp_process_set_request(&request); + } + } + + if (err == ERR_OK) { + err = snmp_complete_outbound_frame(&request); + + if (err == ERR_OK) { + err = snmp_sendto(request.handle, request.outbound_pbuf, request.source_ip, request.source_port); + + if ((request.request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ) + && (request.error_status == SNMP_ERR_NOERROR) + && (snmp_write_callback != NULL)) { + /* raise write notification for all written objects */ + snmp_execute_write_callbacks(&request); + } + } + } + } + + if (request.outbound_pbuf != NULL) { + pbuf_free(request.outbound_pbuf); + } + } +} + +static u8_t +snmp_msg_getnext_validate_node_inst(struct snmp_node_instance* node_instance, void* validate_arg) +{ + if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_READ) != SNMP_NODE_INSTANCE_ACCESS_READ) || (node_instance->get_value == NULL)) { + return SNMP_ERR_NOSUCHINSTANCE; + } + + if ((node_instance->asn1_type == SNMP_ASN1_TYPE_COUNTER64) && (((struct snmp_request*)validate_arg)->version == SNMP_VERSION_1)) { + /* according to RFC 2089 skip Counter64 objects in GetNext requests from v1 clients */ + return SNMP_ERR_NOSUCHINSTANCE; + } + + return SNMP_ERR_NOERROR; +} + +static void +snmp_process_varbind(struct snmp_request *request, struct snmp_varbind *vb, u8_t get_next) +{ + err_t err; + struct snmp_node_instance node_instance; + memset(&node_instance, 0, sizeof(node_instance)); + + if (get_next) { + struct snmp_obj_id result_oid; + request->error_status = snmp_get_next_node_instance_from_oid(vb->oid.id, vb->oid.len, snmp_msg_getnext_validate_node_inst, request, &result_oid, &node_instance); + + if (request->error_status == SNMP_ERR_NOERROR) { + snmp_oid_assign(&vb->oid, result_oid.id, result_oid.len); + } + } else { + request->error_status = snmp_get_node_instance_from_oid(vb->oid.id, vb->oid.len, &node_instance); + + if (request->error_status == SNMP_ERR_NOERROR) { + /* use 'getnext_validate' method for validation to avoid code duplication (some checks have to be executed here) */ + request->error_status = snmp_msg_getnext_validate_node_inst(&node_instance, request); + + if (request->error_status != SNMP_ERR_NOERROR) { + if (node_instance.release_instance != NULL) { + node_instance.release_instance(&node_instance); + } + } + } + } + + if (request->error_status != SNMP_ERR_NOERROR) { + if (request->error_status >= SNMP_VARBIND_EXCEPTION_OFFSET) { + if ((request->version == SNMP_VERSION_2c) || request->version == SNMP_VERSION_3) { + /* in SNMP v2c a varbind related exception is stored in varbind and not in frame header */ + vb->type = (SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_CLASS_CONTEXT | (request->error_status & SNMP_VARBIND_EXCEPTION_MASK)); + vb->value_len = 0; + + err = snmp_append_outbound_varbind(&(request->outbound_pbuf_stream), vb); + if (err == ERR_OK) { + /* we stored the exception in varbind -> go on */ + request->error_status = SNMP_ERR_NOERROR; + } else if (err == ERR_BUF) { + request->error_status = SNMP_ERR_TOOBIG; + } else { + request->error_status = SNMP_ERR_GENERROR; + } + } + } else { + /* according to RFC 1157/1905, all other errors only return genError */ + request->error_status = SNMP_ERR_GENERROR; + } + } else { + s16_t len = node_instance.get_value(&node_instance, vb->value); + vb->type = node_instance.asn1_type; + + if(len >= 0) { + vb->value_len = (u16_t)len; /* cast is OK because we checked >= 0 above */ + + LWIP_ASSERT("SNMP_MAX_VALUE_SIZE is configured too low", (vb->value_len & ~SNMP_GET_VALUE_RAW_DATA) <= SNMP_MAX_VALUE_SIZE); + err = snmp_append_outbound_varbind(&request->outbound_pbuf_stream, vb); + + if (err == ERR_BUF) { + request->error_status = SNMP_ERR_TOOBIG; + } else if (err != ERR_OK) { + request->error_status = SNMP_ERR_GENERROR; + } + } else { + request->error_status = SNMP_ERR_GENERROR; + } + + if (node_instance.release_instance != NULL) { + node_instance.release_instance(&node_instance); + } + } +} + + +/** + * Service an internal or external event for SNMP GET. + * + * @param request points to the associated message process state + */ +static err_t +snmp_process_get_request(struct snmp_request *request) +{ + snmp_vb_enumerator_err_t err; + struct snmp_varbind vb; + vb.value = request->value_buffer; + + LWIP_DEBUGF(SNMP_DEBUG, ("SNMP get request\n")); + + while (request->error_status == SNMP_ERR_NOERROR) { + err = snmp_vb_enumerator_get_next(&request->inbound_varbind_enumerator, &vb); + if (err == SNMP_VB_ENUMERATOR_ERR_OK) { + if ((vb.type == SNMP_ASN1_TYPE_NULL) && (vb.value_len == 0)) { + snmp_process_varbind(request, &vb, 0); + } else { + request->error_status = SNMP_ERR_GENERROR; + } + } else if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) { + /* no more varbinds in request */ + break; + } else if (err == SNMP_VB_ENUMERATOR_ERR_ASN1ERROR) { + /* malformed ASN.1, don't answer */ + return ERR_ARG; + } else { + request->error_status = SNMP_ERR_GENERROR; + } + } + + return ERR_OK; +} + +/** + * Service an internal or external event for SNMP GET. + * + * @param request points to the associated message process state + */ +static err_t +snmp_process_getnext_request(struct snmp_request *request) +{ + snmp_vb_enumerator_err_t err; + struct snmp_varbind vb; + vb.value = request->value_buffer; + + LWIP_DEBUGF(SNMP_DEBUG, ("SNMP get-next request\n")); + + while (request->error_status == SNMP_ERR_NOERROR) { + err = snmp_vb_enumerator_get_next(&request->inbound_varbind_enumerator, &vb); + if (err == SNMP_VB_ENUMERATOR_ERR_OK) { + if ((vb.type == SNMP_ASN1_TYPE_NULL) && (vb.value_len == 0)) { + snmp_process_varbind(request, &vb, 1); + } else { + request->error_status = SNMP_ERR_GENERROR; + } + } else if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) { + /* no more varbinds in request */ + break; + } else if (err == SNMP_VB_ENUMERATOR_ERR_ASN1ERROR) { + /* malformed ASN.1, don't answer */ + return ERR_ARG; + } else { + request->error_status = SNMP_ERR_GENERROR; + } + } + + return ERR_OK; +} + +/** + * Service an internal or external event for SNMP GETBULKT. + * + * @param request points to the associated message process state + */ +static err_t +snmp_process_getbulk_request(struct snmp_request *request) +{ + snmp_vb_enumerator_err_t err; + s32_t non_repeaters = request->non_repeaters; + s32_t repetitions; + u16_t repetition_offset = 0; + struct snmp_varbind_enumerator repetition_varbind_enumerator; + struct snmp_varbind vb; + vb.value = request->value_buffer; + + if (SNMP_LWIP_GETBULK_MAX_REPETITIONS > 0) { + repetitions = LWIP_MIN(request->max_repetitions, SNMP_LWIP_GETBULK_MAX_REPETITIONS); + } else { + repetitions = request->max_repetitions; + } + + LWIP_DEBUGF(SNMP_DEBUG, ("SNMP get-bulk request\n")); + + /* process non repeaters and first repetition */ + while (request->error_status == SNMP_ERR_NOERROR) { + if (non_repeaters == 0) { + repetition_offset = request->outbound_pbuf_stream.offset; + + if (repetitions == 0) { + /* do not resolve repeaters when repetitions is set to 0 */ + break; + } + repetitions--; + } + + err = snmp_vb_enumerator_get_next(&request->inbound_varbind_enumerator, &vb); + if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) { + /* no more varbinds in request */ + break; + } else if (err == SNMP_VB_ENUMERATOR_ERR_ASN1ERROR) { + /* malformed ASN.1, don't answer */ + return ERR_ARG; + } else if ((err != SNMP_VB_ENUMERATOR_ERR_OK) || (vb.type != SNMP_ASN1_TYPE_NULL) || (vb.value_len != 0)) { + request->error_status = SNMP_ERR_GENERROR; + } else { + snmp_process_varbind(request, &vb, 1); + non_repeaters--; + } + } + + /* process repetitions > 1 */ + while ((request->error_status == SNMP_ERR_NOERROR) && (repetitions > 0) && (request->outbound_pbuf_stream.offset != repetition_offset)) { + + u8_t all_endofmibview = 1; + + snmp_vb_enumerator_init(&repetition_varbind_enumerator, request->outbound_pbuf, repetition_offset, request->outbound_pbuf_stream.offset - repetition_offset); + repetition_offset = request->outbound_pbuf_stream.offset; /* for next loop */ + + while (request->error_status == SNMP_ERR_NOERROR) { + vb.value = NULL; /* do NOT decode value (we enumerate outbound buffer here, so all varbinds have values assigned) */ + err = snmp_vb_enumerator_get_next(&repetition_varbind_enumerator, &vb); + if (err == SNMP_VB_ENUMERATOR_ERR_OK) { + vb.value = request->value_buffer; + snmp_process_varbind(request, &vb, 1); + + if (request->error_status != SNMP_ERR_NOERROR) { + /* already set correct error-index (here it cannot be taken from inbound varbind enumerator) */ + request->error_index = request->non_repeaters + repetition_varbind_enumerator.varbind_count; + } else if (vb.type != (SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTEXT_VARBIND_END_OF_MIB_VIEW)) { + all_endofmibview = 0; + } + } else if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) { + /* no more varbinds in request */ + break; + } else { + LWIP_DEBUGF(SNMP_DEBUG, ("Very strange, we cannot parse the varbind output that we created just before!")); + request->error_status = SNMP_ERR_GENERROR; + request->error_index = request->non_repeaters + repetition_varbind_enumerator.varbind_count; + } + } + + if ((request->error_status == SNMP_ERR_NOERROR) && all_endofmibview) { + /* stop when all varbinds in a loop return EndOfMibView */ + break; + } + + repetitions--; + } + + if (request->error_status == SNMP_ERR_TOOBIG) { + /* for GetBulk it is ok, if not all requested variables fit into the response -> just return the varbinds added so far */ + request->error_status = SNMP_ERR_NOERROR; + } + + return ERR_OK; +} + +/** + * Service an internal or external event for SNMP SET. + * + * @param request points to the associated message process state + */ +static err_t +snmp_process_set_request(struct snmp_request *request) +{ + snmp_vb_enumerator_err_t err; + struct snmp_varbind vb; + vb.value = request->value_buffer; + + LWIP_DEBUGF(SNMP_DEBUG, ("SNMP set request\n")); + + /* perform set test on all objects */ + while (request->error_status == SNMP_ERR_NOERROR) { + err = snmp_vb_enumerator_get_next(&request->inbound_varbind_enumerator, &vb); + if (err == SNMP_VB_ENUMERATOR_ERR_OK) { + struct snmp_node_instance node_instance; + memset(&node_instance, 0, sizeof(node_instance)); + + request->error_status = snmp_get_node_instance_from_oid(vb.oid.id, vb.oid.len, &node_instance); + if (request->error_status == SNMP_ERR_NOERROR) { + if (node_instance.asn1_type != vb.type) { + request->error_status = SNMP_ERR_WRONGTYPE; + } else if (((node_instance.access & SNMP_NODE_INSTANCE_ACCESS_WRITE) != SNMP_NODE_INSTANCE_ACCESS_WRITE) || (node_instance.set_value == NULL)) { + request->error_status = SNMP_ERR_NOTWRITABLE; + } else { + if (node_instance.set_test != NULL) { + request->error_status = node_instance.set_test(&node_instance, vb.value_len, vb.value); + } + } + + if (node_instance.release_instance != NULL) { + node_instance.release_instance(&node_instance); + } + } + } else if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) { + /* no more varbinds in request */ + break; + } else if (err == SNMP_VB_ENUMERATOR_ERR_INVALIDLENGTH) { + request->error_status = SNMP_ERR_WRONGLENGTH; + } else if (err == SNMP_VB_ENUMERATOR_ERR_ASN1ERROR) { + /* malformed ASN.1, don't answer */ + return ERR_ARG; + } else { + request->error_status = SNMP_ERR_GENERROR; + } + } + + /* perform real set operation on all objects */ + if (request->error_status == SNMP_ERR_NOERROR) { + snmp_vb_enumerator_init(&request->inbound_varbind_enumerator, request->inbound_pbuf, request->inbound_varbind_offset, request->inbound_varbind_len); + while (request->error_status == SNMP_ERR_NOERROR) { + err = snmp_vb_enumerator_get_next(&request->inbound_varbind_enumerator, &vb); + if (err == SNMP_VB_ENUMERATOR_ERR_OK) { + struct snmp_node_instance node_instance; + memset(&node_instance, 0, sizeof(node_instance)); + request->error_status = snmp_get_node_instance_from_oid(vb.oid.id, vb.oid.len, &node_instance); + if (request->error_status == SNMP_ERR_NOERROR) { + if (node_instance.set_value(&node_instance, vb.value_len, vb.value) != SNMP_ERR_NOERROR) { + if (request->inbound_varbind_enumerator.varbind_count == 1) { + request->error_status = SNMP_ERR_COMMITFAILED; + } else { + /* we cannot undo the set operations done so far */ + request->error_status = SNMP_ERR_UNDOFAILED; + } + } + + if (node_instance.release_instance != NULL) { + node_instance.release_instance(&node_instance); + } + } + } else if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) { + /* no more varbinds in request */ + break; + } else { + /* first time enumerating varbinds work but second time not, although nothing should have changed in between ??? */ + request->error_status = SNMP_ERR_GENERROR; + } + } + } + + return ERR_OK; +} + +#define PARSE_EXEC(code, retValue) \ + if ((code) != ERR_OK) { \ + LWIP_DEBUGF(SNMP_DEBUG, ("Malformed ASN.1 detected.\n")); \ + snmp_stats.inasnparseerrs++; \ + return retValue; \ + } + +#define PARSE_ASSERT(cond, retValue) \ + if (!(cond)) { \ + LWIP_DEBUGF(SNMP_DEBUG, ("SNMP parse assertion failed!: " # cond)); \ + snmp_stats.inasnparseerrs++; \ + return retValue; \ + } + +#define BUILD_EXEC(code, retValue) \ + if ((code) != ERR_OK) { \ + LWIP_DEBUGF(SNMP_DEBUG, ("SNMP error during creation of outbound frame!: " # code)); \ + return retValue; \ + } + +#define IF_PARSE_EXEC(code) PARSE_EXEC(code, ERR_ARG) +#define IF_PARSE_ASSERT(code) PARSE_ASSERT(code, ERR_ARG) + +/** + * Checks and decodes incoming SNMP message header, logs header errors. + * + * @param request points to the current message request state return + * @return + * - ERR_OK SNMP header is sane and accepted + * - ERR_VAL SNMP header is either malformed or rejected + */ +static err_t +snmp_parse_inbound_frame(struct snmp_request *request) +{ + struct snmp_pbuf_stream pbuf_stream; + struct snmp_asn1_tlv tlv; + s32_t parent_tlv_value_len; + s32_t s32_value; + err_t err; + + IF_PARSE_EXEC(snmp_pbuf_stream_init(&pbuf_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len)); + + /* decode main container consisting of version, community and PDU */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT((tlv.type == SNMP_ASN1_TYPE_SEQUENCE) && (tlv.value_len == pbuf_stream.length)); + parent_tlv_value_len = tlv.value_len; + + /* decode version */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER); + parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value)); + if ((s32_value != SNMP_VERSION_1) && + (s32_value != SNMP_VERSION_2c) +#if LWIP_SNMP_V3 + && (s32_value != SNMP_VERSION_3) +#endif + ) + { + /* unsupported SNMP version */ + snmp_stats.inbadversions++; + return ERR_ARG; + } + request->version = (u8_t)s32_value; + +#if LWIP_SNMP_V3 + if (request->version == SNMP_VERSION_3) { + u16_t u16_value; + u16_t inbound_msgAuthenticationParameters_offset; + + /* SNMPv3 doesn't use communities */ + /* @todo: Differentiate read/write access */ + strcpy((char*)request->community, snmp_community); + request->community_strlen = (u16_t)strlen(snmp_community); + + /* RFC3414 globalData */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_SEQUENCE); + parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + /* decode msgID */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER); + parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value)); + request->msg_id = s32_value; + + /* decode msgMaxSize */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER); + parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value)); + request->msg_max_size = s32_value; + + /* decode msgFlags */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING); + parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value)); + request->msg_flags = (u8_t)s32_value; + + /* decode msgSecurityModel */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER); + parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value)); + request->msg_security_model = s32_value; + + /* RFC3414 msgSecurityParameters + * The User-based Security Model defines the contents of the OCTET + * STRING as a SEQUENCE. + * + * We skip the protective dummy OCTET STRING header + * to access the SEQUENCE header. + */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING); + parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + /* msgSecurityParameters SEQUENCE header */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_SEQUENCE); + parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + /* decode msgAuthoritativeEngineID */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING); + parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_authoritative_engine_id, + &u16_value, SNMP_V3_MAX_ENGINE_ID_LENGTH)); + request->msg_authoritative_engine_id_len = (u8_t)u16_value; + + /* msgAuthoritativeEngineBoots */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER); + parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->msg_authoritative_engine_boots)); + + /* msgAuthoritativeEngineTime */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER); + parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->msg_authoritative_engine_time)); + /* @todo: Implement time window checking */ + + /* msgUserName */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING); + parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_user_name, + &u16_value, SNMP_V3_MAX_USER_LENGTH)); + request->msg_user_name_len = (u8_t)u16_value; + /* @todo: Implement unknown user error response */ + IF_PARSE_EXEC(snmpv3_get_user((char*)request->msg_user_name, NULL, NULL, NULL, NULL)); + + /* msgAuthenticationParameters */ + memset(request->msg_authentication_parameters, 0, SNMP_V3_MAX_AUTH_PARAM_LENGTH); + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING); + parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + /* Remember position */ + inbound_msgAuthenticationParameters_offset = pbuf_stream.offset; + LWIP_UNUSED_ARG(inbound_msgAuthenticationParameters_offset); + /* Read auth parameters */ + IF_PARSE_ASSERT(tlv.value_len <= SNMP_V3_MAX_AUTH_PARAM_LENGTH); + IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_authentication_parameters, + &u16_value, tlv.value_len)); + +#if LWIP_SNMP_V3_CRYPTO + if (request->msg_flags & SNMP_V3_AUTH_FLAG) { + const u8_t zero_arr[SNMP_V3_MAX_AUTH_PARAM_LENGTH] = { 0 }; + u8_t key[20]; + u8_t algo; + u8_t hmac[LWIP_MAX(SNMP_V3_SHA_LEN, SNMP_V3_MD5_LEN)]; + struct snmp_pbuf_stream auth_stream; + + /* Rewind stream */ + IF_PARSE_EXEC(snmp_pbuf_stream_init(&pbuf_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len)); + IF_PARSE_EXEC(snmp_pbuf_stream_seek_abs(&pbuf_stream, inbound_msgAuthenticationParameters_offset)); + /* Set auth parameters to zero for verification */ + IF_PARSE_EXEC(snmp_asn1_enc_raw(&pbuf_stream, zero_arr, tlv.value_len)); + + /* Verify authentication */ + IF_PARSE_EXEC(snmp_pbuf_stream_init(&auth_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len)); + + IF_PARSE_EXEC(snmpv3_get_user((char*)request->msg_user_name, &algo, key, NULL, NULL)); + IF_PARSE_EXEC(snmpv3_auth(&auth_stream, request->inbound_pbuf->tot_len, key, algo, hmac)); + /* @todo: Implement error response */ + IF_PARSE_EXEC(memcmp(request->msg_authentication_parameters, hmac, SNMP_V3_MAX_AUTH_PARAM_LENGTH)); + } +#else + /* Ungraceful exit if we encounter cryptography and don't support it. + * @todo: Implement error response + */ + IF_PARSE_ASSERT(!(request->msg_flags & (SNMP_V3_AUTH_FLAG | SNMP_V3_PRIV_FLAG))); +#endif + + /* msgPrivacyParameters */ + memset(request->msg_privacy_parameters, 0, SNMP_V3_MAX_PRIV_PARAM_LENGTH); + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING); + parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_privacy_parameters, + &u16_value, SNMP_V3_MAX_PRIV_PARAM_LENGTH)); + +#if LWIP_SNMP_V3_CRYPTO + /* Decrypt message */ + if (request->msg_flags & SNMP_V3_PRIV_FLAG) { + u8_t key[20]; + u8_t algo; + + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING); + parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + IF_PARSE_EXEC(snmpv3_get_user((char*)request->msg_user_name, NULL, NULL, &algo, key)); + IF_PARSE_EXEC(snmpv3_crypt(&pbuf_stream, tlv.value_len, key, + request->msg_privacy_parameters, request->msg_authoritative_engine_boots, + request->msg_authoritative_engine_time, algo, SNMP_V3_PRIV_MODE_DECRYPT)); + } +#endif + + /* Scoped PDU + * Encryption context + */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_SEQUENCE); + parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + /* contextEngineID */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING); + parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->context_engine_id, + &u16_value, SNMP_V3_MAX_ENGINE_ID_LENGTH)); + request->context_engine_id_len = (u8_t)u16_value; + + /* contextName */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING); + parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->context_name, + &u16_value, SNMP_V3_MAX_ENGINE_ID_LENGTH)); + request->context_name_len = (u8_t)u16_value; + } else +#endif + { + /* decode community */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING); + parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + err = snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->community, &request->community_strlen, SNMP_MAX_COMMUNITY_STR_LEN); + if (err == ERR_MEM) { + /* community string does not fit in our buffer -> its too long -> its invalid */ + request->community_strlen = 0; + snmp_pbuf_stream_seek(&pbuf_stream, tlv.value_len); + } else { + IF_PARSE_ASSERT(err == ERR_OK); + } + /* add zero terminator */ + request->community[request->community_strlen] = 0; + } + + /* decode PDU type (next container level) */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.value_len <= pbuf_stream.length); + request->inbound_padding_len = pbuf_stream.length - tlv.value_len; + parent_tlv_value_len = tlv.value_len; + + /* validate PDU type */ + switch(tlv.type) { + case (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_REQ): + /* GetRequest PDU */ + snmp_stats.ingetrequests++; + break; + case (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_NEXT_REQ): + /* GetNextRequest PDU */ + snmp_stats.ingetnexts++; + break; + case (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ): + /* GetBulkRequest PDU */ + if (request->version < SNMP_VERSION_2c) { + /* RFC2089: invalid, drop packet */ + return ERR_ARG; + } + break; + case (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_SET_REQ): + /* SetRequest PDU */ + snmp_stats.insetrequests++; + break; + default: + /* unsupported input PDU for this agent (no parse error) */ + LWIP_DEBUGF(SNMP_DEBUG, ("Unknown/Invalid SNMP PDU type received: %d", tlv.type)); \ + return ERR_ARG; + break; + } + request->request_type = tlv.type & SNMP_ASN1_DATATYPE_MASK; + + /* validate community (do this after decoding PDU type because we don't want to increase 'inbadcommunitynames' for wrong frame types */ + if (request->community_strlen == 0) { + /* community string was too long or really empty*/ + snmp_stats.inbadcommunitynames++; + snmp_authfail_trap(); + return ERR_ARG; + } else if (request->request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ) { + if (snmp_community_write[0] == 0) { + /* our write community is empty, that means all our objects are readonly */ + request->error_status = SNMP_ERR_NOTWRITABLE; + request->error_index = 1; + } else if (strncmp(snmp_community_write, (const char*)request->community, SNMP_MAX_COMMUNITY_STR_LEN) != 0) { + /* community name does not match */ + snmp_stats.inbadcommunitynames++; + snmp_authfail_trap(); + return ERR_ARG; + } + } else { + if (strncmp(snmp_community, (const char*)request->community, SNMP_MAX_COMMUNITY_STR_LEN) != 0) { + /* community name does not match */ + snmp_stats.inbadcommunitynames++; + snmp_authfail_trap(); + return ERR_ARG; + } + } + + /* decode request ID */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER); + parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->request_id)); + + /* decode error status / non-repeaters */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER); + parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + if (request->request_type == SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ) { + IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->non_repeaters)); + if (request->non_repeaters < 0) { + /* RFC 1905, 4.2.3 */ + request->non_repeaters = 0; + } + } else { + /* only check valid value, don't touch 'request->error_status', maybe a response error status was already set to above; */ + IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value)); + IF_PARSE_ASSERT(s32_value == SNMP_ERR_NOERROR); + } + + /* decode error index / max-repetitions */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER); + parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + if (request->request_type == SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ) { + IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->max_repetitions)); + if (request->max_repetitions < 0) { + /* RFC 1905, 4.2.3 */ + request->max_repetitions = 0; + } + } else { + IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->error_index)); + IF_PARSE_ASSERT(s32_value == 0); + } + + /* decode varbind-list type (next container level) */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT((tlv.type == SNMP_ASN1_TYPE_SEQUENCE) && (tlv.value_len <= pbuf_stream.length)); + + request->inbound_varbind_offset = pbuf_stream.offset; + request->inbound_varbind_len = pbuf_stream.length - request->inbound_padding_len; + snmp_vb_enumerator_init(&(request->inbound_varbind_enumerator), request->inbound_pbuf, request->inbound_varbind_offset, request->inbound_varbind_len); + + return ERR_OK; +} + +#define OF_BUILD_EXEC(code) BUILD_EXEC(code, ERR_ARG) + +static err_t +snmp_prepare_outbound_frame(struct snmp_request *request) +{ + struct snmp_asn1_tlv tlv; + struct snmp_pbuf_stream* pbuf_stream = &(request->outbound_pbuf_stream); + + /* try allocating pbuf(s) for maximum response size */ + request->outbound_pbuf = pbuf_alloc(PBUF_TRANSPORT, 1472, PBUF_RAM); + if (request->outbound_pbuf == NULL) { + return ERR_MEM; + } + + snmp_pbuf_stream_init(pbuf_stream, request->outbound_pbuf, 0, request->outbound_pbuf->tot_len); + + /* 'Message' sequence */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, 0); + OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); + + /* version */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0); + snmp_asn1_enc_s32t_cnt(request->version, &tlv.value_len); + OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); + OF_BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->version) ); + +#if LWIP_SNMP_V3 + if (request->version < SNMP_VERSION_3) { +#endif + /* community */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, request->community_strlen); + OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); + OF_BUILD_EXEC( snmp_asn1_enc_raw(pbuf_stream, request->community, request->community_strlen) ); +#if LWIP_SNMP_V3 + } else { + const char* id; + + /* globalData */ + request->outbound_msg_global_data_offset = pbuf_stream->offset; + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 1, 0); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + + /* msgID */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 1); + snmp_asn1_enc_s32t_cnt(request->msg_id, &tlv.value_len); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + OF_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->msg_id)); + + /* msgMaxSize */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 1); + snmp_asn1_enc_s32t_cnt(request->msg_max_size, &tlv.value_len); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + OF_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->msg_max_size)); + + /* msgFlags */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, 1); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, &request->msg_flags, 1)); + + /* msgSecurityModel */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 1); + snmp_asn1_enc_s32t_cnt(request->msg_security_model, &tlv.value_len); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + OF_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->msg_security_model)); + + /* end of msgGlobalData */ + request->outbound_msg_global_data_end = pbuf_stream->offset; + + /* msgSecurityParameters */ + request->outbound_msg_security_parameters_str_offset = pbuf_stream->offset; + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 1, 0); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + + request->outbound_msg_security_parameters_seq_offset = pbuf_stream->offset; + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 1, 0); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + + /* msgAuthoritativeEngineID */ + snmpv3_get_engine_id(&id, &request->msg_authoritative_engine_id_len); + MEMCPY(request->msg_authoritative_engine_id, id, request->msg_authoritative_engine_id_len); + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, request->msg_authoritative_engine_id_len); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->msg_authoritative_engine_id, request->msg_authoritative_engine_id_len)); + + request->msg_authoritative_engine_time = snmpv3_get_engine_time(); + request->msg_authoritative_engine_boots = snmpv3_get_engine_boots(); + + /* msgAuthoritativeEngineBoots */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0); + snmp_asn1_enc_s32t_cnt(request->msg_authoritative_engine_boots, &tlv.value_len); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + OF_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->msg_authoritative_engine_boots)); + + /* msgAuthoritativeEngineTime */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0); + snmp_asn1_enc_s32t_cnt(request->msg_authoritative_engine_time, &tlv.value_len); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + OF_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->msg_authoritative_engine_time)); + + /* msgUserName */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, request->msg_user_name_len); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->msg_user_name, request->msg_user_name_len)); + +#if LWIP_SNMP_V3_CRYPTO + /* msgAuthenticationParameters */ + if (request->msg_flags & SNMP_V3_AUTH_FLAG) { + memset(request->msg_authentication_parameters, 0, SNMP_V3_MAX_AUTH_PARAM_LENGTH); + request->outbound_msg_authentication_parameters_offset = pbuf_stream->offset; + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 1, SNMP_V3_MAX_AUTH_PARAM_LENGTH); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->msg_authentication_parameters, SNMP_V3_MAX_AUTH_PARAM_LENGTH)); + } else +#endif + { + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, 0); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + } + +#if LWIP_SNMP_V3_CRYPTO + /* msgPrivacyParameters */ + if (request->msg_flags & SNMP_V3_PRIV_FLAG) { + snmpv3_build_priv_param(request->msg_privacy_parameters); + + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, SNMP_V3_MAX_PRIV_PARAM_LENGTH); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->msg_privacy_parameters, SNMP_V3_MAX_PRIV_PARAM_LENGTH)); + } else +#endif + { + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, 0); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); + } + + /* End of msgSecurityParameters, so we can calculate the length of this sequence later */ + request->outbound_msg_security_parameters_end = pbuf_stream->offset; + +#if LWIP_SNMP_V3_CRYPTO + /* For encryption we have to encapsulate the payload in an octet string */ + if (request->msg_flags & SNMP_V3_PRIV_FLAG) { + request->outbound_scoped_pdu_string_offset = pbuf_stream->offset; + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 3, 0); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + } +#endif + /* Scoped PDU + * Encryption context + */ + request->outbound_scoped_pdu_seq_offset = pbuf_stream->offset; + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, 0); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + + /* contextEngineID */ + snmpv3_get_engine_id(&id, &request->context_engine_id_len); + MEMCPY(request->context_engine_id, id, request->context_engine_id_len); + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, request->context_engine_id_len); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->context_engine_id, request->context_engine_id_len)); + + /* contextName */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, request->context_name_len); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->context_name, request->context_name_len)); + } +#endif + + /* 'PDU' sequence */ + request->outbound_pdu_offset = pbuf_stream->offset; + SNMP_ASN1_SET_TLV_PARAMS(tlv, (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_RESP), 3, 0); + OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); + + /* request ID */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0); + snmp_asn1_enc_s32t_cnt(request->request_id, &tlv.value_len); + OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); + OF_BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->request_id) ); + + /* error status */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 1); + OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); + request->outbound_error_status_offset = pbuf_stream->offset; + OF_BUILD_EXEC( snmp_pbuf_stream_write(pbuf_stream, 0) ); + + /* error index */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 1); + OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); + request->outbound_error_index_offset = pbuf_stream->offset; + OF_BUILD_EXEC( snmp_pbuf_stream_write(pbuf_stream, 0) ); + + /* 'VarBindList' sequence */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, 0); + OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); + + request->outbound_varbind_offset = pbuf_stream->offset; + + return ERR_OK; +} + +/** Calculate the length of a varbind list */ +err_t +snmp_varbind_length(struct snmp_varbind *varbind, struct snmp_varbind_len *len) +{ + /* calculate required lengths */ + snmp_asn1_enc_oid_cnt(varbind->oid.id, varbind->oid.len, &len->oid_value_len); + snmp_asn1_enc_length_cnt(len->oid_value_len, &len->oid_len_len); + + if (varbind->value_len == 0) { + len->value_value_len = 0; + } else if (varbind->value_len & SNMP_GET_VALUE_RAW_DATA) { + len->value_value_len = varbind->value_len & (~SNMP_GET_VALUE_RAW_DATA); + } else { + switch (varbind->type) { + case SNMP_ASN1_TYPE_INTEGER: + if (varbind->value_len != sizeof (s32_t)) { + return ERR_VAL; + } + snmp_asn1_enc_s32t_cnt(*((s32_t*) varbind->value), &len->value_value_len); + break; + case SNMP_ASN1_TYPE_COUNTER: + case SNMP_ASN1_TYPE_GAUGE: + case SNMP_ASN1_TYPE_TIMETICKS: + if (varbind->value_len != sizeof (u32_t)) { + return ERR_VAL; + } + snmp_asn1_enc_u32t_cnt(*((u32_t*) varbind->value), &len->value_value_len); + break; + case SNMP_ASN1_TYPE_OCTET_STRING: + case SNMP_ASN1_TYPE_IPADDR: + case SNMP_ASN1_TYPE_OPAQUE: + len->value_value_len = varbind->value_len; + break; + case SNMP_ASN1_TYPE_NULL: + if (varbind->value_len != 0) { + return ERR_VAL; + } + len->value_value_len = 0; + break; + case SNMP_ASN1_TYPE_OBJECT_ID: + if ((varbind->value_len & 0x03) != 0) { + return ERR_VAL; + } + snmp_asn1_enc_oid_cnt((u32_t*) varbind->value, varbind->value_len >> 2, &len->value_value_len); + break; + case SNMP_ASN1_TYPE_COUNTER64: + if (varbind->value_len != (2 * sizeof (u32_t))) { + return ERR_VAL; + } + snmp_asn1_enc_u64t_cnt((u32_t*) varbind->value, &len->value_value_len); + break; + default: + /* unsupported type */ + return ERR_VAL; + } + } + snmp_asn1_enc_length_cnt(len->value_value_len, &len->value_len_len); + + len->vb_value_len = 1 + len->oid_len_len + len->oid_value_len + 1 + len->value_len_len + len->value_value_len; + snmp_asn1_enc_length_cnt(len->vb_value_len, &len->vb_len_len); + + return ERR_OK; +} + +#define OVB_BUILD_EXEC(code) BUILD_EXEC(code, ERR_ARG) + +err_t +snmp_append_outbound_varbind(struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind* varbind) +{ + struct snmp_asn1_tlv tlv; + struct snmp_varbind_len len; + err_t err; + + err = snmp_varbind_length(varbind, &len); + + if (err != ERR_OK) { + return err; + } + + /* check length already before adding first data because in case of GetBulk, + * data added so far is returned and therefore no partial data shall be added + */ + if ((1 + len.vb_len_len + len.vb_value_len) > pbuf_stream->length) { + return ERR_BUF; + } + + /* 'VarBind' sequence */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, len.vb_len_len, len.vb_value_len); + OVB_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + + /* VarBind OID */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OBJECT_ID, len.oid_len_len, len.oid_value_len); + OVB_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + OVB_BUILD_EXEC(snmp_asn1_enc_oid(pbuf_stream, varbind->oid.id, varbind->oid.len)); + + /* VarBind value */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, varbind->type, len.value_len_len, len.value_value_len); + OVB_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + + if (len.value_value_len > 0) { + if (varbind->value_len & SNMP_GET_VALUE_RAW_DATA) { + OVB_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, (u8_t*) varbind->value, len.value_value_len)); + } else { + switch (varbind->type) { + case SNMP_ASN1_TYPE_INTEGER: + OVB_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, len.value_value_len, *((s32_t*) varbind->value))); + break; + case SNMP_ASN1_TYPE_COUNTER: + case SNMP_ASN1_TYPE_GAUGE: + case SNMP_ASN1_TYPE_TIMETICKS: + OVB_BUILD_EXEC(snmp_asn1_enc_u32t(pbuf_stream, len.value_value_len, *((u32_t*) varbind->value))); + break; + case SNMP_ASN1_TYPE_OCTET_STRING: + case SNMP_ASN1_TYPE_IPADDR: + case SNMP_ASN1_TYPE_OPAQUE: + OVB_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, (u8_t*) varbind->value, len.value_value_len)); + len.value_value_len = varbind->value_len; + break; + case SNMP_ASN1_TYPE_OBJECT_ID: + OVB_BUILD_EXEC(snmp_asn1_enc_oid(pbuf_stream, (u32_t*) varbind->value, varbind->value_len / sizeof (u32_t))); + break; + case SNMP_ASN1_TYPE_COUNTER64: + OVB_BUILD_EXEC(snmp_asn1_enc_u64t(pbuf_stream, len.value_value_len, (u32_t*) varbind->value)); + break; + default: + LWIP_ASSERT("Unknown variable type", 0); + break; + } + } + } + + return ERR_OK; +} + +static err_t +snmp_complete_outbound_frame(struct snmp_request *request) +{ + struct snmp_asn1_tlv tlv; + u16_t frame_size; + u8_t outbound_padding = 0; + + if (request->version == SNMP_VERSION_1) { + if (request->error_status != SNMP_ERR_NOERROR) { + /* map v2c error codes to v1 compliant error code (according to RFC 2089) */ + switch (request->error_status) { + /* mapping of implementation specific "virtual" error codes + * (during processing of frame we already stored them in error_status field, + * so no need to check all varbinds here for those exceptions as suggested by RFC) */ + case SNMP_ERR_NOSUCHINSTANCE: + case SNMP_ERR_NOSUCHOBJECT: + case SNMP_ERR_ENDOFMIBVIEW: + request->error_status = SNMP_ERR_NOSUCHNAME; + break; + /* mapping according to RFC */ + case SNMP_ERR_WRONGVALUE: + case SNMP_ERR_WRONGENCODING: + case SNMP_ERR_WRONGTYPE: + case SNMP_ERR_WRONGLENGTH: + case SNMP_ERR_INCONSISTENTVALUE: + request->error_status = SNMP_ERR_BADVALUE; + break; + case SNMP_ERR_NOACCESS: + case SNMP_ERR_NOTWRITABLE: + case SNMP_ERR_NOCREATION: + case SNMP_ERR_INCONSISTENTNAME: + case SNMP_ERR_AUTHORIZATIONERROR: + request->error_status = SNMP_ERR_NOSUCHNAME; + break; + case SNMP_ERR_RESOURCEUNAVAILABLE: + case SNMP_ERR_COMMITFAILED: + case SNMP_ERR_UNDOFAILED: + default: + request->error_status = SNMP_ERR_GENERROR; + break; + } + } + } else { + if (request->request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ) { + /* map error codes to according to RFC 1905 (4.2.5. The SetRequest-PDU) return 'NotWritable' for unknown OIDs) */ + switch (request->error_status) { + case SNMP_ERR_NOSUCHINSTANCE: + case SNMP_ERR_NOSUCHOBJECT: + case SNMP_ERR_ENDOFMIBVIEW: + request->error_status = SNMP_ERR_NOTWRITABLE; + break; + default: + break; + } + } + + if (request->error_status >= SNMP_VARBIND_EXCEPTION_OFFSET) { + /* should never occur because v2 frames store exceptions directly inside varbinds and not as frame error_status */ + LWIP_DEBUGF(SNMP_DEBUG, ("snmp_complete_outbound_frame() > Found v2 request with varbind exception code stored as error status!\n")); + return ERR_ARG; + } + } + + if ((request->error_status != SNMP_ERR_NOERROR) || (request->request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ)) { + /* all inbound vars are returned in response without any modification for error responses and successful set requests*/ + struct snmp_pbuf_stream inbound_stream; + OF_BUILD_EXEC( snmp_pbuf_stream_init(&inbound_stream, request->inbound_pbuf, request->inbound_varbind_offset, request->inbound_varbind_len) ); + OF_BUILD_EXEC( snmp_pbuf_stream_init(&(request->outbound_pbuf_stream), request->outbound_pbuf, request->outbound_varbind_offset, request->outbound_pbuf->tot_len - request->outbound_varbind_offset) ); + snmp_pbuf_stream_writeto(&inbound_stream, &(request->outbound_pbuf_stream), 0); + } + + frame_size = request->outbound_pbuf_stream.offset; + +#if LWIP_SNMP_V3 && LWIP_SNMP_V3_CRYPTO + /* Calculate padding for encryption */ + if (request->version == SNMP_VERSION_3 && (request->msg_flags & SNMP_V3_PRIV_FLAG)) { + u8_t i; + outbound_padding = (8 - (u8_t)((frame_size - request->outbound_scoped_pdu_seq_offset) & 0x07)) & 0x07; + for (i = 0; i < outbound_padding; i++) { + snmp_pbuf_stream_write(&request->outbound_pbuf_stream, 0); + } + } +#endif + + /* complete missing length in 'Message' sequence ; 'Message' tlv is located at the beginning (offset 0) */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, frame_size + outbound_padding - 1 - 3); /* - type - length_len(fixed, see snmp_prepare_outbound_frame()) */ + OF_BUILD_EXEC( snmp_pbuf_stream_init(&(request->outbound_pbuf_stream), request->outbound_pbuf, 0, request->outbound_pbuf->tot_len) ); + OF_BUILD_EXEC( snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv) ); + +#if LWIP_SNMP_V3 + if (request->version == SNMP_VERSION_3) { + /* complete missing length in 'globalData' sequence */ + /* - type - length_len(fixed, see snmp_prepare_outbound_frame()) */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 1, request->outbound_msg_global_data_end + - request->outbound_msg_global_data_offset - 1 - 1); + OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_msg_global_data_offset)); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv)); + + /* complete missing length in 'msgSecurityParameters' sequence */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 1, request->outbound_msg_security_parameters_end + - request->outbound_msg_security_parameters_str_offset - 1 - 1); + OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_msg_security_parameters_str_offset)); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv)); + + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 1, request->outbound_msg_security_parameters_end + - request->outbound_msg_security_parameters_seq_offset - 1 - 1); + OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_msg_security_parameters_seq_offset)); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv)); + + /* complete missing length in scoped PDU sequence */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, frame_size - request->outbound_scoped_pdu_seq_offset - 1 - 3); + OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_scoped_pdu_seq_offset)); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv)); + } +#endif + + /* complete missing length in 'PDU' sequence */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_RESP), 3, + frame_size - request->outbound_pdu_offset - 1 - 3); /* - type - length_len(fixed, see snmp_prepare_outbound_frame()) */ + OF_BUILD_EXEC( snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_pdu_offset) ); + OF_BUILD_EXEC( snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv) ); + + /* process and encode final error status */ + if (request->error_status != 0) { + u16_t len; + snmp_asn1_enc_s32t_cnt(request->error_status, &len); + if (len != 1) { + /* error, we only reserved one byte for it */ + return ERR_ARG; + } + OF_BUILD_EXEC( snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_error_status_offset) ); + OF_BUILD_EXEC( snmp_asn1_enc_s32t(&(request->outbound_pbuf_stream), len, request->error_status) ); + + /* for compatibility to v1, log statistics; in v2 (RFC 1907) these statistics are obsoleted */ + switch (request->error_status) { + case SNMP_ERR_TOOBIG: + snmp_stats.outtoobigs++; + break; + case SNMP_ERR_NOSUCHNAME: + snmp_stats.outnosuchnames++; + break; + case SNMP_ERR_BADVALUE: + snmp_stats.outbadvalues++; + break; + case SNMP_ERR_GENERROR: + default: + snmp_stats.outgenerrs++; + break; + } + + if (request->error_status == SNMP_ERR_TOOBIG) { + request->error_index = 0; /* defined by RFC 1157 */ + } else if (request->error_index == 0) { + /* set index to varbind where error occured (if not already set before, e.g. during GetBulk processing) */ + request->error_index = request->inbound_varbind_enumerator.varbind_count; + } + } else { + if (request->request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ) { + snmp_stats.intotalsetvars += request->inbound_varbind_enumerator.varbind_count; + } else { + snmp_stats.intotalreqvars += request->inbound_varbind_enumerator.varbind_count; + } + } + + /* encode final error index*/ + if (request->error_index != 0) { + u16_t len; + snmp_asn1_enc_s32t_cnt(request->error_index, &len); + if (len != 1) { + /* error, we only reserved one byte for it */ + return ERR_VAL; + } + OF_BUILD_EXEC( snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_error_index_offset) ); + OF_BUILD_EXEC( snmp_asn1_enc_s32t(&(request->outbound_pbuf_stream), len, request->error_index) ); + } + + /* complete missing length in 'VarBindList' sequence ; 'VarBindList' tlv is located directly before varbind offset */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, frame_size - request->outbound_varbind_offset); + OF_BUILD_EXEC( snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_varbind_offset - 1 - 3) ); /* - type - length_len(fixed, see snmp_prepare_outbound_frame()) */ + OF_BUILD_EXEC( snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv) ); + + /* Authenticate response */ +#if LWIP_SNMP_V3 && LWIP_SNMP_V3_CRYPTO + /* Encrypt response */ + if (request->version == SNMP_VERSION_3 && (request->msg_flags & SNMP_V3_PRIV_FLAG)) { + u8_t key[20]; + u8_t algo; + + /* complete missing length in PDU sequence */ + OF_BUILD_EXEC(snmp_pbuf_stream_init(&request->outbound_pbuf_stream, request->outbound_pbuf, 0, request->outbound_pbuf->tot_len)); + OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_scoped_pdu_string_offset)); + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 3, frame_size + outbound_padding + - request->outbound_scoped_pdu_string_offset - 1 - 3); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv)); + + OF_BUILD_EXEC(snmpv3_get_user((char*)request->msg_user_name, NULL, NULL, &algo, key)); + + OF_BUILD_EXEC(snmpv3_crypt(&request->outbound_pbuf_stream, tlv.value_len, key, + request->msg_privacy_parameters, request->msg_authoritative_engine_boots, + request->msg_authoritative_engine_time, algo, SNMP_V3_PRIV_MODE_ENCRYPT)); + } + + if (request->version == SNMP_VERSION_3 && (request->msg_flags & SNMP_V3_AUTH_FLAG)) { + u8_t key[20]; + u8_t algo; + u8_t hmac[20]; + + OF_BUILD_EXEC(snmpv3_get_user((char*)request->msg_user_name, &algo, key, NULL, NULL)); + OF_BUILD_EXEC(snmp_pbuf_stream_init(&(request->outbound_pbuf_stream), + request->outbound_pbuf, 0, request->outbound_pbuf->tot_len)); + OF_BUILD_EXEC(snmpv3_auth(&request->outbound_pbuf_stream, frame_size + outbound_padding, key, algo, hmac)); + + MEMCPY(request->msg_authentication_parameters, hmac, SNMP_V3_MAX_AUTH_PARAM_LENGTH); + OF_BUILD_EXEC(snmp_pbuf_stream_init(&request->outbound_pbuf_stream, + request->outbound_pbuf, 0, request->outbound_pbuf->tot_len)); + OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&request->outbound_pbuf_stream, + request->outbound_msg_authentication_parameters_offset)); + + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 1, SNMP_V3_MAX_AUTH_PARAM_LENGTH); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(&request->outbound_pbuf_stream, &tlv)); + OF_BUILD_EXEC(snmp_asn1_enc_raw(&request->outbound_pbuf_stream, + request->msg_authentication_parameters, SNMP_V3_MAX_AUTH_PARAM_LENGTH)); + } +#endif + + pbuf_realloc(request->outbound_pbuf, frame_size + outbound_padding); + + snmp_stats.outgetresponses++; + snmp_stats.outpkts++; + + return ERR_OK; +} + +static void +snmp_execute_write_callbacks(struct snmp_request *request) +{ + struct snmp_varbind_enumerator inbound_varbind_enumerator; + struct snmp_varbind vb; + + snmp_vb_enumerator_init(&inbound_varbind_enumerator, request->inbound_pbuf, request->inbound_varbind_offset, request->inbound_varbind_len); + vb.value = NULL; /* do NOT decode value (we enumerate outbound buffer here, so all varbinds have values assigned, which we don't need here) */ + + while (snmp_vb_enumerator_get_next(&inbound_varbind_enumerator, &vb) == SNMP_VB_ENUMERATOR_ERR_OK) { + snmp_write_callback(vb.oid.id, vb.oid.len, snmp_write_callback_arg); + } +} + + +/* ----------------------------------------------------------------------- */ +/* VarBind enumerator methods */ +/* ----------------------------------------------------------------------- */ + +void +snmp_vb_enumerator_init(struct snmp_varbind_enumerator* enumerator, struct pbuf* p, u16_t offset, u16_t length) +{ + snmp_pbuf_stream_init(&(enumerator->pbuf_stream), p, offset, length); + enumerator->varbind_count = 0; +} + +#define VB_PARSE_EXEC(code) PARSE_EXEC(code, SNMP_VB_ENUMERATOR_ERR_ASN1ERROR) +#define VB_PARSE_ASSERT(code) PARSE_ASSERT(code, SNMP_VB_ENUMERATOR_ERR_ASN1ERROR) + +snmp_vb_enumerator_err_t +snmp_vb_enumerator_get_next(struct snmp_varbind_enumerator* enumerator, struct snmp_varbind* varbind) +{ + struct snmp_asn1_tlv tlv; + u16_t varbind_len; + err_t err; + + if (enumerator->pbuf_stream.length == 0) + { + return SNMP_VB_ENUMERATOR_ERR_EOVB; + } + enumerator->varbind_count++; + + /* decode varbind itself (parent container of a varbind) */ + VB_PARSE_EXEC(snmp_asn1_dec_tlv(&(enumerator->pbuf_stream), &tlv)); + VB_PARSE_ASSERT((tlv.type == SNMP_ASN1_TYPE_SEQUENCE) && (tlv.value_len <= enumerator->pbuf_stream.length)); + varbind_len = tlv.value_len; + + /* decode varbind name (object id) */ + VB_PARSE_EXEC(snmp_asn1_dec_tlv(&(enumerator->pbuf_stream), &tlv)); + VB_PARSE_ASSERT((tlv.type == SNMP_ASN1_TYPE_OBJECT_ID) && (SNMP_ASN1_TLV_LENGTH(tlv) < varbind_len) && (tlv.value_len < enumerator->pbuf_stream.length)); + + VB_PARSE_EXEC(snmp_asn1_dec_oid(&(enumerator->pbuf_stream), tlv.value_len, varbind->oid.id, &(varbind->oid.len), SNMP_MAX_OBJ_ID_LEN)); + varbind_len -= SNMP_ASN1_TLV_LENGTH(tlv); + + /* decode varbind value (object id) */ + VB_PARSE_EXEC(snmp_asn1_dec_tlv(&(enumerator->pbuf_stream), &tlv)); + VB_PARSE_ASSERT((SNMP_ASN1_TLV_LENGTH(tlv) == varbind_len) && (tlv.value_len <= enumerator->pbuf_stream.length)); + varbind->type = tlv.type; + + /* shall the value be decoded ? */ + if (varbind->value != NULL) { + switch (varbind->type) { + case SNMP_ASN1_TYPE_INTEGER: + VB_PARSE_EXEC(snmp_asn1_dec_s32t(&(enumerator->pbuf_stream), tlv.value_len, (s32_t*)varbind->value)); + varbind->value_len = sizeof(s32_t*); + break; + case SNMP_ASN1_TYPE_COUNTER: + case SNMP_ASN1_TYPE_GAUGE: + case SNMP_ASN1_TYPE_TIMETICKS: + VB_PARSE_EXEC(snmp_asn1_dec_u32t(&(enumerator->pbuf_stream), tlv.value_len, (u32_t*)varbind->value)); + varbind->value_len = sizeof(u32_t*); + break; + case SNMP_ASN1_TYPE_OCTET_STRING: + case SNMP_ASN1_TYPE_OPAQUE: + err = snmp_asn1_dec_raw(&(enumerator->pbuf_stream), tlv.value_len, (u8_t*)varbind->value, &varbind->value_len, SNMP_MAX_VALUE_SIZE); + if (err == ERR_MEM) { + return SNMP_VB_ENUMERATOR_ERR_INVALIDLENGTH; + } + VB_PARSE_ASSERT(err == ERR_OK); + break; + case SNMP_ASN1_TYPE_NULL: + varbind->value_len = 0; + break; + case SNMP_ASN1_TYPE_OBJECT_ID: + /* misuse tlv.length_len as OID_length transporter */ + err = snmp_asn1_dec_oid(&(enumerator->pbuf_stream), tlv.value_len, (u32_t*)varbind->value, &tlv.length_len, SNMP_MAX_OBJ_ID_LEN); + if (err == ERR_MEM) { + return SNMP_VB_ENUMERATOR_ERR_INVALIDLENGTH; + } + VB_PARSE_ASSERT(err == ERR_OK); + varbind->value_len = tlv.length_len * sizeof(u32_t); + break; + case SNMP_ASN1_TYPE_IPADDR: + if (tlv.value_len == 4) { + /* must be exactly 4 octets! */ + VB_PARSE_EXEC(snmp_asn1_dec_raw(&(enumerator->pbuf_stream), tlv.value_len, (u8_t*)varbind->value, &varbind->value_len, SNMP_MAX_VALUE_SIZE)); + } else { + VB_PARSE_ASSERT(0); + } + break; + case SNMP_ASN1_TYPE_COUNTER64: + VB_PARSE_EXEC(snmp_asn1_dec_u64t(&(enumerator->pbuf_stream), tlv.value_len, (u32_t*)varbind->value)); + varbind->value_len = 2 * sizeof(u32_t*); + break; + default: + VB_PARSE_ASSERT(0); + break; + } + } else { + snmp_pbuf_stream_seek(&(enumerator->pbuf_stream), tlv.value_len); + varbind->value_len = tlv.value_len; + } + + return SNMP_VB_ENUMERATOR_ERR_OK; +} + +#endif /* LWIP_SNMP */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_msg.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_msg.h new file mode 100644 index 0000000..2d01ef3 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_msg.h @@ -0,0 +1,194 @@ +/** + * @file + * SNMP Agent message handling structures (internal API, do not use in client code). + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * Copyright (c) 2016 Elias Oenal. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons <christiaan.simons@axon.tv> + * Martin Hentschel <info@cl-soft.de> + * Elias Oenal <lwip@eliasoenal.com> + */ + +#ifndef LWIP_HDR_APPS_SNMP_MSG_H +#define LWIP_HDR_APPS_SNMP_MSG_H + +#include "lwip/apps/snmp_opts.h" + +#if LWIP_SNMP + +#include "lwip/apps/snmp.h" +#include "lwip/apps/snmp_core.h" +#include "snmp_pbuf_stream.h" +#include "lwip/ip_addr.h" +#include "lwip/err.h" + +#if LWIP_SNMP_V3 +#include "snmpv3_priv.h" +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +/* The listen port of the SNMP agent. Clients have to make their requests to + this port. Most standard clients won't work if you change this! */ +#ifndef SNMP_IN_PORT +#define SNMP_IN_PORT 161 +#endif +/* The remote port the SNMP agent sends traps to. Most standard trap sinks won't + work if you change this! */ +#ifndef SNMP_TRAP_PORT +#define SNMP_TRAP_PORT 162 +#endif + +/* version defines used in PDU */ +#define SNMP_VERSION_1 0 +#define SNMP_VERSION_2c 1 +#define SNMP_VERSION_3 3 + +struct snmp_varbind_enumerator +{ + struct snmp_pbuf_stream pbuf_stream; + u16_t varbind_count; +}; + +typedef enum { + SNMP_VB_ENUMERATOR_ERR_OK = 0, + SNMP_VB_ENUMERATOR_ERR_EOVB = 1, + SNMP_VB_ENUMERATOR_ERR_ASN1ERROR = 2, + SNMP_VB_ENUMERATOR_ERR_INVALIDLENGTH = 3 +} snmp_vb_enumerator_err_t; + +void snmp_vb_enumerator_init(struct snmp_varbind_enumerator* enumerator, struct pbuf* p, u16_t offset, u16_t length); +snmp_vb_enumerator_err_t snmp_vb_enumerator_get_next(struct snmp_varbind_enumerator* enumerator, struct snmp_varbind* varbind); + +struct snmp_request +{ + /* Communication handle */ + void *handle; + /* source IP address */ + const ip_addr_t *source_ip; + /* source UDP port */ + u16_t source_port; + /* incoming snmp version */ + u8_t version; + /* community name (zero terminated) */ + u8_t community[SNMP_MAX_COMMUNITY_STR_LEN + 1]; + /* community string length (exclusive zero term) */ + u16_t community_strlen; + /* request type */ + u8_t request_type; + /* request ID */ + s32_t request_id; + /* error status */ + s32_t error_status; + /* error index */ + s32_t error_index; + /* non-repeaters (getBulkRequest (SNMPv2c)) */ + s32_t non_repeaters; + /* max-repetitions (getBulkRequest (SNMPv2c)) */ + s32_t max_repetitions; + +#if LWIP_SNMP_V3 + s32_t msg_id; + s32_t msg_max_size; + u8_t msg_flags; + s32_t msg_security_model; + u8_t msg_authoritative_engine_id[SNMP_V3_MAX_ENGINE_ID_LENGTH]; + u8_t msg_authoritative_engine_id_len; + s32_t msg_authoritative_engine_boots; + s32_t msg_authoritative_engine_time; + u8_t msg_user_name[SNMP_V3_MAX_USER_LENGTH]; + u8_t msg_user_name_len; + u8_t msg_authentication_parameters[SNMP_V3_MAX_AUTH_PARAM_LENGTH]; + u8_t msg_privacy_parameters[SNMP_V3_MAX_PRIV_PARAM_LENGTH]; + u8_t context_engine_id[SNMP_V3_MAX_ENGINE_ID_LENGTH]; + u8_t context_engine_id_len; + u8_t context_name[SNMP_V3_MAX_ENGINE_ID_LENGTH]; + u8_t context_name_len; +#endif + + struct pbuf *inbound_pbuf; + struct snmp_varbind_enumerator inbound_varbind_enumerator; + u16_t inbound_varbind_offset; + u16_t inbound_varbind_len; + u16_t inbound_padding_len; + + struct pbuf *outbound_pbuf; + struct snmp_pbuf_stream outbound_pbuf_stream; + u16_t outbound_pdu_offset; + u16_t outbound_error_status_offset; + u16_t outbound_error_index_offset; + u16_t outbound_varbind_offset; +#if LWIP_SNMP_V3 + u16_t outbound_msg_global_data_offset; + u16_t outbound_msg_global_data_end; + u16_t outbound_msg_security_parameters_str_offset; + u16_t outbound_msg_security_parameters_seq_offset; + u16_t outbound_msg_security_parameters_end; + u16_t outbound_msg_authentication_parameters_offset; + u16_t outbound_scoped_pdu_seq_offset; + u16_t outbound_scoped_pdu_string_offset; +#endif + + u8_t value_buffer[SNMP_MAX_VALUE_SIZE]; +}; + +/** A helper struct keeping length information about varbinds */ +struct snmp_varbind_len +{ + u8_t vb_len_len; + u16_t vb_value_len; + u8_t oid_len_len; + u16_t oid_value_len; + u8_t value_len_len; + u16_t value_value_len; +}; + +/** Agent community string */ +extern const char *snmp_community; +/** Agent community string for write access */ +extern const char *snmp_community_write; +/** handle for sending traps */ +extern void* snmp_traps_handle; + +void snmp_receive(void *handle, struct pbuf *p, const ip_addr_t *source_ip, u16_t port); +err_t snmp_sendto(void *handle, struct pbuf *p, const ip_addr_t *dst, u16_t port); +u8_t snmp_get_local_ip_for_dst(void* handle, const ip_addr_t *dst, ip_addr_t *result); +err_t snmp_varbind_length(struct snmp_varbind *varbind, struct snmp_varbind_len *len); +err_t snmp_append_outbound_varbind(struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind* varbind); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_SNMP */ + +#endif /* LWIP_HDR_APPS_SNMP_MSG_H */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_netconn.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_netconn.c new file mode 100644 index 0000000..24c3e26 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_netconn.c @@ -0,0 +1,121 @@ +/** + * @file + * SNMP netconn frontend. + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Dirk Ziegelmeier <dziegel@gmx.de> + */ + +#include "lwip/apps/snmp_opts.h" + +#if LWIP_SNMP && SNMP_USE_NETCONN + +#include <string.h> +#include "lwip/api.h" +#include "lwip/ip.h" +#include "lwip/udp.h" +#include "snmp_msg.h" +#include "lwip/sys.h" + +/** SNMP netconn API worker thread */ +static void +snmp_netconn_thread(void *arg) +{ + struct netconn *conn; + struct netbuf *buf; + err_t err; + LWIP_UNUSED_ARG(arg); + + /* Bind to SNMP port with default IP address */ +#if LWIP_IPV6 + conn = netconn_new(NETCONN_UDP_IPV6); + netconn_bind(conn, IP6_ADDR_ANY, SNMP_IN_PORT); +#else /* LWIP_IPV6 */ + conn = netconn_new(NETCONN_UDP); + netconn_bind(conn, IP4_ADDR_ANY, SNMP_IN_PORT); +#endif /* LWIP_IPV6 */ + LWIP_ERROR("snmp_netconn: invalid conn", (conn != NULL), return;); + + snmp_traps_handle = conn; + + do { + err = netconn_recv(conn, &buf); + + if (err == ERR_OK) { + snmp_receive(conn, buf->p, &buf->addr, buf->port); + } + + if (buf != NULL) { + netbuf_delete(buf); + } + } while(1); +} + +err_t +snmp_sendto(void *handle, struct pbuf *p, const ip_addr_t *dst, u16_t port) +{ + err_t result; + struct netbuf buf; + + memset(&buf, 0, sizeof(buf)); + buf.p = p; + result = netconn_sendto((struct netconn*)handle, &buf, dst, port); + + return result; +} + +u8_t +snmp_get_local_ip_for_dst(void* handle, const ip_addr_t *dst, ip_addr_t *result) +{ + struct netconn* conn = (struct netconn*)handle; + struct netif *dst_if; + const ip_addr_t* dst_ip; + + LWIP_UNUSED_ARG(conn); /* unused in case of IPV4 only configuration */ + + ip_route_get_local_ip(&conn->pcb.udp->local_ip, dst, dst_if, dst_ip); + + if ((dst_if != NULL) && (dst_ip != NULL)) { + ip_addr_copy(*result, *dst_ip); + return 1; + } else { + return 0; + } +} + +/** + * Starts SNMP Agent. + */ +void +snmp_init(void) +{ + sys_thread_new("snmp_netconn", snmp_netconn_thread, NULL, SNMP_STACK_SIZE, SNMP_THREAD_PRIO); +} + +#endif /* LWIP_SNMP && SNMP_USE_NETCONN */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_pbuf_stream.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_pbuf_stream.c new file mode 100644 index 0000000..3c1217d --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_pbuf_stream.c @@ -0,0 +1,156 @@ +/** + * @file + * SNMP pbuf stream wrapper implementation (internal API, do not use in client code). + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel <info@cl-soft.de> + * + */ + +#include "lwip/apps/snmp_opts.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "snmp_pbuf_stream.h" +#include "lwip/def.h" +#include <string.h> + +err_t +snmp_pbuf_stream_init(struct snmp_pbuf_stream* pbuf_stream, struct pbuf* p, u16_t offset, u16_t length) +{ + pbuf_stream->offset = offset; + pbuf_stream->length = length; + pbuf_stream->pbuf = p; + + return ERR_OK; +} + +err_t +snmp_pbuf_stream_read(struct snmp_pbuf_stream* pbuf_stream, u8_t* data) +{ + if (pbuf_stream->length == 0) { + return ERR_BUF; + } + + if (pbuf_copy_partial(pbuf_stream->pbuf, data, 1, pbuf_stream->offset) == 0) { + return ERR_BUF; + } + + pbuf_stream->offset++; + pbuf_stream->length--; + + return ERR_OK; +} + +err_t +snmp_pbuf_stream_write(struct snmp_pbuf_stream* pbuf_stream, u8_t data) +{ + return snmp_pbuf_stream_writebuf(pbuf_stream, &data, 1); +} + +err_t +snmp_pbuf_stream_writebuf(struct snmp_pbuf_stream* pbuf_stream, const void* buf, u16_t buf_len) +{ + if (pbuf_stream->length < buf_len) { + return ERR_BUF; + } + + if (pbuf_take_at(pbuf_stream->pbuf, buf, buf_len, pbuf_stream->offset) != ERR_OK) { + return ERR_BUF; + } + + pbuf_stream->offset += buf_len; + pbuf_stream->length -= buf_len; + + return ERR_OK; +} + +err_t +snmp_pbuf_stream_writeto(struct snmp_pbuf_stream* pbuf_stream, struct snmp_pbuf_stream* target_pbuf_stream, u16_t len) +{ + + if ((pbuf_stream == NULL) || (target_pbuf_stream == NULL)) { + return ERR_ARG; + } + if ((len > pbuf_stream->length) || (len > target_pbuf_stream->length)) { + return ERR_ARG; + } + + if (len == 0) { + len = LWIP_MIN(pbuf_stream->length, target_pbuf_stream->length); + } + + while (len > 0) { + u16_t chunk_len; + err_t err; + u16_t target_offset; + struct pbuf* pbuf = pbuf_skip(pbuf_stream->pbuf, pbuf_stream->offset, &target_offset); + + if ((pbuf == NULL) || (pbuf->len == 0)) { + return ERR_BUF; + } + + chunk_len = LWIP_MIN(len, pbuf->len); + err = snmp_pbuf_stream_writebuf(target_pbuf_stream, &((u8_t*)pbuf->payload)[target_offset], chunk_len); + if (err != ERR_OK) { + return err; + } + + pbuf_stream->offset += chunk_len; + pbuf_stream->length -= chunk_len; + len -= chunk_len; + } + + return ERR_OK; +} + +err_t +snmp_pbuf_stream_seek(struct snmp_pbuf_stream* pbuf_stream, s32_t offset) +{ + if ((offset < 0) || (offset > pbuf_stream->length)) { + /* we cannot seek backwards or forward behind stream end */ + return ERR_ARG; + } + + pbuf_stream->offset += (u16_t)offset; + pbuf_stream->length -= (u16_t)offset; + + return ERR_OK; +} + +err_t +snmp_pbuf_stream_seek_abs(struct snmp_pbuf_stream* pbuf_stream, u32_t offset) +{ + s32_t rel_offset = offset - pbuf_stream->offset; + return snmp_pbuf_stream_seek(pbuf_stream, rel_offset); +} + +#endif /* LWIP_SNMP */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_pbuf_stream.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_pbuf_stream.h new file mode 100644 index 0000000..9778de7 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_pbuf_stream.h @@ -0,0 +1,73 @@ +/** + * @file + * SNMP pbuf stream wrapper (internal API, do not use in client code). + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel <info@cl-soft.de> + * + */ + +#ifndef LWIP_HDR_APPS_SNMP_PBUF_STREAM_H +#define LWIP_HDR_APPS_SNMP_PBUF_STREAM_H + +#include "lwip/apps/snmp_opts.h" + +#if LWIP_SNMP + +#include "lwip/err.h" +#include "lwip/pbuf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct snmp_pbuf_stream +{ + struct pbuf* pbuf; + u16_t offset; + u16_t length; +}; + +err_t snmp_pbuf_stream_init(struct snmp_pbuf_stream* pbuf_stream, struct pbuf* p, u16_t offset, u16_t length); +err_t snmp_pbuf_stream_read(struct snmp_pbuf_stream* pbuf_stream, u8_t* data); +err_t snmp_pbuf_stream_write(struct snmp_pbuf_stream* pbuf_stream, u8_t data); +err_t snmp_pbuf_stream_writebuf(struct snmp_pbuf_stream* pbuf_stream, const void* buf, u16_t buf_len); +err_t snmp_pbuf_stream_writeto(struct snmp_pbuf_stream* pbuf_stream, struct snmp_pbuf_stream* target_pbuf_stream, u16_t len); +err_t snmp_pbuf_stream_seek(struct snmp_pbuf_stream* pbuf_stream, s32_t offset); +err_t snmp_pbuf_stream_seek_abs(struct snmp_pbuf_stream* pbuf_stream, u32_t offset); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_SNMP */ + +#endif /* LWIP_HDR_APPS_SNMP_PBUF_STREAM_H */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_raw.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_raw.c new file mode 100644 index 0000000..4a40864 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_raw.c @@ -0,0 +1,100 @@ +/** + * @file + * SNMP RAW API frontend. + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Dirk Ziegelmeier <dziegel@gmx.de> + */ + +#include "lwip/apps/snmp_opts.h" +#include "lwip/ip_addr.h" + +#if LWIP_SNMP && SNMP_USE_RAW + +#include "lwip/udp.h" +#include "lwip/ip.h" +#include "snmp_msg.h" + +/* lwIP UDP receive callback function */ +static void +snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) +{ + LWIP_UNUSED_ARG(arg); + + snmp_receive(pcb, p, addr, port); + + pbuf_free(p); +} + +err_t +snmp_sendto(void *handle, struct pbuf *p, const ip_addr_t *dst, u16_t port) +{ + return udp_sendto((struct udp_pcb*)handle, p, dst, port); +} + +u8_t +snmp_get_local_ip_for_dst(void* handle, const ip_addr_t *dst, ip_addr_t *result) +{ + struct udp_pcb* udp_pcb = (struct udp_pcb*)handle; + struct netif *dst_if; + const ip_addr_t* dst_ip; + + LWIP_UNUSED_ARG(udp_pcb); /* unused in case of IPV4 only configuration */ + + ip_route_get_local_ip(&udp_pcb->local_ip, dst, dst_if, dst_ip); + + if ((dst_if != NULL) && (dst_ip != NULL)) { + ip_addr_copy(*result, *dst_ip); + return 1; + } else { + return 0; + } +} + +/** + * @ingroup snmp_core + * Starts SNMP Agent. + * Allocates UDP pcb and binds it to IP_ANY_TYPE port 161. + */ +void +snmp_init(void) +{ + err_t err; + + struct udp_pcb *snmp_pcb = udp_new_ip_type(IPADDR_TYPE_ANY); + LWIP_ERROR("snmp_raw: no PCB", (snmp_pcb != NULL), return;); + + snmp_traps_handle = snmp_pcb; + + udp_recv(snmp_pcb, snmp_recv, (void *)SNMP_IN_PORT); + err = udp_bind(snmp_pcb, IP_ANY_TYPE, SNMP_IN_PORT); + LWIP_ERROR("snmp_raw: Unable to bind PCB", (err == ERR_OK), return;); +} + +#endif /* LWIP_SNMP && SNMP_USE_RAW */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_scalar.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_scalar.c new file mode 100644 index 0000000..136c9ec --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_scalar.c @@ -0,0 +1,220 @@ +/** + * @file + * SNMP scalar node support implementation. + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel <info@cl-soft.de> + * + */ + +#include "lwip/apps/snmp_opts.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/apps/snmp_scalar.h" +#include "lwip/apps/snmp_core.h" + +static s16_t snmp_scalar_array_get_value(struct snmp_node_instance* instance, void* value); +static snmp_err_t snmp_scalar_array_set_test(struct snmp_node_instance* instance, u16_t value_len, void* value); +static snmp_err_t snmp_scalar_array_set_value(struct snmp_node_instance* instance, u16_t value_len, void* value); + +snmp_err_t +snmp_scalar_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance) +{ + const struct snmp_scalar_node* scalar_node = (const struct snmp_scalar_node*)(const void*)instance->node; + + LWIP_UNUSED_ARG(root_oid); + LWIP_UNUSED_ARG(root_oid_len); + + /* scalar only has one dedicated instance: .0 */ + if ((instance->instance_oid.len != 1) || (instance->instance_oid.id[0] != 0)) { + return SNMP_ERR_NOSUCHINSTANCE; + } + + instance->access = scalar_node->access; + instance->asn1_type = scalar_node->asn1_type; + instance->get_value = scalar_node->get_value; + instance->set_test = scalar_node->set_test; + instance->set_value = scalar_node->set_value; + return SNMP_ERR_NOERROR; +} + +snmp_err_t +snmp_scalar_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance) +{ + /* because our only instance is .0 we can only return a next instance if no instance oid is passed */ + if (instance->instance_oid.len == 0) { + instance->instance_oid.len = 1; + instance->instance_oid.id[0] = 0; + + return snmp_scalar_get_instance(root_oid, root_oid_len, instance); + } + + return SNMP_ERR_NOSUCHINSTANCE; +} + + +snmp_err_t +snmp_scalar_array_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance) +{ + LWIP_UNUSED_ARG(root_oid); + LWIP_UNUSED_ARG(root_oid_len); + + if ((instance->instance_oid.len == 2) && (instance->instance_oid.id[1] == 0)) { + const struct snmp_scalar_array_node* array_node = (const struct snmp_scalar_array_node*)(const void*)instance->node; + const struct snmp_scalar_array_node_def* array_node_def = array_node->array_nodes; + u32_t i = 0; + + while (i < array_node->array_node_count) { + if (array_node_def->oid == instance->instance_oid.id[0]) { + break; + } + + array_node_def++; + i++; + } + + if (i < array_node->array_node_count) { + instance->access = array_node_def->access; + instance->asn1_type = array_node_def->asn1_type; + instance->get_value = snmp_scalar_array_get_value; + instance->set_test = snmp_scalar_array_set_test; + instance->set_value = snmp_scalar_array_set_value; + instance->reference.const_ptr = array_node_def; + + return SNMP_ERR_NOERROR; + } + } + + return SNMP_ERR_NOSUCHINSTANCE; +} + +snmp_err_t +snmp_scalar_array_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance) +{ + const struct snmp_scalar_array_node* array_node = (const struct snmp_scalar_array_node*)(const void*)instance->node; + const struct snmp_scalar_array_node_def* array_node_def = array_node->array_nodes; + const struct snmp_scalar_array_node_def* result = NULL; + + LWIP_UNUSED_ARG(root_oid); + LWIP_UNUSED_ARG(root_oid_len); + + if ((instance->instance_oid.len == 0) && (array_node->array_node_count > 0)) { + /* return node with lowest OID */ + u16_t i = 0; + + result = array_node_def; + array_node_def++; + + for (i = 1; i < array_node->array_node_count; i++) { + if (array_node_def->oid < result->oid) { + result = array_node_def; + } + array_node_def++; + } + } else if (instance->instance_oid.len >= 1) { + if (instance->instance_oid.len == 1) { + /* if we have the requested OID we return its instance, otherwise we search for the next available */ + u16_t i = 0; + while (i < array_node->array_node_count) { + if (array_node_def->oid == instance->instance_oid.id[0]) { + result = array_node_def; + break; + } + + array_node_def++; + i++; + } + } + if (result == NULL) { + u32_t oid_dist = 0xFFFFFFFFUL; + u16_t i = 0; + array_node_def = array_node->array_nodes; /* may be already at the end when if case before was executed without result -> reinitialize to start */ + while (i < array_node->array_node_count) { + if ((array_node_def->oid > instance->instance_oid.id[0]) && + ((u32_t)(array_node_def->oid - instance->instance_oid.id[0]) < oid_dist)) { + result = array_node_def; + oid_dist = array_node_def->oid - instance->instance_oid.id[0]; + } + + array_node_def++; + i++; + } + } + } + + if (result == NULL) { + /* nothing to return */ + return SNMP_ERR_NOSUCHINSTANCE; + } + + instance->instance_oid.len = 2; + instance->instance_oid.id[0] = result->oid; + instance->instance_oid.id[1] = 0; + + instance->access = result->access; + instance->asn1_type = result->asn1_type; + instance->get_value = snmp_scalar_array_get_value; + instance->set_test = snmp_scalar_array_set_test; + instance->set_value = snmp_scalar_array_set_value; + instance->reference.const_ptr = result; + + return SNMP_ERR_NOERROR; +} + +static s16_t +snmp_scalar_array_get_value(struct snmp_node_instance* instance, void* value) +{ + const struct snmp_scalar_array_node* array_node = (const struct snmp_scalar_array_node*)(const void*)instance->node; + const struct snmp_scalar_array_node_def* array_node_def = (const struct snmp_scalar_array_node_def*)instance->reference.const_ptr; + + return array_node->get_value(array_node_def, value); +} + +static snmp_err_t +snmp_scalar_array_set_test(struct snmp_node_instance* instance, u16_t value_len, void* value) +{ + const struct snmp_scalar_array_node* array_node = (const struct snmp_scalar_array_node*)(const void*)instance->node; + const struct snmp_scalar_array_node_def* array_node_def = (const struct snmp_scalar_array_node_def*)instance->reference.const_ptr; + + return array_node->set_test(array_node_def, value_len, value); +} + +static snmp_err_t +snmp_scalar_array_set_value(struct snmp_node_instance* instance, u16_t value_len, void* value) +{ + const struct snmp_scalar_array_node* array_node = (const struct snmp_scalar_array_node*)(const void*)instance->node; + const struct snmp_scalar_array_node_def* array_node_def = (const struct snmp_scalar_array_node_def*)instance->reference.const_ptr; + + return array_node->set_value(array_node_def, value_len, value); +} + +#endif /* LWIP_SNMP */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_table.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_table.c new file mode 100644 index 0000000..63ca595 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_table.c @@ -0,0 +1,343 @@ +/** + * @file + * SNMP table support implementation. + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel <info@cl-soft.de> + * + */ + +#include "lwip/apps/snmp_opts.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/apps/snmp_core.h" +#include "lwip/apps/snmp_table.h" +#include <string.h> + +snmp_err_t snmp_table_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance) +{ + snmp_err_t ret = SNMP_ERR_NOSUCHINSTANCE; + const struct snmp_table_node* table_node = (const struct snmp_table_node*)(const void*)instance->node; + + LWIP_UNUSED_ARG(root_oid); + LWIP_UNUSED_ARG(root_oid_len); + + /* check min. length (fixed row entry definition, column, row instance oid with at least one entry */ + /* fixed row entry always has oid 1 */ + if ((instance->instance_oid.len >= 3) && (instance->instance_oid.id[0] == 1)) { + /* search column */ + const struct snmp_table_col_def* col_def = table_node->columns; + u16_t i = table_node->column_count; + while (i > 0) { + if (col_def->index == instance->instance_oid.id[1]) { + break; + } + + col_def++; + i--; + } + + if (i > 0) { + /* everything may be overwritten by get_cell_instance_method() in order to implement special handling for single columns/cells */ + instance->asn1_type = col_def->asn1_type; + instance->access = col_def->access; + instance->get_value = table_node->get_value; + instance->set_test = table_node->set_test; + instance->set_value = table_node->set_value; + + ret = table_node->get_cell_instance( + &(instance->instance_oid.id[1]), + &(instance->instance_oid.id[2]), + instance->instance_oid.len-2, + instance); + } + } + + return ret; +} + +snmp_err_t snmp_table_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance) +{ + const struct snmp_table_node* table_node = (const struct snmp_table_node*)(const void*)instance->node; + const struct snmp_table_col_def* col_def; + struct snmp_obj_id row_oid; + u32_t column = 0; + snmp_err_t result; + + LWIP_UNUSED_ARG(root_oid); + LWIP_UNUSED_ARG(root_oid_len); + + /* check that first part of id is 0 or 1, referencing fixed row entry */ + if ((instance->instance_oid.len > 0) && (instance->instance_oid.id[0] > 1)) { + return SNMP_ERR_NOSUCHINSTANCE; + } + if (instance->instance_oid.len > 1) { + column = instance->instance_oid.id[1]; + } + if (instance->instance_oid.len > 2) { + snmp_oid_assign(&row_oid, &(instance->instance_oid.id[2]), instance->instance_oid.len - 2); + } else { + row_oid.len = 0; + } + + instance->get_value = table_node->get_value; + instance->set_test = table_node->set_test; + instance->set_value = table_node->set_value; + + /* resolve column and value */ + do { + u16_t i; + const struct snmp_table_col_def* next_col_def = NULL; + col_def = table_node->columns; + + for (i = 0; i < table_node->column_count; i++) { + if (col_def->index == column) { + next_col_def = col_def; + break; + } else if ((col_def->index > column) && ((next_col_def == NULL) || (col_def->index < next_col_def->index))) { + next_col_def = col_def; + } + col_def++; + } + + if (next_col_def == NULL) { + /* no further column found */ + return SNMP_ERR_NOSUCHINSTANCE; + } + + instance->asn1_type = next_col_def->asn1_type; + instance->access = next_col_def->access; + + result = table_node->get_next_cell_instance( + &next_col_def->index, + &row_oid, + instance); + + if (result == SNMP_ERR_NOERROR) { + col_def = next_col_def; + break; + } + + row_oid.len = 0; /* reset row_oid because we switch to next column and start with the first entry there */ + column = next_col_def->index + 1; + } while (1); + + /* build resulting oid */ + instance->instance_oid.len = 2; + instance->instance_oid.id[0] = 1; + instance->instance_oid.id[1] = col_def->index; + snmp_oid_append(&instance->instance_oid, row_oid.id, row_oid.len); + + return SNMP_ERR_NOERROR; +} + + +snmp_err_t snmp_table_simple_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance) +{ + snmp_err_t ret = SNMP_ERR_NOSUCHINSTANCE; + const struct snmp_table_simple_node* table_node = (const struct snmp_table_simple_node*)(const void*)instance->node; + + LWIP_UNUSED_ARG(root_oid); + LWIP_UNUSED_ARG(root_oid_len); + + /* check min. length (fixed row entry definition, column, row instance oid with at least one entry */ + /* fixed row entry always has oid 1 */ + if ((instance->instance_oid.len >= 3) && (instance->instance_oid.id[0] == 1)) { + ret = table_node->get_cell_value( + &(instance->instance_oid.id[1]), + &(instance->instance_oid.id[2]), + instance->instance_oid.len-2, + &instance->reference, + &instance->reference_len); + + if (ret == SNMP_ERR_NOERROR) { + /* search column */ + const struct snmp_table_simple_col_def* col_def = table_node->columns; + u32_t i = table_node->column_count; + while (i > 0) { + if (col_def->index == instance->instance_oid.id[1]) { + break; + } + + col_def++; + i--; + } + + if (i > 0) { + instance->asn1_type = col_def->asn1_type; + instance->access = SNMP_NODE_INSTANCE_READ_ONLY; + instance->set_test = NULL; + instance->set_value = NULL; + + switch (col_def->data_type) { + case SNMP_VARIANT_VALUE_TYPE_U32: + instance->get_value = snmp_table_extract_value_from_u32ref; + break; + case SNMP_VARIANT_VALUE_TYPE_S32: + instance->get_value = snmp_table_extract_value_from_s32ref; + break; + case SNMP_VARIANT_VALUE_TYPE_PTR: /* fall through */ + case SNMP_VARIANT_VALUE_TYPE_CONST_PTR: + instance->get_value = snmp_table_extract_value_from_refconstptr; + break; + default: + LWIP_DEBUGF(SNMP_DEBUG, ("snmp_table_simple_get_instance(): unknown column data_type: %d\n", col_def->data_type)); + return SNMP_ERR_GENERROR; + } + + ret = SNMP_ERR_NOERROR; + } else { + ret = SNMP_ERR_NOSUCHINSTANCE; + } + } + } + + return ret; +} + +snmp_err_t snmp_table_simple_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance) +{ + const struct snmp_table_simple_node* table_node = (const struct snmp_table_simple_node*)(const void*)instance->node; + const struct snmp_table_simple_col_def* col_def; + struct snmp_obj_id row_oid; + u32_t column = 0; + snmp_err_t result; + + LWIP_UNUSED_ARG(root_oid); + LWIP_UNUSED_ARG(root_oid_len); + + /* check that first part of id is 0 or 1, referencing fixed row entry */ + if ((instance->instance_oid.len > 0) && (instance->instance_oid.id[0] > 1)) { + return SNMP_ERR_NOSUCHINSTANCE; + } + if (instance->instance_oid.len > 1) { + column = instance->instance_oid.id[1]; + } + if (instance->instance_oid.len > 2) { + snmp_oid_assign(&row_oid, &(instance->instance_oid.id[2]), instance->instance_oid.len - 2); + } else { + row_oid.len = 0; + } + + /* resolve column and value */ + do { + u32_t i; + const struct snmp_table_simple_col_def* next_col_def = NULL; + col_def = table_node->columns; + + for (i = 0; i < table_node->column_count; i++) { + if (col_def->index == column) { + next_col_def = col_def; + break; + } else if ((col_def->index > column) && ((next_col_def == NULL) || + (col_def->index < next_col_def->index))) { + next_col_def = col_def; + } + col_def++; + } + + if (next_col_def == NULL) { + /* no further column found */ + return SNMP_ERR_NOSUCHINSTANCE; + } + + result = table_node->get_next_cell_instance_and_value( + &next_col_def->index, + &row_oid, + &instance->reference, + &instance->reference_len); + + if (result == SNMP_ERR_NOERROR) { + col_def = next_col_def; + break; + } + + row_oid.len = 0; /* reset row_oid because we switch to next column and start with the first entry there */ + column = next_col_def->index + 1; + } + while (1); + + instance->asn1_type = col_def->asn1_type; + instance->access = SNMP_NODE_INSTANCE_READ_ONLY; + instance->set_test = NULL; + instance->set_value = NULL; + + switch (col_def->data_type) { + case SNMP_VARIANT_VALUE_TYPE_U32: + instance->get_value = snmp_table_extract_value_from_u32ref; + break; + case SNMP_VARIANT_VALUE_TYPE_S32: + instance->get_value = snmp_table_extract_value_from_s32ref; + break; + case SNMP_VARIANT_VALUE_TYPE_PTR: /* fall through */ + case SNMP_VARIANT_VALUE_TYPE_CONST_PTR: + instance->get_value = snmp_table_extract_value_from_refconstptr; + break; + default: + LWIP_DEBUGF(SNMP_DEBUG, ("snmp_table_simple_get_instance(): unknown column data_type: %d\n", col_def->data_type)); + return SNMP_ERR_GENERROR; + } + + /* build resulting oid */ + instance->instance_oid.len = 2; + instance->instance_oid.id[0] = 1; + instance->instance_oid.id[1] = col_def->index; + snmp_oid_append(&instance->instance_oid, row_oid.id, row_oid.len); + + return SNMP_ERR_NOERROR; +} + + +s16_t +snmp_table_extract_value_from_s32ref(struct snmp_node_instance* instance, void* value) +{ + s32_t *dst = (s32_t*)value; + *dst = instance->reference.s32; + return sizeof(*dst); +} + +s16_t +snmp_table_extract_value_from_u32ref(struct snmp_node_instance* instance, void* value) +{ + u32_t *dst = (u32_t*)value; + *dst = instance->reference.u32; + return sizeof(*dst); +} + +s16_t +snmp_table_extract_value_from_refconstptr(struct snmp_node_instance* instance, void* value) +{ + MEMCPY(value, instance->reference.const_ptr, instance->reference_len); + return (u16_t)instance->reference_len; +} + +#endif /* LWIP_SNMP */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_threadsync.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_threadsync.c new file mode 100644 index 0000000..204f265 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_threadsync.c @@ -0,0 +1,219 @@ +/** + * @file + * SNMP thread synchronization implementation. + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Dirk Ziegelmeier <dziegel@gmx.de> + */ + +#include "lwip/apps/snmp_opts.h" + +#if LWIP_SNMP && (NO_SYS == 0) /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/apps/snmp_threadsync.h" +#include "lwip/apps/snmp_core.h" +#include "lwip/sys.h" +#include <string.h> + +static void +call_synced_function(struct threadsync_data *call_data, snmp_threadsync_called_fn fn) +{ + sys_mutex_lock(&call_data->threadsync_node->instance->sem_usage_mutex); + call_data->threadsync_node->instance->sync_fn(fn, call_data); + sys_sem_wait(&call_data->threadsync_node->instance->sem); + sys_mutex_unlock(&call_data->threadsync_node->instance->sem_usage_mutex); +} + +static void +threadsync_get_value_synced(void *ctx) +{ + struct threadsync_data *call_data = (struct threadsync_data*)ctx; + + call_data->retval.s16 = call_data->proxy_instance.get_value(&call_data->proxy_instance, call_data->arg1.value); + + sys_sem_signal(&call_data->threadsync_node->instance->sem); +} + +static s16_t +threadsync_get_value(struct snmp_node_instance* instance, void* value) +{ + struct threadsync_data *call_data = (struct threadsync_data*)instance->reference.ptr; + + call_data->arg1.value = value; + call_synced_function(call_data, threadsync_get_value_synced); + + return call_data->retval.s16; +} + +static void +threadsync_set_test_synced(void *ctx) +{ + struct threadsync_data *call_data = (struct threadsync_data*)ctx; + + call_data->retval.err = call_data->proxy_instance.set_test(&call_data->proxy_instance, call_data->arg2.len, call_data->arg1.value); + + sys_sem_signal(&call_data->threadsync_node->instance->sem); +} + +static snmp_err_t +threadsync_set_test(struct snmp_node_instance* instance, u16_t len, void *value) +{ + struct threadsync_data *call_data = (struct threadsync_data*)instance->reference.ptr; + + call_data->arg1.value = value; + call_data->arg2.len = len; + call_synced_function(call_data, threadsync_set_test_synced); + + return call_data->retval.err; +} + +static void +threadsync_set_value_synced(void *ctx) +{ + struct threadsync_data *call_data = (struct threadsync_data*)ctx; + + call_data->retval.err = call_data->proxy_instance.set_value(&call_data->proxy_instance, call_data->arg2.len, call_data->arg1.value); + + sys_sem_signal(&call_data->threadsync_node->instance->sem); +} + +static snmp_err_t +threadsync_set_value(struct snmp_node_instance* instance, u16_t len, void *value) +{ + struct threadsync_data *call_data = (struct threadsync_data*)instance->reference.ptr; + + call_data->arg1.value = value; + call_data->arg2.len = len; + call_synced_function(call_data, threadsync_set_value_synced); + + return call_data->retval.err; +} + +static void +threadsync_release_instance_synced(void* ctx) +{ + struct threadsync_data *call_data = (struct threadsync_data*)ctx; + + call_data->proxy_instance.release_instance(&call_data->proxy_instance); + + sys_sem_signal(&call_data->threadsync_node->instance->sem); +} + +static void +threadsync_release_instance(struct snmp_node_instance *instance) +{ + struct threadsync_data *call_data = (struct threadsync_data*)instance->reference.ptr; + + if (call_data->proxy_instance.release_instance != NULL) { + call_synced_function(call_data, threadsync_release_instance_synced); + } +} + +static void +get_instance_synced(void* ctx) +{ + struct threadsync_data *call_data = (struct threadsync_data*)ctx; + const struct snmp_leaf_node *leaf = (const struct snmp_leaf_node*)(const void*)call_data->proxy_instance.node; + + call_data->retval.err = leaf->get_instance(call_data->arg1.root_oid, call_data->arg2.root_oid_len, &call_data->proxy_instance); + + sys_sem_signal(&call_data->threadsync_node->instance->sem); +} + +static void +get_next_instance_synced(void* ctx) +{ + struct threadsync_data *call_data = (struct threadsync_data*)ctx; + const struct snmp_leaf_node *leaf = (const struct snmp_leaf_node*)(const void*)call_data->proxy_instance.node; + + call_data->retval.err = leaf->get_next_instance(call_data->arg1.root_oid, call_data->arg2.root_oid_len, &call_data->proxy_instance); + + sys_sem_signal(&call_data->threadsync_node->instance->sem); +} + +static snmp_err_t +do_sync(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance, snmp_threadsync_called_fn fn) +{ + const struct snmp_threadsync_node *threadsync_node = (const struct snmp_threadsync_node*)(const void*)instance->node; + struct threadsync_data *call_data = &threadsync_node->instance->data; + + if (threadsync_node->node.node.oid != threadsync_node->target->node.oid) { + LWIP_DEBUGF(SNMP_DEBUG, ("Sync node OID does not match target node OID")); + return SNMP_ERR_NOSUCHINSTANCE; + } + + memset(&call_data->proxy_instance, 0, sizeof(call_data->proxy_instance)); + + instance->reference.ptr = call_data; + snmp_oid_assign(&call_data->proxy_instance.instance_oid, instance->instance_oid.id, instance->instance_oid.len); + + call_data->proxy_instance.node = &threadsync_node->target->node; + call_data->threadsync_node = threadsync_node; + + call_data->arg1.root_oid = root_oid; + call_data->arg2.root_oid_len = root_oid_len; + call_synced_function(call_data, fn); + + if (call_data->retval.err == SNMP_ERR_NOERROR) { + instance->access = call_data->proxy_instance.access; + instance->asn1_type = call_data->proxy_instance.asn1_type; + instance->release_instance = threadsync_release_instance; + instance->get_value = (call_data->proxy_instance.get_value != NULL)? threadsync_get_value : NULL; + instance->set_value = (call_data->proxy_instance.set_value != NULL)? threadsync_set_value : NULL; + instance->set_test = (call_data->proxy_instance.set_test != NULL)? threadsync_set_test : NULL; + snmp_oid_assign(&instance->instance_oid, call_data->proxy_instance.instance_oid.id, call_data->proxy_instance.instance_oid.len); + } + + return call_data->retval.err; +} + +snmp_err_t +snmp_threadsync_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance) +{ + return do_sync(root_oid, root_oid_len, instance, get_instance_synced); +} + +snmp_err_t +snmp_threadsync_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance) +{ + return do_sync(root_oid, root_oid_len, instance, get_next_instance_synced); +} + +/** Initializes thread synchronization instance */ +void snmp_threadsync_init(struct snmp_threadsync_instance *instance, snmp_threadsync_synchronizer_fn sync_fn) +{ + err_t err = sys_mutex_new(&instance->sem_usage_mutex); + LWIP_ASSERT("Failed to set up mutex", err == ERR_OK); + err = sys_sem_new(&instance->sem, 0); + LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */ + LWIP_ASSERT("Failed to set up semaphore", err == ERR_OK); + instance->sync_fn = sync_fn; +} + +#endif /* LWIP_SNMP */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_traps.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_traps.c new file mode 100644 index 0000000..0d2df64 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_traps.c @@ -0,0 +1,445 @@ +/** + * @file + * SNMPv1 traps implementation. + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * Christiaan Simons <christiaan.simons@axon.tv> + * + */ + +#include "lwip/apps/snmp_opts.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include <string.h> + +#include "lwip/snmp.h" +#include "lwip/sys.h" +#include "lwip/apps/snmp.h" +#include "lwip/apps/snmp_core.h" +#include "snmp_msg.h" +#include "snmp_asn1.h" +#include "snmp_core_priv.h" + +struct snmp_msg_trap +{ + /* source enterprise ID (sysObjectID) */ + const struct snmp_obj_id *enterprise; + /* source IP address, raw network order format */ + ip_addr_t sip; + /* generic trap code */ + u32_t gen_trap; + /* specific trap code */ + u32_t spc_trap; + /* timestamp */ + u32_t ts; + /* snmp_version */ + u32_t snmp_version; + + /* output trap lengths used in ASN encoding */ + /* encoding pdu length */ + u16_t pdulen; + /* encoding community length */ + u16_t comlen; + /* encoding sequence length */ + u16_t seqlen; + /* encoding varbinds sequence length */ + u16_t vbseqlen; +}; + +static u16_t snmp_trap_varbind_sum(struct snmp_msg_trap *trap, struct snmp_varbind *varbinds); +static u16_t snmp_trap_header_sum(struct snmp_msg_trap *trap, u16_t vb_len); +static void snmp_trap_header_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream); +static void snmp_trap_varbind_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind *varbinds); + +/** Agent community string for sending traps */ +extern const char *snmp_community_trap; + +void* snmp_traps_handle; + +struct snmp_trap_dst +{ + /* destination IP address in network order */ + ip_addr_t dip; + /* set to 0 when disabled, >0 when enabled */ + u8_t enable; +}; +static struct snmp_trap_dst trap_dst[SNMP_TRAP_DESTINATIONS]; + +static u8_t snmp_auth_traps_enabled = 0; + +/** + * @ingroup snmp_traps + * Sets enable switch for this trap destination. + * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1 + * @param enable switch if 0 destination is disabled >0 enabled. + */ +void +snmp_trap_dst_enable(u8_t dst_idx, u8_t enable) +{ + if (dst_idx < SNMP_TRAP_DESTINATIONS) { + trap_dst[dst_idx].enable = enable; + } +} + +/** + * @ingroup snmp_traps + * Sets IPv4 address for this trap destination. + * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1 + * @param dst IPv4 address in host order. + */ +void +snmp_trap_dst_ip_set(u8_t dst_idx, const ip_addr_t *dst) +{ + if (dst_idx < SNMP_TRAP_DESTINATIONS) { + ip_addr_set(&trap_dst[dst_idx].dip, dst); + } +} + +/** + * @ingroup snmp_traps + * Enable/disable authentication traps + */ +void +snmp_set_auth_traps_enabled(u8_t enable) +{ + snmp_auth_traps_enabled = enable; +} + +/** + * @ingroup snmp_traps + * Get authentication traps enabled state + */ +u8_t +snmp_get_auth_traps_enabled(void) +{ + return snmp_auth_traps_enabled; +} + + +/** + * @ingroup snmp_traps + * Sends a generic or enterprise specific trap message. + * + * @param eoid points to enterprise object identifier + * @param generic_trap is the trap code + * @param specific_trap used for enterprise traps when generic_trap == 6 + * @param varbinds linked list of varbinds to be sent + * @return ERR_OK when success, ERR_MEM if we're out of memory + * + * @note the use of the enterprise identifier field + * is per RFC1215. + * Use .iso.org.dod.internet.mgmt.mib-2.snmp for generic traps + * and .iso.org.dod.internet.private.enterprises.yourenterprise + * (sysObjectID) for specific traps. + */ +err_t +snmp_send_trap(const struct snmp_obj_id* eoid, s32_t generic_trap, s32_t specific_trap, struct snmp_varbind *varbinds) +{ + struct snmp_msg_trap trap_msg; + struct snmp_trap_dst *td; + struct pbuf *p; + u16_t i, tot_len; + err_t err = ERR_OK; + + trap_msg.snmp_version = 0; + + for (i = 0, td = &trap_dst[0]; i < SNMP_TRAP_DESTINATIONS; i++, td++) { + if ((td->enable != 0) && !ip_addr_isany(&td->dip)) { + /* lookup current source address for this dst */ + if (snmp_get_local_ip_for_dst(snmp_traps_handle, &td->dip, &trap_msg.sip)) { + if (eoid == NULL) { + trap_msg.enterprise = snmp_get_device_enterprise_oid(); + } else { + trap_msg.enterprise = eoid; + } + + trap_msg.gen_trap = generic_trap; + if (generic_trap == SNMP_GENTRAP_ENTERPRISE_SPECIFIC) { + trap_msg.spc_trap = specific_trap; + } else { + trap_msg.spc_trap = 0; + } + + MIB2_COPY_SYSUPTIME_TO(&trap_msg.ts); + + /* pass 0, calculate length fields */ + tot_len = snmp_trap_varbind_sum(&trap_msg, varbinds); + tot_len = snmp_trap_header_sum(&trap_msg, tot_len); + + /* allocate pbuf(s) */ + p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_RAM); + if (p != NULL) { + struct snmp_pbuf_stream pbuf_stream; + snmp_pbuf_stream_init(&pbuf_stream, p, 0, tot_len); + + /* pass 1, encode packet ino the pbuf(s) */ + snmp_trap_header_enc(&trap_msg, &pbuf_stream); + snmp_trap_varbind_enc(&trap_msg, &pbuf_stream, varbinds); + + snmp_stats.outtraps++; + snmp_stats.outpkts++; + + /** send to the TRAP destination */ + snmp_sendto(snmp_traps_handle, p, &td->dip, SNMP_TRAP_PORT); + pbuf_free(p); + } else { + err = ERR_MEM; + } + } else { + /* routing error */ + err = ERR_RTE; + } + } + } + return err; +} + +/** + * @ingroup snmp_traps + * Send generic SNMP trap + */ +err_t +snmp_send_trap_generic(s32_t generic_trap) +{ + static const struct snmp_obj_id oid = { 7, { 1, 3, 6, 1, 2, 1, 11 } }; + return snmp_send_trap(&oid, generic_trap, 0, NULL); +} + +/** + * @ingroup snmp_traps + * Send specific SNMP trap with variable bindings + */ +err_t +snmp_send_trap_specific(s32_t specific_trap, struct snmp_varbind *varbinds) +{ + return snmp_send_trap(NULL, SNMP_GENTRAP_ENTERPRISE_SPECIFIC, specific_trap, varbinds); +} + +/** + * @ingroup snmp_traps + * Send coldstart trap + */ +void +snmp_coldstart_trap(void) +{ + snmp_send_trap_generic(SNMP_GENTRAP_COLDSTART); +} + +/** + * @ingroup snmp_traps + * Send authentication failure trap (used internally by agent) + */ +void +snmp_authfail_trap(void) +{ + if (snmp_auth_traps_enabled != 0) { + snmp_send_trap_generic(SNMP_GENTRAP_AUTH_FAILURE); + } +} + +static u16_t +snmp_trap_varbind_sum(struct snmp_msg_trap *trap, struct snmp_varbind *varbinds) +{ + struct snmp_varbind *varbind; + u16_t tot_len; + u8_t tot_len_len; + + tot_len = 0; + varbind = varbinds; + while (varbind != NULL) { + struct snmp_varbind_len len; + + if (snmp_varbind_length(varbind, &len) == ERR_OK) { + tot_len += 1 + len.vb_len_len + len.vb_value_len; + } + + varbind = varbind->next; + } + + trap->vbseqlen = tot_len; + snmp_asn1_enc_length_cnt(trap->vbseqlen, &tot_len_len); + tot_len += 1 + tot_len_len; + + return tot_len; +} + +/** + * Sums trap header field lengths from tail to head and + * returns trap_header_lengths for second encoding pass. + * + * @param trap Trap message + * @param vb_len varbind-list length + * @return the required length for encoding the trap header + */ +static u16_t +snmp_trap_header_sum(struct snmp_msg_trap *trap, u16_t vb_len) +{ + u16_t tot_len; + u16_t len; + u8_t lenlen; + + tot_len = vb_len; + + snmp_asn1_enc_u32t_cnt(trap->ts, &len); + snmp_asn1_enc_length_cnt(len, &lenlen); + tot_len += 1 + len + lenlen; + + snmp_asn1_enc_s32t_cnt(trap->spc_trap, &len); + snmp_asn1_enc_length_cnt(len, &lenlen); + tot_len += 1 + len + lenlen; + + snmp_asn1_enc_s32t_cnt(trap->gen_trap, &len); + snmp_asn1_enc_length_cnt(len, &lenlen); + tot_len += 1 + len + lenlen; + + if (IP_IS_V6_VAL(trap->sip)) { +#if LWIP_IPV6 + len = sizeof(ip_2_ip6(&trap->sip)->addr); +#endif + } else { +#if LWIP_IPV4 + len = sizeof(ip_2_ip4(&trap->sip)->addr); +#endif + } + snmp_asn1_enc_length_cnt(len, &lenlen); + tot_len += 1 + len + lenlen; + + snmp_asn1_enc_oid_cnt(trap->enterprise->id, trap->enterprise->len, &len); + snmp_asn1_enc_length_cnt(len, &lenlen); + tot_len += 1 + len + lenlen; + + trap->pdulen = tot_len; + snmp_asn1_enc_length_cnt(trap->pdulen, &lenlen); + tot_len += 1 + lenlen; + + trap->comlen = (u16_t)LWIP_MIN(strlen(snmp_community_trap), 0xFFFF); + snmp_asn1_enc_length_cnt(trap->comlen, &lenlen); + tot_len += 1 + lenlen + trap->comlen; + + snmp_asn1_enc_s32t_cnt(trap->snmp_version, &len); + snmp_asn1_enc_length_cnt(len, &lenlen); + tot_len += 1 + len + lenlen; + + trap->seqlen = tot_len; + snmp_asn1_enc_length_cnt(trap->seqlen, &lenlen); + tot_len += 1 + lenlen; + + return tot_len; +} + +static void +snmp_trap_varbind_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind *varbinds) +{ + struct snmp_asn1_tlv tlv; + struct snmp_varbind *varbind; + + varbind = varbinds; + + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 0, trap->vbseqlen); + snmp_ans1_enc_tlv(pbuf_stream, &tlv); + + while (varbind != NULL) { + snmp_append_outbound_varbind(pbuf_stream, varbind); + + varbind = varbind->next; + } +} + +/** + * Encodes trap header from head to tail. + */ +static void +snmp_trap_header_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream) +{ + struct snmp_asn1_tlv tlv; + + /* 'Message' sequence */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 0, trap->seqlen); + snmp_ans1_enc_tlv(pbuf_stream, &tlv); + + /* version */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0); + snmp_asn1_enc_s32t_cnt(trap->snmp_version, &tlv.value_len); + snmp_ans1_enc_tlv(pbuf_stream, &tlv); + snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->snmp_version); + + /* community */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, trap->comlen); + snmp_ans1_enc_tlv(pbuf_stream, &tlv); + snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)snmp_community_trap, trap->comlen); + + /* 'PDU' sequence */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_TRAP), 0, trap->pdulen); + snmp_ans1_enc_tlv(pbuf_stream, &tlv); + + /* object ID */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OBJECT_ID, 0, 0); + snmp_asn1_enc_oid_cnt(trap->enterprise->id, trap->enterprise->len, &tlv.value_len); + snmp_ans1_enc_tlv(pbuf_stream, &tlv); + snmp_asn1_enc_oid(pbuf_stream, trap->enterprise->id, trap->enterprise->len); + + /* IP addr */ + if (IP_IS_V6_VAL(trap->sip)) { +#if LWIP_IPV6 + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_IPADDR, 0, sizeof(ip_2_ip6(&trap->sip)->addr)); + snmp_ans1_enc_tlv(pbuf_stream, &tlv); + snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)&ip_2_ip6(&trap->sip)->addr, sizeof(ip_2_ip6(&trap->sip)->addr)); +#endif + } else { +#if LWIP_IPV4 + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_IPADDR, 0, sizeof(ip_2_ip4(&trap->sip)->addr)); + snmp_ans1_enc_tlv(pbuf_stream, &tlv); + snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)&ip_2_ip4(&trap->sip)->addr, sizeof(ip_2_ip4(&trap->sip)->addr)); +#endif + } + + /* trap length */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0); + snmp_asn1_enc_s32t_cnt(trap->gen_trap, &tlv.value_len); + snmp_ans1_enc_tlv(pbuf_stream, &tlv); + snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->gen_trap); + + /* specific trap */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0); + snmp_asn1_enc_s32t_cnt(trap->spc_trap, &tlv.value_len); + snmp_ans1_enc_tlv(pbuf_stream, &tlv); + snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->spc_trap); + + /* timestamp */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_TIMETICKS, 0, 0); + snmp_asn1_enc_s32t_cnt(trap->ts, &tlv.value_len); + snmp_ans1_enc_tlv(pbuf_stream, &tlv); + snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->ts); +} + +#endif /* LWIP_SNMP */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmpv3.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmpv3.c new file mode 100644 index 0000000..69fb3a0 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmpv3.c @@ -0,0 +1,136 @@ +/** + * @file + * Additional SNMPv3 functionality RFC3414 and RFC3826. + */ + +/* + * Copyright (c) 2016 Elias Oenal. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Elias Oenal <lwip@eliasoenal.com> + */ + +#include "snmpv3_priv.h" +#include "lwip/apps/snmpv3.h" +#include "lwip/sys.h" +#include <string.h> + +#if LWIP_SNMP && LWIP_SNMP_V3 + +#ifdef LWIP_SNMPV3_INCLUDE_ENGINE +#include LWIP_SNMPV3_INCLUDE_ENGINE +#endif + +#define SNMP_MAX_TIME_BOOT 2147483647UL + +/** Call this if engine has been changed. Has to reset boots, see below */ +void +snmpv3_engine_id_changed(void) +{ + snmpv3_set_engine_boots(0); +} + +/** According to RFC3414 2.2.2. + * + * The number of times that the SNMP engine has + * (re-)initialized itself since snmpEngineID + * was last configured. + */ +u32_t +snmpv3_get_engine_boots_internal(void) +{ + if (snmpv3_get_engine_boots() == 0 || + snmpv3_get_engine_boots() < SNMP_MAX_TIME_BOOT) { + return snmpv3_get_engine_boots(); + } + + snmpv3_set_engine_boots(SNMP_MAX_TIME_BOOT); + return snmpv3_get_engine_boots(); +} + +/** RFC3414 2.2.2. + * + * Once the timer reaches 2147483647 it gets reset to zero and the + * engine boot ups get incremented. + */ +u32_t +snmpv3_get_engine_time_internal(void) +{ + if (snmpv3_get_engine_time() >= SNMP_MAX_TIME_BOOT) { + snmpv3_reset_engine_time(); + + if (snmpv3_get_engine_boots() < SNMP_MAX_TIME_BOOT - 1) { + snmpv3_set_engine_boots(snmpv3_get_engine_boots() + 1); + } else { + snmpv3_set_engine_boots(SNMP_MAX_TIME_BOOT); + } + } + + return snmpv3_get_engine_time(); +} + +#if LWIP_SNMP_V3_CRYPTO + +/* This function ignores the byte order suggestion in RFC3414 + * since it simply doesn't influence the effectiveness of an IV. + * + * Implementing RFC3826 priv param algorithm if LWIP_RAND is available. + * + * @todo: This is a potential thread safety issue. + */ +err_t +snmpv3_build_priv_param(u8_t* priv_param) +{ +#ifdef LWIP_RAND /* Based on RFC3826 */ + static u8_t init; + static u32_t priv1, priv2; + + /* Lazy initialisation */ + if (init == 0) { + init = 1; + priv1 = LWIP_RAND(); + priv2 = LWIP_RAND(); + } + + SMEMCPY(&priv_param[0], &priv1, sizeof(priv1)); + SMEMCPY(&priv_param[4], &priv2, sizeof(priv2)); + + /* Emulate 64bit increment */ + priv1++; + if (!priv1) { /* Overflow */ + priv2++; + } +#else /* Based on RFC3414 */ + static u32_t ctr; + u32_t boots = LWIP_SNMPV3_GET_ENGINE_BOOTS(); + SMEMCPY(&priv_param[0], &boots, 4); + SMEMCPY(&priv_param[4], &ctr, 4); + ctr++; +#endif + return ERR_OK; +} +#endif /* LWIP_SNMP_V3_CRYPTO */ + +#endif diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmpv3_dummy.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmpv3_dummy.c new file mode 100644 index 0000000..bdfe844 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmpv3_dummy.c @@ -0,0 +1,145 @@ +/** + * @file + * Dummy SNMPv3 functions. + */ + +/* + * Copyright (c) 2016 Elias Oenal. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Elias Oenal <lwip@eliasoenal.com> + * Dirk Ziegelmeier <dirk@ziegelmeier.net> + */ + +#include "lwip/apps/snmpv3.h" +#include "snmpv3_priv.h" +#include <string.h> +#include "lwip/err.h" + +#if LWIP_SNMP && LWIP_SNMP_V3 + +/** + * @param username is a pointer to a string. + * @param auth_algo is a pointer to u8_t. The implementation has to set this if user was found. + * @param auth_key is a pointer to a pointer to a string. Implementation has to set this if user was found. + * @param priv_algo is a pointer to u8_t. The implementation has to set this if user was found. + * @param priv_key is a pointer to a pointer to a string. Implementation has to set this if user was found. + */ +err_t +snmpv3_get_user(const char* username, u8_t *auth_algo, u8_t *auth_key, u8_t *priv_algo, u8_t *priv_key) +{ + const char* engine_id; + u8_t engine_id_len; + + if(strlen(username) == 0) { + return ERR_OK; + } + + if(memcmp(username, "lwip", 4) != 0) { + return ERR_VAL; + } + + snmpv3_get_engine_id(&engine_id, &engine_id_len); + + if(auth_key != NULL) { + snmpv3_password_to_key_sha((const u8_t*)"maplesyrup", 10, + (const u8_t*)engine_id, engine_id_len, + auth_key); + *auth_algo = SNMP_V3_AUTH_ALGO_SHA; + } + if(priv_key != NULL) { + snmpv3_password_to_key_sha((const u8_t*)"maplesyrup", 10, + (const u8_t*)engine_id, engine_id_len, + priv_key); + *priv_algo = SNMP_V3_PRIV_ALGO_DES; + } + return ERR_OK; +} + +/** + * Get engine ID from persistence + * @param id + * @param len + */ +void +snmpv3_get_engine_id(const char **id, u8_t *len) +{ + *id = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02"; + *len = 12; +} + +/** + * Store engine ID in persistence + * @param id + * @param len + */ +err_t +snmpv3_set_engine_id(const char *id, u8_t len) +{ + LWIP_UNUSED_ARG(id); + LWIP_UNUSED_ARG(len); + return ERR_OK; +} + +/** + * Get engine boots from persistence. Must be increased on each boot. + * @return + */ +u32_t +snmpv3_get_engine_boots(void) +{ + return 0; +} + +/** + * Store engine boots in persistence + * @param boots + */ +void +snmpv3_set_engine_boots(u32_t boots) +{ + LWIP_UNUSED_ARG(boots); +} + +/** + * RFC3414 2.2.2. + * Once the timer reaches 2147483647 it gets reset to zero and the + * engine boot ups get incremented. + */ +u32_t +snmpv3_get_engine_time(void) +{ + return 0; +} + +/** + * Reset current engine time to 0 + */ +void +snmpv3_reset_engine_time(void) +{ +} + +#endif /* LWIP_SNMP && LWIP_SNMP_V3 */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmpv3_mbedtls.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmpv3_mbedtls.c new file mode 100644 index 0000000..0b1eefb --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmpv3_mbedtls.c @@ -0,0 +1,331 @@ +/** + * @file + * SNMPv3 crypto/auth functions implemented for ARM mbedtls. + */ + +/* + * Copyright (c) 2016 Elias Oenal and Dirk Ziegelmeier. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Elias Oenal <lwip@eliasoenal.com> + * Dirk Ziegelmeier <dirk@ziegelmeier.net> + */ + +#include "lwip/apps/snmpv3.h" +#include "snmpv3_priv.h" +#include "lwip/arch.h" +#include "snmp_msg.h" +#include "lwip/sys.h" +#include <string.h> + +#if LWIP_SNMP && LWIP_SNMP_V3 && LWIP_SNMP_V3_MBEDTLS + +#include "mbedtls/md.h" +#include "mbedtls/cipher.h" + +#include "mbedtls/md5.h" +#include "mbedtls/sha1.h" + +err_t +snmpv3_auth(struct snmp_pbuf_stream* stream, u16_t length, + const u8_t* key, u8_t algo, u8_t* hmac_out) +{ + u32_t i; + u8_t key_len; + const mbedtls_md_info_t *md_info; + mbedtls_md_context_t ctx; + struct snmp_pbuf_stream read_stream; + snmp_pbuf_stream_init(&read_stream, stream->pbuf, stream->offset, stream->length); + + if (algo == SNMP_V3_AUTH_ALGO_MD5) { + md_info = mbedtls_md_info_from_type(MBEDTLS_MD_MD5); + key_len = SNMP_V3_MD5_LEN; + } else if (algo == SNMP_V3_AUTH_ALGO_SHA) { + md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1); + key_len = SNMP_V3_SHA_LEN; + } else { + return ERR_ARG; + } + + mbedtls_md_init(&ctx); + if(mbedtls_md_setup(&ctx, md_info, 1) != 0) { + return ERR_ARG; + } + + if (mbedtls_md_hmac_starts(&ctx, key, key_len) != 0) { + goto free_md; + } + + for (i = 0; i < length; i++) { + u8_t byte; + + if (snmp_pbuf_stream_read(&read_stream, &byte)) { + goto free_md; + } + + if (mbedtls_md_hmac_update(&ctx, &byte, 1) != 0) { + goto free_md; + } + } + + if (mbedtls_md_hmac_finish(&ctx, hmac_out) != 0) { + goto free_md; + } + + mbedtls_md_free(&ctx); + return ERR_OK; + +free_md: + mbedtls_md_free(&ctx); + return ERR_ARG; +} + +#if LWIP_SNMP_V3_CRYPTO + +err_t +snmpv3_crypt(struct snmp_pbuf_stream* stream, u16_t length, + const u8_t* key, const u8_t* priv_param, const u32_t engine_boots, + const u32_t engine_time, u8_t algo, u8_t mode) +{ + size_t i; + mbedtls_cipher_context_t ctx; + const mbedtls_cipher_info_t *cipher_info; + + struct snmp_pbuf_stream read_stream; + struct snmp_pbuf_stream write_stream; + snmp_pbuf_stream_init(&read_stream, stream->pbuf, stream->offset, stream->length); + snmp_pbuf_stream_init(&write_stream, stream->pbuf, stream->offset, stream->length); + mbedtls_cipher_init(&ctx); + + if (algo == SNMP_V3_PRIV_ALGO_DES) { + u8_t iv_local[8]; + u8_t out_bytes[8]; + size_t out_len; + + /* RFC 3414 mandates padding for DES */ + if ((length & 0x07) != 0) { + return ERR_ARG; + } + + cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_DES_CBC); + if(mbedtls_cipher_setup(&ctx, cipher_info) != 0) { + return ERR_ARG; + } + if(mbedtls_cipher_set_padding_mode(&ctx, MBEDTLS_PADDING_NONE) != 0) { + return ERR_ARG; + } + if(mbedtls_cipher_setkey(&ctx, key, 8*8, (mode == SNMP_V3_PRIV_MODE_ENCRYPT)? MBEDTLS_ENCRYPT : MBEDTLS_DECRYPT) != 0) { + goto error; + } + + /* Prepare IV */ + for (i = 0; i < LWIP_ARRAYSIZE(iv_local); i++) { + iv_local[i] = priv_param[i] ^ key[i + 8]; + } + if(mbedtls_cipher_set_iv(&ctx, iv_local, LWIP_ARRAYSIZE(iv_local)) != 0) { + goto error; + } + + for (i = 0; i < length; i += 8) { + size_t j; + u8_t in_bytes[8]; + out_len = LWIP_ARRAYSIZE(out_bytes) ; + + for (j = 0; j < LWIP_ARRAYSIZE(in_bytes); j++) { + snmp_pbuf_stream_read(&read_stream, &in_bytes[j]); + } + + if(mbedtls_cipher_update(&ctx, in_bytes, LWIP_ARRAYSIZE(in_bytes), out_bytes, &out_len) != 0) { + goto error; + } + + snmp_pbuf_stream_writebuf(&write_stream, out_bytes, out_len); + } + + out_len = LWIP_ARRAYSIZE(out_bytes); + if(mbedtls_cipher_finish(&ctx, out_bytes, &out_len) != 0) { + goto error; + } + snmp_pbuf_stream_writebuf(&write_stream, out_bytes, out_len); + } else if (algo == SNMP_V3_PRIV_ALGO_AES) { + u8_t iv_local[16]; + + cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_CFB128); + if(mbedtls_cipher_setup(&ctx, cipher_info) != 0) { + return ERR_ARG; + } + if(mbedtls_cipher_setkey(&ctx, key, 16*8, (mode == SNMP_V3_PRIV_MODE_ENCRYPT)? MBEDTLS_ENCRYPT : MBEDTLS_DECRYPT) != 0) { + goto error; + } + + /* + * IV is the big endian concatenation of boots, + * uptime and priv param - see RFC3826. + */ + iv_local[0 + 0] = (engine_boots >> 24) & 0xFF; + iv_local[0 + 1] = (engine_boots >> 16) & 0xFF; + iv_local[0 + 2] = (engine_boots >> 8) & 0xFF; + iv_local[0 + 3] = (engine_boots >> 0) & 0xFF; + iv_local[4 + 0] = (engine_time >> 24) & 0xFF; + iv_local[4 + 1] = (engine_time >> 16) & 0xFF; + iv_local[4 + 2] = (engine_time >> 8) & 0xFF; + iv_local[4 + 3] = (engine_time >> 0) & 0xFF; + SMEMCPY(iv_local + 8, priv_param, 8); + if(mbedtls_cipher_set_iv(&ctx, iv_local, LWIP_ARRAYSIZE(iv_local)) != 0) { + goto error; + } + + for (i = 0; i < length; i++) { + u8_t in_byte; + u8_t out_byte; + size_t out_len = sizeof(out_byte); + + snmp_pbuf_stream_read(&read_stream, &in_byte); + if(mbedtls_cipher_update(&ctx, &in_byte, sizeof(in_byte), &out_byte, &out_len) != 0) { + goto error; + } + snmp_pbuf_stream_write(&write_stream, out_byte); + } + } else { + return ERR_ARG; + } + + mbedtls_cipher_free(&ctx); + return ERR_OK; + +error: + mbedtls_cipher_free(&ctx); + return ERR_OK; +} + +#endif /* LWIP_SNMP_V3_CRYPTO */ + +/* A.2.1. Password to Key Sample Code for MD5 */ +void +snmpv3_password_to_key_md5( + const u8_t *password, /* IN */ + u8_t passwordlen, /* IN */ + const u8_t *engineID, /* IN - pointer to snmpEngineID */ + u8_t engineLength,/* IN - length of snmpEngineID */ + u8_t *key) /* OUT - pointer to caller 16-octet buffer */ +{ + mbedtls_md5_context MD; + u8_t *cp, password_buf[64]; + u32_t password_index = 0; + u8_t i; + u32_t count = 0; + + mbedtls_md5_init(&MD); /* initialize MD5 */ + mbedtls_md5_starts(&MD); + + /**********************************************/ + /* Use while loop until we've done 1 Megabyte */ + /**********************************************/ + while (count < 1048576) { + cp = password_buf; + for (i = 0; i < 64; i++) { + /*************************************************/ + /* Take the next octet of the password, wrapping */ + /* to the beginning of the password as necessary.*/ + /*************************************************/ + *cp++ = password[password_index++ % passwordlen]; + } + mbedtls_md5_update(&MD, password_buf, 64); + count += 64; + } + mbedtls_md5_finish(&MD, key); /* tell MD5 we're done */ + + /*****************************************************/ + /* Now localize the key with the engineID and pass */ + /* through MD5 to produce final key */ + /* May want to ensure that engineLength <= 32, */ + /* otherwise need to use a buffer larger than 64 */ + /*****************************************************/ + SMEMCPY(password_buf, key, 16); + MEMCPY(password_buf + 16, engineID, engineLength); + SMEMCPY(password_buf + 16 + engineLength, key, 16); + + mbedtls_md5_starts(&MD); + mbedtls_md5_update(&MD, password_buf, 32 + engineLength); + mbedtls_md5_finish(&MD, key); + + mbedtls_md5_free(&MD); + return; +} + +/* A.2.2. Password to Key Sample Code for SHA */ +void +snmpv3_password_to_key_sha( + const u8_t *password, /* IN */ + u8_t passwordlen, /* IN */ + const u8_t *engineID, /* IN - pointer to snmpEngineID */ + u8_t engineLength,/* IN - length of snmpEngineID */ + u8_t *key) /* OUT - pointer to caller 20-octet buffer */ +{ + mbedtls_sha1_context SH; + u8_t *cp, password_buf[72]; + u32_t password_index = 0; + u8_t i; + u32_t count = 0; + + mbedtls_sha1_init(&SH); /* initialize SHA */ + mbedtls_sha1_starts(&SH); + + /**********************************************/ + /* Use while loop until we've done 1 Megabyte */ + /**********************************************/ + while (count < 1048576) { + cp = password_buf; + for (i = 0; i < 64; i++) { + /*************************************************/ + /* Take the next octet of the password, wrapping */ + /* to the beginning of the password as necessary.*/ + /*************************************************/ + *cp++ = password[password_index++ % passwordlen]; + } + mbedtls_sha1_update(&SH, password_buf, 64); + count += 64; + } + mbedtls_sha1_finish(&SH, key); /* tell SHA we're done */ + + /*****************************************************/ + /* Now localize the key with the engineID and pass */ + /* through SHA to produce final key */ + /* May want to ensure that engineLength <= 32, */ + /* otherwise need to use a buffer larger than 72 */ + /*****************************************************/ + SMEMCPY(password_buf, key, 20); + MEMCPY(password_buf + 20, engineID, engineLength); + SMEMCPY(password_buf + 20 + engineLength, key, 20); + + mbedtls_sha1_starts(&SH); + mbedtls_sha1_update(&SH, password_buf, 40 + engineLength); + mbedtls_sha1_finish(&SH, key); + + mbedtls_sha1_free(&SH); + return; +} + +#endif /* LWIP_SNMP && LWIP_SNMP_V3 && LWIP_SNMP_V3_MBEDTLS */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmpv3_priv.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmpv3_priv.h new file mode 100644 index 0000000..b87666d --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmpv3_priv.h @@ -0,0 +1,66 @@ +/** + * @file + * Additional SNMPv3 functionality RFC3414 and RFC3826 (internal API, do not use in client code). + */ + +/* + * Copyright (c) 2016 Elias Oenal. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Elias Oenal <lwip@eliasoenal.com> + */ + +#ifndef LWIP_HDR_APPS_SNMP_V3_PRIV_H +#define LWIP_HDR_APPS_SNMP_V3_PRIV_H + +#include "lwip/apps/snmp_opts.h" + +#if LWIP_SNMP && LWIP_SNMP_V3 + +#include "snmp_pbuf_stream.h" + +/* According to RFC 3411 */ +#define SNMP_V3_MAX_ENGINE_ID_LENGTH 32 +#define SNMP_V3_MAX_USER_LENGTH 32 + +#define SNMP_V3_MAX_AUTH_PARAM_LENGTH 12 +#define SNMP_V3_MAX_PRIV_PARAM_LENGTH 8 + +#define SNMP_V3_AUTH_FLAG 0x01 +#define SNMP_V3_PRIV_FLAG 0x02 + +#define SNMP_V3_MD5_LEN 16 +#define SNMP_V3_SHA_LEN 20 + +u32_t snmpv3_get_engine_boots_internal(void); +u32_t snmpv3_get_engine_time_internal(void); +err_t snmpv3_auth(struct snmp_pbuf_stream* stream, u16_t length, const u8_t* key, u8_t algo, u8_t* hmac_out); +err_t snmpv3_crypt(struct snmp_pbuf_stream* stream, u16_t length, const u8_t* key, + const u8_t* priv_param, const u32_t engine_boots, const u32_t engine_time, u8_t algo, u8_t mode); +err_t snmpv3_build_priv_param(u8_t* priv_param); + +#endif + +#endif /* LWIP_HDR_APPS_SNMP_V3_PRIV_H */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/sntp/sntp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/sntp/sntp.c new file mode 100644 index 0000000..71b2abe --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/sntp/sntp.c @@ -0,0 +1,727 @@ +/** + * @file + * SNTP client module + */ + +/* + * Copyright (c) 2007-2009 Frédéric Bernon, Simon Goldschmidt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Frédéric Bernon, Simon Goldschmidt + */ + + +/** + * @defgroup sntp SNTP + * @ingroup apps + * + * This is simple "SNTP" client for the lwIP raw API. + * It is a minimal implementation of SNTPv4 as specified in RFC 4330. + * + * For a list of some public NTP servers, see this link : + * http://support.ntp.org/bin/view/Servers/NTPPoolServers + * + * @todo: + * - set/change servers at runtime + * - complete SNTP_CHECK_RESPONSE checks 3 and 4 + */ + +#include "lwip/apps/sntp.h" + +#include "lwip/opt.h" +#include "lwip/timeouts.h" +#include "lwip/udp.h" +#include "lwip/dns.h" +#include "lwip/ip_addr.h" +#include "lwip/pbuf.h" +#include "lwip/dhcp.h" + +#include <string.h> +#include <time.h> + +#if LWIP_UDP + +/* Handle support for more than one server via SNTP_MAX_SERVERS */ +#if SNTP_MAX_SERVERS > 1 +#define SNTP_SUPPORT_MULTIPLE_SERVERS 1 +#else /* NTP_MAX_SERVERS > 1 */ +#define SNTP_SUPPORT_MULTIPLE_SERVERS 0 +#endif /* NTP_MAX_SERVERS > 1 */ + +#if (SNTP_UPDATE_DELAY < 15000) && !defined(SNTP_SUPPRESS_DELAY_CHECK) +#error "SNTPv4 RFC 4330 enforces a minimum update time of 15 seconds (define SNTP_SUPPRESS_DELAY_CHECK to disable this error)!" +#endif + +/* Configure behaviour depending on microsecond or second precision */ +#ifdef SNTP_SET_SYSTEM_TIME_US +#define SNTP_CALC_TIME_US 1 +#define SNTP_RECEIVE_TIME_SIZE 2 +#else +#define SNTP_SET_SYSTEM_TIME_US(sec, us) +#define SNTP_CALC_TIME_US 0 +#define SNTP_RECEIVE_TIME_SIZE 1 +#endif + + +/* the various debug levels for this file */ +#define SNTP_DEBUG_TRACE (SNTP_DEBUG | LWIP_DBG_TRACE) +#define SNTP_DEBUG_STATE (SNTP_DEBUG | LWIP_DBG_STATE) +#define SNTP_DEBUG_WARN (SNTP_DEBUG | LWIP_DBG_LEVEL_WARNING) +#define SNTP_DEBUG_WARN_STATE (SNTP_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE) +#define SNTP_DEBUG_SERIOUS (SNTP_DEBUG | LWIP_DBG_LEVEL_SERIOUS) + +#define SNTP_ERR_KOD 1 + +/* SNTP protocol defines */ +#define SNTP_MSG_LEN 48 + +#define SNTP_OFFSET_LI_VN_MODE 0 +#define SNTP_LI_MASK 0xC0 +#define SNTP_LI_NO_WARNING 0x00 +#define SNTP_LI_LAST_MINUTE_61_SEC 0x01 +#define SNTP_LI_LAST_MINUTE_59_SEC 0x02 +#define SNTP_LI_ALARM_CONDITION 0x03 /* (clock not synchronized) */ + +#define SNTP_VERSION_MASK 0x38 +#define SNTP_VERSION (4/* NTP Version 4*/<<3) + +#define SNTP_MODE_MASK 0x07 +#define SNTP_MODE_CLIENT 0x03 +#define SNTP_MODE_SERVER 0x04 +#define SNTP_MODE_BROADCAST 0x05 + +#define SNTP_OFFSET_STRATUM 1 +#define SNTP_STRATUM_KOD 0x00 + +#define SNTP_OFFSET_ORIGINATE_TIME 24 +#define SNTP_OFFSET_RECEIVE_TIME 32 +#define SNTP_OFFSET_TRANSMIT_TIME 40 + +/* number of seconds between 1900 and 1970 (MSB=1)*/ +#define DIFF_SEC_1900_1970 (2208988800UL) +/* number of seconds between 1970 and Feb 7, 2036 (6:28:16 UTC) (MSB=0) */ +#define DIFF_SEC_1970_2036 (2085978496UL) + +/** + * SNTP packet format (without optional fields) + * Timestamps are coded as 64 bits: + * - 32 bits seconds since Jan 01, 1970, 00:00 + * - 32 bits seconds fraction (0-padded) + * For future use, if the MSB in the seconds part is set, seconds are based + * on Feb 07, 2036, 06:28:16. + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct sntp_msg { + PACK_STRUCT_FLD_8(u8_t li_vn_mode); + PACK_STRUCT_FLD_8(u8_t stratum); + PACK_STRUCT_FLD_8(u8_t poll); + PACK_STRUCT_FLD_8(u8_t precision); + PACK_STRUCT_FIELD(u32_t root_delay); + PACK_STRUCT_FIELD(u32_t root_dispersion); + PACK_STRUCT_FIELD(u32_t reference_identifier); + PACK_STRUCT_FIELD(u32_t reference_timestamp[2]); + PACK_STRUCT_FIELD(u32_t originate_timestamp[2]); + PACK_STRUCT_FIELD(u32_t receive_timestamp[2]); + PACK_STRUCT_FIELD(u32_t transmit_timestamp[2]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* function prototypes */ +static void sntp_request(void *arg); + +/** The operating mode */ +static u8_t sntp_opmode; + +/** The UDP pcb used by the SNTP client */ +static struct udp_pcb* sntp_pcb; +/** Names/Addresses of servers */ +struct sntp_server { +#if SNTP_SERVER_DNS + char* name; +#endif /* SNTP_SERVER_DNS */ + ip_addr_t addr; +}; +static struct sntp_server sntp_servers[SNTP_MAX_SERVERS]; + +#if SNTP_GET_SERVERS_FROM_DHCP +static u8_t sntp_set_servers_from_dhcp; +#endif /* SNTP_GET_SERVERS_FROM_DHCP */ +#if SNTP_SUPPORT_MULTIPLE_SERVERS +/** The currently used server (initialized to 0) */ +static u8_t sntp_current_server; +#else /* SNTP_SUPPORT_MULTIPLE_SERVERS */ +#define sntp_current_server 0 +#endif /* SNTP_SUPPORT_MULTIPLE_SERVERS */ + +#if SNTP_RETRY_TIMEOUT_EXP +#define SNTP_RESET_RETRY_TIMEOUT() sntp_retry_timeout = SNTP_RETRY_TIMEOUT +/** Retry time, initialized with SNTP_RETRY_TIMEOUT and doubled with each retry. */ +static u32_t sntp_retry_timeout; +#else /* SNTP_RETRY_TIMEOUT_EXP */ +#define SNTP_RESET_RETRY_TIMEOUT() +#define sntp_retry_timeout SNTP_RETRY_TIMEOUT +#endif /* SNTP_RETRY_TIMEOUT_EXP */ + +#if SNTP_CHECK_RESPONSE >= 1 +/** Saves the last server address to compare with response */ +static ip_addr_t sntp_last_server_address; +#endif /* SNTP_CHECK_RESPONSE >= 1 */ + +#if SNTP_CHECK_RESPONSE >= 2 +/** Saves the last timestamp sent (which is sent back by the server) + * to compare against in response */ +static u32_t sntp_last_timestamp_sent[2]; +#endif /* SNTP_CHECK_RESPONSE >= 2 */ + +/** + * SNTP processing of received timestamp + */ +static void +sntp_process(u32_t *receive_timestamp) +{ + /* convert SNTP time (1900-based) to unix GMT time (1970-based) + * if MSB is 0, SNTP time is 2036-based! + */ + u32_t rx_secs = lwip_ntohl(receive_timestamp[0]); + int is_1900_based = ((rx_secs & 0x80000000) != 0); + u32_t t = is_1900_based ? (rx_secs - DIFF_SEC_1900_1970) : (rx_secs + DIFF_SEC_1970_2036); + time_t tim = t; + +#if SNTP_CALC_TIME_US + u32_t us = lwip_ntohl(receive_timestamp[1]) / 4295; + SNTP_SET_SYSTEM_TIME_US(t, us); + /* display local time from GMT time */ + LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_process: %s, %"U32_F" us", ctime(&tim), us)); + +#else /* SNTP_CALC_TIME_US */ + + /* change system time and/or the update the RTC clock */ + SNTP_SET_SYSTEM_TIME(t); + /* display local time from GMT time */ + LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_process: %s", ctime(&tim))); +#endif /* SNTP_CALC_TIME_US */ + LWIP_UNUSED_ARG(tim); +} + +/** + * Initialize request struct to be sent to server. + */ +static void +sntp_initialize_request(struct sntp_msg *req) +{ + memset(req, 0, SNTP_MSG_LEN); + req->li_vn_mode = SNTP_LI_NO_WARNING | SNTP_VERSION | SNTP_MODE_CLIENT; + +#if SNTP_CHECK_RESPONSE >= 2 + { + u32_t sntp_time_sec, sntp_time_us; + /* fill in transmit timestamp and save it in 'sntp_last_timestamp_sent' */ + SNTP_GET_SYSTEM_TIME(sntp_time_sec, sntp_time_us); + sntp_last_timestamp_sent[0] = lwip_htonl(sntp_time_sec + DIFF_SEC_1900_1970); + req->transmit_timestamp[0] = sntp_last_timestamp_sent[0]; + /* we send/save us instead of fraction to be faster... */ + sntp_last_timestamp_sent[1] = lwip_htonl(sntp_time_us); + req->transmit_timestamp[1] = sntp_last_timestamp_sent[1]; + } +#endif /* SNTP_CHECK_RESPONSE >= 2 */ +} + +/** + * Retry: send a new request (and increase retry timeout). + * + * @param arg is unused (only necessary to conform to sys_timeout) + */ +static void +sntp_retry(void* arg) +{ + LWIP_UNUSED_ARG(arg); + + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_retry: Next request will be sent in %"U32_F" ms\n", + sntp_retry_timeout)); + + /* set up a timer to send a retry and increase the retry delay */ + sys_timeout(sntp_retry_timeout, sntp_request, NULL); + +#if SNTP_RETRY_TIMEOUT_EXP + { + u32_t new_retry_timeout; + /* increase the timeout for next retry */ + new_retry_timeout = sntp_retry_timeout << 1; + /* limit to maximum timeout and prevent overflow */ + if ((new_retry_timeout <= SNTP_RETRY_TIMEOUT_MAX) && + (new_retry_timeout > sntp_retry_timeout)) { + sntp_retry_timeout = new_retry_timeout; + } + } +#endif /* SNTP_RETRY_TIMEOUT_EXP */ +} + +#if SNTP_SUPPORT_MULTIPLE_SERVERS +/** + * If Kiss-of-Death is received (or another packet parsing error), + * try the next server or retry the current server and increase the retry + * timeout if only one server is available. + * (implicitly, SNTP_MAX_SERVERS > 1) + * + * @param arg is unused (only necessary to conform to sys_timeout) + */ +static void +sntp_try_next_server(void* arg) +{ + u8_t old_server, i; + LWIP_UNUSED_ARG(arg); + + old_server = sntp_current_server; + for (i = 0; i < SNTP_MAX_SERVERS - 1; i++) { + sntp_current_server++; + if (sntp_current_server >= SNTP_MAX_SERVERS) { + sntp_current_server = 0; + } + if (!ip_addr_isany(&sntp_servers[sntp_current_server].addr) +#if SNTP_SERVER_DNS + || (sntp_servers[sntp_current_server].name != NULL) +#endif + ) { + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_try_next_server: Sending request to server %"U16_F"\n", + (u16_t)sntp_current_server)); + /* new server: reset retry timeout */ + SNTP_RESET_RETRY_TIMEOUT(); + /* instantly send a request to the next server */ + sntp_request(NULL); + return; + } + } + /* no other valid server found */ + sntp_current_server = old_server; + sntp_retry(NULL); +} +#else /* SNTP_SUPPORT_MULTIPLE_SERVERS */ +/* Always retry on error if only one server is supported */ +#define sntp_try_next_server sntp_retry +#endif /* SNTP_SUPPORT_MULTIPLE_SERVERS */ + +/** UDP recv callback for the sntp pcb */ +static void +sntp_recv(void *arg, struct udp_pcb* pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) +{ + u8_t mode; + u8_t stratum; + u32_t receive_timestamp[SNTP_RECEIVE_TIME_SIZE]; + err_t err; + + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(pcb); + + /* packet received: stop retry timeout */ + sys_untimeout(sntp_try_next_server, NULL); + sys_untimeout(sntp_request, NULL); + + err = ERR_ARG; +#if SNTP_CHECK_RESPONSE >= 1 + /* check server address and port */ + if (((sntp_opmode != SNTP_OPMODE_POLL) || ip_addr_cmp(addr, &sntp_last_server_address)) && + (port == SNTP_PORT)) +#else /* SNTP_CHECK_RESPONSE >= 1 */ + LWIP_UNUSED_ARG(addr); + LWIP_UNUSED_ARG(port); +#endif /* SNTP_CHECK_RESPONSE >= 1 */ + { + /* process the response */ + if (p->tot_len == SNTP_MSG_LEN) { + pbuf_copy_partial(p, &mode, 1, SNTP_OFFSET_LI_VN_MODE); + mode &= SNTP_MODE_MASK; + /* if this is a SNTP response... */ + if (((sntp_opmode == SNTP_OPMODE_POLL) && (mode == SNTP_MODE_SERVER)) || + ((sntp_opmode == SNTP_OPMODE_LISTENONLY) && (mode == SNTP_MODE_BROADCAST))) { + pbuf_copy_partial(p, &stratum, 1, SNTP_OFFSET_STRATUM); + if (stratum == SNTP_STRATUM_KOD) { + /* Kiss-of-death packet. Use another server or increase UPDATE_DELAY. */ + err = SNTP_ERR_KOD; + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_recv: Received Kiss-of-Death\n")); + } else { +#if SNTP_CHECK_RESPONSE >= 2 + /* check originate_timetamp against sntp_last_timestamp_sent */ + u32_t originate_timestamp[2]; + pbuf_copy_partial(p, &originate_timestamp, 8, SNTP_OFFSET_ORIGINATE_TIME); + if ((originate_timestamp[0] != sntp_last_timestamp_sent[0]) || + (originate_timestamp[1] != sntp_last_timestamp_sent[1])) + { + LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid originate timestamp in response\n")); + } else +#endif /* SNTP_CHECK_RESPONSE >= 2 */ + /* @todo: add code for SNTP_CHECK_RESPONSE >= 3 and >= 4 here */ + { + /* correct answer */ + err = ERR_OK; + pbuf_copy_partial(p, &receive_timestamp, SNTP_RECEIVE_TIME_SIZE * 4, SNTP_OFFSET_TRANSMIT_TIME); + } + } + } else { + LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid mode in response: %"U16_F"\n", (u16_t)mode)); + /* wait for correct response */ + err = ERR_TIMEOUT; + } + } else { + LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid packet length: %"U16_F"\n", p->tot_len)); + } + } +#if SNTP_CHECK_RESPONSE >= 1 + else { + /* packet from wrong remote address or port, wait for correct response */ + err = ERR_TIMEOUT; + } +#endif /* SNTP_CHECK_RESPONSE >= 1 */ + pbuf_free(p); + if (err == ERR_OK) { + sntp_process(receive_timestamp); + + /* Set up timeout for next request (only if poll response was received)*/ + if (sntp_opmode == SNTP_OPMODE_POLL) { + /* Correct response, reset retry timeout */ + SNTP_RESET_RETRY_TIMEOUT(); + + sys_timeout((u32_t)SNTP_UPDATE_DELAY, sntp_request, NULL); + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_recv: Scheduled next time request: %"U32_F" ms\n", + (u32_t)SNTP_UPDATE_DELAY)); + } + } else if (err != ERR_TIMEOUT) { + /* Errors are only processed in case of an explicit poll response */ + if (sntp_opmode == SNTP_OPMODE_POLL) { + if (err == SNTP_ERR_KOD) { + /* Kiss-of-death packet. Use another server or increase UPDATE_DELAY. */ + sntp_try_next_server(NULL); + } else { + /* another error, try the same server again */ + sntp_retry(NULL); + } + } + } +} + +/** Actually send an sntp request to a server. + * + * @param server_addr resolved IP address of the SNTP server + */ +static void +sntp_send_request(const ip_addr_t *server_addr) +{ + struct pbuf* p; + p = pbuf_alloc(PBUF_TRANSPORT, SNTP_MSG_LEN, PBUF_RAM); + if (p != NULL) { + struct sntp_msg *sntpmsg = (struct sntp_msg *)p->payload; + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_send_request: Sending request to server\n")); + /* initialize request message */ + sntp_initialize_request(sntpmsg); + /* send request */ + udp_sendto(sntp_pcb, p, server_addr, SNTP_PORT); + /* free the pbuf after sending it */ + pbuf_free(p); + /* set up receive timeout: try next server or retry on timeout */ + sys_timeout((u32_t)SNTP_RECV_TIMEOUT, sntp_try_next_server, NULL); +#if SNTP_CHECK_RESPONSE >= 1 + /* save server address to verify it in sntp_recv */ + ip_addr_set(&sntp_last_server_address, server_addr); +#endif /* SNTP_CHECK_RESPONSE >= 1 */ + } else { + LWIP_DEBUGF(SNTP_DEBUG_SERIOUS, ("sntp_send_request: Out of memory, trying again in %"U32_F" ms\n", + (u32_t)SNTP_RETRY_TIMEOUT)); + /* out of memory: set up a timer to send a retry */ + sys_timeout((u32_t)SNTP_RETRY_TIMEOUT, sntp_request, NULL); + } +} + +#if SNTP_SERVER_DNS +/** + * DNS found callback when using DNS names as server address. + */ +static void +sntp_dns_found(const char* hostname, const ip_addr_t *ipaddr, void *arg) +{ + LWIP_UNUSED_ARG(hostname); + LWIP_UNUSED_ARG(arg); + + if (ipaddr != NULL) { + /* Address resolved, send request */ + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_dns_found: Server address resolved, sending request\n")); + sntp_send_request(ipaddr); + } else { + /* DNS resolving failed -> try another server */ + LWIP_DEBUGF(SNTP_DEBUG_WARN_STATE, ("sntp_dns_found: Failed to resolve server address resolved, trying next server\n")); + sntp_try_next_server(NULL); + } +} +#endif /* SNTP_SERVER_DNS */ + +/** + * Send out an sntp request. + * + * @param arg is unused (only necessary to conform to sys_timeout) + */ +static void +sntp_request(void *arg) +{ + ip_addr_t sntp_server_address; + err_t err; + + LWIP_UNUSED_ARG(arg); + + /* initialize SNTP server address */ +#if SNTP_SERVER_DNS + if (sntp_servers[sntp_current_server].name) { + /* always resolve the name and rely on dns-internal caching & timeout */ + ip_addr_set_zero(&sntp_servers[sntp_current_server].addr); + err = dns_gethostbyname(sntp_servers[sntp_current_server].name, &sntp_server_address, + sntp_dns_found, NULL); + if (err == ERR_INPROGRESS) { + /* DNS request sent, wait for sntp_dns_found being called */ + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_request: Waiting for server address to be resolved.\n")); + return; + } else if (err == ERR_OK) { + sntp_servers[sntp_current_server].addr = sntp_server_address; + } + } else +#endif /* SNTP_SERVER_DNS */ + { + sntp_server_address = sntp_servers[sntp_current_server].addr; + err = (ip_addr_isany_val(sntp_server_address)) ? ERR_ARG : ERR_OK; + } + + if (err == ERR_OK) { + LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_request: current server address is %s\n", + ipaddr_ntoa(&sntp_server_address))); + sntp_send_request(&sntp_server_address); + } else { + /* address conversion failed, try another server */ + LWIP_DEBUGF(SNTP_DEBUG_WARN_STATE, ("sntp_request: Invalid server address, trying next server.\n")); + sys_timeout((u32_t)SNTP_RETRY_TIMEOUT, sntp_try_next_server, NULL); + } +} + +/** + * @ingroup sntp + * Initialize this module. + * Send out request instantly or after SNTP_STARTUP_DELAY(_FUNC). + */ +void +sntp_init(void) +{ +#ifdef SNTP_SERVER_ADDRESS +#if SNTP_SERVER_DNS + sntp_setservername(0, SNTP_SERVER_ADDRESS); +#else +#error SNTP_SERVER_ADDRESS string not supported SNTP_SERVER_DNS==0 +#endif +#endif /* SNTP_SERVER_ADDRESS */ + + if (sntp_pcb == NULL) { + sntp_pcb = udp_new_ip_type(IPADDR_TYPE_ANY); + LWIP_ASSERT("Failed to allocate udp pcb for sntp client", sntp_pcb != NULL); + if (sntp_pcb != NULL) { + udp_recv(sntp_pcb, sntp_recv, NULL); + + if (sntp_opmode == SNTP_OPMODE_POLL) { + SNTP_RESET_RETRY_TIMEOUT(); +#if SNTP_STARTUP_DELAY + sys_timeout((u32_t)SNTP_STARTUP_DELAY_FUNC, sntp_request, NULL); +#else + sntp_request(NULL); +#endif + } else if (sntp_opmode == SNTP_OPMODE_LISTENONLY) { + ip_set_option(sntp_pcb, SOF_BROADCAST); + udp_bind(sntp_pcb, IP_ANY_TYPE, SNTP_PORT); + } + } + } +} + +/** + * @ingroup sntp + * Stop this module. + */ +void +sntp_stop(void) +{ + if (sntp_pcb != NULL) { + sys_untimeout(sntp_request, NULL); + sys_untimeout(sntp_try_next_server, NULL); + udp_remove(sntp_pcb); + sntp_pcb = NULL; + } +} + +/** + * @ingroup sntp + * Get enabled state. + */ +u8_t sntp_enabled(void) +{ + return (sntp_pcb != NULL)? 1 : 0; +} + +/** + * @ingroup sntp + * Sets the operating mode. + * @param operating_mode one of the available operating modes + */ +void +sntp_setoperatingmode(u8_t operating_mode) +{ + LWIP_ASSERT("Invalid operating mode", operating_mode <= SNTP_OPMODE_LISTENONLY); + LWIP_ASSERT("Operating mode must not be set while SNTP client is running", sntp_pcb == NULL); + sntp_opmode = operating_mode; +} + +/** + * @ingroup sntp + * Gets the operating mode. + */ +u8_t +sntp_getoperatingmode(void) +{ + return sntp_opmode; +} + +#if SNTP_GET_SERVERS_FROM_DHCP +/** + * Config SNTP server handling by IP address, name, or DHCP; clear table + * @param set_servers_from_dhcp enable or disable getting server addresses from dhcp + */ +void +sntp_servermode_dhcp(int set_servers_from_dhcp) +{ + u8_t new_mode = set_servers_from_dhcp ? 1 : 0; + if (sntp_set_servers_from_dhcp != new_mode) { + sntp_set_servers_from_dhcp = new_mode; + } +} +#endif /* SNTP_GET_SERVERS_FROM_DHCP */ + +/** + * @ingroup sntp + * Initialize one of the NTP servers by IP address + * + * @param idx the index of the NTP server to set must be < SNTP_MAX_SERVERS + * @param server IP address of the NTP server to set + */ +void +sntp_setserver(u8_t idx, const ip_addr_t *server) +{ + if (idx < SNTP_MAX_SERVERS) { + if (server != NULL) { + sntp_servers[idx].addr = (*server); + } else { + ip_addr_set_zero(&sntp_servers[idx].addr); + } +#if SNTP_SERVER_DNS + sntp_servers[idx].name = NULL; +#endif + } +} + +#if LWIP_DHCP && SNTP_GET_SERVERS_FROM_DHCP +/** + * Initialize one of the NTP servers by IP address, required by DHCP + * + * @param numdns the index of the NTP server to set must be < SNTP_MAX_SERVERS + * @param dnsserver IP address of the NTP server to set + */ +void +dhcp_set_ntp_servers(u8_t num, const ip4_addr_t *server) +{ + LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp: %s %u.%u.%u.%u as NTP server #%u via DHCP\n", + (sntp_set_servers_from_dhcp ? "Got" : "Rejected"), + ip4_addr1(server), ip4_addr2(server), ip4_addr3(server), ip4_addr4(server), num)); + if (sntp_set_servers_from_dhcp && num) { + u8_t i; + for (i = 0; (i < num) && (i < SNTP_MAX_SERVERS); i++) { + ip_addr_t addr; + ip_addr_copy_from_ip4(addr, server[i]); + sntp_setserver(i, &addr); + } + for (i = num; i < SNTP_MAX_SERVERS; i++) { + sntp_setserver(i, NULL); + } + } +} +#endif /* LWIP_DHCP && SNTP_GET_SERVERS_FROM_DHCP */ + +/** + * @ingroup sntp + * Obtain one of the currently configured by IP address (or DHCP) NTP servers + * + * @param idx the index of the NTP server + * @return IP address of the indexed NTP server or "ip_addr_any" if the NTP + * server has not been configured by address (or at all). + */ +const ip_addr_t* +sntp_getserver(u8_t idx) +{ + if (idx < SNTP_MAX_SERVERS) { + return &sntp_servers[idx].addr; + } + return IP_ADDR_ANY; +} + +#if SNTP_SERVER_DNS +/** + * Initialize one of the NTP servers by name + * + * @param numdns the index of the NTP server to set must be < SNTP_MAX_SERVERS + * @param dnsserver DNS name of the NTP server to set, to be resolved at contact time + */ +void +sntp_setservername(u8_t idx, char *server) +{ + if (idx < SNTP_MAX_SERVERS) { + sntp_servers[idx].name = server; + } +} + +/** + * Obtain one of the currently configured by name NTP servers. + * + * @param numdns the index of the NTP server + * @return IP address of the indexed NTP server or NULL if the NTP + * server has not been configured by name (or at all) + */ +char * +sntp_getservername(u8_t idx) +{ + if (idx < SNTP_MAX_SERVERS) { + return sntp_servers[idx].name; + } + return NULL; +} +#endif /* SNTP_SERVER_DNS */ + +#endif /* LWIP_UDP */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/tftp/tftp_server.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/tftp/tftp_server.c new file mode 100644 index 0000000..243b092 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/tftp/tftp_server.c @@ -0,0 +1,417 @@ +/****************************************************************//** + * + * @file tftp_server.c + * + * @author Logan Gunthorpe <logang@deltatee.com> + * Dirk Ziegelmeier <dziegel@gmx.de> + * + * @brief Trivial File Transfer Protocol (RFC 1350) + * + * Copyright (c) Deltatee Enterprises Ltd. 2013 + * All rights reserved. + * + ********************************************************************/ + +/* + * Redistribution and use in source and binary forms, with or without + * modification,are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Author: Logan Gunthorpe <logang@deltatee.com> + * Dirk Ziegelmeier <dziegel@gmx.de> + * + */ + +/** + * @defgroup tftp TFTP server + * @ingroup apps + * + * This is simple TFTP server for the lwIP raw API. + */ + +#include "lwip/apps/tftp_server.h" + +#if LWIP_UDP + +#include "lwip/udp.h" +#include "lwip/timeouts.h" +#include "lwip/debug.h" + +#define TFTP_MAX_PAYLOAD_SIZE 512 +#define TFTP_HEADER_LENGTH 4 + +#define TFTP_RRQ 1 +#define TFTP_WRQ 2 +#define TFTP_DATA 3 +#define TFTP_ACK 4 +#define TFTP_ERROR 5 + +enum tftp_error { + TFTP_ERROR_FILE_NOT_FOUND = 1, + TFTP_ERROR_ACCESS_VIOLATION = 2, + TFTP_ERROR_DISK_FULL = 3, + TFTP_ERROR_ILLEGAL_OPERATION = 4, + TFTP_ERROR_UNKNOWN_TRFR_ID = 5, + TFTP_ERROR_FILE_EXISTS = 6, + TFTP_ERROR_NO_SUCH_USER = 7 +}; + +#include <string.h> + +struct tftp_state { + const struct tftp_context *ctx; + void *handle; + struct pbuf *last_data; + struct udp_pcb *upcb; + ip_addr_t addr; + u16_t port; + int timer; + int last_pkt; + u16_t blknum; + u8_t retries; + u8_t mode_write; +}; + +static struct tftp_state tftp_state; + +static void tftp_tmr(void* arg); + +static void +close_handle(void) +{ + tftp_state.port = 0; + ip_addr_set_any(0, &tftp_state.addr); + + if(tftp_state.last_data != NULL) { + pbuf_free(tftp_state.last_data); + tftp_state.last_data = NULL; + } + + sys_untimeout(tftp_tmr, NULL); + + if (tftp_state.handle) { + tftp_state.ctx->close(tftp_state.handle); + tftp_state.handle = NULL; + LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: closing\n")); + } +} + +static void +send_error(const ip_addr_t *addr, u16_t port, enum tftp_error code, const char *str) +{ + int str_length = strlen(str); + struct pbuf* p; + u16_t* payload; + + p = pbuf_alloc(PBUF_TRANSPORT, (u16_t)(TFTP_HEADER_LENGTH + str_length + 1), PBUF_RAM); + if(p == NULL) { + return; + } + + payload = (u16_t*) p->payload; + payload[0] = PP_HTONS(TFTP_ERROR); + payload[1] = lwip_htons(code); + MEMCPY(&payload[2], str, str_length + 1); + + udp_sendto(tftp_state.upcb, p, addr, port); + pbuf_free(p); +} + +static void +send_ack(u16_t blknum) +{ + struct pbuf* p; + u16_t* payload; + + p = pbuf_alloc(PBUF_TRANSPORT, TFTP_HEADER_LENGTH, PBUF_RAM); + if(p == NULL) { + return; + } + payload = (u16_t*) p->payload; + + payload[0] = PP_HTONS(TFTP_ACK); + payload[1] = lwip_htons(blknum); + udp_sendto(tftp_state.upcb, p, &tftp_state.addr, tftp_state.port); + pbuf_free(p); +} + +static void +resend_data(void) +{ + struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, tftp_state.last_data->len, PBUF_RAM); + if(p == NULL) { + return; + } + + if(pbuf_copy(p, tftp_state.last_data) != ERR_OK) { + pbuf_free(p); + return; + } + + udp_sendto(tftp_state.upcb, p, &tftp_state.addr, tftp_state.port); + pbuf_free(p); +} + +static void +send_data(void) +{ + u16_t *payload; + int ret; + + if(tftp_state.last_data != NULL) { + pbuf_free(tftp_state.last_data); + } + + tftp_state.last_data = pbuf_alloc(PBUF_TRANSPORT, TFTP_HEADER_LENGTH + TFTP_MAX_PAYLOAD_SIZE, PBUF_RAM); + if(tftp_state.last_data == NULL) { + return; + } + + payload = (u16_t *) tftp_state.last_data->payload; + payload[0] = PP_HTONS(TFTP_DATA); + payload[1] = lwip_htons(tftp_state.blknum); + + ret = tftp_state.ctx->read(tftp_state.handle, &payload[2], TFTP_MAX_PAYLOAD_SIZE); + if (ret < 0) { + send_error(&tftp_state.addr, tftp_state.port, TFTP_ERROR_ACCESS_VIOLATION, "Error occured while reading the file."); + close_handle(); + return; + } + + pbuf_realloc(tftp_state.last_data, (u16_t)(TFTP_HEADER_LENGTH + ret)); + resend_data(); +} + +static void +recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) +{ + u16_t *sbuf = (u16_t *) p->payload; + int opcode; + + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(upcb); + + if (((tftp_state.port != 0) && (port != tftp_state.port)) || + (!ip_addr_isany_val(tftp_state.addr) && !ip_addr_cmp(&tftp_state.addr, addr))) { + send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Only one connection at a time is supported"); + pbuf_free(p); + return; + } + + opcode = sbuf[0]; + + tftp_state.last_pkt = tftp_state.timer; + tftp_state.retries = 0; + + switch (opcode) { + case PP_HTONS(TFTP_RRQ): /* fall through */ + case PP_HTONS(TFTP_WRQ): + { + const char tftp_null = 0; + char filename[TFTP_MAX_FILENAME_LEN]; + char mode[TFTP_MAX_MODE_LEN]; + u16_t filename_end_offset; + u16_t mode_end_offset; + + if(tftp_state.handle != NULL) { + send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Only one connection at a time is supported"); + break; + } + + sys_timeout(TFTP_TIMER_MSECS, tftp_tmr, NULL); + + /* find \0 in pbuf -> end of filename string */ + filename_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), 2); + if((u16_t)(filename_end_offset-2) > sizeof(filename)) { + send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Filename too long/not NULL terminated"); + break; + } + pbuf_copy_partial(p, filename, filename_end_offset-2, 2); + + /* find \0 in pbuf -> end of mode string */ + mode_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), filename_end_offset+1); + if((u16_t)(mode_end_offset-filename_end_offset) > sizeof(mode)) { + send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Mode too long/not NULL terminated"); + break; + } + pbuf_copy_partial(p, mode, mode_end_offset-filename_end_offset, filename_end_offset+1); + + tftp_state.handle = tftp_state.ctx->open(filename, mode, opcode == PP_HTONS(TFTP_WRQ)); + tftp_state.blknum = 1; + + if (!tftp_state.handle) { + send_error(addr, port, TFTP_ERROR_FILE_NOT_FOUND, "Unable to open requested file."); + break; + } + + LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: %s request from ", (opcode == PP_HTONS(TFTP_WRQ)) ? "write" : "read")); + ip_addr_debug_print(TFTP_DEBUG | LWIP_DBG_STATE, addr); + LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, (" for '%s' mode '%s'\n", filename, mode)); + + ip_addr_copy(tftp_state.addr, *addr); + tftp_state.port = port; + + if (opcode == PP_HTONS(TFTP_WRQ)) { + tftp_state.mode_write = 1; + send_ack(0); + } else { + tftp_state.mode_write = 0; + send_data(); + } + + break; + } + + case PP_HTONS(TFTP_DATA): + { + int ret; + u16_t blknum; + + if (tftp_state.handle == NULL) { + send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "No connection"); + break; + } + + if (tftp_state.mode_write != 1) { + send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Not a write connection"); + break; + } + + blknum = lwip_ntohs(sbuf[1]); + pbuf_header(p, -TFTP_HEADER_LENGTH); + + ret = tftp_state.ctx->write(tftp_state.handle, p); + if (ret < 0) { + send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "error writing file"); + close_handle(); + } else { + send_ack(blknum); + } + + if (p->tot_len < TFTP_MAX_PAYLOAD_SIZE) { + close_handle(); + } + break; + } + + case PP_HTONS(TFTP_ACK): + { + u16_t blknum; + int lastpkt; + + if (tftp_state.handle == NULL) { + send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "No connection"); + break; + } + + if (tftp_state.mode_write != 0) { + send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Not a read connection"); + break; + } + + blknum = lwip_ntohs(sbuf[1]); + if (blknum != tftp_state.blknum) { + send_error(addr, port, TFTP_ERROR_UNKNOWN_TRFR_ID, "Wrong block number"); + break; + } + + lastpkt = 0; + + if (tftp_state.last_data != NULL) { + lastpkt = tftp_state.last_data->tot_len != (TFTP_MAX_PAYLOAD_SIZE + TFTP_HEADER_LENGTH); + } + + if (!lastpkt) { + tftp_state.blknum++; + send_data(); + } else { + close_handle(); + } + + break; + } + + default: + send_error(addr, port, TFTP_ERROR_ILLEGAL_OPERATION, "Unknown operation"); + break; + } + + pbuf_free(p); +} + +static void +tftp_tmr(void* arg) +{ + LWIP_UNUSED_ARG(arg); + + tftp_state.timer++; + + if (tftp_state.handle == NULL) { + return; + } + + sys_timeout(TFTP_TIMER_MSECS, tftp_tmr, NULL); + + if ((tftp_state.timer - tftp_state.last_pkt) > (TFTP_TIMEOUT_MSECS / TFTP_TIMER_MSECS)) { + if ((tftp_state.last_data != NULL) && (tftp_state.retries < TFTP_MAX_RETRIES)) { + LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: timeout, retrying\n")); + resend_data(); + tftp_state.retries++; + } else { + LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: timeout\n")); + close_handle(); + } + } +} + +/** @ingroup tftp + * Initialize TFTP server. + * @param ctx TFTP callback struct + */ +err_t +tftp_init(const struct tftp_context *ctx) +{ + err_t ret; + + struct udp_pcb *pcb = udp_new_ip_type(IPADDR_TYPE_ANY); + if (pcb == NULL) { + return ERR_MEM; + } + + ret = udp_bind(pcb, IP_ANY_TYPE, TFTP_PORT); + if (ret != ERR_OK) { + udp_remove(pcb); + return ret; + } + + tftp_state.handle = NULL; + tftp_state.port = 0; + tftp_state.ctx = ctx; + tftp_state.timer = 0; + tftp_state.last_data = NULL; + tftp_state.upcb = pcb; + + udp_recv(pcb, recv, NULL); + + return ERR_OK; +} + +#endif /* LWIP_UDP */ |