diff --git a/src/mongoose b/src/mongoose new file mode 120000 index 0000000..978d60b --- /dev/null +++ b/src/mongoose @@ -0,0 +1 @@ +mongoose-6.11/ \ No newline at end of file diff --git a/src/mongoose-6.11/.mbedignore b/src/mongoose-6.11/.mbedignore new file mode 100644 index 0000000..e966095 --- /dev/null +++ b/src/mongoose-6.11/.mbedignore @@ -0,0 +1,9 @@ +binary/ +deps/ +docker/ +docs/ +examples/ +platforms/ +src/ +test/ +multilingual/ diff --git a/src/mongoose-6.11/CONTRIBUTING.md b/src/mongoose-6.11/CONTRIBUTING.md new file mode 100644 index 0000000..4581b01 --- /dev/null +++ b/src/mongoose-6.11/CONTRIBUTING.md @@ -0,0 +1,8 @@ +People who have agreed to the +[Cesanta CLA](https://docs.cesanta.com/contributors_la.shtml) +can make contributions. Note that the CLA isn't a copyright +_assigment_ but rather a copyright _license_. +You retain the copyright on your contributions. + +We follow the Google C/C++ style guide: https://google.github.io/styleguide/cppguide.html +We'd appreciate if your contribution follows the same style guide. diff --git a/src/mongoose-6.11/LICENSE b/src/mongoose-6.11/LICENSE new file mode 100644 index 0000000..9d5778c --- /dev/null +++ b/src/mongoose-6.11/LICENSE @@ -0,0 +1,16 @@ +Copyright (c) 2004-2013 Sergey Lyubka +Copyright (c) 2013-2016 Cesanta Software Limited +All rights reserved + +This software is dual-licensed: you can redistribute it and/or modify +it under the terms of the GNU General Public License version 2 as +published by the Free Software Foundation. For the terms of this +license, see . + +You are free to use this software under the terms of the GNU General +Public License, but WITHOUT ANY WARRANTY; without even the implied +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +See the GNU General Public License for more details. + +Alternatively, you can license this software under a commercial +license, as set out in . diff --git a/src/mongoose-6.11/README.md b/src/mongoose-6.11/README.md new file mode 100644 index 0000000..63d8f8e --- /dev/null +++ b/src/mongoose-6.11/README.md @@ -0,0 +1,68 @@ +# Mongoose - Embedded Web Server / Embedded Networking Library + +![](https://img.shields.io/badge/license-GPL_2-green.svg "License") + +Mongoose is ideal for embedded environments. It has been designed +for connecting devices and bringing them online. On the market since 2004, +used by vast number of open source and +commercial products - it even runs on the International Space station! +Mongoose makes embedded network programming fast, robust, and easy. + +- [Download Mongoose Source Code here](https://www.cesanta.com/download.html) + +Looking for a complete IoT firmware solution? + +Check out [Mongoose OS](https://mongoose-os.com) - open source embedded operating system for low-power connected microcontrollers. Secure, designed for Internet of Things, complete environment for prototyping, development and managing. + +# Support +- [Study mongoose example code](https://github.com/cesanta/mongoose/tree/master/examples) +- [Read User Guide and API reference](https://cesanta.com/docs/overview/intro.html) +- [Support Forum - ask your technical questions here](https://forum.mongoose-os.com/categories/mongoose) +- [Commercial licensing and support available](https://www.cesanta.com/licensing.html) +- [Check our latest releases](https://github.com/cesanta/mongoose/releases) + +# Features + +* Cross-platform: works on Linux/UNIX, MacOS, QNX, eCos, Windows, Android, + iPhone, FreeRTOS (TI CC3200, ESP8266), etc +* Supported hardware platforms: TI CC3200, TI MSP432, NRF52, STM32, PIC32, ESP8266, ESP32 and more +* Builtin protocols: + - plain TCP, plain UDP, SSL/TLS (over TCP, one-way or two-way) + - HTTP client, HTTP server + - WebSocket client, WebSocket server + - MQTT client, MQTT broker + - CoAP client, CoAP server + - DNS client, DNS server, async DNS resolver +* Single-threaded, asynchronous, non-blocking core with simple event-based API +* Native support for [PicoTCP embedded TCP/IP stack](http://www.picotcp.com), + [LWIP embedded TCP/IP stack](https://en.wikipedia.org/wiki/LwIP) +* Tiny static and run-time footprint +* Source code is both ISO C and ISO C++ compliant +* Very easy to integrate: just copy + [mongoose.c](https://raw.githubusercontent.com/cesanta/mongoose/master/mongoose.c) and + [mongoose.h](https://raw.githubusercontent.com/cesanta/mongoose/master/mongoose.h) + files to your build tree + +# Licensing + +Mongoose is released under Commercial and [GNU GPL v.2](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html) open source licenses. + +Commercial Projects: [Contact us for commercial license.](https://www.cesanta.com/contact.html) + +# Dashboard Example + +Mongoose is often used to implement device dashboards and real-time +data exchange over Websocket. Here is a dashboard example that illustrates +the functionality: + +![](http://www.cesanta.com/images/dashboard.png) + +[Developing a new product? Contact us today to discuss how Mongoose can help.](https://www.cesanta.com/contact.html) + +# Contributions + +To submit contributions, sign [Cesanta CLA](https://cesanta.com/cla.html) +and send GitHub pull request. You retain the copyright on your contributions. + +# Looking for a pre-compiled Mongoose web server Windows or Mac binary? +- [Download pre-compiled Mongoose web server binary.](https://www.cesanta.com/binary.html) diff --git a/src/mongoose-6.11/jni/Android.mk b/src/mongoose-6.11/jni/Android.mk new file mode 100644 index 0000000..a2e67b5 --- /dev/null +++ b/src/mongoose-6.11/jni/Android.mk @@ -0,0 +1,8 @@ +LOCAL_PATH := $(call my-dir)/.. +include $(CLEAR_VARS) + +LOCAL_CFLAGS := -std=c99 -O2 -W -Wall -pthread -pipe $(COPT) +LOCAL_MODULE := mongoose +LOCAL_SRC_FILES := examples/simplest_web_server/simplest_web_server.c mongoose.c + +include $(BUILD_EXECUTABLE) diff --git a/src/mongoose-6.11/mongoose.c b/src/mongoose-6.11/mongoose.c new file mode 100644 index 0000000..141e501 --- /dev/null +++ b/src/mongoose-6.11/mongoose.c @@ -0,0 +1,16132 @@ +#include "mongoose.h" +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_internal.h" +#endif +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_MONGOOSE_SRC_INTERNAL_H_ +#define CS_MONGOOSE_SRC_INTERNAL_H_ + +/* Amalgamated: #include "common/mg_mem.h" */ + +#ifndef MBUF_REALLOC +#define MBUF_REALLOC MG_REALLOC +#endif + +#ifndef MBUF_FREE +#define MBUF_FREE MG_FREE +#endif + +#define MG_SET_PTRPTR(_ptr, _v) \ + do { \ + if (_ptr) *(_ptr) = _v; \ + } while (0) + +#ifndef MG_INTERNAL +#define MG_INTERNAL static +#endif + +#ifdef PICOTCP +#define NO_LIBC +#define MG_DISABLE_PFS +#endif + +/* Amalgamated: #include "common/cs_dbg.h" */ +/* Amalgamated: #include "mg_http.h" */ +/* Amalgamated: #include "mg_net.h" */ + +#define MG_CTL_MSG_MESSAGE_SIZE 8192 + +/* internals that need to be accessible in unit tests */ +MG_INTERNAL struct mg_connection *mg_do_connect(struct mg_connection *nc, + int proto, + union socket_address *sa); + +MG_INTERNAL int mg_parse_address(const char *str, union socket_address *sa, + int *proto, char *host, size_t host_len); +MG_INTERNAL void mg_call(struct mg_connection *nc, + mg_event_handler_t ev_handler, void *user_data, int ev, + void *ev_data); +void mg_forward(struct mg_connection *from, struct mg_connection *to); +MG_INTERNAL void mg_add_conn(struct mg_mgr *mgr, struct mg_connection *c); +MG_INTERNAL void mg_remove_conn(struct mg_connection *c); +MG_INTERNAL struct mg_connection *mg_create_connection( + struct mg_mgr *mgr, mg_event_handler_t callback, + struct mg_add_sock_opts opts); +#ifdef _WIN32 +/* Retur value is the same as for MultiByteToWideChar. */ +int to_wchar(const char *path, wchar_t *wbuf, size_t wbuf_len); +#endif + +struct ctl_msg { + mg_event_handler_t callback; + char message[MG_CTL_MSG_MESSAGE_SIZE]; +}; + +#if MG_ENABLE_MQTT +struct mg_mqtt_message; + +#define MG_MQTT_ERROR_INCOMPLETE_MSG -1 +#define MG_MQTT_ERROR_MALFORMED_MSG -2 + +MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm); +#endif + +/* Forward declarations for testing. */ +extern void *(*test_malloc)(size_t size); +extern void *(*test_calloc)(size_t count, size_t size); + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +#if MG_ENABLE_HTTP +struct mg_serve_http_opts; + +/* + * Reassemble the content of the buffer (buf, blen) which should be + * in the HTTP chunked encoding, by collapsing data chunks to the + * beginning of the buffer. + * + * If chunks get reassembled, modify hm->body to point to the reassembled + * body and fire MG_EV_HTTP_CHUNK event. If handler sets MG_F_DELETE_CHUNK + * in nc->flags, delete reassembled body from the mbuf. + * + * Return reassembled body size. + */ +MG_INTERNAL size_t mg_handle_chunked(struct mg_connection *nc, + struct http_message *hm, char *buf, + size_t blen); + +#if MG_ENABLE_FILESYSTEM +MG_INTERNAL int mg_uri_to_local_path(struct http_message *hm, + const struct mg_serve_http_opts *opts, + char **local_path, + struct mg_str *remainder); +MG_INTERNAL time_t mg_parse_date_string(const char *datetime); +MG_INTERNAL int mg_is_not_modified(struct http_message *hm, cs_stat_t *st); +#endif +#if MG_ENABLE_HTTP_CGI +MG_INTERNAL void mg_handle_cgi(struct mg_connection *nc, const char *prog, + const struct mg_str *path_info, + const struct http_message *hm, + const struct mg_serve_http_opts *opts); +struct mg_http_proto_data_cgi; +MG_INTERNAL void mg_http_free_proto_data_cgi(struct mg_http_proto_data_cgi *d); +#endif +#if MG_ENABLE_HTTP_SSI +MG_INTERNAL void mg_handle_ssi_request(struct mg_connection *nc, + struct http_message *hm, + const char *path, + const struct mg_serve_http_opts *opts); +#endif +#if MG_ENABLE_HTTP_WEBDAV +MG_INTERNAL int mg_is_dav_request(const struct mg_str *s); +MG_INTERNAL void mg_handle_propfind(struct mg_connection *nc, const char *path, + cs_stat_t *stp, struct http_message *hm, + struct mg_serve_http_opts *opts); +MG_INTERNAL void mg_handle_lock(struct mg_connection *nc, const char *path); +MG_INTERNAL void mg_handle_mkcol(struct mg_connection *nc, const char *path, + struct http_message *hm); +MG_INTERNAL void mg_handle_move(struct mg_connection *c, + const struct mg_serve_http_opts *opts, + const char *path, struct http_message *hm); +MG_INTERNAL void mg_handle_delete(struct mg_connection *nc, + const struct mg_serve_http_opts *opts, + const char *path); +MG_INTERNAL void mg_handle_put(struct mg_connection *nc, const char *path, + struct http_message *hm); +#endif +#if MG_ENABLE_HTTP_WEBSOCKET +MG_INTERNAL void mg_ws_handler(struct mg_connection *nc, int ev, + void *ev_data MG_UD_ARG(void *user_data)); +MG_INTERNAL void mg_ws_handshake(struct mg_connection *nc, + const struct mg_str *key, + struct http_message *); +#endif +#endif /* MG_ENABLE_HTTP */ + +MG_INTERNAL int mg_get_errno(void); + +MG_INTERNAL void mg_close_conn(struct mg_connection *conn); + +#if MG_ENABLE_SNTP +MG_INTERNAL int mg_sntp_parse_reply(const char *buf, int len, + struct mg_sntp_message *msg); +#endif + +#endif /* CS_MONGOOSE_SRC_INTERNAL_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/mg_mem.h" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_MG_MEM_H_ +#define CS_COMMON_MG_MEM_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef MG_MALLOC +#define MG_MALLOC malloc +#endif + +#ifndef MG_CALLOC +#define MG_CALLOC calloc +#endif + +#ifndef MG_REALLOC +#define MG_REALLOC realloc +#endif + +#ifndef MG_FREE +#define MG_FREE free +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* CS_COMMON_MG_MEM_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/cs_base64.c" +#endif +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#ifndef EXCLUDE_COMMON + +/* Amalgamated: #include "common/cs_base64.h" */ + +#include + +/* Amalgamated: #include "common/cs_dbg.h" */ + +/* ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ */ + +#define NUM_UPPERCASES ('Z' - 'A' + 1) +#define NUM_LETTERS (NUM_UPPERCASES * 2) +#define NUM_DIGITS ('9' - '0' + 1) + +/* + * Emit a base64 code char. + * + * Doesn't use memory, thus it's safe to use to safely dump memory in crashdumps + */ +static void cs_base64_emit_code(struct cs_base64_ctx *ctx, int v) { + if (v < NUM_UPPERCASES) { + ctx->b64_putc(v + 'A', ctx->user_data); + } else if (v < (NUM_LETTERS)) { + ctx->b64_putc(v - NUM_UPPERCASES + 'a', ctx->user_data); + } else if (v < (NUM_LETTERS + NUM_DIGITS)) { + ctx->b64_putc(v - NUM_LETTERS + '0', ctx->user_data); + } else { + ctx->b64_putc(v - NUM_LETTERS - NUM_DIGITS == 0 ? '+' : '/', + ctx->user_data); + } +} + +static void cs_base64_emit_chunk(struct cs_base64_ctx *ctx) { + int a, b, c; + + a = ctx->chunk[0]; + b = ctx->chunk[1]; + c = ctx->chunk[2]; + + cs_base64_emit_code(ctx, a >> 2); + cs_base64_emit_code(ctx, ((a & 3) << 4) | (b >> 4)); + if (ctx->chunk_size > 1) { + cs_base64_emit_code(ctx, (b & 15) << 2 | (c >> 6)); + } + if (ctx->chunk_size > 2) { + cs_base64_emit_code(ctx, c & 63); + } +} + +void cs_base64_init(struct cs_base64_ctx *ctx, cs_base64_putc_t b64_putc, + void *user_data) { + ctx->chunk_size = 0; + ctx->b64_putc = b64_putc; + ctx->user_data = user_data; +} + +void cs_base64_update(struct cs_base64_ctx *ctx, const char *str, size_t len) { + const unsigned char *src = (const unsigned char *) str; + size_t i; + for (i = 0; i < len; i++) { + ctx->chunk[ctx->chunk_size++] = src[i]; + if (ctx->chunk_size == 3) { + cs_base64_emit_chunk(ctx); + ctx->chunk_size = 0; + } + } +} + +void cs_base64_finish(struct cs_base64_ctx *ctx) { + if (ctx->chunk_size > 0) { + int i; + memset(&ctx->chunk[ctx->chunk_size], 0, 3 - ctx->chunk_size); + cs_base64_emit_chunk(ctx); + for (i = 0; i < (3 - ctx->chunk_size); i++) { + ctx->b64_putc('=', ctx->user_data); + } + } +} + +#define BASE64_ENCODE_BODY \ + static const char *b64 = \ + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; \ + int i, j, a, b, c; \ + \ + for (i = j = 0; i < src_len; i += 3) { \ + a = src[i]; \ + b = i + 1 >= src_len ? 0 : src[i + 1]; \ + c = i + 2 >= src_len ? 0 : src[i + 2]; \ + \ + BASE64_OUT(b64[a >> 2]); \ + BASE64_OUT(b64[((a & 3) << 4) | (b >> 4)]); \ + if (i + 1 < src_len) { \ + BASE64_OUT(b64[(b & 15) << 2 | (c >> 6)]); \ + } \ + if (i + 2 < src_len) { \ + BASE64_OUT(b64[c & 63]); \ + } \ + } \ + \ + while (j % 4 != 0) { \ + BASE64_OUT('='); \ + } \ + BASE64_FLUSH() + +#define BASE64_OUT(ch) \ + do { \ + dst[j++] = (ch); \ + } while (0) + +#define BASE64_FLUSH() \ + do { \ + dst[j++] = '\0'; \ + } while (0) + +void cs_base64_encode(const unsigned char *src, int src_len, char *dst) { + BASE64_ENCODE_BODY; +} + +#undef BASE64_OUT +#undef BASE64_FLUSH + +#if CS_ENABLE_STDIO +#define BASE64_OUT(ch) \ + do { \ + fprintf(f, "%c", (ch)); \ + j++; \ + } while (0) + +#define BASE64_FLUSH() + +void cs_fprint_base64(FILE *f, const unsigned char *src, int src_len) { + BASE64_ENCODE_BODY; +} + +#undef BASE64_OUT +#undef BASE64_FLUSH +#endif /* CS_ENABLE_STDIO */ + +/* Convert one byte of encoded base64 input stream to 6-bit chunk */ +static unsigned char from_b64(unsigned char ch) { + /* Inverse lookup map */ + static const unsigned char tab[128] = { + 255, 255, 255, 255, + 255, 255, 255, 255, /* 0 */ + 255, 255, 255, 255, + 255, 255, 255, 255, /* 8 */ + 255, 255, 255, 255, + 255, 255, 255, 255, /* 16 */ + 255, 255, 255, 255, + 255, 255, 255, 255, /* 24 */ + 255, 255, 255, 255, + 255, 255, 255, 255, /* 32 */ + 255, 255, 255, 62, + 255, 255, 255, 63, /* 40 */ + 52, 53, 54, 55, + 56, 57, 58, 59, /* 48 */ + 60, 61, 255, 255, + 255, 200, 255, 255, /* 56 '=' is 200, on index 61 */ + 255, 0, 1, 2, + 3, 4, 5, 6, /* 64 */ + 7, 8, 9, 10, + 11, 12, 13, 14, /* 72 */ + 15, 16, 17, 18, + 19, 20, 21, 22, /* 80 */ + 23, 24, 25, 255, + 255, 255, 255, 255, /* 88 */ + 255, 26, 27, 28, + 29, 30, 31, 32, /* 96 */ + 33, 34, 35, 36, + 37, 38, 39, 40, /* 104 */ + 41, 42, 43, 44, + 45, 46, 47, 48, /* 112 */ + 49, 50, 51, 255, + 255, 255, 255, 255, /* 120 */ + }; + return tab[ch & 127]; +} + +int cs_base64_decode(const unsigned char *s, int len, char *dst, int *dec_len) { + unsigned char a, b, c, d; + int orig_len = len; + char *orig_dst = dst; + while (len >= 4 && (a = from_b64(s[0])) != 255 && + (b = from_b64(s[1])) != 255 && (c = from_b64(s[2])) != 255 && + (d = from_b64(s[3])) != 255) { + s += 4; + len -= 4; + if (a == 200 || b == 200) break; /* '=' can't be there */ + *dst++ = a << 2 | b >> 4; + if (c == 200) break; + *dst++ = b << 4 | c >> 2; + if (d == 200) break; + *dst++ = c << 6 | d; + } + *dst = 0; + if (dec_len != NULL) *dec_len = (dst - orig_dst); + return orig_len - len; +} + +#endif /* EXCLUDE_COMMON */ +#ifdef MG_MODULE_LINES +#line 1 "common/cs_dbg.h" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_CS_DBG_H_ +#define CS_COMMON_CS_DBG_H_ + +/* Amalgamated: #include "common/platform.h" */ + +#if CS_ENABLE_STDIO +#include +#endif + +#ifndef CS_ENABLE_DEBUG +#define CS_ENABLE_DEBUG 0 +#endif + +#ifndef CS_LOG_ENABLE_TS_DIFF +#define CS_LOG_ENABLE_TS_DIFF 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * Log level; `LL_INFO` is the default. Use `cs_log_set_level()` to change it. + */ +enum cs_log_level { + LL_NONE = -1, + LL_ERROR = 0, + LL_WARN = 1, + LL_INFO = 2, + LL_DEBUG = 3, + LL_VERBOSE_DEBUG = 4, + + _LL_MIN = -2, + _LL_MAX = 5, +}; + +/* + * Set max log level to print; messages with the level above the given one will + * not be printed. + */ +void cs_log_set_level(enum cs_log_level level); + +/* + * Set log filter. NULL (a default) logs everything. + * Otherwise, function name and file name will be tested against the given + * pattern, and only matching messages will be printed. + * + * For the pattern syntax, refer to `mg_match_prefix()` in `str_util.h`. + * + * Example: + * ```c + * void foo(void) { + * LOG(LL_INFO, ("hello from foo")); + * } + * + * void bar(void) { + * LOG(LL_INFO, ("hello from bar")); + * } + * + * void test(void) { + * cs_log_set_filter(NULL); + * foo(); + * bar(); + * + * cs_log_set_filter("f*"); + * foo(); + * bar(); // Will NOT print anything + * + * cs_log_set_filter("bar"); + * foo(); // Will NOT print anything + * bar(); + * } + * ``` + */ +void cs_log_set_filter(const char *pattern); + +/* + * Helper function which prints message prefix with the given `level`, function + * name `func` and `filename`. If message should be printed (accordingly to the + * current log level and filter), prints the prefix and returns 1, otherwise + * returns 0. + * + * Clients should typically just use `LOG()` macro. + */ +int cs_log_print_prefix(enum cs_log_level level, const char *func, + const char *filename); + +extern enum cs_log_level cs_log_threshold; + +#if CS_ENABLE_STDIO + +/* + * Set file to write logs into. If `NULL`, logs go to `stderr`. + */ +void cs_log_set_file(FILE *file); + +/* + * Prints log to the current log file, appends "\n" in the end and flushes the + * stream. + */ +void cs_log_printf(const char *fmt, ...) +#ifdef __GNUC__ + __attribute__((format(printf, 1, 2))) +#endif + ; + +/* + * Format and print message `x` with the given level `l`. Example: + * + * ```c + * LOG(LL_INFO, ("my info message: %d", 123)); + * LOG(LL_DEBUG, ("my debug message: %d", 123)); + * ``` + */ +#define LOG(l, x) \ + do { \ + if (cs_log_print_prefix(l, __func__, __FILE__)) cs_log_printf x; \ + } while (0) + +#ifndef CS_NDEBUG + +/* + * Shortcut for `LOG(LL_VERBOSE_DEBUG, (...))` + */ +#define DBG(x) LOG(LL_VERBOSE_DEBUG, x) + +#else /* NDEBUG */ + +#define DBG(x) + +#endif + +#else /* CS_ENABLE_STDIO */ + +#define LOG(l, x) +#define DBG(x) + +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CS_COMMON_CS_DBG_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/cs_dbg.c" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +/* Amalgamated: #include "common/cs_dbg.h" */ + +#include +#include +#include + +/* Amalgamated: #include "common/cs_time.h" */ +/* Amalgamated: #include "common/str_util.h" */ + +enum cs_log_level cs_log_threshold WEAK = +#if CS_ENABLE_DEBUG + LL_VERBOSE_DEBUG; +#else + LL_ERROR; +#endif + +static char *s_filter_pattern = NULL; +static size_t s_filter_pattern_len; + +void cs_log_set_filter(const char *pattern) WEAK; + +#if CS_ENABLE_STDIO + +FILE *cs_log_file WEAK = NULL; + +#if CS_LOG_ENABLE_TS_DIFF +double cs_log_ts WEAK; +#endif + +enum cs_log_level cs_log_cur_msg_level WEAK = LL_NONE; + +void cs_log_set_filter(const char *pattern) { + free(s_filter_pattern); + if (pattern != NULL) { + s_filter_pattern = strdup(pattern); + s_filter_pattern_len = strlen(pattern); + } else { + s_filter_pattern = NULL; + s_filter_pattern_len = 0; + } +} + +int cs_log_print_prefix(enum cs_log_level, const char *, const char *) WEAK; +int cs_log_print_prefix(enum cs_log_level level, const char *func, + const char *filename) { + char prefix[21]; + + if (level > cs_log_threshold) return 0; + if (s_filter_pattern != NULL && + mg_match_prefix(s_filter_pattern, s_filter_pattern_len, func) == 0 && + mg_match_prefix(s_filter_pattern, s_filter_pattern_len, filename) == 0) { + return 0; + } + + strncpy(prefix, func, 20); + prefix[20] = '\0'; + if (cs_log_file == NULL) cs_log_file = stderr; + cs_log_cur_msg_level = level; + fprintf(cs_log_file, "%-20s ", prefix); +#if CS_LOG_ENABLE_TS_DIFF + { + double now = cs_time(); + fprintf(cs_log_file, "%7u ", (unsigned int) ((now - cs_log_ts) * 1000000)); + cs_log_ts = now; + } +#endif + return 1; +} + +void cs_log_printf(const char *fmt, ...) WEAK; +void cs_log_printf(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vfprintf(cs_log_file, fmt, ap); + va_end(ap); + fputc('\n', cs_log_file); + fflush(cs_log_file); + cs_log_cur_msg_level = LL_NONE; +} + +void cs_log_set_file(FILE *file) WEAK; +void cs_log_set_file(FILE *file) { + cs_log_file = file; +} + +#else + +void cs_log_set_filter(const char *pattern) { + (void) pattern; +} + +#endif /* CS_ENABLE_STDIO */ + +void cs_log_set_level(enum cs_log_level level) WEAK; +void cs_log_set_level(enum cs_log_level level) { + cs_log_threshold = level; +#if CS_LOG_ENABLE_TS_DIFF && CS_ENABLE_STDIO + cs_log_ts = cs_time(); +#endif +} +#ifdef MG_MODULE_LINES +#line 1 "common/cs_dirent.h" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_CS_DIRENT_H_ +#define CS_COMMON_CS_DIRENT_H_ + +#include + +/* Amalgamated: #include "common/platform.h" */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifdef CS_DEFINE_DIRENT +typedef struct { int dummy; } DIR; + +struct dirent { + int d_ino; +#ifdef _WIN32 + char d_name[MAX_PATH]; +#else + /* TODO(rojer): Use PATH_MAX but make sure it's sane on every platform */ + char d_name[256]; +#endif +}; + +DIR *opendir(const char *dir_name); +int closedir(DIR *dir); +struct dirent *readdir(DIR *dir); +#endif /* CS_DEFINE_DIRENT */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CS_COMMON_CS_DIRENT_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/cs_dirent.c" +#endif +/* + * Copyright (c) 2015 Cesanta Software Limited + * All rights reserved + */ + +#ifndef EXCLUDE_COMMON + +/* Amalgamated: #include "common/mg_mem.h" */ +/* Amalgamated: #include "common/cs_dirent.h" */ + +/* + * This file contains POSIX opendir/closedir/readdir API implementation + * for systems which do not natively support it (e.g. Windows). + */ + +#ifdef _WIN32 +struct win32_dir { + DIR d; + HANDLE handle; + WIN32_FIND_DATAW info; + struct dirent result; +}; + +DIR *opendir(const char *name) { + struct win32_dir *dir = NULL; + wchar_t wpath[MAX_PATH]; + DWORD attrs; + + if (name == NULL) { + SetLastError(ERROR_BAD_ARGUMENTS); + } else if ((dir = (struct win32_dir *) MG_MALLOC(sizeof(*dir))) == NULL) { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + } else { + to_wchar(name, wpath, ARRAY_SIZE(wpath)); + attrs = GetFileAttributesW(wpath); + if (attrs != 0xFFFFFFFF && (attrs & FILE_ATTRIBUTE_DIRECTORY)) { + (void) wcscat(wpath, L"\\*"); + dir->handle = FindFirstFileW(wpath, &dir->info); + dir->result.d_name[0] = '\0'; + } else { + MG_FREE(dir); + dir = NULL; + } + } + + return (DIR *) dir; +} + +int closedir(DIR *d) { + struct win32_dir *dir = (struct win32_dir *) d; + int result = 0; + + if (dir != NULL) { + if (dir->handle != INVALID_HANDLE_VALUE) + result = FindClose(dir->handle) ? 0 : -1; + MG_FREE(dir); + } else { + result = -1; + SetLastError(ERROR_BAD_ARGUMENTS); + } + + return result; +} + +struct dirent *readdir(DIR *d) { + struct win32_dir *dir = (struct win32_dir *) d; + struct dirent *result = NULL; + + if (dir) { + memset(&dir->result, 0, sizeof(dir->result)); + if (dir->handle != INVALID_HANDLE_VALUE) { + result = &dir->result; + (void) WideCharToMultiByte(CP_UTF8, 0, dir->info.cFileName, -1, + result->d_name, sizeof(result->d_name), NULL, + NULL); + + if (!FindNextFileW(dir->handle, &dir->info)) { + (void) FindClose(dir->handle); + dir->handle = INVALID_HANDLE_VALUE; + } + + } else { + SetLastError(ERROR_FILE_NOT_FOUND); + } + } else { + SetLastError(ERROR_BAD_ARGUMENTS); + } + + return result; +} +#endif + +#endif /* EXCLUDE_COMMON */ + +/* ISO C requires a translation unit to contain at least one declaration */ +typedef int cs_dirent_dummy; +#ifdef MG_MODULE_LINES +#line 1 "common/cs_time.c" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +/* Amalgamated: #include "common/cs_time.h" */ + +#ifndef _WIN32 +#include +/* + * There is no sys/time.h on ARMCC. + */ +#if !(defined(__ARMCC_VERSION) || defined(__ICCARM__)) && \ + !defined(__TI_COMPILER_VERSION__) && \ + (!defined(CS_PLATFORM) || CS_PLATFORM != CS_P_NXP_LPC) +#include +#endif +#else +#include +#endif + +double cs_time(void) WEAK; +double cs_time(void) { + double now; +#ifndef _WIN32 + struct timeval tv; + if (gettimeofday(&tv, NULL /* tz */) != 0) return 0; + now = (double) tv.tv_sec + (((double) tv.tv_usec) / 1000000.0); +#else + SYSTEMTIME sysnow; + FILETIME ftime; + GetLocalTime(&sysnow); + SystemTimeToFileTime(&sysnow, &ftime); + /* + * 1. VC 6.0 doesn't support conversion uint64 -> double, so, using int64 + * This should not cause a problems in this (21th) century + * 2. Windows FILETIME is a number of 100-nanosecond intervals since January + * 1, 1601 while time_t is a number of _seconds_ since January 1, 1970 UTC, + * thus, we need to convert to seconds and adjust amount (subtract 11644473600 + * seconds) + */ + now = (double) (((int64_t) ftime.dwLowDateTime + + ((int64_t) ftime.dwHighDateTime << 32)) / + 10000000.0) - + 11644473600; +#endif /* _WIN32 */ + return now; +} + +double cs_timegm(const struct tm *tm) { + /* Month-to-day offset for non-leap-years. */ + static const int month_day[12] = {0, 31, 59, 90, 120, 151, + 181, 212, 243, 273, 304, 334}; + + /* Most of the calculation is easy; leap years are the main difficulty. */ + int month = tm->tm_mon % 12; + int year = tm->tm_year + tm->tm_mon / 12; + int year_for_leap; + int64_t rt; + + if (month < 0) { /* Negative values % 12 are still negative. */ + month += 12; + --year; + } + + /* This is the number of Februaries since 1900. */ + year_for_leap = (month > 1) ? year + 1 : year; + + rt = + tm->tm_sec /* Seconds */ + + + 60 * + (tm->tm_min /* Minute = 60 seconds */ + + + 60 * (tm->tm_hour /* Hour = 60 minutes */ + + + 24 * (month_day[month] + tm->tm_mday - 1 /* Day = 24 hours */ + + 365 * (year - 70) /* Year = 365 days */ + + (year_for_leap - 69) / 4 /* Every 4 years is leap... */ + - (year_for_leap - 1) / 100 /* Except centuries... */ + + (year_for_leap + 299) / 400))); /* Except 400s. */ + return rt < 0 ? -1 : (double) rt; +} +#ifdef MG_MODULE_LINES +#line 1 "common/cs_endian.h" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_CS_ENDIAN_H_ +#define CS_COMMON_CS_ENDIAN_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * clang with std=-c99 uses __LITTLE_ENDIAN, by default + * while for ex, RTOS gcc - LITTLE_ENDIAN, by default + * it depends on __USE_BSD, but let's have everything + */ +#if !defined(BYTE_ORDER) && defined(__BYTE_ORDER) +#define BYTE_ORDER __BYTE_ORDER +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN __LITTLE_ENDIAN +#endif /* LITTLE_ENDIAN */ +#ifndef BIG_ENDIAN +#define BIG_ENDIAN __LITTLE_ENDIAN +#endif /* BIG_ENDIAN */ +#endif /* BYTE_ORDER */ + +#ifdef __cplusplus +} +#endif + +#endif /* CS_COMMON_CS_ENDIAN_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/cs_md5.c" +#endif +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +/* Amalgamated: #include "common/cs_md5.h" */ +/* Amalgamated: #include "common/str_util.h" */ + +#if !defined(EXCLUDE_COMMON) +#if !CS_DISABLE_MD5 + +/* Amalgamated: #include "common/cs_endian.h" */ + +static void byteReverse(unsigned char *buf, unsigned longs) { +/* Forrest: MD5 expect LITTLE_ENDIAN, swap if BIG_ENDIAN */ +#if BYTE_ORDER == BIG_ENDIAN + do { + uint32_t t = (uint32_t)((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + *(uint32_t *) buf = t; + buf += 4; + } while (--longs); +#else + (void) buf; + (void) longs; +#endif +} + +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +#define MD5STEP(f, w, x, y, z, data, s) \ + (w += f(x, y, z) + data, w = w << s | w >> (32 - s), w += x) + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void cs_md5_init(cs_md5_ctx *ctx) { + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +static void cs_md5_transform(uint32_t buf[4], uint32_t const in[16]) { + register uint32_t a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +void cs_md5_update(cs_md5_ctx *ctx, const unsigned char *buf, size_t len) { + uint32_t t; + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t) ctx->bits[1]++; + ctx->bits[1] += (uint32_t) len >> 29; + + t = (t >> 3) & 0x3f; + + if (t) { + unsigned char *p = (unsigned char *) ctx->in + t; + + t = 64 - t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse(ctx->in, 16); + cs_md5_transform(ctx->buf, (uint32_t *) ctx->in); + buf += t; + len -= t; + } + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + cs_md5_transform(ctx->buf, (uint32_t *) ctx->in); + buf += 64; + len -= 64; + } + + memcpy(ctx->in, buf, len); +} + +void cs_md5_final(unsigned char digest[16], cs_md5_ctx *ctx) { + unsigned count; + unsigned char *p; + uint32_t *a; + + count = (ctx->bits[0] >> 3) & 0x3F; + + p = ctx->in + count; + *p++ = 0x80; + count = 64 - 1 - count; + if (count < 8) { + memset(p, 0, count); + byteReverse(ctx->in, 16); + cs_md5_transform(ctx->buf, (uint32_t *) ctx->in); + memset(ctx->in, 0, 56); + } else { + memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); + + a = (uint32_t *) ctx->in; + a[14] = ctx->bits[0]; + a[15] = ctx->bits[1]; + + cs_md5_transform(ctx->buf, (uint32_t *) ctx->in); + byteReverse((unsigned char *) ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset((char *) ctx, 0, sizeof(*ctx)); +} + +#endif /* CS_DISABLE_MD5 */ +#endif /* EXCLUDE_COMMON */ +#ifdef MG_MODULE_LINES +#line 1 "common/cs_sha1.c" +#endif +/* Copyright(c) By Steve Reid */ +/* 100% Public Domain */ + +/* Amalgamated: #include "common/cs_sha1.h" */ + +#if !CS_DISABLE_SHA1 && !defined(EXCLUDE_COMMON) + +/* Amalgamated: #include "common/cs_endian.h" */ + +#define SHA1HANDSOFF +#if defined(__sun) +/* Amalgamated: #include "common/solarisfixes.h" */ +#endif + +union char64long16 { + unsigned char c[64]; + uint32_t l[16]; +}; + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +static uint32_t blk0(union char64long16 *block, int i) { +/* Forrest: SHA expect BIG_ENDIAN, swap if LITTLE_ENDIAN */ +#if BYTE_ORDER == LITTLE_ENDIAN + block->l[i] = + (rol(block->l[i], 24) & 0xFF00FF00) | (rol(block->l[i], 8) & 0x00FF00FF); +#endif + return block->l[i]; +} + +/* Avoid redefine warning (ARM /usr/include/sys/ucontext.h define R0~R4) */ +#undef blk +#undef R0 +#undef R1 +#undef R2 +#undef R3 +#undef R4 + +#define blk(i) \ + (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ block->l[(i + 8) & 15] ^ \ + block->l[(i + 2) & 15] ^ block->l[i & 15], \ + 1)) +#define R0(v, w, x, y, z, i) \ + z += ((w & (x ^ y)) ^ y) + blk0(block, i) + 0x5A827999 + rol(v, 5); \ + w = rol(w, 30); +#define R1(v, w, x, y, z, i) \ + z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \ + w = rol(w, 30); +#define R2(v, w, x, y, z, i) \ + z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); \ + w = rol(w, 30); +#define R3(v, w, x, y, z, i) \ + z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \ + w = rol(w, 30); +#define R4(v, w, x, y, z, i) \ + z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \ + w = rol(w, 30); + +void cs_sha1_transform(uint32_t state[5], const unsigned char buffer[64]) { + uint32_t a, b, c, d, e; + union char64long16 block[1]; + + memcpy(block, buffer, 64); + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + R0(a, b, c, d, e, 0); + R0(e, a, b, c, d, 1); + R0(d, e, a, b, c, 2); + R0(c, d, e, a, b, 3); + R0(b, c, d, e, a, 4); + R0(a, b, c, d, e, 5); + R0(e, a, b, c, d, 6); + R0(d, e, a, b, c, 7); + R0(c, d, e, a, b, 8); + R0(b, c, d, e, a, 9); + R0(a, b, c, d, e, 10); + R0(e, a, b, c, d, 11); + R0(d, e, a, b, c, 12); + R0(c, d, e, a, b, 13); + R0(b, c, d, e, a, 14); + R0(a, b, c, d, e, 15); + R1(e, a, b, c, d, 16); + R1(d, e, a, b, c, 17); + R1(c, d, e, a, b, 18); + R1(b, c, d, e, a, 19); + R2(a, b, c, d, e, 20); + R2(e, a, b, c, d, 21); + R2(d, e, a, b, c, 22); + R2(c, d, e, a, b, 23); + R2(b, c, d, e, a, 24); + R2(a, b, c, d, e, 25); + R2(e, a, b, c, d, 26); + R2(d, e, a, b, c, 27); + R2(c, d, e, a, b, 28); + R2(b, c, d, e, a, 29); + R2(a, b, c, d, e, 30); + R2(e, a, b, c, d, 31); + R2(d, e, a, b, c, 32); + R2(c, d, e, a, b, 33); + R2(b, c, d, e, a, 34); + R2(a, b, c, d, e, 35); + R2(e, a, b, c, d, 36); + R2(d, e, a, b, c, 37); + R2(c, d, e, a, b, 38); + R2(b, c, d, e, a, 39); + R3(a, b, c, d, e, 40); + R3(e, a, b, c, d, 41); + R3(d, e, a, b, c, 42); + R3(c, d, e, a, b, 43); + R3(b, c, d, e, a, 44); + R3(a, b, c, d, e, 45); + R3(e, a, b, c, d, 46); + R3(d, e, a, b, c, 47); + R3(c, d, e, a, b, 48); + R3(b, c, d, e, a, 49); + R3(a, b, c, d, e, 50); + R3(e, a, b, c, d, 51); + R3(d, e, a, b, c, 52); + R3(c, d, e, a, b, 53); + R3(b, c, d, e, a, 54); + R3(a, b, c, d, e, 55); + R3(e, a, b, c, d, 56); + R3(d, e, a, b, c, 57); + R3(c, d, e, a, b, 58); + R3(b, c, d, e, a, 59); + R4(a, b, c, d, e, 60); + R4(e, a, b, c, d, 61); + R4(d, e, a, b, c, 62); + R4(c, d, e, a, b, 63); + R4(b, c, d, e, a, 64); + R4(a, b, c, d, e, 65); + R4(e, a, b, c, d, 66); + R4(d, e, a, b, c, 67); + R4(c, d, e, a, b, 68); + R4(b, c, d, e, a, 69); + R4(a, b, c, d, e, 70); + R4(e, a, b, c, d, 71); + R4(d, e, a, b, c, 72); + R4(c, d, e, a, b, 73); + R4(b, c, d, e, a, 74); + R4(a, b, c, d, e, 75); + R4(e, a, b, c, d, 76); + R4(d, e, a, b, c, 77); + R4(c, d, e, a, b, 78); + R4(b, c, d, e, a, 79); + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + /* Erase working structures. The order of operations is important, + * used to ensure that compiler doesn't optimize those out. */ + memset(block, 0, sizeof(block)); + a = b = c = d = e = 0; + (void) a; + (void) b; + (void) c; + (void) d; + (void) e; +} + +void cs_sha1_init(cs_sha1_ctx *context) { + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + +void cs_sha1_update(cs_sha1_ctx *context, const unsigned char *data, + uint32_t len) { + uint32_t i, j; + + j = context->count[0]; + if ((context->count[0] += len << 3) < j) context->count[1]++; + context->count[1] += (len >> 29); + j = (j >> 3) & 63; + if ((j + len) > 63) { + memcpy(&context->buffer[j], data, (i = 64 - j)); + cs_sha1_transform(context->state, context->buffer); + for (; i + 63 < len; i += 64) { + cs_sha1_transform(context->state, &data[i]); + } + j = 0; + } else + i = 0; + memcpy(&context->buffer[j], &data[i], len - i); +} + +void cs_sha1_final(unsigned char digest[20], cs_sha1_ctx *context) { + unsigned i; + unsigned char finalcount[8], c; + + for (i = 0; i < 8; i++) { + finalcount[i] = (unsigned char) ((context->count[(i >= 4 ? 0 : 1)] >> + ((3 - (i & 3)) * 8)) & + 255); + } + c = 0200; + cs_sha1_update(context, &c, 1); + while ((context->count[0] & 504) != 448) { + c = 0000; + cs_sha1_update(context, &c, 1); + } + cs_sha1_update(context, finalcount, 8); + for (i = 0; i < 20; i++) { + digest[i] = + (unsigned char) ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255); + } + memset(context, '\0', sizeof(*context)); + memset(&finalcount, '\0', sizeof(finalcount)); +} + +void cs_hmac_sha1(const unsigned char *key, size_t keylen, + const unsigned char *data, size_t datalen, + unsigned char out[20]) { + cs_sha1_ctx ctx; + unsigned char buf1[64], buf2[64], tmp_key[20], i; + + if (keylen > sizeof(buf1)) { + cs_sha1_init(&ctx); + cs_sha1_update(&ctx, key, keylen); + cs_sha1_final(tmp_key, &ctx); + key = tmp_key; + keylen = sizeof(tmp_key); + } + + memset(buf1, 0, sizeof(buf1)); + memset(buf2, 0, sizeof(buf2)); + memcpy(buf1, key, keylen); + memcpy(buf2, key, keylen); + + for (i = 0; i < sizeof(buf1); i++) { + buf1[i] ^= 0x36; + buf2[i] ^= 0x5c; + } + + cs_sha1_init(&ctx); + cs_sha1_update(&ctx, buf1, sizeof(buf1)); + cs_sha1_update(&ctx, data, datalen); + cs_sha1_final(out, &ctx); + + cs_sha1_init(&ctx); + cs_sha1_update(&ctx, buf2, sizeof(buf2)); + cs_sha1_update(&ctx, out, 20); + cs_sha1_final(out, &ctx); +} + +#endif /* EXCLUDE_COMMON */ +#ifdef MG_MODULE_LINES +#line 1 "common/mbuf.c" +#endif +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#ifndef EXCLUDE_COMMON + +#include +#include +/* Amalgamated: #include "common/mbuf.h" */ + +#ifndef MBUF_REALLOC +#define MBUF_REALLOC realloc +#endif + +#ifndef MBUF_FREE +#define MBUF_FREE free +#endif + +void mbuf_init(struct mbuf *mbuf, size_t initial_size) WEAK; +void mbuf_init(struct mbuf *mbuf, size_t initial_size) { + mbuf->len = mbuf->size = 0; + mbuf->buf = NULL; + mbuf_resize(mbuf, initial_size); +} + +void mbuf_free(struct mbuf *mbuf) WEAK; +void mbuf_free(struct mbuf *mbuf) { + if (mbuf->buf != NULL) { + MBUF_FREE(mbuf->buf); + mbuf_init(mbuf, 0); + } +} + +void mbuf_resize(struct mbuf *a, size_t new_size) WEAK; +void mbuf_resize(struct mbuf *a, size_t new_size) { + if (new_size > a->size || (new_size < a->size && new_size >= a->len)) { + char *buf = (char *) MBUF_REALLOC(a->buf, new_size); + /* + * In case realloc fails, there's not much we can do, except keep things as + * they are. Note that NULL is a valid return value from realloc when + * size == 0, but that is covered too. + */ + if (buf == NULL && new_size != 0) return; + a->buf = buf; + a->size = new_size; + } +} + +void mbuf_trim(struct mbuf *mbuf) WEAK; +void mbuf_trim(struct mbuf *mbuf) { + mbuf_resize(mbuf, mbuf->len); +} + +size_t mbuf_insert(struct mbuf *a, size_t off, const void *buf, size_t) WEAK; +size_t mbuf_insert(struct mbuf *a, size_t off, const void *buf, size_t len) { + char *p = NULL; + + assert(a != NULL); + assert(a->len <= a->size); + assert(off <= a->len); + + /* check overflow */ + if (~(size_t) 0 - (size_t) a->buf < len) return 0; + + if (a->len + len <= a->size) { + memmove(a->buf + off + len, a->buf + off, a->len - off); + if (buf != NULL) { + memcpy(a->buf + off, buf, len); + } + a->len += len; + } else { + size_t new_size = (size_t)((a->len + len) * MBUF_SIZE_MULTIPLIER); + if ((p = (char *) MBUF_REALLOC(a->buf, new_size)) != NULL) { + a->buf = p; + memmove(a->buf + off + len, a->buf + off, a->len - off); + if (buf != NULL) memcpy(a->buf + off, buf, len); + a->len += len; + a->size = new_size; + } else { + len = 0; + } + } + + return len; +} + +size_t mbuf_append(struct mbuf *a, const void *buf, size_t len) WEAK; +size_t mbuf_append(struct mbuf *a, const void *buf, size_t len) { + return mbuf_insert(a, a->len, buf, len); +} + +void mbuf_remove(struct mbuf *mb, size_t n) WEAK; +void mbuf_remove(struct mbuf *mb, size_t n) { + if (n > 0 && n <= mb->len) { + memmove(mb->buf, mb->buf + n, mb->len - n); + mb->len -= n; + } +} + +#endif /* EXCLUDE_COMMON */ +#ifdef MG_MODULE_LINES +#line 1 "common/mg_str.c" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +/* Amalgamated: #include "common/mg_mem.h" */ +/* Amalgamated: #include "common/mg_str.h" */ + +#include +#include + +int mg_ncasecmp(const char *s1, const char *s2, size_t len) WEAK; + +struct mg_str mg_mk_str(const char *s) WEAK; +struct mg_str mg_mk_str(const char *s) { + struct mg_str ret = {s, 0}; + if (s != NULL) ret.len = strlen(s); + return ret; +} + +struct mg_str mg_mk_str_n(const char *s, size_t len) WEAK; +struct mg_str mg_mk_str_n(const char *s, size_t len) { + struct mg_str ret = {s, len}; + return ret; +} + +int mg_vcmp(const struct mg_str *str1, const char *str2) WEAK; +int mg_vcmp(const struct mg_str *str1, const char *str2) { + size_t n2 = strlen(str2), n1 = str1->len; + int r = strncmp(str1->p, str2, (n1 < n2) ? n1 : n2); + if (r == 0) { + return n1 - n2; + } + return r; +} + +int mg_vcasecmp(const struct mg_str *str1, const char *str2) WEAK; +int mg_vcasecmp(const struct mg_str *str1, const char *str2) { + size_t n2 = strlen(str2), n1 = str1->len; + int r = mg_ncasecmp(str1->p, str2, (n1 < n2) ? n1 : n2); + if (r == 0) { + return n1 - n2; + } + return r; +} + +static struct mg_str mg_strdup_common(const struct mg_str s, + int nul_terminate) { + struct mg_str r = {NULL, 0}; + if (s.len > 0 && s.p != NULL) { + char *sc = (char *) MG_MALLOC(s.len + (nul_terminate ? 1 : 0)); + if (sc != NULL) { + memcpy(sc, s.p, s.len); + if (nul_terminate) sc[s.len] = '\0'; + r.p = sc; + r.len = s.len; + } + } + return r; +} + +struct mg_str mg_strdup(const struct mg_str s) WEAK; +struct mg_str mg_strdup(const struct mg_str s) { + return mg_strdup_common(s, 0 /* NUL-terminate */); +} + +struct mg_str mg_strdup_nul(const struct mg_str s) WEAK; +struct mg_str mg_strdup_nul(const struct mg_str s) { + return mg_strdup_common(s, 1 /* NUL-terminate */); +} + +const char *mg_strchr(const struct mg_str s, int c) WEAK; +const char *mg_strchr(const struct mg_str s, int c) { + size_t i; + for (i = 0; i < s.len; i++) { + if (s.p[i] == c) return &s.p[i]; + } + return NULL; +} + +int mg_strcmp(const struct mg_str str1, const struct mg_str str2) WEAK; +int mg_strcmp(const struct mg_str str1, const struct mg_str str2) { + size_t i = 0; + while (i < str1.len && i < str2.len) { + if (str1.p[i] < str2.p[i]) return -1; + if (str1.p[i] > str2.p[i]) return 1; + i++; + } + if (i < str1.len) return 1; + if (i < str2.len) return -1; + return 0; +} + +int mg_strncmp(const struct mg_str, const struct mg_str, size_t n) WEAK; +int mg_strncmp(const struct mg_str str1, const struct mg_str str2, size_t n) { + struct mg_str s1 = str1; + struct mg_str s2 = str2; + + if (s1.len > n) { + s1.len = n; + } + if (s2.len > n) { + s2.len = n; + } + return mg_strcmp(s1, s2); +} + +const char *mg_strstr(const struct mg_str haystack, + const struct mg_str needle) WEAK; +const char *mg_strstr(const struct mg_str haystack, + const struct mg_str needle) { + size_t i; + if (needle.len > haystack.len) return NULL; + for (i = 0; i <= haystack.len - needle.len; i++) { + if (memcmp(haystack.p + i, needle.p, needle.len) == 0) { + return haystack.p + i; + } + } + return NULL; +} +#ifdef MG_MODULE_LINES +#line 1 "common/str_util.c" +#endif +/* + * Copyright (c) 2015 Cesanta Software Limited + * All rights reserved + */ + +#ifndef EXCLUDE_COMMON + +/* Amalgamated: #include "common/str_util.h" */ +/* Amalgamated: #include "common/mg_mem.h" */ +/* Amalgamated: #include "common/platform.h" */ + +#ifndef C_DISABLE_BUILTIN_SNPRINTF +#define C_DISABLE_BUILTIN_SNPRINTF 0 +#endif + +/* Amalgamated: #include "common/mg_mem.h" */ + +size_t c_strnlen(const char *s, size_t maxlen) WEAK; +size_t c_strnlen(const char *s, size_t maxlen) { + size_t l = 0; + for (; l < maxlen && s[l] != '\0'; l++) { + } + return l; +} + +#define C_SNPRINTF_APPEND_CHAR(ch) \ + do { \ + if (i < (int) buf_size) buf[i] = ch; \ + i++; \ + } while (0) + +#define C_SNPRINTF_FLAG_ZERO 1 + +#if C_DISABLE_BUILTIN_SNPRINTF +int c_vsnprintf(char *buf, size_t buf_size, const char *fmt, va_list ap) WEAK; +int c_vsnprintf(char *buf, size_t buf_size, const char *fmt, va_list ap) { + return vsnprintf(buf, buf_size, fmt, ap); +} +#else +static int c_itoa(char *buf, size_t buf_size, int64_t num, int base, int flags, + int field_width) { + char tmp[40]; + int i = 0, k = 0, neg = 0; + + if (num < 0) { + neg++; + num = -num; + } + + /* Print into temporary buffer - in reverse order */ + do { + int rem = num % base; + if (rem < 10) { + tmp[k++] = '0' + rem; + } else { + tmp[k++] = 'a' + (rem - 10); + } + num /= base; + } while (num > 0); + + /* Zero padding */ + if (flags && C_SNPRINTF_FLAG_ZERO) { + while (k < field_width && k < (int) sizeof(tmp) - 1) { + tmp[k++] = '0'; + } + } + + /* And sign */ + if (neg) { + tmp[k++] = '-'; + } + + /* Now output */ + while (--k >= 0) { + C_SNPRINTF_APPEND_CHAR(tmp[k]); + } + + return i; +} + +int c_vsnprintf(char *buf, size_t buf_size, const char *fmt, va_list ap) WEAK; +int c_vsnprintf(char *buf, size_t buf_size, const char *fmt, va_list ap) { + int ch, i = 0, len_mod, flags, precision, field_width; + + while ((ch = *fmt++) != '\0') { + if (ch != '%') { + C_SNPRINTF_APPEND_CHAR(ch); + } else { + /* + * Conversion specification: + * zero or more flags (one of: # 0 - + ') + * an optional minimum field width (digits) + * an optional precision (. followed by digits, or *) + * an optional length modifier (one of: hh h l ll L q j z t) + * conversion specifier (one of: d i o u x X e E f F g G a A c s p n) + */ + flags = field_width = precision = len_mod = 0; + + /* Flags. only zero-pad flag is supported. */ + if (*fmt == '0') { + flags |= C_SNPRINTF_FLAG_ZERO; + } + + /* Field width */ + while (*fmt >= '0' && *fmt <= '9') { + field_width *= 10; + field_width += *fmt++ - '0'; + } + /* Dynamic field width */ + if (*fmt == '*') { + field_width = va_arg(ap, int); + fmt++; + } + + /* Precision */ + if (*fmt == '.') { + fmt++; + if (*fmt == '*') { + precision = va_arg(ap, int); + fmt++; + } else { + while (*fmt >= '0' && *fmt <= '9') { + precision *= 10; + precision += *fmt++ - '0'; + } + } + } + + /* Length modifier */ + switch (*fmt) { + case 'h': + case 'l': + case 'L': + case 'I': + case 'q': + case 'j': + case 'z': + case 't': + len_mod = *fmt++; + if (*fmt == 'h') { + len_mod = 'H'; + fmt++; + } + if (*fmt == 'l') { + len_mod = 'q'; + fmt++; + } + break; + } + + ch = *fmt++; + if (ch == 's') { + const char *s = va_arg(ap, const char *); /* Always fetch parameter */ + int j; + int pad = field_width - (precision >= 0 ? c_strnlen(s, precision) : 0); + for (j = 0; j < pad; j++) { + C_SNPRINTF_APPEND_CHAR(' '); + } + + /* `s` may be NULL in case of %.*s */ + if (s != NULL) { + /* Ignore negative and 0 precisions */ + for (j = 0; (precision <= 0 || j < precision) && s[j] != '\0'; j++) { + C_SNPRINTF_APPEND_CHAR(s[j]); + } + } + } else if (ch == 'c') { + ch = va_arg(ap, int); /* Always fetch parameter */ + C_SNPRINTF_APPEND_CHAR(ch); + } else if (ch == 'd' && len_mod == 0) { + i += c_itoa(buf + i, buf_size - i, va_arg(ap, int), 10, flags, + field_width); + } else if (ch == 'd' && len_mod == 'l') { + i += c_itoa(buf + i, buf_size - i, va_arg(ap, long), 10, flags, + field_width); +#ifdef SSIZE_MAX + } else if (ch == 'd' && len_mod == 'z') { + i += c_itoa(buf + i, buf_size - i, va_arg(ap, ssize_t), 10, flags, + field_width); +#endif + } else if (ch == 'd' && len_mod == 'q') { + i += c_itoa(buf + i, buf_size - i, va_arg(ap, int64_t), 10, flags, + field_width); + } else if ((ch == 'x' || ch == 'u') && len_mod == 0) { + i += c_itoa(buf + i, buf_size - i, va_arg(ap, unsigned), + ch == 'x' ? 16 : 10, flags, field_width); + } else if ((ch == 'x' || ch == 'u') && len_mod == 'l') { + i += c_itoa(buf + i, buf_size - i, va_arg(ap, unsigned long), + ch == 'x' ? 16 : 10, flags, field_width); + } else if ((ch == 'x' || ch == 'u') && len_mod == 'z') { + i += c_itoa(buf + i, buf_size - i, va_arg(ap, size_t), + ch == 'x' ? 16 : 10, flags, field_width); + } else if (ch == 'p') { + unsigned long num = (unsigned long) (uintptr_t) va_arg(ap, void *); + C_SNPRINTF_APPEND_CHAR('0'); + C_SNPRINTF_APPEND_CHAR('x'); + i += c_itoa(buf + i, buf_size - i, num, 16, flags, 0); + } else { +#ifndef NO_LIBC + /* + * TODO(lsm): abort is not nice in a library, remove it + * Also, ESP8266 SDK doesn't have it + */ + abort(); +#endif + } + } + } + + /* Zero-terminate the result */ + if (buf_size > 0) { + buf[i < (int) buf_size ? i : (int) buf_size - 1] = '\0'; + } + + return i; +} +#endif + +int c_snprintf(char *buf, size_t buf_size, const char *fmt, ...) WEAK; +int c_snprintf(char *buf, size_t buf_size, const char *fmt, ...) { + int result; + va_list ap; + va_start(ap, fmt); + result = c_vsnprintf(buf, buf_size, fmt, ap); + va_end(ap); + return result; +} + +#ifdef _WIN32 +int to_wchar(const char *path, wchar_t *wbuf, size_t wbuf_len) { + int ret; + char buf[MAX_PATH * 2], buf2[MAX_PATH * 2], *p; + + strncpy(buf, path, sizeof(buf)); + buf[sizeof(buf) - 1] = '\0'; + + /* Trim trailing slashes. Leave backslash for paths like "X:\" */ + p = buf + strlen(buf) - 1; + while (p > buf && p[-1] != ':' && (p[0] == '\\' || p[0] == '/')) *p-- = '\0'; + + memset(wbuf, 0, wbuf_len * sizeof(wchar_t)); + ret = MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len); + + /* + * Convert back to Unicode. If doubly-converted string does not match the + * original, something is fishy, reject. + */ + WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2), + NULL, NULL); + if (strcmp(buf, buf2) != 0) { + wbuf[0] = L'\0'; + ret = 0; + } + + return ret; +} +#endif /* _WIN32 */ + +/* The simplest O(mn) algorithm. Better implementation are GPLed */ +const char *c_strnstr(const char *s, const char *find, size_t slen) WEAK; +const char *c_strnstr(const char *s, const char *find, size_t slen) { + size_t find_length = strlen(find); + size_t i; + + for (i = 0; i < slen; i++) { + if (i + find_length > slen) { + return NULL; + } + + if (strncmp(&s[i], find, find_length) == 0) { + return &s[i]; + } + } + + return NULL; +} + +#if CS_ENABLE_STRDUP +char *strdup(const char *src) WEAK; +char *strdup(const char *src) { + size_t len = strlen(src) + 1; + char *ret = MG_MALLOC(len); + if (ret != NULL) { + strcpy(ret, src); + } + return ret; +} +#endif + +void cs_to_hex(char *to, const unsigned char *p, size_t len) WEAK; +void cs_to_hex(char *to, const unsigned char *p, size_t len) { + static const char *hex = "0123456789abcdef"; + + for (; len--; p++) { + *to++ = hex[p[0] >> 4]; + *to++ = hex[p[0] & 0x0f]; + } + *to = '\0'; +} + +static int fourbit(int ch) { + if (ch >= '0' && ch <= '9') { + return ch - '0'; + } else if (ch >= 'a' && ch <= 'f') { + return ch - 'a' + 10; + } else if (ch >= 'A' && ch <= 'F') { + return ch - 'A' + 10; + } + return 0; +} + +void cs_from_hex(char *to, const char *p, size_t len) WEAK; +void cs_from_hex(char *to, const char *p, size_t len) { + size_t i; + + for (i = 0; i < len; i += 2) { + *to++ = (fourbit(p[i]) << 4) + fourbit(p[i + 1]); + } + *to = '\0'; +} + +#if CS_ENABLE_TO64 +int64_t cs_to64(const char *s) WEAK; +int64_t cs_to64(const char *s) { + int64_t result = 0; + int64_t neg = 1; + while (*s && isspace((unsigned char) *s)) s++; + if (*s == '-') { + neg = -1; + s++; + } + while (isdigit((unsigned char) *s)) { + result *= 10; + result += (*s - '0'); + s++; + } + return result * neg; +} +#endif + +static int str_util_lowercase(const char *s) { + return tolower(*(const unsigned char *) s); +} + +int mg_ncasecmp(const char *s1, const char *s2, size_t len) WEAK; +int mg_ncasecmp(const char *s1, const char *s2, size_t len) { + int diff = 0; + + if (len > 0) do { + diff = str_util_lowercase(s1++) - str_util_lowercase(s2++); + } while (diff == 0 && s1[-1] != '\0' && --len > 0); + + return diff; +} + +int mg_casecmp(const char *s1, const char *s2) WEAK; +int mg_casecmp(const char *s1, const char *s2) { + return mg_ncasecmp(s1, s2, (size_t) ~0); +} + +int mg_asprintf(char **buf, size_t size, const char *fmt, ...) WEAK; +int mg_asprintf(char **buf, size_t size, const char *fmt, ...) { + int ret; + va_list ap; + va_start(ap, fmt); + ret = mg_avprintf(buf, size, fmt, ap); + va_end(ap); + return ret; +} + +int mg_avprintf(char **buf, size_t size, const char *fmt, va_list ap) WEAK; +int mg_avprintf(char **buf, size_t size, const char *fmt, va_list ap) { + va_list ap_copy; + int len; + + va_copy(ap_copy, ap); + len = vsnprintf(*buf, size, fmt, ap_copy); + va_end(ap_copy); + + if (len < 0) { + /* eCos and Windows are not standard-compliant and return -1 when + * the buffer is too small. Keep allocating larger buffers until we + * succeed or out of memory. */ + *buf = NULL; /* LCOV_EXCL_START */ + while (len < 0) { + MG_FREE(*buf); + if (size == 0) { + size = 5; + } + size *= 2; + if ((*buf = (char *) MG_MALLOC(size)) == NULL) { + len = -1; + break; + } + va_copy(ap_copy, ap); + len = vsnprintf(*buf, size - 1, fmt, ap_copy); + va_end(ap_copy); + } + + /* + * Microsoft version of vsnprintf() is not always null-terminated, so put + * the terminator manually + */ + (*buf)[len] = 0; + /* LCOV_EXCL_STOP */ + } else if (len >= (int) size) { + /* Standard-compliant code path. Allocate a buffer that is large enough. */ + if ((*buf = (char *) MG_MALLOC(len + 1)) == NULL) { + len = -1; /* LCOV_EXCL_LINE */ + } else { /* LCOV_EXCL_LINE */ + va_copy(ap_copy, ap); + len = vsnprintf(*buf, len + 1, fmt, ap_copy); + va_end(ap_copy); + } + } + + return len; +} + +const char *mg_next_comma_list_entry(const char *, struct mg_str *, + struct mg_str *) WEAK; +const char *mg_next_comma_list_entry(const char *list, struct mg_str *val, + struct mg_str *eq_val) { + struct mg_str ret = mg_next_comma_list_entry_n(mg_mk_str(list), val, eq_val); + return ret.p; +} + +struct mg_str mg_next_comma_list_entry_n(struct mg_str list, struct mg_str *val, + struct mg_str *eq_val) WEAK; +struct mg_str mg_next_comma_list_entry_n(struct mg_str list, struct mg_str *val, + struct mg_str *eq_val) { + if (list.len == 0) { + /* End of the list */ + list = mg_mk_str(NULL); + } else { + const char *chr = NULL; + *val = list; + + if ((chr = mg_strchr(*val, ',')) != NULL) { + /* Comma found. Store length and shift the list ptr */ + val->len = chr - val->p; + chr++; + list.len -= (chr - list.p); + list.p = chr; + } else { + /* This value is the last one */ + list = mg_mk_str_n(list.p + list.len, 0); + } + + if (eq_val != NULL) { + /* Value has form "x=y", adjust pointers and lengths */ + /* so that val points to "x", and eq_val points to "y". */ + eq_val->len = 0; + eq_val->p = (const char *) memchr(val->p, '=', val->len); + if (eq_val->p != NULL) { + eq_val->p++; /* Skip over '=' character */ + eq_val->len = val->p + val->len - eq_val->p; + val->len = (eq_val->p - val->p) - 1; + } + } + } + + return list; +} + +size_t mg_match_prefix_n(const struct mg_str, const struct mg_str) WEAK; +size_t mg_match_prefix_n(const struct mg_str pattern, const struct mg_str str) { + const char *or_str; + size_t res = 0, len = 0, i = 0, j = 0; + + if ((or_str = (const char *) memchr(pattern.p, '|', pattern.len)) != NULL || + (or_str = (const char *) memchr(pattern.p, ',', pattern.len)) != NULL) { + struct mg_str pstr = {pattern.p, (size_t)(or_str - pattern.p)}; + res = mg_match_prefix_n(pstr, str); + if (res > 0) return res; + pstr.p = or_str + 1; + pstr.len = (pattern.p + pattern.len) - (or_str + 1); + return mg_match_prefix_n(pstr, str); + } + + for (; i < pattern.len && j < str.len; i++, j++) { + if (pattern.p[i] == '?') { + continue; + } else if (pattern.p[i] == '*') { + i++; + if (i < pattern.len && pattern.p[i] == '*') { + i++; + len = str.len - j; + } else { + len = 0; + while (j + len < str.len && str.p[j + len] != '/') len++; + } + if (i == pattern.len || (pattern.p[i] == '$' && i == pattern.len - 1)) + return j + len; + do { + const struct mg_str pstr = {pattern.p + i, pattern.len - i}; + const struct mg_str sstr = {str.p + j + len, str.len - j - len}; + res = mg_match_prefix_n(pstr, sstr); + } while (res == 0 && len != 0 && len-- > 0); + return res == 0 ? 0 : j + res + len; + } else if (str_util_lowercase(&pattern.p[i]) != + str_util_lowercase(&str.p[j])) { + break; + } + } + if (i < pattern.len && pattern.p[i] == '$') { + return j == str.len ? str.len : 0; + } + return i == pattern.len ? j : 0; +} + +size_t mg_match_prefix(const char *, int, const char *) WEAK; +size_t mg_match_prefix(const char *pattern, int pattern_len, const char *str) { + const struct mg_str pstr = {pattern, (size_t) pattern_len}; + struct mg_str s = {str, 0}; + if (str != NULL) s.len = strlen(str); + return mg_match_prefix_n(pstr, s); +} + +#endif /* EXCLUDE_COMMON */ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_net.c" +#endif +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + * + * This software is dual-licensed: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. For the terms of this + * license, see . + * + * You are free to use this software under the terms of the GNU General + * Public License, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * Alternatively, you can license this software under a commercial + * license, as set out in . + */ + +/* Amalgamated: #include "common/cs_time.h" */ +/* Amalgamated: #include "mg_dns.h" */ +/* Amalgamated: #include "mg_internal.h" */ +/* Amalgamated: #include "mg_resolv.h" */ +/* Amalgamated: #include "mg_util.h" */ + +#define MG_MAX_HOST_LEN 200 + +#define MG_COPY_COMMON_CONNECTION_OPTIONS(dst, src) \ + memcpy(dst, src, sizeof(*dst)); + +/* Which flags can be pre-set by the user at connection creation time. */ +#define _MG_ALLOWED_CONNECT_FLAGS_MASK \ + (MG_F_USER_1 | MG_F_USER_2 | MG_F_USER_3 | MG_F_USER_4 | MG_F_USER_5 | \ + MG_F_USER_6 | MG_F_WEBSOCKET_NO_DEFRAG | MG_F_ENABLE_BROADCAST) +/* Which flags should be modifiable by user's callbacks. */ +#define _MG_CALLBACK_MODIFIABLE_FLAGS_MASK \ + (MG_F_USER_1 | MG_F_USER_2 | MG_F_USER_3 | MG_F_USER_4 | MG_F_USER_5 | \ + MG_F_USER_6 | MG_F_WEBSOCKET_NO_DEFRAG | MG_F_SEND_AND_CLOSE | \ + MG_F_CLOSE_IMMEDIATELY | MG_F_IS_WEBSOCKET | MG_F_DELETE_CHUNK) + +#ifndef intptr_t +#define intptr_t long +#endif + +MG_INTERNAL void mg_add_conn(struct mg_mgr *mgr, struct mg_connection *c) { + DBG(("%p %p", mgr, c)); + c->mgr = mgr; + c->next = mgr->active_connections; + mgr->active_connections = c; + c->prev = NULL; + if (c->next != NULL) c->next->prev = c; + if (c->sock != INVALID_SOCKET) { + c->iface->vtable->add_conn(c); + } +} + +MG_INTERNAL void mg_remove_conn(struct mg_connection *conn) { + if (conn->prev == NULL) conn->mgr->active_connections = conn->next; + if (conn->prev) conn->prev->next = conn->next; + if (conn->next) conn->next->prev = conn->prev; + conn->prev = conn->next = NULL; + conn->iface->vtable->remove_conn(conn); +} + +MG_INTERNAL void mg_call(struct mg_connection *nc, + mg_event_handler_t ev_handler, void *user_data, int ev, + void *ev_data) { + static int nesting_level = 0; + nesting_level++; + if (ev_handler == NULL) { + /* + * If protocol handler is specified, call it. Otherwise, call user-specified + * event handler. + */ + ev_handler = nc->proto_handler ? nc->proto_handler : nc->handler; + } + if (ev != MG_EV_POLL) { + DBG(("%p %s ev=%d ev_data=%p flags=%lu rmbl=%d smbl=%d", nc, + ev_handler == nc->handler ? "user" : "proto", ev, ev_data, nc->flags, + (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); + } + +#if !defined(NO_LIBC) && MG_ENABLE_HEXDUMP + if (nc->mgr->hexdump_file != NULL && ev != MG_EV_POLL && ev != MG_EV_RECV && + ev != MG_EV_SEND /* handled separately */) { + mg_hexdump_connection(nc, nc->mgr->hexdump_file, NULL, 0, ev); + } +#endif + if (ev_handler != NULL) { + unsigned long flags_before = nc->flags; + size_t recv_mbuf_before = nc->recv_mbuf.len, recved; + ev_handler(nc, ev, ev_data MG_UD_ARG(user_data)); + recved = (recv_mbuf_before - nc->recv_mbuf.len); + /* Prevent user handler from fiddling with system flags. */ + if (ev_handler == nc->handler && nc->flags != flags_before) { + nc->flags = (flags_before & ~_MG_CALLBACK_MODIFIABLE_FLAGS_MASK) | + (nc->flags & _MG_CALLBACK_MODIFIABLE_FLAGS_MASK); + } + /* It's important to not double-count recved bytes, and since mg_call can be + * called recursively (e.g. proto_handler invokes user handler), we keep + * track of recursion and only report received bytes at the top level. */ + if (nesting_level == 1 && recved > 0 && !(nc->flags & MG_F_UDP)) { + nc->iface->vtable->recved(nc, recved); + } + } + if (ev != MG_EV_POLL) { + DBG(("%p after %s flags=%lu rmbl=%d smbl=%d", nc, + ev_handler == nc->handler ? "user" : "proto", nc->flags, + (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); + } + nesting_level--; +#if !MG_ENABLE_CALLBACK_USERDATA + (void) user_data; +#endif +} + +void mg_if_timer(struct mg_connection *c, double now) { + if (c->ev_timer_time > 0 && now >= c->ev_timer_time) { + double old_value = c->ev_timer_time; + c->ev_timer_time = 0; + mg_call(c, NULL, c->user_data, MG_EV_TIMER, &old_value); + } +} + +void mg_if_poll(struct mg_connection *nc, time_t now) { + if (!(nc->flags & MG_F_SSL) || (nc->flags & MG_F_SSL_HANDSHAKE_DONE)) { + mg_call(nc, NULL, nc->user_data, MG_EV_POLL, &now); + } +} + +void mg_destroy_conn(struct mg_connection *conn, int destroy_if) { + if (destroy_if) conn->iface->vtable->destroy_conn(conn); + if (conn->proto_data != NULL && conn->proto_data_destructor != NULL) { + conn->proto_data_destructor(conn->proto_data); + } +#if MG_ENABLE_SSL + mg_ssl_if_conn_free(conn); +#endif + mbuf_free(&conn->recv_mbuf); + mbuf_free(&conn->send_mbuf); + + memset(conn, 0, sizeof(*conn)); + MG_FREE(conn); +} + +void mg_close_conn(struct mg_connection *conn) { + DBG(("%p %lu %d", conn, conn->flags, conn->sock)); +#if MG_ENABLE_SSL + if (conn->flags & MG_F_SSL_HANDSHAKE_DONE) { + mg_ssl_if_conn_close_notify(conn); + } +#endif + mg_remove_conn(conn); + conn->iface->vtable->destroy_conn(conn); + mg_call(conn, NULL, conn->user_data, MG_EV_CLOSE, NULL); + mg_destroy_conn(conn, 0 /* destroy_if */); +} + +void mg_mgr_init(struct mg_mgr *m, void *user_data) { + struct mg_mgr_init_opts opts; + memset(&opts, 0, sizeof(opts)); + mg_mgr_init_opt(m, user_data, opts); +} + +void mg_mgr_init_opt(struct mg_mgr *m, void *user_data, + struct mg_mgr_init_opts opts) { + memset(m, 0, sizeof(*m)); +#if MG_ENABLE_BROADCAST + m->ctl[0] = m->ctl[1] = INVALID_SOCKET; +#endif + m->user_data = user_data; + +#ifdef _WIN32 + { + WSADATA data; + WSAStartup(MAKEWORD(2, 2), &data); + } +#elif defined(__unix__) + /* Ignore SIGPIPE signal, so if client cancels the request, it + * won't kill the whole process. */ + signal(SIGPIPE, SIG_IGN); +#endif + +#if MG_ENABLE_SSL + { + static int init_done; + if (!init_done) { + mg_ssl_if_init(); + init_done++; + } + } +#endif + { + int i; + if (opts.num_ifaces == 0) { + opts.num_ifaces = mg_num_ifaces; + opts.ifaces = mg_ifaces; + } + if (opts.main_iface != NULL) { + opts.ifaces[MG_MAIN_IFACE] = opts.main_iface; + } + m->num_ifaces = opts.num_ifaces; + m->ifaces = + (struct mg_iface **) MG_MALLOC(sizeof(*m->ifaces) * opts.num_ifaces); + for (i = 0; i < mg_num_ifaces; i++) { + m->ifaces[i] = mg_if_create_iface(opts.ifaces[i], m); + m->ifaces[i]->vtable->init(m->ifaces[i]); + } + } + if (opts.nameserver != NULL) { + m->nameserver = strdup(opts.nameserver); + } + DBG(("==================================")); + DBG(("init mgr=%p", m)); +} + +void mg_mgr_free(struct mg_mgr *m) { + struct mg_connection *conn, *tmp_conn; + + DBG(("%p", m)); + if (m == NULL) return; + /* Do one last poll, see https://github.com/cesanta/mongoose/issues/286 */ + mg_mgr_poll(m, 0); + +#if MG_ENABLE_BROADCAST + if (m->ctl[0] != INVALID_SOCKET) closesocket(m->ctl[0]); + if (m->ctl[1] != INVALID_SOCKET) closesocket(m->ctl[1]); + m->ctl[0] = m->ctl[1] = INVALID_SOCKET; +#endif + + for (conn = m->active_connections; conn != NULL; conn = tmp_conn) { + tmp_conn = conn->next; + mg_close_conn(conn); + } + + { + int i; + for (i = 0; i < m->num_ifaces; i++) { + m->ifaces[i]->vtable->free(m->ifaces[i]); + MG_FREE(m->ifaces[i]); + } + MG_FREE(m->ifaces); + } + + MG_FREE((char *) m->nameserver); +} + +time_t mg_mgr_poll(struct mg_mgr *m, int timeout_ms) { + int i; + time_t now = 0; /* oh GCC, seriously ? */ + + if (m->num_ifaces == 0) { + LOG(LL_ERROR, ("cannot poll: no interfaces")); + return 0; + } + + for (i = 0; i < m->num_ifaces; i++) { + now = m->ifaces[i]->vtable->poll(m->ifaces[i], timeout_ms); + } + return now; +} + +int mg_vprintf(struct mg_connection *nc, const char *fmt, va_list ap) { + char mem[MG_VPRINTF_BUFFER_SIZE], *buf = mem; + int len; + + if ((len = mg_avprintf(&buf, sizeof(mem), fmt, ap)) > 0) { + mg_send(nc, buf, len); + } + if (buf != mem && buf != NULL) { + MG_FREE(buf); /* LCOV_EXCL_LINE */ + } /* LCOV_EXCL_LINE */ + + return len; +} + +int mg_printf(struct mg_connection *conn, const char *fmt, ...) { + int len; + va_list ap; + va_start(ap, fmt); + len = mg_vprintf(conn, fmt, ap); + va_end(ap); + return len; +} + +#if MG_ENABLE_SYNC_RESOLVER +/* TODO(lsm): use non-blocking resolver */ +static int mg_resolve2(const char *host, struct in_addr *ina) { +#if MG_ENABLE_GETADDRINFO + int rv = 0; + struct addrinfo hints, *servinfo, *p; + struct sockaddr_in *h = NULL; + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + if ((rv = getaddrinfo(host, NULL, NULL, &servinfo)) != 0) { + DBG(("getaddrinfo(%s) failed: %s", host, strerror(mg_get_errno()))); + return 0; + } + for (p = servinfo; p != NULL; p = p->ai_next) { + memcpy(&h, &p->ai_addr, sizeof(struct sockaddr_in *)); + memcpy(ina, &h->sin_addr, sizeof(ina)); + } + freeaddrinfo(servinfo); + return 1; +#else + struct hostent *he; + if ((he = gethostbyname(host)) == NULL) { + DBG(("gethostbyname(%s) failed: %s", host, strerror(mg_get_errno()))); + } else { + memcpy(ina, he->h_addr_list[0], sizeof(*ina)); + return 1; + } + return 0; +#endif /* MG_ENABLE_GETADDRINFO */ +} + +int mg_resolve(const char *host, char *buf, size_t n) { + struct in_addr ad; + return mg_resolve2(host, &ad) ? snprintf(buf, n, "%s", inet_ntoa(ad)) : 0; +} +#endif /* MG_ENABLE_SYNC_RESOLVER */ + +MG_INTERNAL struct mg_connection *mg_create_connection_base( + struct mg_mgr *mgr, mg_event_handler_t callback, + struct mg_add_sock_opts opts) { + struct mg_connection *conn; + + if ((conn = (struct mg_connection *) MG_CALLOC(1, sizeof(*conn))) != NULL) { + conn->sock = INVALID_SOCKET; + conn->handler = callback; + conn->mgr = mgr; + conn->last_io_time = (time_t) mg_time(); + conn->iface = + (opts.iface != NULL ? opts.iface : mgr->ifaces[MG_MAIN_IFACE]); + conn->flags = opts.flags & _MG_ALLOWED_CONNECT_FLAGS_MASK; + conn->user_data = opts.user_data; + /* + * SIZE_MAX is defined as a long long constant in + * system headers on some platforms and so it + * doesn't compile with pedantic ansi flags. + */ + conn->recv_mbuf_limit = ~0; + } else { + MG_SET_PTRPTR(opts.error_string, "failed to create connection"); + } + + return conn; +} + +MG_INTERNAL struct mg_connection *mg_create_connection( + struct mg_mgr *mgr, mg_event_handler_t callback, + struct mg_add_sock_opts opts) { + struct mg_connection *conn = mg_create_connection_base(mgr, callback, opts); + + if (conn != NULL && !conn->iface->vtable->create_conn(conn)) { + MG_FREE(conn); + conn = NULL; + } + if (conn == NULL) { + MG_SET_PTRPTR(opts.error_string, "failed to init connection"); + } + + return conn; +} + +/* + * Address format: [PROTO://][HOST]:PORT + * + * HOST could be IPv4/IPv6 address or a host name. + * `host` is a destination buffer to hold parsed HOST part. Should be at least + * MG_MAX_HOST_LEN bytes long. + * `proto` is a returned socket type, either SOCK_STREAM or SOCK_DGRAM + * + * Return: + * -1 on parse error + * 0 if HOST needs DNS lookup + * >0 length of the address string + */ +MG_INTERNAL int mg_parse_address(const char *str, union socket_address *sa, + int *proto, char *host, size_t host_len) { + unsigned int a, b, c, d, port = 0; + int ch, len = 0; +#if MG_ENABLE_IPV6 + char buf[100]; +#endif + + /* + * MacOS needs that. If we do not zero it, subsequent bind() will fail. + * Also, all-zeroes in the socket address means binding to all addresses + * for both IPv4 and IPv6 (INADDR_ANY and IN6ADDR_ANY_INIT). + */ + memset(sa, 0, sizeof(*sa)); + sa->sin.sin_family = AF_INET; + + *proto = SOCK_STREAM; + + if (strncmp(str, "udp://", 6) == 0) { + str += 6; + *proto = SOCK_DGRAM; + } else if (strncmp(str, "tcp://", 6) == 0) { + str += 6; + } + + if (sscanf(str, "%u.%u.%u.%u:%u%n", &a, &b, &c, &d, &port, &len) == 5) { + /* Bind to a specific IPv4 address, e.g. 192.168.1.5:8080 */ + sa->sin.sin_addr.s_addr = + htonl(((uint32_t) a << 24) | ((uint32_t) b << 16) | c << 8 | d); + sa->sin.sin_port = htons((uint16_t) port); +#if MG_ENABLE_IPV6 + } else if (sscanf(str, "[%99[^]]]:%u%n", buf, &port, &len) == 2 && + inet_pton(AF_INET6, buf, &sa->sin6.sin6_addr)) { + /* IPv6 address, e.g. [3ffe:2a00:100:7031::1]:8080 */ + sa->sin6.sin6_family = AF_INET6; + sa->sin.sin_port = htons((uint16_t) port); +#endif +#if MG_ENABLE_ASYNC_RESOLVER + } else if (strlen(str) < host_len && + sscanf(str, "%[^ :]:%u%n", host, &port, &len) == 2) { + sa->sin.sin_port = htons((uint16_t) port); + if (mg_resolve_from_hosts_file(host, sa) != 0) { + /* + * if resolving from hosts file failed and the host + * we are trying to resolve is `localhost` - we should + * try to resolve it using `gethostbyname` and do not try + * to resolve it via DNS server if gethostbyname has failed too + */ + if (mg_ncasecmp(host, "localhost", 9) != 0) { + return 0; + } + +#if MG_ENABLE_SYNC_RESOLVER + if (!mg_resolve2(host, &sa->sin.sin_addr)) { + return -1; + } +#else + return -1; +#endif + } +#endif + } else if (sscanf(str, ":%u%n", &port, &len) == 1 || + sscanf(str, "%u%n", &port, &len) == 1) { + /* If only port is specified, bind to IPv4, INADDR_ANY */ + sa->sin.sin_port = htons((uint16_t) port); + } else { + return -1; + } + + /* Required for MG_ENABLE_ASYNC_RESOLVER=0 */ + (void) host; + (void) host_len; + + ch = str[len]; /* Character that follows the address */ + return port < 0xffffUL && (ch == '\0' || ch == ',' || isspace(ch)) ? len : -1; +} + +struct mg_connection *mg_if_accept_new_conn(struct mg_connection *lc) { + struct mg_add_sock_opts opts; + struct mg_connection *nc; + memset(&opts, 0, sizeof(opts)); + nc = mg_create_connection(lc->mgr, lc->handler, opts); + if (nc == NULL) return NULL; + nc->listener = lc; + nc->proto_handler = lc->proto_handler; + nc->user_data = lc->user_data; + nc->recv_mbuf_limit = lc->recv_mbuf_limit; + nc->iface = lc->iface; + if (lc->flags & MG_F_SSL) nc->flags |= MG_F_SSL; + mg_add_conn(nc->mgr, nc); + DBG(("%p %p %d %d", lc, nc, nc->sock, (int) nc->flags)); + return nc; +} + +void mg_if_accept_tcp_cb(struct mg_connection *nc, union socket_address *sa, + size_t sa_len) { + (void) sa_len; + nc->sa = *sa; + mg_call(nc, NULL, nc->user_data, MG_EV_ACCEPT, &nc->sa); +} + +void mg_send(struct mg_connection *nc, const void *buf, int len) { + nc->last_io_time = (time_t) mg_time(); + if (nc->flags & MG_F_UDP) { + nc->iface->vtable->udp_send(nc, buf, len); + } else { + nc->iface->vtable->tcp_send(nc, buf, len); + } +} + +void mg_if_sent_cb(struct mg_connection *nc, int num_sent) { + DBG(("%p %d", nc, num_sent)); +#if !defined(NO_LIBC) && MG_ENABLE_HEXDUMP + if (nc->mgr && nc->mgr->hexdump_file != NULL) { + char *buf = nc->send_mbuf.buf; + mg_hexdump_connection(nc, nc->mgr->hexdump_file, buf, num_sent, MG_EV_SEND); + } +#endif + if (num_sent < 0) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } else { + mbuf_remove(&nc->send_mbuf, num_sent); + mbuf_trim(&nc->send_mbuf); + } + mg_call(nc, NULL, nc->user_data, MG_EV_SEND, &num_sent); +} + +MG_INTERNAL void mg_recv_common(struct mg_connection *nc, void *buf, int len, + int own) { + DBG(("%p %d %u", nc, len, (unsigned int) nc->recv_mbuf.len)); + +#if !defined(NO_LIBC) && MG_ENABLE_HEXDUMP + if (nc->mgr && nc->mgr->hexdump_file != NULL) { + mg_hexdump_connection(nc, nc->mgr->hexdump_file, buf, len, MG_EV_RECV); + } +#endif + + if (nc->flags & MG_F_CLOSE_IMMEDIATELY) { + DBG(("%p discarded %d bytes", nc, len)); + /* + * This connection will not survive next poll. Do not deliver events, + * send data to /dev/null without acking. + */ + if (own) { + MG_FREE(buf); + } + return; + } + nc->last_io_time = (time_t) mg_time(); + if (!own) { + mbuf_append(&nc->recv_mbuf, buf, len); + } else if (nc->recv_mbuf.len == 0) { + /* Adopt buf as recv_mbuf's backing store. */ + mbuf_free(&nc->recv_mbuf); + nc->recv_mbuf.buf = (char *) buf; + nc->recv_mbuf.size = nc->recv_mbuf.len = len; + } else { + mbuf_append(&nc->recv_mbuf, buf, len); + MG_FREE(buf); + } + mg_call(nc, NULL, nc->user_data, MG_EV_RECV, &len); +} + +void mg_if_recv_tcp_cb(struct mg_connection *nc, void *buf, int len, int own) { + mg_recv_common(nc, buf, len, own); +} + +void mg_if_recv_udp_cb(struct mg_connection *nc, void *buf, int len, + union socket_address *sa, size_t sa_len) { + assert(nc->flags & MG_F_UDP); + DBG(("%p %u", nc, (unsigned int) len)); + if (nc->flags & MG_F_LISTENING) { + struct mg_connection *lc = nc; + /* + * Do we have an existing connection for this source? + * This is very inefficient for long connection lists. + */ + for (nc = mg_next(lc->mgr, NULL); nc != NULL; nc = mg_next(lc->mgr, nc)) { + if (memcmp(&nc->sa.sa, &sa->sa, sa_len) == 0 && nc->listener == lc) { + break; + } + } + if (nc == NULL) { + struct mg_add_sock_opts opts; + memset(&opts, 0, sizeof(opts)); + /* Create fake connection w/out sock initialization */ + nc = mg_create_connection_base(lc->mgr, lc->handler, opts); + if (nc != NULL) { + nc->sock = lc->sock; + nc->listener = lc; + nc->sa = *sa; + nc->proto_handler = lc->proto_handler; + nc->user_data = lc->user_data; + nc->recv_mbuf_limit = lc->recv_mbuf_limit; + nc->flags = MG_F_UDP; + /* + * Long-lived UDP "connections" i.e. interactions that involve more + * than one request and response are rare, most are transactional: + * response is sent and the "connection" is closed. Or - should be. + * But users (including ourselves) tend to forget about that part, + * because UDP is connectionless and one does not think about + * processing a UDP request as handling a connection that needs to be + * closed. Thus, we begin with SEND_AND_CLOSE flag set, which should + * be a reasonable default for most use cases, but it is possible to + * turn it off the connection should be kept alive after processing. + */ + nc->flags |= MG_F_SEND_AND_CLOSE; + mg_add_conn(lc->mgr, nc); + mg_call(nc, NULL, nc->user_data, MG_EV_ACCEPT, &nc->sa); + } else { + DBG(("OOM")); + /* No return here, we still need to drop on the floor */ + } + } + } + if (nc != NULL) { + mg_recv_common(nc, buf, len, 1); + } else { + /* Drop on the floor. */ + MG_FREE(buf); + } +} + +/* + * Schedules an async connect for a resolved address and proto. + * Called from two places: `mg_connect_opt()` and from async resolver. + * When called from the async resolver, it must trigger `MG_EV_CONNECT` event + * with a failure flag to indicate connection failure. + */ +MG_INTERNAL struct mg_connection *mg_do_connect(struct mg_connection *nc, + int proto, + union socket_address *sa) { + DBG(("%p %s://%s:%hu", nc, proto == SOCK_DGRAM ? "udp" : "tcp", + inet_ntoa(sa->sin.sin_addr), ntohs(sa->sin.sin_port))); + + nc->flags |= MG_F_CONNECTING; + if (proto == SOCK_DGRAM) { + nc->iface->vtable->connect_udp(nc); + } else { + nc->iface->vtable->connect_tcp(nc, sa); + } + mg_add_conn(nc->mgr, nc); + return nc; +} + +void mg_if_connect_cb(struct mg_connection *nc, int err) { + DBG(("%p connect, err=%d", nc, err)); + nc->flags &= ~MG_F_CONNECTING; + if (err != 0) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } + mg_call(nc, NULL, nc->user_data, MG_EV_CONNECT, &err); +} + +#if MG_ENABLE_ASYNC_RESOLVER +/* + * Callback for the async resolver on mg_connect_opt() call. + * Main task of this function is to trigger MG_EV_CONNECT event with + * either failure (and dealloc the connection) + * or success (and proceed with connect() + */ +static void resolve_cb(struct mg_dns_message *msg, void *data, + enum mg_resolve_err e) { + struct mg_connection *nc = (struct mg_connection *) data; + int i; + int failure = -1; + + nc->flags &= ~MG_F_RESOLVING; + if (msg != NULL) { + /* + * Take the first DNS A answer and run... + */ + for (i = 0; i < msg->num_answers; i++) { + if (msg->answers[i].rtype == MG_DNS_A_RECORD) { + /* + * Async resolver guarantees that there is at least one answer. + * TODO(lsm): handle IPv6 answers too + */ + mg_dns_parse_record_data(msg, &msg->answers[i], &nc->sa.sin.sin_addr, + 4); + mg_do_connect(nc, nc->flags & MG_F_UDP ? SOCK_DGRAM : SOCK_STREAM, + &nc->sa); + return; + } + } + } + + if (e == MG_RESOLVE_TIMEOUT) { + double now = mg_time(); + mg_call(nc, NULL, nc->user_data, MG_EV_TIMER, &now); + } + + /* + * If we get there was no MG_DNS_A_RECORD in the answer + */ + mg_call(nc, NULL, nc->user_data, MG_EV_CONNECT, &failure); + mg_call(nc, NULL, nc->user_data, MG_EV_CLOSE, NULL); + mg_destroy_conn(nc, 1 /* destroy_if */); +} +#endif + +struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *address, + MG_CB(mg_event_handler_t callback, + void *user_data)) { + struct mg_connect_opts opts; + memset(&opts, 0, sizeof(opts)); + return mg_connect_opt(mgr, address, MG_CB(callback, user_data), opts); +} + +struct mg_connection *mg_connect_opt(struct mg_mgr *mgr, const char *address, + MG_CB(mg_event_handler_t callback, + void *user_data), + struct mg_connect_opts opts) { + struct mg_connection *nc = NULL; + int proto, rc; + struct mg_add_sock_opts add_sock_opts; + char host[MG_MAX_HOST_LEN]; + + MG_COPY_COMMON_CONNECTION_OPTIONS(&add_sock_opts, &opts); + + if ((nc = mg_create_connection(mgr, callback, add_sock_opts)) == NULL) { + return NULL; + } + + if ((rc = mg_parse_address(address, &nc->sa, &proto, host, sizeof(host))) < + 0) { + /* Address is malformed */ + MG_SET_PTRPTR(opts.error_string, "cannot parse address"); + mg_destroy_conn(nc, 1 /* destroy_if */); + return NULL; + } + + nc->flags |= opts.flags & _MG_ALLOWED_CONNECT_FLAGS_MASK; + nc->flags |= (proto == SOCK_DGRAM) ? MG_F_UDP : 0; +#if MG_ENABLE_CALLBACK_USERDATA + nc->user_data = user_data; +#else + nc->user_data = opts.user_data; +#endif + +#if MG_ENABLE_SSL + DBG(("%p %s %s,%s,%s", nc, address, (opts.ssl_cert ? opts.ssl_cert : "-"), + (opts.ssl_key ? opts.ssl_key : "-"), + (opts.ssl_ca_cert ? opts.ssl_ca_cert : "-"))); + + if (opts.ssl_cert != NULL || opts.ssl_ca_cert != NULL || + opts.ssl_psk_identity != NULL) { + const char *err_msg = NULL; + struct mg_ssl_if_conn_params params; + if (nc->flags & MG_F_UDP) { + MG_SET_PTRPTR(opts.error_string, "SSL for UDP is not supported"); + mg_destroy_conn(nc, 1 /* destroy_if */); + return NULL; + } + memset(¶ms, 0, sizeof(params)); + params.cert = opts.ssl_cert; + params.key = opts.ssl_key; + params.ca_cert = opts.ssl_ca_cert; + params.cipher_suites = opts.ssl_cipher_suites; + params.psk_identity = opts.ssl_psk_identity; + params.psk_key = opts.ssl_psk_key; + if (opts.ssl_ca_cert != NULL) { + if (opts.ssl_server_name != NULL) { + if (strcmp(opts.ssl_server_name, "*") != 0) { + params.server_name = opts.ssl_server_name; + } + } else if (rc == 0) { /* If it's a DNS name, use host. */ + params.server_name = host; + } + } + if (mg_ssl_if_conn_init(nc, ¶ms, &err_msg) != MG_SSL_OK) { + MG_SET_PTRPTR(opts.error_string, err_msg); + mg_destroy_conn(nc, 1 /* destroy_if */); + return NULL; + } + nc->flags |= MG_F_SSL; + } +#endif /* MG_ENABLE_SSL */ + + if (rc == 0) { +#if MG_ENABLE_ASYNC_RESOLVER + /* + * DNS resolution is required for host. + * mg_parse_address() fills port in nc->sa, which we pass to resolve_cb() + */ + struct mg_connection *dns_conn = NULL; + struct mg_resolve_async_opts o; + memset(&o, 0, sizeof(o)); + o.dns_conn = &dns_conn; + o.nameserver = opts.nameserver; + if (mg_resolve_async_opt(nc->mgr, host, MG_DNS_A_RECORD, resolve_cb, nc, + o) != 0) { + MG_SET_PTRPTR(opts.error_string, "cannot schedule DNS lookup"); + mg_destroy_conn(nc, 1 /* destroy_if */); + return NULL; + } + nc->priv_2 = dns_conn; + nc->flags |= MG_F_RESOLVING; + return nc; +#else + MG_SET_PTRPTR(opts.error_string, "Resolver is disabled"); + mg_destroy_conn(nc, 1 /* destroy_if */); + return NULL; +#endif + } else { + /* Address is parsed and resolved to IP. proceed with connect() */ + return mg_do_connect(nc, proto, &nc->sa); + } +} + +struct mg_connection *mg_bind(struct mg_mgr *srv, const char *address, + MG_CB(mg_event_handler_t event_handler, + void *user_data)) { + struct mg_bind_opts opts; + memset(&opts, 0, sizeof(opts)); + return mg_bind_opt(srv, address, MG_CB(event_handler, user_data), opts); +} + +struct mg_connection *mg_bind_opt(struct mg_mgr *mgr, const char *address, + MG_CB(mg_event_handler_t callback, + void *user_data), + struct mg_bind_opts opts) { + union socket_address sa; + struct mg_connection *nc = NULL; + int proto, rc; + struct mg_add_sock_opts add_sock_opts; + char host[MG_MAX_HOST_LEN]; + +#if MG_ENABLE_CALLBACK_USERDATA + opts.user_data = user_data; +#endif + + if (callback == NULL) { + MG_SET_PTRPTR(opts.error_string, "handler is required"); + return NULL; + } + + MG_COPY_COMMON_CONNECTION_OPTIONS(&add_sock_opts, &opts); + + if (mg_parse_address(address, &sa, &proto, host, sizeof(host)) <= 0) { + MG_SET_PTRPTR(opts.error_string, "cannot parse address"); + return NULL; + } + + nc = mg_create_connection(mgr, callback, add_sock_opts); + if (nc == NULL) { + return NULL; + } + + nc->sa = sa; + nc->flags |= MG_F_LISTENING; + if (proto == SOCK_DGRAM) nc->flags |= MG_F_UDP; + +#if MG_ENABLE_SSL + DBG(("%p %s %s,%s,%s", nc, address, (opts.ssl_cert ? opts.ssl_cert : "-"), + (opts.ssl_key ? opts.ssl_key : "-"), + (opts.ssl_ca_cert ? opts.ssl_ca_cert : "-"))); + + if (opts.ssl_cert != NULL || opts.ssl_ca_cert != NULL) { + const char *err_msg = NULL; + struct mg_ssl_if_conn_params params; + if (nc->flags & MG_F_UDP) { + MG_SET_PTRPTR(opts.error_string, "SSL for UDP is not supported"); + mg_destroy_conn(nc, 1 /* destroy_if */); + return NULL; + } + memset(¶ms, 0, sizeof(params)); + params.cert = opts.ssl_cert; + params.key = opts.ssl_key; + params.ca_cert = opts.ssl_ca_cert; + params.cipher_suites = opts.ssl_cipher_suites; + if (mg_ssl_if_conn_init(nc, ¶ms, &err_msg) != MG_SSL_OK) { + MG_SET_PTRPTR(opts.error_string, err_msg); + mg_destroy_conn(nc, 1 /* destroy_if */); + return NULL; + } + nc->flags |= MG_F_SSL; + } +#endif /* MG_ENABLE_SSL */ + + if (nc->flags & MG_F_UDP) { + rc = nc->iface->vtable->listen_udp(nc, &nc->sa); + } else { + rc = nc->iface->vtable->listen_tcp(nc, &nc->sa); + } + if (rc != 0) { + DBG(("Failed to open listener: %d", rc)); + MG_SET_PTRPTR(opts.error_string, "failed to open listener"); + mg_destroy_conn(nc, 1 /* destroy_if */); + return NULL; + } + mg_add_conn(nc->mgr, nc); + + return nc; +} + +struct mg_connection *mg_next(struct mg_mgr *s, struct mg_connection *conn) { + return conn == NULL ? s->active_connections : conn->next; +} + +#if MG_ENABLE_BROADCAST +void mg_broadcast(struct mg_mgr *mgr, mg_event_handler_t cb, void *data, + size_t len) { + struct ctl_msg ctl_msg; + + /* + * Mongoose manager has a socketpair, `struct mg_mgr::ctl`, + * where `mg_broadcast()` pushes the message. + * `mg_mgr_poll()` wakes up, reads a message from the socket pair, and calls + * specified callback for each connection. Thus the callback function executes + * in event manager thread. + */ + if (mgr->ctl[0] != INVALID_SOCKET && data != NULL && + len < sizeof(ctl_msg.message)) { + size_t dummy; + + ctl_msg.callback = cb; + memcpy(ctl_msg.message, data, len); + dummy = MG_SEND_FUNC(mgr->ctl[0], (char *) &ctl_msg, + offsetof(struct ctl_msg, message) + len, 0); + dummy = MG_RECV_FUNC(mgr->ctl[0], (char *) &len, 1, 0); + (void) dummy; /* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25509 */ + } +} +#endif /* MG_ENABLE_BROADCAST */ + +static int isbyte(int n) { + return n >= 0 && n <= 255; +} + +static int parse_net(const char *spec, uint32_t *net, uint32_t *mask) { + int n, a, b, c, d, slash = 32, len = 0; + + if ((sscanf(spec, "%d.%d.%d.%d/%d%n", &a, &b, &c, &d, &slash, &n) == 5 || + sscanf(spec, "%d.%d.%d.%d%n", &a, &b, &c, &d, &n) == 4) && + isbyte(a) && isbyte(b) && isbyte(c) && isbyte(d) && slash >= 0 && + slash < 33) { + len = n; + *net = + ((uint32_t) a << 24) | ((uint32_t) b << 16) | ((uint32_t) c << 8) | d; + *mask = slash ? 0xffffffffU << (32 - slash) : 0; + } + + return len; +} + +int mg_check_ip_acl(const char *acl, uint32_t remote_ip) { + int allowed, flag; + uint32_t net, mask; + struct mg_str vec; + + /* If any ACL is set, deny by default */ + allowed = (acl == NULL || *acl == '\0') ? '+' : '-'; + + while ((acl = mg_next_comma_list_entry(acl, &vec, NULL)) != NULL) { + flag = vec.p[0]; + if ((flag != '+' && flag != '-') || + parse_net(&vec.p[1], &net, &mask) == 0) { + return -1; + } + + if (net == (remote_ip & mask)) { + allowed = flag; + } + } + + DBG(("%08x %c", (unsigned int) remote_ip, allowed)); + return allowed == '+'; +} + +/* Move data from one connection to another */ +void mg_forward(struct mg_connection *from, struct mg_connection *to) { + mg_send(to, from->recv_mbuf.buf, from->recv_mbuf.len); + mbuf_remove(&from->recv_mbuf, from->recv_mbuf.len); +} + +double mg_set_timer(struct mg_connection *c, double timestamp) { + double result = c->ev_timer_time; + c->ev_timer_time = timestamp; + /* + * If this connection is resolving, it's not in the list of active + * connections, so not processed yet. It has a DNS resolver connection + * linked to it. Set up a timer for the DNS connection. + */ + DBG(("%p %p %d -> %lu", c, c->priv_2, (c->flags & MG_F_RESOLVING ? 1 : 0), + (unsigned long) timestamp)); + if ((c->flags & MG_F_RESOLVING) && c->priv_2 != NULL) { + ((struct mg_connection *) c->priv_2)->ev_timer_time = timestamp; + } + return result; +} + +void mg_sock_set(struct mg_connection *nc, sock_t sock) { + if (sock != INVALID_SOCKET) { + nc->iface->vtable->sock_set(nc, sock); + } +} + +void mg_if_get_conn_addr(struct mg_connection *nc, int remote, + union socket_address *sa) { + nc->iface->vtable->get_conn_addr(nc, remote, sa); +} + +struct mg_connection *mg_add_sock_opt(struct mg_mgr *s, sock_t sock, + MG_CB(mg_event_handler_t callback, + void *user_data), + struct mg_add_sock_opts opts) { +#if MG_ENABLE_CALLBACK_USERDATA + opts.user_data = user_data; +#endif + + struct mg_connection *nc = mg_create_connection_base(s, callback, opts); + if (nc != NULL) { + mg_sock_set(nc, sock); + mg_add_conn(nc->mgr, nc); + } + return nc; +} + +struct mg_connection *mg_add_sock(struct mg_mgr *s, sock_t sock, + MG_CB(mg_event_handler_t callback, + void *user_data)) { + struct mg_add_sock_opts opts; + memset(&opts, 0, sizeof(opts)); + return mg_add_sock_opt(s, sock, MG_CB(callback, user_data), opts); +} + +double mg_time(void) { + return cs_time(); +} +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_net_if_socket.h" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_MONGOOSE_SRC_NET_IF_SOCKET_H_ +#define CS_MONGOOSE_SRC_NET_IF_SOCKET_H_ + +/* Amalgamated: #include "mg_net_if.h" */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef MG_ENABLE_NET_IF_SOCKET +#define MG_ENABLE_NET_IF_SOCKET MG_NET_IF == MG_NET_IF_SOCKET +#endif + +extern const struct mg_iface_vtable mg_socket_iface_vtable; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CS_MONGOOSE_SRC_NET_IF_SOCKET_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_net_if_socks.h" +#endif +/* +* Copyright (c) 2014-2017 Cesanta Software Limited +* All rights reserved +*/ + +#ifndef CS_MONGOOSE_SRC_NET_IF_SOCKS_H_ +#define CS_MONGOOSE_SRC_NET_IF_SOCKS_H_ + +#if MG_ENABLE_SOCKS +/* Amalgamated: #include "mg_net_if.h" */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +extern const struct mg_iface_vtable mg_socks_iface_vtable; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* MG_ENABLE_SOCKS */ +#endif /* CS_MONGOOSE_SRC_NET_IF_SOCKS_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_net_if.c" +#endif +/* Amalgamated: #include "mg_net_if.h" */ +/* Amalgamated: #include "mg_internal.h" */ +/* Amalgamated: #include "mg_net_if_socket.h" */ + +extern const struct mg_iface_vtable mg_default_iface_vtable; + +const struct mg_iface_vtable *mg_ifaces[] = { + &mg_default_iface_vtable, +}; + +int mg_num_ifaces = (int) (sizeof(mg_ifaces) / sizeof(mg_ifaces[0])); + +struct mg_iface *mg_if_create_iface(const struct mg_iface_vtable *vtable, + struct mg_mgr *mgr) { + struct mg_iface *iface = (struct mg_iface *) MG_CALLOC(1, sizeof(*iface)); + iface->mgr = mgr; + iface->data = NULL; + iface->vtable = vtable; + return iface; +} + +struct mg_iface *mg_find_iface(struct mg_mgr *mgr, + const struct mg_iface_vtable *vtable, + struct mg_iface *from) { + int i = 0; + if (from != NULL) { + for (i = 0; i < mgr->num_ifaces; i++) { + if (mgr->ifaces[i] == from) { + i++; + break; + } + } + } + + for (; i < mgr->num_ifaces; i++) { + if (mgr->ifaces[i]->vtable == vtable) { + return mgr->ifaces[i]; + } + } + return NULL; +} +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_net_if_socket.c" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_NET_IF_SOCKET + +/* Amalgamated: #include "mg_net_if_socket.h" */ +/* Amalgamated: #include "mg_internal.h" */ +/* Amalgamated: #include "mg_util.h" */ + +#define MG_TCP_RECV_BUFFER_SIZE 1024 +#define MG_UDP_RECV_BUFFER_SIZE 1500 + +static sock_t mg_open_listening_socket(union socket_address *sa, int type, + int proto); +#if MG_ENABLE_SSL +static void mg_ssl_begin(struct mg_connection *nc); +#endif + +void mg_set_non_blocking_mode(sock_t sock) { +#ifdef _WIN32 + unsigned long on = 1; + ioctlsocket(sock, FIONBIO, &on); +#else + int flags = fcntl(sock, F_GETFL, 0); + fcntl(sock, F_SETFL, flags | O_NONBLOCK); +#endif +} + +static int mg_is_error(void) { + int err = mg_get_errno(); + return err != EINPROGRESS && err != EWOULDBLOCK +#ifndef WINCE + && err != EAGAIN && err != EINTR +#endif +#ifdef _WIN32 + && WSAGetLastError() != WSAEINTR && WSAGetLastError() != WSAEWOULDBLOCK +#endif + ; +} + +void mg_socket_if_connect_tcp(struct mg_connection *nc, + const union socket_address *sa) { + int rc, proto = 0; + nc->sock = socket(AF_INET, SOCK_STREAM, proto); + if (nc->sock == INVALID_SOCKET) { + nc->err = mg_get_errno() ? mg_get_errno() : 1; + return; + } +#if !defined(MG_ESP8266) + mg_set_non_blocking_mode(nc->sock); +#endif + rc = connect(nc->sock, &sa->sa, sizeof(sa->sin)); + nc->err = rc < 0 && mg_is_error() ? mg_get_errno() : 0; + DBG(("%p sock %d rc %d errno %d err %d", nc, nc->sock, rc, mg_get_errno(), + nc->err)); +} + +void mg_socket_if_connect_udp(struct mg_connection *nc) { + nc->sock = socket(AF_INET, SOCK_DGRAM, 0); + if (nc->sock == INVALID_SOCKET) { + nc->err = mg_get_errno() ? mg_get_errno() : 1; + return; + } + if (nc->flags & MG_F_ENABLE_BROADCAST) { + int optval = 1; + if (setsockopt(nc->sock, SOL_SOCKET, SO_BROADCAST, (const char *) &optval, + sizeof(optval)) < 0) { + nc->err = mg_get_errno() ? mg_get_errno() : 1; + return; + } + } + nc->err = 0; +} + +int mg_socket_if_listen_tcp(struct mg_connection *nc, + union socket_address *sa) { + int proto = 0; + sock_t sock = mg_open_listening_socket(sa, SOCK_STREAM, proto); + if (sock == INVALID_SOCKET) { + return (mg_get_errno() ? mg_get_errno() : 1); + } + mg_sock_set(nc, sock); + return 0; +} + +int mg_socket_if_listen_udp(struct mg_connection *nc, + union socket_address *sa) { + sock_t sock = mg_open_listening_socket(sa, SOCK_DGRAM, 0); + if (sock == INVALID_SOCKET) return (mg_get_errno() ? mg_get_errno() : 1); + mg_sock_set(nc, sock); + return 0; +} + +void mg_socket_if_tcp_send(struct mg_connection *nc, const void *buf, + size_t len) { + mbuf_append(&nc->send_mbuf, buf, len); +} + +void mg_socket_if_udp_send(struct mg_connection *nc, const void *buf, + size_t len) { + mbuf_append(&nc->send_mbuf, buf, len); +} + +void mg_socket_if_recved(struct mg_connection *nc, size_t len) { + (void) nc; + (void) len; +} + +int mg_socket_if_create_conn(struct mg_connection *nc) { + (void) nc; + return 1; +} + +void mg_socket_if_destroy_conn(struct mg_connection *nc) { + if (nc->sock == INVALID_SOCKET) return; + if (!(nc->flags & MG_F_UDP)) { + closesocket(nc->sock); + } else { + /* Only close outgoing UDP sockets or listeners. */ + if (nc->listener == NULL) closesocket(nc->sock); + } + nc->sock = INVALID_SOCKET; +} + +static int mg_accept_conn(struct mg_connection *lc) { + struct mg_connection *nc; + union socket_address sa; + socklen_t sa_len = sizeof(sa); + /* NOTE(lsm): on Windows, sock is always > FD_SETSIZE */ + sock_t sock = accept(lc->sock, &sa.sa, &sa_len); + if (sock == INVALID_SOCKET) { + if (mg_is_error()) DBG(("%p: failed to accept: %d", lc, mg_get_errno())); + return 0; + } + nc = mg_if_accept_new_conn(lc); + if (nc == NULL) { + closesocket(sock); + return 0; + } + DBG(("%p conn from %s:%d", nc, inet_ntoa(sa.sin.sin_addr), + ntohs(sa.sin.sin_port))); + mg_sock_set(nc, sock); +#if MG_ENABLE_SSL + if (lc->flags & MG_F_SSL) { + if (mg_ssl_if_conn_accept(nc, lc) != MG_SSL_OK) mg_close_conn(nc); + } else +#endif + { + mg_if_accept_tcp_cb(nc, &sa, sa_len); + } + return 1; +} + +/* 'sa' must be an initialized address to bind to */ +static sock_t mg_open_listening_socket(union socket_address *sa, int type, + int proto) { + socklen_t sa_len = + (sa->sa.sa_family == AF_INET) ? sizeof(sa->sin) : sizeof(sa->sin6); + sock_t sock = INVALID_SOCKET; +#if !MG_LWIP + int on = 1; +#endif + + if ((sock = socket(sa->sa.sa_family, type, proto)) != INVALID_SOCKET && +#if !MG_LWIP /* LWIP doesn't support either */ +#if defined(_WIN32) && defined(SO_EXCLUSIVEADDRUSE) && !defined(WINCE) + /* "Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE" http://goo.gl/RmrFTm */ + !setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (void *) &on, + sizeof(on)) && +#endif + +#if !defined(_WIN32) || !defined(SO_EXCLUSIVEADDRUSE) + /* + * SO_RESUSEADDR is not enabled on Windows because the semantics of + * SO_REUSEADDR on UNIX and Windows is different. On Windows, + * SO_REUSEADDR allows to bind a socket to a port without error even if + * the port is already open by another program. This is not the behavior + * SO_REUSEADDR was designed for, and leads to hard-to-track failure + * scenarios. Therefore, SO_REUSEADDR was disabled on Windows unless + * SO_EXCLUSIVEADDRUSE is supported and set on a socket. + */ + !setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof(on)) && +#endif +#endif /* !MG_LWIP */ + + !bind(sock, &sa->sa, sa_len) && + (type == SOCK_DGRAM || listen(sock, SOMAXCONN) == 0)) { +#if !MG_LWIP + mg_set_non_blocking_mode(sock); + /* In case port was set to 0, get the real port number */ + (void) getsockname(sock, &sa->sa, &sa_len); +#endif + } else if (sock != INVALID_SOCKET) { + closesocket(sock); + sock = INVALID_SOCKET; + } + + return sock; +} + +static void mg_write_to_socket(struct mg_connection *nc) { + struct mbuf *io = &nc->send_mbuf; + int n = 0; + +#if MG_LWIP + /* With LWIP we don't know if the socket is ready */ + if (io->len == 0) return; +#endif + + assert(io->len > 0); + + if (nc->flags & MG_F_UDP) { + int n = + sendto(nc->sock, io->buf, io->len, 0, &nc->sa.sa, sizeof(nc->sa.sin)); + DBG(("%p %d %d %d %s:%hu", nc, nc->sock, n, mg_get_errno(), + inet_ntoa(nc->sa.sin.sin_addr), ntohs(nc->sa.sin.sin_port))); + mg_if_sent_cb(nc, n); + return; + } + +#if MG_ENABLE_SSL + if (nc->flags & MG_F_SSL) { + if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) { + n = mg_ssl_if_write(nc, io->buf, io->len); + DBG(("%p %d bytes -> %d (SSL)", nc, n, nc->sock)); + if (n < 0) { + if (n != MG_SSL_WANT_READ && n != MG_SSL_WANT_WRITE) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } + return; + } else { + /* Successful SSL operation, clear off SSL wait flags */ + nc->flags &= ~(MG_F_WANT_READ | MG_F_WANT_WRITE); + } + } else { + mg_ssl_begin(nc); + return; + } + } else +#endif + { + n = (int) MG_SEND_FUNC(nc->sock, io->buf, io->len, 0); + DBG(("%p %d bytes -> %d", nc, n, nc->sock)); + } + + mg_if_sent_cb(nc, n); +} + +MG_INTERNAL size_t recv_avail_size(struct mg_connection *conn, size_t max) { + size_t avail; + if (conn->recv_mbuf_limit < conn->recv_mbuf.len) return 0; + avail = conn->recv_mbuf_limit - conn->recv_mbuf.len; + return avail > max ? max : avail; +} + +static void mg_handle_tcp_read(struct mg_connection *conn) { + int n = 0; + char *buf = (char *) MG_MALLOC(MG_TCP_RECV_BUFFER_SIZE); + + if (buf == NULL) { + DBG(("OOM")); + return; + } + +#if MG_ENABLE_SSL + if (conn->flags & MG_F_SSL) { + if (conn->flags & MG_F_SSL_HANDSHAKE_DONE) { + /* SSL library may have more bytes ready to read than we ask to read. + * Therefore, read in a loop until we read everything. Without the loop, + * we skip to the next select() cycle which can just timeout. */ + while ((n = mg_ssl_if_read(conn, buf, MG_TCP_RECV_BUFFER_SIZE)) > 0) { + DBG(("%p %d bytes <- %d (SSL)", conn, n, conn->sock)); + mg_if_recv_tcp_cb(conn, buf, n, 1 /* own */); + buf = NULL; + if (conn->flags & MG_F_CLOSE_IMMEDIATELY) break; + /* buf has been freed, we need a new one. */ + buf = (char *) MG_MALLOC(MG_TCP_RECV_BUFFER_SIZE); + if (buf == NULL) break; + } + MG_FREE(buf); + if (n < 0 && n != MG_SSL_WANT_READ) conn->flags |= MG_F_CLOSE_IMMEDIATELY; + } else { + MG_FREE(buf); + mg_ssl_begin(conn); + return; + } + } else +#endif + { + n = (int) MG_RECV_FUNC(conn->sock, buf, + recv_avail_size(conn, MG_TCP_RECV_BUFFER_SIZE), 0); + DBG(("%p %d bytes (PLAIN) <- %d", conn, n, conn->sock)); + if (n > 0) { + mg_if_recv_tcp_cb(conn, buf, n, 1 /* own */); + } else { + MG_FREE(buf); + } + if (n == 0) { + /* Orderly shutdown of the socket, try flushing output. */ + conn->flags |= MG_F_SEND_AND_CLOSE; + } else if (n < 0 && mg_is_error()) { + conn->flags |= MG_F_CLOSE_IMMEDIATELY; + } + } +} + +static int mg_recvfrom(struct mg_connection *nc, union socket_address *sa, + socklen_t *sa_len, char **buf) { + int n; + *buf = (char *) MG_MALLOC(MG_UDP_RECV_BUFFER_SIZE); + if (*buf == NULL) { + DBG(("Out of memory")); + return -ENOMEM; + } + n = recvfrom(nc->sock, *buf, MG_UDP_RECV_BUFFER_SIZE, 0, &sa->sa, sa_len); + if (n <= 0) { + DBG(("%p recvfrom: %s", nc, strerror(mg_get_errno()))); + MG_FREE(*buf); + } + return n; +} + +static void mg_handle_udp_read(struct mg_connection *nc) { + char *buf = NULL; + union socket_address sa; + socklen_t sa_len = sizeof(sa); + int n = mg_recvfrom(nc, &sa, &sa_len, &buf); + DBG(("%p %d bytes from %s:%d", nc, n, inet_ntoa(nc->sa.sin.sin_addr), + ntohs(nc->sa.sin.sin_port))); + mg_if_recv_udp_cb(nc, buf, n, &sa, sa_len); +} + +#if MG_ENABLE_SSL +static void mg_ssl_begin(struct mg_connection *nc) { + int server_side = (nc->listener != NULL); + enum mg_ssl_if_result res = mg_ssl_if_handshake(nc); + DBG(("%p %d res %d", nc, server_side, res)); + + if (res == MG_SSL_OK) { + nc->flags |= MG_F_SSL_HANDSHAKE_DONE; + nc->flags &= ~(MG_F_WANT_READ | MG_F_WANT_WRITE); + + if (server_side) { + union socket_address sa; + socklen_t sa_len = sizeof(sa); + (void) getpeername(nc->sock, &sa.sa, &sa_len); + mg_if_accept_tcp_cb(nc, &sa, sa_len); + } else { + mg_if_connect_cb(nc, 0); + } + } else if (res != MG_SSL_WANT_READ && res != MG_SSL_WANT_WRITE) { + if (!server_side) { + mg_if_connect_cb(nc, res); + } + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } +} +#endif /* MG_ENABLE_SSL */ + +#define _MG_F_FD_CAN_READ 1 +#define _MG_F_FD_CAN_WRITE 1 << 1 +#define _MG_F_FD_ERROR 1 << 2 + +void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) { + int worth_logging = + fd_flags != 0 || (nc->flags & (MG_F_WANT_READ | MG_F_WANT_WRITE)); + if (worth_logging) { + DBG(("%p fd=%d fd_flags=%d nc_flags=%lu rmbl=%d smbl=%d", nc, nc->sock, + fd_flags, nc->flags, (int) nc->recv_mbuf.len, + (int) nc->send_mbuf.len)); + } + + if (nc->flags & MG_F_CONNECTING) { + if (fd_flags != 0) { + int err = 0; +#if !defined(MG_ESP8266) + if (!(nc->flags & MG_F_UDP)) { + socklen_t len = sizeof(err); + int ret = + getsockopt(nc->sock, SOL_SOCKET, SO_ERROR, (char *) &err, &len); + if (ret != 0) { + err = 1; + } else if (err == EAGAIN || err == EWOULDBLOCK) { + err = 0; + } + } +#else + /* + * On ESP8266 we use blocking connect. + */ + err = nc->err; +#endif +#if MG_ENABLE_SSL + if ((nc->flags & MG_F_SSL) && err == 0) { + mg_ssl_begin(nc); + } else { + mg_if_connect_cb(nc, err); + } +#else + mg_if_connect_cb(nc, err); +#endif + } else if (nc->err != 0) { + mg_if_connect_cb(nc, nc->err); + } + } + + if (fd_flags & _MG_F_FD_CAN_READ) { + if (nc->flags & MG_F_UDP) { + mg_handle_udp_read(nc); + } else { + if (nc->flags & MG_F_LISTENING) { + /* + * We're not looping here, and accepting just one connection at + * a time. The reason is that eCos does not respect non-blocking + * flag on a listening socket and hangs in a loop. + */ + mg_accept_conn(nc); + } else { + mg_handle_tcp_read(nc); + } + } + } + + if (!(nc->flags & MG_F_CLOSE_IMMEDIATELY)) { + if ((fd_flags & _MG_F_FD_CAN_WRITE) && nc->send_mbuf.len > 0) { + mg_write_to_socket(nc); + } + mg_if_poll(nc, (time_t) now); + mg_if_timer(nc, now); + } + + if (worth_logging) { + DBG(("%p after fd=%d nc_flags=%lu rmbl=%d smbl=%d", nc, nc->sock, nc->flags, + (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); + } +} + +#if MG_ENABLE_BROADCAST +static void mg_mgr_handle_ctl_sock(struct mg_mgr *mgr) { + struct ctl_msg ctl_msg; + int len = + (int) MG_RECV_FUNC(mgr->ctl[1], (char *) &ctl_msg, sizeof(ctl_msg), 0); + size_t dummy = MG_SEND_FUNC(mgr->ctl[1], ctl_msg.message, 1, 0); + DBG(("read %d from ctl socket", len)); + (void) dummy; /* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25509 */ + if (len >= (int) sizeof(ctl_msg.callback) && ctl_msg.callback != NULL) { + struct mg_connection *nc; + for (nc = mg_next(mgr, NULL); nc != NULL; nc = mg_next(mgr, nc)) { + ctl_msg.callback(nc, MG_EV_POLL, + ctl_msg.message MG_UD_ARG(nc->user_data)); + } + } +} +#endif + +/* Associate a socket to a connection. */ +void mg_socket_if_sock_set(struct mg_connection *nc, sock_t sock) { + mg_set_non_blocking_mode(sock); + mg_set_close_on_exec(sock); + nc->sock = sock; + DBG(("%p %d", nc, sock)); +} + +void mg_socket_if_init(struct mg_iface *iface) { + (void) iface; + DBG(("%p using select()", iface->mgr)); +#if MG_ENABLE_BROADCAST + mg_socketpair(iface->mgr->ctl, SOCK_DGRAM); +#endif +} + +void mg_socket_if_free(struct mg_iface *iface) { + (void) iface; +} + +void mg_socket_if_add_conn(struct mg_connection *nc) { + (void) nc; +} + +void mg_socket_if_remove_conn(struct mg_connection *nc) { + (void) nc; +} + +void mg_add_to_set(sock_t sock, fd_set *set, sock_t *max_fd) { + if (sock != INVALID_SOCKET +#ifdef __unix__ + && sock < (sock_t) FD_SETSIZE +#endif + ) { + FD_SET(sock, set); + if (*max_fd == INVALID_SOCKET || sock > *max_fd) { + *max_fd = sock; + } + } +} + +time_t mg_socket_if_poll(struct mg_iface *iface, int timeout_ms) { + struct mg_mgr *mgr = iface->mgr; + double now = mg_time(); + double min_timer; + struct mg_connection *nc, *tmp; + struct timeval tv; + fd_set read_set, write_set, err_set; + sock_t max_fd = INVALID_SOCKET; + int num_fds, num_ev, num_timers = 0; +#ifdef __unix__ + int try_dup = 1; +#endif + + FD_ZERO(&read_set); + FD_ZERO(&write_set); + FD_ZERO(&err_set); +#if MG_ENABLE_BROADCAST + mg_add_to_set(mgr->ctl[1], &read_set, &max_fd); +#endif + + /* + * Note: it is ok to have connections with sock == INVALID_SOCKET in the list, + * e.g. timer-only "connections". + */ + min_timer = 0; + for (nc = mgr->active_connections, num_fds = 0; nc != NULL; nc = tmp) { + tmp = nc->next; + + if (nc->sock != INVALID_SOCKET) { + num_fds++; + +#ifdef __unix__ + /* A hack to make sure all our file descriptos fit into FD_SETSIZE. */ + if (nc->sock >= (sock_t) FD_SETSIZE && try_dup) { + int new_sock = dup(nc->sock); + if (new_sock >= 0) { + if (new_sock < (sock_t) FD_SETSIZE) { + closesocket(nc->sock); + DBG(("new sock %d -> %d", nc->sock, new_sock)); + nc->sock = new_sock; + } else { + closesocket(new_sock); + DBG(("new sock is still larger than FD_SETSIZE, disregard")); + try_dup = 0; + } + } else { + try_dup = 0; + } + } +#endif + + if (!(nc->flags & MG_F_WANT_WRITE) && + nc->recv_mbuf.len < nc->recv_mbuf_limit && + (!(nc->flags & MG_F_UDP) || nc->listener == NULL)) { + mg_add_to_set(nc->sock, &read_set, &max_fd); + } + + if (((nc->flags & MG_F_CONNECTING) && !(nc->flags & MG_F_WANT_READ)) || + (nc->send_mbuf.len > 0 && !(nc->flags & MG_F_CONNECTING))) { + mg_add_to_set(nc->sock, &write_set, &max_fd); + mg_add_to_set(nc->sock, &err_set, &max_fd); + } + } + + if (nc->ev_timer_time > 0) { + if (num_timers == 0 || nc->ev_timer_time < min_timer) { + min_timer = nc->ev_timer_time; + } + num_timers++; + } + } + + /* + * If there is a timer to be fired earlier than the requested timeout, + * adjust the timeout. + */ + if (num_timers > 0) { + double timer_timeout_ms = (min_timer - mg_time()) * 1000 + 1 /* rounding */; + if (timer_timeout_ms < timeout_ms) { + timeout_ms = (int) timer_timeout_ms; + } + } + if (timeout_ms < 0) timeout_ms = 0; + + tv.tv_sec = timeout_ms / 1000; + tv.tv_usec = (timeout_ms % 1000) * 1000; + + num_ev = select((int) max_fd + 1, &read_set, &write_set, &err_set, &tv); + now = mg_time(); +#if 0 + DBG(("select @ %ld num_ev=%d of %d, timeout=%d", (long) now, num_ev, num_fds, + timeout_ms)); +#endif + +#if MG_ENABLE_BROADCAST + if (num_ev > 0 && mgr->ctl[1] != INVALID_SOCKET && + FD_ISSET(mgr->ctl[1], &read_set)) { + mg_mgr_handle_ctl_sock(mgr); + } +#endif + + for (nc = mgr->active_connections; nc != NULL; nc = tmp) { + int fd_flags = 0; + if (nc->sock != INVALID_SOCKET) { + if (num_ev > 0) { + fd_flags = (FD_ISSET(nc->sock, &read_set) && + (!(nc->flags & MG_F_UDP) || nc->listener == NULL) + ? _MG_F_FD_CAN_READ + : 0) | + (FD_ISSET(nc->sock, &write_set) ? _MG_F_FD_CAN_WRITE : 0) | + (FD_ISSET(nc->sock, &err_set) ? _MG_F_FD_ERROR : 0); + } +#if MG_LWIP + /* With LWIP socket emulation layer, we don't get write events for UDP */ + if ((nc->flags & MG_F_UDP) && nc->listener == NULL) { + fd_flags |= _MG_F_FD_CAN_WRITE; + } +#endif + } + tmp = nc->next; + mg_mgr_handle_conn(nc, fd_flags, now); + } + + for (nc = mgr->active_connections; nc != NULL; nc = tmp) { + tmp = nc->next; + if ((nc->flags & MG_F_CLOSE_IMMEDIATELY) || + (nc->send_mbuf.len == 0 && (nc->flags & MG_F_SEND_AND_CLOSE))) { + mg_close_conn(nc); + } + } + + return (time_t) now; +} + +#if MG_ENABLE_BROADCAST +MG_INTERNAL void mg_socketpair_close(sock_t *sock) { + while (1) { + if (closesocket(*sock) == -1 && errno == EINTR) continue; + break; + } + *sock = INVALID_SOCKET; +} + +MG_INTERNAL sock_t +mg_socketpair_accept(sock_t sock, union socket_address *sa, socklen_t sa_len) { + sock_t rc; + while (1) { + if ((rc = accept(sock, &sa->sa, &sa_len)) == INVALID_SOCKET && + errno == EINTR) + continue; + break; + } + return rc; +} + +int mg_socketpair(sock_t sp[2], int sock_type) { + union socket_address sa; + sock_t sock; + socklen_t len = sizeof(sa.sin); + int ret = 0; + + sock = sp[0] = sp[1] = INVALID_SOCKET; + + (void) memset(&sa, 0, sizeof(sa)); + sa.sin.sin_family = AF_INET; + sa.sin.sin_port = htons(0); + sa.sin.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */ + + if ((sock = socket(AF_INET, sock_type, 0)) == INVALID_SOCKET) { + } else if (bind(sock, &sa.sa, len) != 0) { + } else if (sock_type == SOCK_STREAM && listen(sock, 1) != 0) { + } else if (getsockname(sock, &sa.sa, &len) != 0) { + } else if ((sp[0] = socket(AF_INET, sock_type, 0)) == INVALID_SOCKET) { + } else if (connect(sp[0], &sa.sa, len) != 0) { + } else if (sock_type == SOCK_DGRAM && + (getsockname(sp[0], &sa.sa, &len) != 0 || + connect(sock, &sa.sa, len) != 0)) { + } else if ((sp[1] = (sock_type == SOCK_DGRAM ? sock : mg_socketpair_accept( + sock, &sa, len))) == + INVALID_SOCKET) { + } else { + mg_set_close_on_exec(sp[0]); + mg_set_close_on_exec(sp[1]); + if (sock_type == SOCK_STREAM) mg_socketpair_close(&sock); + ret = 1; + } + + if (!ret) { + if (sp[0] != INVALID_SOCKET) mg_socketpair_close(&sp[0]); + if (sp[1] != INVALID_SOCKET) mg_socketpair_close(&sp[1]); + if (sock != INVALID_SOCKET) mg_socketpair_close(&sock); + } + + return ret; +} +#endif /* MG_ENABLE_BROADCAST */ + +static void mg_sock_get_addr(sock_t sock, int remote, + union socket_address *sa) { + socklen_t slen = sizeof(*sa); + memset(sa, 0, slen); + if (remote) { + getpeername(sock, &sa->sa, &slen); + } else { + getsockname(sock, &sa->sa, &slen); + } +} + +void mg_sock_to_str(sock_t sock, char *buf, size_t len, int flags) { + union socket_address sa; + mg_sock_get_addr(sock, flags & MG_SOCK_STRINGIFY_REMOTE, &sa); + mg_sock_addr_to_str(&sa, buf, len, flags); +} + +void mg_socket_if_get_conn_addr(struct mg_connection *nc, int remote, + union socket_address *sa) { + if ((nc->flags & MG_F_UDP) && remote) { + memcpy(sa, &nc->sa, sizeof(*sa)); + return; + } + mg_sock_get_addr(nc->sock, remote, sa); +} + +/* clang-format off */ +#define MG_SOCKET_IFACE_VTABLE \ + { \ + mg_socket_if_init, \ + mg_socket_if_free, \ + mg_socket_if_add_conn, \ + mg_socket_if_remove_conn, \ + mg_socket_if_poll, \ + mg_socket_if_listen_tcp, \ + mg_socket_if_listen_udp, \ + mg_socket_if_connect_tcp, \ + mg_socket_if_connect_udp, \ + mg_socket_if_tcp_send, \ + mg_socket_if_udp_send, \ + mg_socket_if_recved, \ + mg_socket_if_create_conn, \ + mg_socket_if_destroy_conn, \ + mg_socket_if_sock_set, \ + mg_socket_if_get_conn_addr, \ + } +/* clang-format on */ + +const struct mg_iface_vtable mg_socket_iface_vtable = MG_SOCKET_IFACE_VTABLE; +#if MG_NET_IF == MG_NET_IF_SOCKET +const struct mg_iface_vtable mg_default_iface_vtable = MG_SOCKET_IFACE_VTABLE; +#endif + +#endif /* MG_ENABLE_NET_IF_SOCKET */ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_net_if_socks.c" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_SOCKS + +struct socksdata { + char *proxy_addr; /* HOST:PORT of the socks5 proxy server */ + struct mg_connection *s; /* Respective connection to the server */ + struct mg_connection *c; /* Connection to the client */ + struct mbuf tmp; /* Temporary buffer for sent data */ +}; + +static void socks_if_disband(struct socksdata *d) { + LOG(LL_DEBUG, ("disbanding proxy %p %p", d->c, d->s)); + if (d->c) d->c->flags |= MG_F_SEND_AND_CLOSE; + if (d->s) d->s->flags |= MG_F_SEND_AND_CLOSE; + d->c = d->s = NULL; +} + +static void socks_if_handler(struct mg_connection *c, int ev, void *ev_data) { + struct socksdata *d = (struct socksdata *) c->user_data; + if (ev == MG_EV_CONNECT) { + int res = *(int *) ev_data; + if (res == 0) { + /* Send handshake to the proxy server */ + unsigned char buf[] = {MG_SOCKS_VERSION, 1, MG_SOCKS_HANDSHAKE_NOAUTH}; + mg_send(d->s, buf, sizeof(buf)); + LOG(LL_DEBUG, ("Sent handshake to %s", d->proxy_addr)); + } else { + LOG(LL_ERROR, ("Cannot connect to %s: %d", d->proxy_addr, res)); + d->c->flags |= MG_F_CLOSE_IMMEDIATELY; + } + } else if (ev == MG_EV_CLOSE) { + socks_if_disband(d); + } else if (ev == MG_EV_RECV) { + /* Handle handshake reply */ + if (!(c->flags & MG_SOCKS_HANDSHAKE_DONE)) { + /* TODO(lsm): process IPv6 too */ + unsigned char buf[10] = {MG_SOCKS_VERSION, MG_SOCKS_CMD_CONNECT, 0, + MG_SOCKS_ADDR_IPV4}; + if (c->recv_mbuf.len < 2) return; + if ((unsigned char) c->recv_mbuf.buf[1] == MG_SOCKS_HANDSHAKE_FAILURE) { + LOG(LL_ERROR, ("Server kicked us out")); + socks_if_disband(d); + return; + } + mbuf_remove(&c->recv_mbuf, 2); + c->flags |= MG_SOCKS_HANDSHAKE_DONE; + + /* Send connect request */ + memcpy(buf + 4, &d->c->sa.sin.sin_addr, 4); + memcpy(buf + 8, &d->c->sa.sin.sin_port, 2); + mg_send(c, buf, sizeof(buf)); + } + /* Process connect request */ + if ((c->flags & MG_SOCKS_HANDSHAKE_DONE) && + !(c->flags & MG_SOCKS_CONNECT_DONE)) { + if (c->recv_mbuf.len < 10) return; + if (c->recv_mbuf.buf[1] != MG_SOCKS_SUCCESS) { + LOG(LL_ERROR, ("Socks connection error: %d", c->recv_mbuf.buf[1])); + socks_if_disband(d); + return; + } + mbuf_remove(&c->recv_mbuf, 10); + c->flags |= MG_SOCKS_CONNECT_DONE; + /* Connected. Move sent data from client, if any, to server */ + if (d->s && d->c) { + mbuf_append(&d->s->send_mbuf, d->tmp.buf, d->tmp.len); + mbuf_free(&d->tmp); + } + } + /* All flags are set, we're in relay mode */ + if ((c->flags & MG_SOCKS_CONNECT_DONE) && d->c && d->s) { + mbuf_append(&d->c->recv_mbuf, d->s->recv_mbuf.buf, d->s->recv_mbuf.len); + mbuf_remove(&d->s->recv_mbuf, d->s->recv_mbuf.len); + } + } +} + +static void mg_socks_if_connect_tcp(struct mg_connection *c, + const union socket_address *sa) { + struct socksdata *d = (struct socksdata *) c->iface->data; + d->c = c; + d->s = mg_connect(c->mgr, d->proxy_addr, socks_if_handler); + d->s->user_data = d; + LOG(LL_DEBUG, ("%p %s", c, d->proxy_addr)); + (void) sa; +} + +static void mg_socks_if_connect_udp(struct mg_connection *c) { + (void) c; +} + +static int mg_socks_if_listen_tcp(struct mg_connection *c, + union socket_address *sa) { + (void) c; + (void) sa; + return 0; +} + +static int mg_socks_if_listen_udp(struct mg_connection *c, + union socket_address *sa) { + (void) c; + (void) sa; + return -1; +} + +static void mg_socks_if_tcp_send(struct mg_connection *c, const void *buf, + size_t len) { + struct socksdata *d = (struct socksdata *) c->iface->data; + LOG(LL_DEBUG, ("%p -> %p %d %d", c, buf, (int) len, (int) c->send_mbuf.len)); + if (d && d->s && d->s->flags & MG_SOCKS_CONNECT_DONE) { + mbuf_append(&d->s->send_mbuf, d->tmp.buf, d->tmp.len); + mbuf_append(&d->s->send_mbuf, buf, len); + mbuf_free(&d->tmp); + } else { + mbuf_append(&d->tmp, buf, len); + } +} + +static void mg_socks_if_udp_send(struct mg_connection *c, const void *buf, + size_t len) { + (void) c; + (void) buf; + (void) len; +} + +static void mg_socks_if_recved(struct mg_connection *c, size_t len) { + (void) c; + (void) len; +} + +static int mg_socks_if_create_conn(struct mg_connection *c) { + (void) c; + return 1; +} + +static void mg_socks_if_destroy_conn(struct mg_connection *c) { + c->iface->vtable->free(c->iface); + MG_FREE(c->iface); + c->iface = NULL; + LOG(LL_DEBUG, ("%p", c)); +} + +static void mg_socks_if_sock_set(struct mg_connection *c, sock_t sock) { + (void) c; + (void) sock; +} + +static void mg_socks_if_init(struct mg_iface *iface) { + (void) iface; +} + +static void mg_socks_if_free(struct mg_iface *iface) { + struct socksdata *d = (struct socksdata *) iface->data; + LOG(LL_DEBUG, ("%p", iface)); + if (d != NULL) { + socks_if_disband(d); + mbuf_free(&d->tmp); + MG_FREE(d->proxy_addr); + MG_FREE(d); + iface->data = NULL; + } +} + +static void mg_socks_if_add_conn(struct mg_connection *c) { + c->sock = INVALID_SOCKET; +} + +static void mg_socks_if_remove_conn(struct mg_connection *c) { + (void) c; +} + +static time_t mg_socks_if_poll(struct mg_iface *iface, int timeout_ms) { + LOG(LL_DEBUG, ("%p", iface)); + (void) iface; + (void) timeout_ms; + return (time_t) cs_time(); +} + +static void mg_socks_if_get_conn_addr(struct mg_connection *c, int remote, + union socket_address *sa) { + LOG(LL_DEBUG, ("%p", c)); + (void) c; + (void) remote; + (void) sa; +} + +const struct mg_iface_vtable mg_socks_iface_vtable = { + mg_socks_if_init, mg_socks_if_free, + mg_socks_if_add_conn, mg_socks_if_remove_conn, + mg_socks_if_poll, mg_socks_if_listen_tcp, + mg_socks_if_listen_udp, mg_socks_if_connect_tcp, + mg_socks_if_connect_udp, mg_socks_if_tcp_send, + mg_socks_if_udp_send, mg_socks_if_recved, + mg_socks_if_create_conn, mg_socks_if_destroy_conn, + mg_socks_if_sock_set, mg_socks_if_get_conn_addr, +}; + +struct mg_iface *mg_socks_mk_iface(struct mg_mgr *mgr, const char *proxy_addr) { + struct mg_iface *iface = mg_if_create_iface(&mg_socks_iface_vtable, mgr); + iface->data = MG_CALLOC(1, sizeof(struct socksdata)); + ((struct socksdata *) iface->data)->proxy_addr = strdup(proxy_addr); + return iface; +} + +#endif +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_ssl_if_openssl.c" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_OPENSSL + +#ifdef __APPLE__ +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + +#include + +struct mg_ssl_if_ctx { + SSL *ssl; + SSL_CTX *ssl_ctx; + struct mbuf psk; + size_t identity_len; +}; + +void mg_ssl_if_init() { + SSL_library_init(); +} + +enum mg_ssl_if_result mg_ssl_if_conn_accept(struct mg_connection *nc, + struct mg_connection *lc) { + struct mg_ssl_if_ctx *ctx = + (struct mg_ssl_if_ctx *) MG_CALLOC(1, sizeof(*ctx)); + struct mg_ssl_if_ctx *lc_ctx = (struct mg_ssl_if_ctx *) lc->ssl_if_data; + nc->ssl_if_data = ctx; + if (ctx == NULL || lc_ctx == NULL) return MG_SSL_ERROR; + ctx->ssl_ctx = lc_ctx->ssl_ctx; + if ((ctx->ssl = SSL_new(ctx->ssl_ctx)) == NULL) { + return MG_SSL_ERROR; + } + return MG_SSL_OK; +} + +static enum mg_ssl_if_result mg_use_cert(SSL_CTX *ctx, const char *cert, + const char *key, const char **err_msg); +static enum mg_ssl_if_result mg_use_ca_cert(SSL_CTX *ctx, const char *cert); +static enum mg_ssl_if_result mg_set_cipher_list(SSL_CTX *ctx, const char *cl); +static enum mg_ssl_if_result mg_ssl_if_ossl_set_psk(struct mg_ssl_if_ctx *ctx, + const char *identity, + const char *key_str); + +enum mg_ssl_if_result mg_ssl_if_conn_init( + struct mg_connection *nc, const struct mg_ssl_if_conn_params *params, + const char **err_msg) { + struct mg_ssl_if_ctx *ctx = + (struct mg_ssl_if_ctx *) MG_CALLOC(1, sizeof(*ctx)); + DBG(("%p %s,%s,%s", nc, (params->cert ? params->cert : ""), + (params->key ? params->key : ""), + (params->ca_cert ? params->ca_cert : ""))); + if (ctx == NULL) { + MG_SET_PTRPTR(err_msg, "Out of memory"); + return MG_SSL_ERROR; + } + nc->ssl_if_data = ctx; + if (nc->flags & MG_F_LISTENING) { + ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method()); + } else { + ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method()); + } + if (ctx->ssl_ctx == NULL) { + MG_SET_PTRPTR(err_msg, "Failed to create SSL context"); + return MG_SSL_ERROR; + } + +#ifndef KR_VERSION + /* Disable deprecated protocols. */ + SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv2); + SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv3); + SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1); +#ifdef MG_SSL_OPENSSL_NO_COMPRESSION + SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_COMPRESSION); +#endif +#ifdef MG_SSL_OPENSSL_CIPHER_SERVER_PREFERENCE + SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); +#endif +#else +/* Krypton only supports TLSv1.2 anyway. */ +#endif + + if (params->cert != NULL && + mg_use_cert(ctx->ssl_ctx, params->cert, params->key, err_msg) != + MG_SSL_OK) { + return MG_SSL_ERROR; + } + + if (params->ca_cert != NULL && + mg_use_ca_cert(ctx->ssl_ctx, params->ca_cert) != MG_SSL_OK) { + MG_SET_PTRPTR(err_msg, "Invalid SSL CA cert"); + return MG_SSL_ERROR; + } + + if (params->server_name != NULL) { +#ifdef KR_VERSION + SSL_CTX_kr_set_verify_name(ctx->ssl_ctx, params->server_name); +#else +/* TODO(rojer): Implement server name verification on OpenSSL. */ +#endif + } + + if (mg_set_cipher_list(ctx->ssl_ctx, params->cipher_suites) != MG_SSL_OK) { + MG_SET_PTRPTR(err_msg, "Invalid cipher suite list"); + return MG_SSL_ERROR; + } + + mbuf_init(&ctx->psk, 0); + if (mg_ssl_if_ossl_set_psk(ctx, params->psk_identity, params->psk_key) != + MG_SSL_OK) { + MG_SET_PTRPTR(err_msg, "Invalid PSK settings"); + return MG_SSL_ERROR; + } + + if (!(nc->flags & MG_F_LISTENING) && + (ctx->ssl = SSL_new(ctx->ssl_ctx)) == NULL) { + MG_SET_PTRPTR(err_msg, "Failed to create SSL session"); + return MG_SSL_ERROR; + } + + nc->flags |= MG_F_SSL; + + return MG_SSL_OK; +} + +static enum mg_ssl_if_result mg_ssl_if_ssl_err(struct mg_connection *nc, + int res) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + int err = SSL_get_error(ctx->ssl, res); + if (err == SSL_ERROR_WANT_READ) return MG_SSL_WANT_READ; + if (err == SSL_ERROR_WANT_WRITE) return MG_SSL_WANT_WRITE; + DBG(("%p %p SSL error: %d %d", nc, ctx->ssl_ctx, res, err)); + nc->err = err; + return MG_SSL_ERROR; +} + +enum mg_ssl_if_result mg_ssl_if_handshake(struct mg_connection *nc) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + int server_side = (nc->listener != NULL); + int res; + /* If descriptor is not yet set, do it now. */ + if (SSL_get_fd(ctx->ssl) < 0) { + if (SSL_set_fd(ctx->ssl, nc->sock) != 1) return MG_SSL_ERROR; + } + res = server_side ? SSL_accept(ctx->ssl) : SSL_connect(ctx->ssl); + if (res != 1) return mg_ssl_if_ssl_err(nc, res); + return MG_SSL_OK; +} + +int mg_ssl_if_read(struct mg_connection *nc, void *buf, size_t buf_size) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + int n = SSL_read(ctx->ssl, buf, buf_size); + DBG(("%p %d -> %d", nc, (int) buf_size, n)); + if (n < 0) return mg_ssl_if_ssl_err(nc, n); + if (n == 0) nc->flags |= MG_F_CLOSE_IMMEDIATELY; + return n; +} + +int mg_ssl_if_write(struct mg_connection *nc, const void *data, size_t len) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + int n = SSL_write(ctx->ssl, data, len); + DBG(("%p %d -> %d", nc, (int) len, n)); + if (n <= 0) return mg_ssl_if_ssl_err(nc, n); + return n; +} + +void mg_ssl_if_conn_close_notify(struct mg_connection *nc) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + if (ctx == NULL) return; + SSL_shutdown(ctx->ssl); +} + +void mg_ssl_if_conn_free(struct mg_connection *nc) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + if (ctx == NULL) return; + nc->ssl_if_data = NULL; + if (ctx->ssl != NULL) SSL_free(ctx->ssl); + if (ctx->ssl_ctx != NULL && nc->listener == NULL) SSL_CTX_free(ctx->ssl_ctx); + mbuf_free(&ctx->psk); + memset(ctx, 0, sizeof(*ctx)); + MG_FREE(ctx); +} + +/* + * Cipher suite options used for TLS negotiation. + * https://wiki.mozilla.org/Security/Server_Side_TLS#Recommended_configurations + */ +static const char mg_s_cipher_list[] = +#if defined(MG_SSL_CRYPTO_MODERN) + "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:" + "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:" + "DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:" + "ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:" + "ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:" + "ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:" + "DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:" + "DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:" + "!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK" +#elif defined(MG_SSL_CRYPTO_OLD) + "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:" + "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:" + "DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:" + "ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:" + "ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:" + "ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:" + "DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:" + "DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:" + "ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:" + "AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:DES-CBC3-SHA:" + "HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:" + "!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA" +#else /* Default - intermediate. */ + "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:" + "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:" + "DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:" + "ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:" + "ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:" + "ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:" + "DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:" + "DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:" + "AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:" + "DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:" + "!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA" +#endif + ; + +/* + * Default DH params for PFS cipher negotiation. This is a 2048-bit group. + * Will be used if none are provided by the user in the certificate file. + */ +#if !MG_DISABLE_PFS && !defined(KR_VERSION) +static const char mg_s_default_dh_params[] = + "\ +-----BEGIN DH PARAMETERS-----\n\ +MIIBCAKCAQEAlvbgD/qh9znWIlGFcV0zdltD7rq8FeShIqIhkQ0C7hYFThrBvF2E\n\ +Z9bmgaP+sfQwGpVlv9mtaWjvERbu6mEG7JTkgmVUJrUt/wiRzwTaCXBqZkdUO8Tq\n\ ++E6VOEQAilstG90ikN1Tfo+K6+X68XkRUIlgawBTKuvKVwBhuvlqTGerOtnXWnrt\n\ +ym//hd3cd5PBYGBix0i7oR4xdghvfR2WLVu0LgdThTBb6XP7gLd19cQ1JuBtAajZ\n\ +wMuPn7qlUkEFDIkAZy59/Hue/H2Q2vU/JsvVhHWCQBL4F1ofEAt50il6ZxR1QfFK\n\ +9VGKDC4oOgm9DlxwwBoC2FjqmvQlqVV3kwIBAg==\n\ +-----END DH PARAMETERS-----\n"; +#endif + +static enum mg_ssl_if_result mg_use_ca_cert(SSL_CTX *ctx, const char *cert) { + if (cert == NULL || strcmp(cert, "*") == 0) { + return MG_SSL_OK; + } + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, 0); + return SSL_CTX_load_verify_locations(ctx, cert, NULL) == 1 ? MG_SSL_OK + : MG_SSL_ERROR; +} + +static enum mg_ssl_if_result mg_use_cert(SSL_CTX *ctx, const char *cert, + const char *key, + const char **err_msg) { + if (key == NULL) key = cert; + if (cert == NULL || cert[0] == '\0' || key == NULL || key[0] == '\0') { + return MG_SSL_OK; + } else if (SSL_CTX_use_certificate_file(ctx, cert, 1) == 0) { + MG_SET_PTRPTR(err_msg, "Invalid SSL cert"); + return MG_SSL_ERROR; + } else if (SSL_CTX_use_PrivateKey_file(ctx, key, 1) == 0) { + MG_SET_PTRPTR(err_msg, "Invalid SSL key"); + return MG_SSL_ERROR; + } else if (SSL_CTX_use_certificate_chain_file(ctx, cert) == 0) { + MG_SET_PTRPTR(err_msg, "Invalid CA bundle"); + return MG_SSL_ERROR; + } else { + SSL_CTX_set_mode(ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); +#if !MG_DISABLE_PFS && !defined(KR_VERSION) + BIO *bio = NULL; + DH *dh = NULL; + + /* Try to read DH parameters from the cert/key file. */ + bio = BIO_new_file(cert, "r"); + if (bio != NULL) { + dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); + BIO_free(bio); + } + /* + * If there are no DH params in the file, fall back to hard-coded ones. + * Not ideal, but better than nothing. + */ + if (dh == NULL) { + bio = BIO_new_mem_buf((void *) mg_s_default_dh_params, -1); + dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); + BIO_free(bio); + } + if (dh != NULL) { + SSL_CTX_set_tmp_dh(ctx, dh); + SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE); + DH_free(dh); + } +#if OPENSSL_VERSION_NUMBER > 0x10002000L + SSL_CTX_set_ecdh_auto(ctx, 1); +#endif +#endif + } + return MG_SSL_OK; +} + +static enum mg_ssl_if_result mg_set_cipher_list(SSL_CTX *ctx, const char *cl) { + return (SSL_CTX_set_cipher_list(ctx, cl ? cl : mg_s_cipher_list) == 1 + ? MG_SSL_OK + : MG_SSL_ERROR); +} + +#ifndef KR_VERSION +static unsigned int mg_ssl_if_ossl_psk_cb(SSL *ssl, const char *hint, + char *identity, + unsigned int max_identity_len, + unsigned char *psk, + unsigned int max_psk_len) { + struct mg_ssl_if_ctx *ctx = + (struct mg_ssl_if_ctx *) SSL_CTX_get_app_data(SSL_get_SSL_CTX(ssl)); + size_t key_len = ctx->psk.len - ctx->identity_len - 1; + DBG(("hint: '%s'", (hint ? hint : ""))); + if (ctx->identity_len + 1 > max_identity_len) { + DBG(("identity too long")); + return 0; + } + if (key_len > max_psk_len) { + DBG(("key too long")); + return 0; + } + memcpy(identity, ctx->psk.buf, ctx->identity_len + 1); + memcpy(psk, ctx->psk.buf + ctx->identity_len + 1, key_len); + (void) ssl; + return key_len; +} + +static enum mg_ssl_if_result mg_ssl_if_ossl_set_psk(struct mg_ssl_if_ctx *ctx, + const char *identity, + const char *key_str) { + unsigned char key[32]; + size_t key_len; + size_t i = 0; + if (identity == NULL && key_str == NULL) return MG_SSL_OK; + if (identity == NULL || key_str == NULL) return MG_SSL_ERROR; + key_len = strlen(key_str); + if (key_len != 32 && key_len != 64) return MG_SSL_ERROR; + memset(key, 0, sizeof(key)); + key_len = 0; + for (i = 0; key_str[i] != '\0'; i++) { + unsigned char c; + char hc = tolower((int) key_str[i]); + if (hc >= '0' && hc <= '9') { + c = hc - '0'; + } else if (hc >= 'a' && hc <= 'f') { + c = hc - 'a' + 0xa; + } else { + return MG_SSL_ERROR; + } + key_len = i / 2; + key[key_len] <<= 4; + key[key_len] |= c; + } + key_len++; + DBG(("identity = '%s', key = (%u)", identity, (unsigned int) key_len)); + ctx->identity_len = strlen(identity); + mbuf_append(&ctx->psk, identity, ctx->identity_len + 1); + mbuf_append(&ctx->psk, key, key_len); + SSL_CTX_set_psk_client_callback(ctx->ssl_ctx, mg_ssl_if_ossl_psk_cb); + SSL_CTX_set_app_data(ctx->ssl_ctx, ctx); + return MG_SSL_OK; +} +#else +static enum mg_ssl_if_result mg_ssl_if_ossl_set_psk(struct mg_ssl_if_ctx *ctx, + const char *identity, + const char *key_str) { + (void) ctx; + (void) identity; + (void) key_str; + /* Krypton does not support PSK. */ + return MG_SSL_ERROR; +} +#endif /* defined(KR_VERSION) */ + +const char *mg_set_ssl(struct mg_connection *nc, const char *cert, + const char *ca_cert) { + const char *err_msg = NULL; + struct mg_ssl_if_conn_params params; + memset(¶ms, 0, sizeof(params)); + params.cert = cert; + params.ca_cert = ca_cert; + if (mg_ssl_if_conn_init(nc, ¶ms, &err_msg) != MG_SSL_OK) { + return err_msg; + } + return NULL; +} + +#endif /* MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_OPENSSL */ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_ssl_if_mbedtls.c" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_MBEDTLS + +#include +#include +#include +#include +#include + +static void mg_ssl_mbed_log(void *ctx, int level, const char *file, int line, + const char *str) { + enum cs_log_level cs_level; + switch (level) { + case 1: + cs_level = LL_ERROR; + break; + case 2: + case 3: + cs_level = LL_DEBUG; + break; + default: + cs_level = LL_VERBOSE_DEBUG; + } + /* mbedTLS passes strings with \n at the end, strip it. */ + LOG(cs_level, ("%p %.*s", ctx, (int) (strlen(str) - 1), str)); + (void) file; + (void) line; +} + +struct mg_ssl_if_ctx { + mbedtls_ssl_config *conf; + mbedtls_ssl_context *ssl; + mbedtls_x509_crt *cert; + mbedtls_pk_context *key; + mbedtls_x509_crt *ca_cert; + struct mbuf cipher_suites; +}; + +/* Must be provided by the platform. ctx is struct mg_connection. */ +extern int mg_ssl_if_mbed_random(void *ctx, unsigned char *buf, size_t len); + +void mg_ssl_if_init() { +} + +enum mg_ssl_if_result mg_ssl_if_conn_accept(struct mg_connection *nc, + struct mg_connection *lc) { + struct mg_ssl_if_ctx *ctx = + (struct mg_ssl_if_ctx *) MG_CALLOC(1, sizeof(*ctx)); + struct mg_ssl_if_ctx *lc_ctx = (struct mg_ssl_if_ctx *) lc->ssl_if_data; + nc->ssl_if_data = ctx; + if (ctx == NULL || lc_ctx == NULL) return MG_SSL_ERROR; + ctx->ssl = (mbedtls_ssl_context *) MG_CALLOC(1, sizeof(*ctx->ssl)); + if (mbedtls_ssl_setup(ctx->ssl, lc_ctx->conf) != 0) { + return MG_SSL_ERROR; + } + return MG_SSL_OK; +} + +static enum mg_ssl_if_result mg_use_cert(struct mg_ssl_if_ctx *ctx, + const char *cert, const char *key, + const char **err_msg); +static enum mg_ssl_if_result mg_use_ca_cert(struct mg_ssl_if_ctx *ctx, + const char *cert); +static enum mg_ssl_if_result mg_set_cipher_list(struct mg_ssl_if_ctx *ctx, + const char *ciphers); +static enum mg_ssl_if_result mg_ssl_if_mbed_set_psk(struct mg_ssl_if_ctx *ctx, + const char *identity, + const char *key); + +enum mg_ssl_if_result mg_ssl_if_conn_init( + struct mg_connection *nc, const struct mg_ssl_if_conn_params *params, + const char **err_msg) { + struct mg_ssl_if_ctx *ctx = + (struct mg_ssl_if_ctx *) MG_CALLOC(1, sizeof(*ctx)); + DBG(("%p %s,%s,%s", nc, (params->cert ? params->cert : ""), + (params->key ? params->key : ""), + (params->ca_cert ? params->ca_cert : ""))); + + if (ctx == NULL) { + MG_SET_PTRPTR(err_msg, "Out of memory"); + return MG_SSL_ERROR; + } + nc->ssl_if_data = ctx; + ctx->conf = (mbedtls_ssl_config *) MG_CALLOC(1, sizeof(*ctx->conf)); + mbuf_init(&ctx->cipher_suites, 0); + mbedtls_ssl_config_init(ctx->conf); + mbedtls_ssl_conf_dbg(ctx->conf, mg_ssl_mbed_log, nc); + if (mbedtls_ssl_config_defaults( + ctx->conf, (nc->flags & MG_F_LISTENING ? MBEDTLS_SSL_IS_SERVER + : MBEDTLS_SSL_IS_CLIENT), + MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT) != 0) { + MG_SET_PTRPTR(err_msg, "Failed to init SSL config"); + return MG_SSL_ERROR; + } + + /* TLS 1.2 and up */ + mbedtls_ssl_conf_min_version(ctx->conf, MBEDTLS_SSL_MAJOR_VERSION_3, + MBEDTLS_SSL_MINOR_VERSION_3); + mbedtls_ssl_conf_rng(ctx->conf, mg_ssl_if_mbed_random, nc); + + if (params->cert != NULL && + mg_use_cert(ctx, params->cert, params->key, err_msg) != MG_SSL_OK) { + return MG_SSL_ERROR; + } + + if (params->ca_cert != NULL && + mg_use_ca_cert(ctx, params->ca_cert) != MG_SSL_OK) { + MG_SET_PTRPTR(err_msg, "Invalid SSL CA cert"); + return MG_SSL_ERROR; + } + + if (mg_set_cipher_list(ctx, params->cipher_suites) != MG_SSL_OK) { + MG_SET_PTRPTR(err_msg, "Invalid cipher suite list"); + return MG_SSL_ERROR; + } + + if (mg_ssl_if_mbed_set_psk(ctx, params->psk_identity, params->psk_key) != + MG_SSL_OK) { + MG_SET_PTRPTR(err_msg, "Invalid PSK settings"); + return MG_SSL_ERROR; + } + + if (!(nc->flags & MG_F_LISTENING)) { + ctx->ssl = (mbedtls_ssl_context *) MG_CALLOC(1, sizeof(*ctx->ssl)); + mbedtls_ssl_init(ctx->ssl); + if (mbedtls_ssl_setup(ctx->ssl, ctx->conf) != 0) { + MG_SET_PTRPTR(err_msg, "Failed to create SSL session"); + return MG_SSL_ERROR; + } + if (params->server_name != NULL && + mbedtls_ssl_set_hostname(ctx->ssl, params->server_name) != 0) { + return MG_SSL_ERROR; + } + } + +#ifdef MG_SSL_IF_MBEDTLS_MAX_FRAG_LEN + if (mbedtls_ssl_conf_max_frag_len(ctx->conf, +#if MG_SSL_IF_MBEDTLS_MAX_FRAG_LEN == 512 + MBEDTLS_SSL_MAX_FRAG_LEN_512 +#elif MG_SSL_IF_MBEDTLS_MAX_FRAG_LEN == 1024 + MBEDTLS_SSL_MAX_FRAG_LEN_1024 +#elif MG_SSL_IF_MBEDTLS_MAX_FRAG_LEN == 2048 + MBEDTLS_SSL_MAX_FRAG_LEN_2048 +#elif MG_SSL_IF_MBEDTLS_MAX_FRAG_LEN == 4096 + MBEDTLS_SSL_MAX_FRAG_LEN_4096 +#else +#error Invalid MG_SSL_IF_MBEDTLS_MAX_FRAG_LEN +#endif + ) != 0) { + return MG_SSL_ERROR; + } +#endif + + nc->flags |= MG_F_SSL; + + return MG_SSL_OK; +} + +#if MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL +int ssl_socket_send(void *ctx, const unsigned char *buf, size_t len); +int ssl_socket_recv(void *ctx, unsigned char *buf, size_t len); +#else +static int ssl_socket_send(void *ctx, const unsigned char *buf, size_t len) { + struct mg_connection *nc = (struct mg_connection *) ctx; + int n = (int) MG_SEND_FUNC(nc->sock, buf, len, 0); + LOG(LL_DEBUG, ("%p %d -> %d", nc, (int) len, n)); + if (n >= 0) return n; + n = mg_get_errno(); + return ((n == EAGAIN || n == EINPROGRESS) ? MBEDTLS_ERR_SSL_WANT_WRITE : -1); +} + +static int ssl_socket_recv(void *ctx, unsigned char *buf, size_t len) { + struct mg_connection *nc = (struct mg_connection *) ctx; + int n = (int) MG_RECV_FUNC(nc->sock, buf, len, 0); + LOG(LL_DEBUG, ("%p %d <- %d", nc, (int) len, n)); + if (n >= 0) return n; + n = mg_get_errno(); + return ((n == EAGAIN || n == EINPROGRESS) ? MBEDTLS_ERR_SSL_WANT_READ : -1); +} +#endif + +static enum mg_ssl_if_result mg_ssl_if_mbed_err(struct mg_connection *nc, + int ret) { + if (ret == MBEDTLS_ERR_SSL_WANT_READ) return MG_SSL_WANT_READ; + if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) return MG_SSL_WANT_WRITE; + if (ret != + MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { /* CLOSE_NOTIFY = Normal shutdown */ + LOG(LL_ERROR, ("%p SSL error: %d", nc, ret)); + } + nc->err = ret; + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + return MG_SSL_ERROR; +} + +static void mg_ssl_if_mbed_free_certs_and_keys(struct mg_ssl_if_ctx *ctx) { + if (ctx->cert != NULL) { + mbedtls_x509_crt_free(ctx->cert); + MG_FREE(ctx->cert); + ctx->cert = NULL; + mbedtls_pk_free(ctx->key); + MG_FREE(ctx->key); + ctx->key = NULL; + } + if (ctx->ca_cert != NULL) { + mbedtls_ssl_conf_ca_chain(ctx->conf, NULL, NULL); +#ifdef MBEDTLS_X509_CA_CHAIN_ON_DISK + if (ctx->ca_cert->ca_chain_file != NULL) { + MG_FREE((void *) ctx->ca_cert->ca_chain_file); + ctx->ca_cert->ca_chain_file = NULL; + } +#endif + mbedtls_x509_crt_free(ctx->ca_cert); + MG_FREE(ctx->ca_cert); + ctx->ca_cert = NULL; + } +} + +enum mg_ssl_if_result mg_ssl_if_handshake(struct mg_connection *nc) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + int err; + /* If bio is not yet set, do it now. */ + if (ctx->ssl->p_bio == NULL) { + mbedtls_ssl_set_bio(ctx->ssl, nc, ssl_socket_send, ssl_socket_recv, NULL); + } + err = mbedtls_ssl_handshake(ctx->ssl); + if (err != 0) return mg_ssl_if_mbed_err(nc, err); +#ifdef MG_SSL_IF_MBEDTLS_FREE_CERTS + /* + * Free the peer certificate, we don't need it after handshake. + * Note that this effectively disables renegotiation. + */ + mbedtls_x509_crt_free(ctx->ssl->session->peer_cert); + mbedtls_free(ctx->ssl->session->peer_cert); + ctx->ssl->session->peer_cert = NULL; + /* On a client connection we can also free our own and CA certs. */ + if (nc->listener == NULL) { + if (ctx->conf->key_cert != NULL) { + /* Note that this assumes one key_cert entry, which matches our init. */ + MG_FREE(ctx->conf->key_cert); + ctx->conf->key_cert = NULL; + } + mbedtls_ssl_conf_ca_chain(ctx->conf, NULL, NULL); + mg_ssl_if_mbed_free_certs_and_keys(ctx); + } +#endif + return MG_SSL_OK; +} + +int mg_ssl_if_read(struct mg_connection *nc, void *buf, size_t buf_size) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + int n = mbedtls_ssl_read(ctx->ssl, (unsigned char *) buf, buf_size); + DBG(("%p %d -> %d", nc, (int) buf_size, n)); + if (n < 0) return mg_ssl_if_mbed_err(nc, n); + if (n == 0) nc->flags |= MG_F_CLOSE_IMMEDIATELY; + return n; +} + +int mg_ssl_if_write(struct mg_connection *nc, const void *data, size_t len) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + int n = mbedtls_ssl_write(ctx->ssl, (const unsigned char *) data, len); + DBG(("%p %d -> %d", nc, (int) len, n)); + if (n < 0) return mg_ssl_if_mbed_err(nc, n); + return n; +} + +void mg_ssl_if_conn_close_notify(struct mg_connection *nc) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + if (ctx == NULL) return; + mbedtls_ssl_close_notify(ctx->ssl); +} + +void mg_ssl_if_conn_free(struct mg_connection *nc) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + if (ctx == NULL) return; + nc->ssl_if_data = NULL; + if (ctx->ssl != NULL) { + mbedtls_ssl_free(ctx->ssl); + MG_FREE(ctx->ssl); + } + mg_ssl_if_mbed_free_certs_and_keys(ctx); + if (ctx->conf != NULL) { + mbedtls_ssl_config_free(ctx->conf); + MG_FREE(ctx->conf); + } + mbuf_free(&ctx->cipher_suites); + memset(ctx, 0, sizeof(*ctx)); + MG_FREE(ctx); +} + +static enum mg_ssl_if_result mg_use_ca_cert(struct mg_ssl_if_ctx *ctx, + const char *ca_cert) { + if (ca_cert == NULL || strcmp(ca_cert, "*") == 0) { + mbedtls_ssl_conf_authmode(ctx->conf, MBEDTLS_SSL_VERIFY_NONE); + return MG_SSL_OK; + } + ctx->ca_cert = (mbedtls_x509_crt *) MG_CALLOC(1, sizeof(*ctx->ca_cert)); + mbedtls_x509_crt_init(ctx->ca_cert); +#ifdef MBEDTLS_X509_CA_CHAIN_ON_DISK + ca_cert = strdup(ca_cert); + if (mbedtls_x509_crt_set_ca_chain_file(ctx->ca_cert, ca_cert) != 0) { + return MG_SSL_ERROR; + } +#else + if (mbedtls_x509_crt_parse_file(ctx->ca_cert, ca_cert) != 0) { + return MG_SSL_ERROR; + } +#endif + mbedtls_ssl_conf_ca_chain(ctx->conf, ctx->ca_cert, NULL); + mbedtls_ssl_conf_authmode(ctx->conf, MBEDTLS_SSL_VERIFY_REQUIRED); + return MG_SSL_OK; +} + +static enum mg_ssl_if_result mg_use_cert(struct mg_ssl_if_ctx *ctx, + const char *cert, const char *key, + const char **err_msg) { + if (key == NULL) key = cert; + if (cert == NULL || cert[0] == '\0' || key == NULL || key[0] == '\0') { + return MG_SSL_OK; + } + ctx->cert = (mbedtls_x509_crt *) MG_CALLOC(1, sizeof(*ctx->cert)); + mbedtls_x509_crt_init(ctx->cert); + ctx->key = (mbedtls_pk_context *) MG_CALLOC(1, sizeof(*ctx->key)); + mbedtls_pk_init(ctx->key); + if (mbedtls_x509_crt_parse_file(ctx->cert, cert) != 0) { + MG_SET_PTRPTR(err_msg, "Invalid SSL cert"); + return MG_SSL_ERROR; + } + if (mbedtls_pk_parse_keyfile(ctx->key, key, NULL) != 0) { + MG_SET_PTRPTR(err_msg, "Invalid SSL key"); + return MG_SSL_ERROR; + } + if (mbedtls_ssl_conf_own_cert(ctx->conf, ctx->cert, ctx->key) != 0) { + MG_SET_PTRPTR(err_msg, "Invalid SSL key or cert"); + return MG_SSL_ERROR; + } + return MG_SSL_OK; +} + +static const int mg_s_cipher_list[] = { +#if CS_PLATFORM != CS_P_ESP8266 + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA, +#else + /* + * ECDHE is way too slow on ESP8266 w/o cryptochip, this sometimes results + * in WiFi STA deauths. Use weaker but faster cipher suites. Sad but true. + * Disable DHE completely because it's just hopelessly slow. + */ + MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA, +#endif /* CS_PLATFORM != CS_P_ESP8266 */ + 0, +}; + +/* + * Ciphers can be specified as a colon-separated list of cipher suite names. + * These can be found in + * https://github.com/ARMmbed/mbedtls/blob/development/library/ssl_ciphersuites.c#L267 + * E.g.: TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-256-CCM + */ +static enum mg_ssl_if_result mg_set_cipher_list(struct mg_ssl_if_ctx *ctx, + const char *ciphers) { + if (ciphers != NULL) { + int l, id; + const char *s = ciphers, *e; + char tmp[50]; + while (s != NULL) { + e = strchr(s, ':'); + l = (e != NULL ? (e - s) : (int) strlen(s)); + strncpy(tmp, s, l); + tmp[l] = '\0'; + id = mbedtls_ssl_get_ciphersuite_id(tmp); + DBG(("%s -> %04x", tmp, id)); + if (id != 0) { + mbuf_append(&ctx->cipher_suites, &id, sizeof(id)); + } + s = (e != NULL ? e + 1 : NULL); + } + if (ctx->cipher_suites.len == 0) return MG_SSL_ERROR; + id = 0; + mbuf_append(&ctx->cipher_suites, &id, sizeof(id)); + mbuf_trim(&ctx->cipher_suites); + mbedtls_ssl_conf_ciphersuites(ctx->conf, + (const int *) ctx->cipher_suites.buf); + } else { + mbedtls_ssl_conf_ciphersuites(ctx->conf, mg_s_cipher_list); + } + return MG_SSL_OK; +} + +static enum mg_ssl_if_result mg_ssl_if_mbed_set_psk(struct mg_ssl_if_ctx *ctx, + const char *identity, + const char *key_str) { + unsigned char key[32]; + size_t key_len; + if (identity == NULL && key_str == NULL) return MG_SSL_OK; + if (identity == NULL || key_str == NULL) return MG_SSL_ERROR; + key_len = strlen(key_str); + if (key_len != 32 && key_len != 64) return MG_SSL_ERROR; + size_t i = 0; + memset(key, 0, sizeof(key)); + key_len = 0; + for (i = 0; key_str[i] != '\0'; i++) { + unsigned char c; + char hc = tolower((int) key_str[i]); + if (hc >= '0' && hc <= '9') { + c = hc - '0'; + } else if (hc >= 'a' && hc <= 'f') { + c = hc - 'a' + 0xa; + } else { + return MG_SSL_ERROR; + } + key_len = i / 2; + key[key_len] <<= 4; + key[key_len] |= c; + } + key_len++; + DBG(("identity = '%s', key = (%u)", identity, (unsigned int) key_len)); + /* mbedTLS makes copies of psk and identity. */ + if (mbedtls_ssl_conf_psk(ctx->conf, (const unsigned char *) key, key_len, + (const unsigned char *) identity, + strlen(identity)) != 0) { + return MG_SSL_ERROR; + } + return MG_SSL_OK; +} + +const char *mg_set_ssl(struct mg_connection *nc, const char *cert, + const char *ca_cert) { + const char *err_msg = NULL; + struct mg_ssl_if_conn_params params; + memset(¶ms, 0, sizeof(params)); + params.cert = cert; + params.ca_cert = ca_cert; + if (mg_ssl_if_conn_init(nc, ¶ms, &err_msg) != MG_SSL_OK) { + return err_msg; + } + return NULL; +} + +/* Lazy RNG. Warning: it would be a bad idea to do this in production! */ +#ifdef MG_SSL_MBED_DUMMY_RANDOM +int mg_ssl_if_mbed_random(void *ctx, unsigned char *buf, size_t len) { + (void) ctx; + while (len--) *buf++ = rand(); + return 0; +} +#endif + +#endif /* MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_MBEDTLS */ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_uri.c" +#endif +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +/* Amalgamated: #include "mg_internal.h" */ +/* Amalgamated: #include "mg_uri.h" */ + +/* + * scan string until encountering one of `seps`, keeping track of component + * boundaries in `res`. + * + * `p` will point to the char after the separator or it will be `end`. + */ +static void parse_uri_component(const char **p, const char *end, + const char *seps, struct mg_str *res) { + const char *q; + res->p = *p; + for (; *p < end; (*p)++) { + for (q = seps; *q != '\0'; q++) { + if (**p == *q) break; + } + if (*q != '\0') break; + } + res->len = (*p) - res->p; + if (*p < end) (*p)++; +} + +int mg_parse_uri(const struct mg_str uri, struct mg_str *scheme, + struct mg_str *user_info, struct mg_str *host, + unsigned int *port, struct mg_str *path, struct mg_str *query, + struct mg_str *fragment) { + struct mg_str rscheme = {0, 0}, ruser_info = {0, 0}, rhost = {0, 0}, + rpath = {0, 0}, rquery = {0, 0}, rfragment = {0, 0}; + unsigned int rport = 0; + enum { + P_START, + P_SCHEME_OR_PORT, + P_USER_INFO, + P_HOST, + P_PORT, + P_REST + } state = P_START; + + const char *p = uri.p, *end = p + uri.len; + while (p < end) { + switch (state) { + case P_START: + /* + * expecting on of: + * - `scheme://xxxx` + * - `xxxx:port` + * - `[a:b:c]:port` + * - `xxxx/path` + */ + if (*p == '[') { + state = P_HOST; + break; + } + for (; p < end; p++) { + if (*p == ':') { + state = P_SCHEME_OR_PORT; + break; + } else if (*p == '/') { + state = P_REST; + break; + } + } + if (state == P_START || state == P_REST) { + rhost.p = uri.p; + rhost.len = p - uri.p; + } + break; + case P_SCHEME_OR_PORT: + if (end - p >= 3 && strncmp(p, "://", 3) == 0) { + rscheme.p = uri.p; + rscheme.len = p - uri.p; + state = P_USER_INFO; + p += 3; + } else { + rhost.p = uri.p; + rhost.len = p - uri.p; + state = P_PORT; + } + break; + case P_USER_INFO: + ruser_info.p = p; + for (; p < end; p++) { + if (*p == '@' || *p == '[' || *p == '/') { + break; + } + } + if (p == end || *p == '/' || *p == '[') { + /* backtrack and parse as host */ + p = ruser_info.p; + } + ruser_info.len = p - ruser_info.p; + state = P_HOST; + break; + case P_HOST: + if (*p == '@') p++; + rhost.p = p; + if (*p == '[') { + int found = 0; + for (; !found && p < end; p++) { + found = (*p == ']'); + } + if (!found) return -1; + } else { + for (; p < end; p++) { + if (*p == ':' || *p == '/') break; + } + } + rhost.len = p - rhost.p; + if (p < end) { + if (*p == ':') { + state = P_PORT; + break; + } else if (*p == '/') { + state = P_REST; + break; + } + } + break; + case P_PORT: + p++; + for (; p < end; p++) { + if (*p == '/') { + state = P_REST; + break; + } + rport *= 10; + rport += *p - '0'; + } + break; + case P_REST: + /* `p` points to separator. `path` includes the separator */ + parse_uri_component(&p, end, "?#", &rpath); + if (p < end && *(p - 1) == '?') { + parse_uri_component(&p, end, "#", &rquery); + } + parse_uri_component(&p, end, "", &rfragment); + break; + } + } + + if (scheme != 0) *scheme = rscheme; + if (user_info != 0) *user_info = ruser_info; + if (host != 0) *host = rhost; + if (port != 0) *port = rport; + if (path != 0) *path = rpath; + if (query != 0) *query = rquery; + if (fragment != 0) *fragment = rfragment; + + return 0; +} + +/* Normalize the URI path. Remove/resolve "." and "..". */ +int mg_normalize_uri_path(const struct mg_str *in, struct mg_str *out) { + const char *s = in->p, *se = s + in->len; + char *cp = (char *) out->p, *d; + + if (in->len == 0 || *s != '/') { + out->len = 0; + return 0; + } + + d = cp; + + while (s < se) { + const char *next = s; + struct mg_str component; + parse_uri_component(&next, se, "/", &component); + if (mg_vcmp(&component, ".") == 0) { + /* Yum. */ + } else if (mg_vcmp(&component, "..") == 0) { + /* Backtrack to previous slash. */ + if (d > cp + 1 && *(d - 1) == '/') d--; + while (d > cp && *(d - 1) != '/') d--; + } else { + memmove(d, s, next - s); + d += next - s; + } + s = next; + } + if (d == cp) *d++ = '/'; + + out->p = cp; + out->len = d - cp; + return 1; +} + +int mg_assemble_uri(const struct mg_str *scheme, const struct mg_str *user_info, + const struct mg_str *host, unsigned int port, + const struct mg_str *path, const struct mg_str *query, + const struct mg_str *fragment, int normalize_path, + struct mg_str *uri) { + int result = -1; + struct mbuf out; + mbuf_init(&out, 0); + + if (scheme != NULL && scheme->len > 0) { + mbuf_append(&out, scheme->p, scheme->len); + mbuf_append(&out, "://", 3); + } + + if (user_info != NULL && user_info->len > 0) { + mbuf_append(&out, user_info->p, user_info->len); + mbuf_append(&out, "@", 1); + } + + if (host != NULL && host->len > 0) { + mbuf_append(&out, host->p, host->len); + } + + if (port != 0) { + char port_str[20]; + int port_str_len = sprintf(port_str, ":%u", port); + mbuf_append(&out, port_str, port_str_len); + } + + if (path != NULL && path->len > 0) { + if (normalize_path) { + struct mg_str npath = mg_strdup(*path); + if (npath.len != path->len) goto out; + if (!mg_normalize_uri_path(path, &npath)) { + free((void *) npath.p); + goto out; + } + mbuf_append(&out, npath.p, npath.len); + free((void *) npath.p); + } else { + mbuf_append(&out, path->p, path->len); + } + } else if (normalize_path) { + mbuf_append(&out, "/", 1); + } + + if (query != NULL && query->len > 0) { + mbuf_append(&out, "?", 1); + mbuf_append(&out, query->p, query->len); + } + + if (fragment != NULL && fragment->len > 0) { + mbuf_append(&out, "#", 1); + mbuf_append(&out, fragment->p, fragment->len); + } + + result = 0; + +out: + if (result == 0) { + uri->p = out.buf; + uri->len = out.len; + } else { + mbuf_free(&out); + uri->p = NULL; + uri->len = 0; + } + return result; +} +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_http.c" +#endif +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_HTTP + +/* Amalgamated: #include "common/cs_md5.h" */ +/* Amalgamated: #include "mg_internal.h" */ +/* Amalgamated: #include "mg_util.h" */ + +/* altbuf {{{ */ + +/* + * Alternate buffer: fills the client-provided buffer with data; and if it's + * not large enough, allocates another buffer (via mbuf), similar to asprintf. + */ +struct altbuf { + struct mbuf m; + char *user_buf; + size_t len; + size_t user_buf_size; +}; + +/* + * Initializes altbuf; `buf`, `buf_size` is the client-provided buffer. + */ +MG_INTERNAL void altbuf_init(struct altbuf *ab, char *buf, size_t buf_size) { + mbuf_init(&ab->m, 0); + ab->user_buf = buf; + ab->user_buf_size = buf_size; + ab->len = 0; +} + +/* + * Appends a single char to the altbuf. + */ +MG_INTERNAL void altbuf_append(struct altbuf *ab, char c) { + if (ab->len < ab->user_buf_size) { + /* The data fits into the original buffer */ + ab->user_buf[ab->len++] = c; + } else { + /* The data can't fit into the original buffer, so write it to mbuf. */ + + /* + * First of all, see if that's the first byte which overflows the original + * buffer: if so, copy the existing data from there to a newly allocated + * mbuf. + */ + if (ab->len > 0 && ab->m.len == 0) { + mbuf_append(&ab->m, ab->user_buf, ab->len); + } + + mbuf_append(&ab->m, &c, 1); + ab->len = ab->m.len; + } +} + +/* + * Resets any data previously appended to altbuf. + */ +MG_INTERNAL void altbuf_reset(struct altbuf *ab) { + mbuf_free(&ab->m); + ab->len = 0; +} + +/* + * Returns whether the additional buffer was allocated (and thus the data + * is in the mbuf, not the client-provided buffer) + */ +MG_INTERNAL int altbuf_reallocated(struct altbuf *ab) { + return ab->len > ab->user_buf_size; +} + +/* + * Returns the actual buffer with data, either the client-provided or a newly + * allocated one. If `trim` is non-zero, mbuf-backed buffer is trimmed first. + */ +MG_INTERNAL char *altbuf_get_buf(struct altbuf *ab, int trim) { + if (altbuf_reallocated(ab)) { + if (trim) { + mbuf_trim(&ab->m); + } + return ab->m.buf; + } else { + return ab->user_buf; + } +} + +/* }}} */ + +static const char *mg_version_header = "Mongoose/" MG_VERSION; + +enum mg_http_proto_data_type { DATA_NONE, DATA_FILE, DATA_PUT }; + +struct mg_http_proto_data_file { + FILE *fp; /* Opened file. */ + int64_t cl; /* Content-Length. How many bytes to send. */ + int64_t sent; /* How many bytes have been already sent. */ + int keepalive; /* Keep connection open after sending. */ + enum mg_http_proto_data_type type; +}; + +#if MG_ENABLE_HTTP_CGI +struct mg_http_proto_data_cgi { + struct mg_connection *cgi_nc; +}; +#endif + +struct mg_http_proto_data_chuncked { + int64_t body_len; /* How many bytes of chunked body was reassembled. */ +}; + +struct mg_http_endpoint { + struct mg_http_endpoint *next; + struct mg_str uri_pattern; /* owned */ + char *auth_domain; /* owned */ + char *auth_file; /* owned */ + + mg_event_handler_t handler; +#if MG_ENABLE_CALLBACK_USERDATA + void *user_data; +#endif +}; + +enum mg_http_multipart_stream_state { + MPS_BEGIN, + MPS_WAITING_FOR_BOUNDARY, + MPS_WAITING_FOR_CHUNK, + MPS_GOT_CHUNK, + MPS_GOT_BOUNDARY, + MPS_FINALIZE, + MPS_FINISHED +}; + +struct mg_http_multipart_stream { + const char *boundary; + int boundary_len; + const char *var_name; + const char *file_name; + void *user_data; + int prev_io_len; + enum mg_http_multipart_stream_state state; + int processing_part; +}; + +struct mg_reverse_proxy_data { + struct mg_connection *linked_conn; +}; + +struct mg_ws_proto_data { + /* + * Defragmented size of the frame so far. + * + * First byte of nc->recv_mbuf.buf is an op, the rest of the data is + * defragmented data. + */ + size_t reass_len; +}; + +struct mg_http_proto_data { +#if MG_ENABLE_FILESYSTEM + struct mg_http_proto_data_file file; +#endif +#if MG_ENABLE_HTTP_CGI + struct mg_http_proto_data_cgi cgi; +#endif +#if MG_ENABLE_HTTP_STREAMING_MULTIPART + struct mg_http_multipart_stream mp_stream; +#endif +#if MG_ENABLE_HTTP_WEBSOCKET + struct mg_ws_proto_data ws_data; +#endif + struct mg_http_proto_data_chuncked chunk; + struct mg_http_endpoint *endpoints; + mg_event_handler_t endpoint_handler; + struct mg_reverse_proxy_data reverse_proxy_data; + size_t rcvd; /* How many bytes we have received. */ +}; + +static void mg_http_conn_destructor(void *proto_data); +struct mg_connection *mg_connect_http_base( + struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data), + struct mg_connect_opts opts, const char *scheme1, const char *scheme2, + const char *scheme_ssl1, const char *scheme_ssl2, const char *url, + struct mg_str *path, struct mg_str *user_info, struct mg_str *host); + +static struct mg_http_proto_data *mg_http_get_proto_data( + struct mg_connection *c) { + if (c->proto_data == NULL) { + c->proto_data = MG_CALLOC(1, sizeof(struct mg_http_proto_data)); + c->proto_data_destructor = mg_http_conn_destructor; + } + + return (struct mg_http_proto_data *) c->proto_data; +} + +#if MG_ENABLE_HTTP_STREAMING_MULTIPART +static void mg_http_free_proto_data_mp_stream( + struct mg_http_multipart_stream *mp) { + MG_FREE((void *) mp->boundary); + MG_FREE((void *) mp->var_name); + MG_FREE((void *) mp->file_name); + memset(mp, 0, sizeof(*mp)); +} +#endif + +#if MG_ENABLE_FILESYSTEM +static void mg_http_free_proto_data_file(struct mg_http_proto_data_file *d) { + if (d != NULL) { + if (d->fp != NULL) { + fclose(d->fp); + } + memset(d, 0, sizeof(struct mg_http_proto_data_file)); + } +} +#endif + +static void mg_http_free_proto_data_endpoints(struct mg_http_endpoint **ep) { + struct mg_http_endpoint *current = *ep; + + while (current != NULL) { + struct mg_http_endpoint *tmp = current->next; + MG_FREE((void *) current->uri_pattern.p); + MG_FREE((void *) current->auth_domain); + MG_FREE((void *) current->auth_file); + MG_FREE(current); + current = tmp; + } + + ep = NULL; +} + +static void mg_http_free_reverse_proxy_data(struct mg_reverse_proxy_data *rpd) { + if (rpd->linked_conn != NULL) { + /* + * Connection has linked one, we have to unlink & close it + * since _this_ connection is going to die and + * it doesn't make sense to keep another one + */ + struct mg_http_proto_data *pd = mg_http_get_proto_data(rpd->linked_conn); + if (pd->reverse_proxy_data.linked_conn != NULL) { + pd->reverse_proxy_data.linked_conn->flags |= MG_F_SEND_AND_CLOSE; + pd->reverse_proxy_data.linked_conn = NULL; + } + rpd->linked_conn = NULL; + } +} + +static void mg_http_conn_destructor(void *proto_data) { + struct mg_http_proto_data *pd = (struct mg_http_proto_data *) proto_data; +#if MG_ENABLE_FILESYSTEM + mg_http_free_proto_data_file(&pd->file); +#endif +#if MG_ENABLE_HTTP_CGI + mg_http_free_proto_data_cgi(&pd->cgi); +#endif +#if MG_ENABLE_HTTP_STREAMING_MULTIPART + mg_http_free_proto_data_mp_stream(&pd->mp_stream); +#endif + mg_http_free_proto_data_endpoints(&pd->endpoints); + mg_http_free_reverse_proxy_data(&pd->reverse_proxy_data); + MG_FREE(proto_data); +} + +#if MG_ENABLE_FILESYSTEM + +#define MIME_ENTRY(_ext, _type) \ + { _ext, sizeof(_ext) - 1, _type } +static const struct { + const char *extension; + size_t ext_len; + const char *mime_type; +} mg_static_builtin_mime_types[] = { + MIME_ENTRY("html", "text/html"), + MIME_ENTRY("html", "text/html"), + MIME_ENTRY("htm", "text/html"), + MIME_ENTRY("shtm", "text/html"), + MIME_ENTRY("shtml", "text/html"), + MIME_ENTRY("css", "text/css"), + MIME_ENTRY("js", "application/x-javascript"), + MIME_ENTRY("ico", "image/x-icon"), + MIME_ENTRY("gif", "image/gif"), + MIME_ENTRY("jpg", "image/jpeg"), + MIME_ENTRY("jpeg", "image/jpeg"), + MIME_ENTRY("png", "image/png"), + MIME_ENTRY("svg", "image/svg+xml"), + MIME_ENTRY("txt", "text/plain"), + MIME_ENTRY("torrent", "application/x-bittorrent"), + MIME_ENTRY("wav", "audio/x-wav"), + MIME_ENTRY("mp3", "audio/x-mp3"), + MIME_ENTRY("mid", "audio/mid"), + MIME_ENTRY("m3u", "audio/x-mpegurl"), + MIME_ENTRY("ogg", "application/ogg"), + MIME_ENTRY("ram", "audio/x-pn-realaudio"), + MIME_ENTRY("xml", "text/xml"), + MIME_ENTRY("ttf", "application/x-font-ttf"), + MIME_ENTRY("json", "application/json"), + MIME_ENTRY("xslt", "application/xml"), + MIME_ENTRY("xsl", "application/xml"), + MIME_ENTRY("ra", "audio/x-pn-realaudio"), + MIME_ENTRY("doc", "application/msword"), + MIME_ENTRY("exe", "application/octet-stream"), + MIME_ENTRY("zip", "application/x-zip-compressed"), + MIME_ENTRY("xls", "application/excel"), + MIME_ENTRY("tgz", "application/x-tar-gz"), + MIME_ENTRY("tar", "application/x-tar"), + MIME_ENTRY("gz", "application/x-gunzip"), + MIME_ENTRY("arj", "application/x-arj-compressed"), + MIME_ENTRY("rar", "application/x-rar-compressed"), + MIME_ENTRY("rtf", "application/rtf"), + MIME_ENTRY("pdf", "application/pdf"), + MIME_ENTRY("swf", "application/x-shockwave-flash"), + MIME_ENTRY("mpg", "video/mpeg"), + MIME_ENTRY("webm", "video/webm"), + MIME_ENTRY("mpeg", "video/mpeg"), + MIME_ENTRY("mov", "video/quicktime"), + MIME_ENTRY("mp4", "video/mp4"), + MIME_ENTRY("m4v", "video/x-m4v"), + MIME_ENTRY("asf", "video/x-ms-asf"), + MIME_ENTRY("avi", "video/x-msvideo"), + MIME_ENTRY("bmp", "image/bmp"), + {NULL, 0, NULL}}; + +static struct mg_str mg_get_mime_type(const char *path, const char *dflt, + const struct mg_serve_http_opts *opts) { + const char *ext, *overrides; + size_t i, path_len; + struct mg_str r, k, v; + + path_len = strlen(path); + + overrides = opts->custom_mime_types; + while ((overrides = mg_next_comma_list_entry(overrides, &k, &v)) != NULL) { + ext = path + (path_len - k.len); + if (path_len > k.len && mg_vcasecmp(&k, ext) == 0) { + return v; + } + } + + for (i = 0; mg_static_builtin_mime_types[i].extension != NULL; i++) { + ext = path + (path_len - mg_static_builtin_mime_types[i].ext_len); + if (path_len > mg_static_builtin_mime_types[i].ext_len && ext[-1] == '.' && + mg_casecmp(ext, mg_static_builtin_mime_types[i].extension) == 0) { + r.p = mg_static_builtin_mime_types[i].mime_type; + r.len = strlen(r.p); + return r; + } + } + + r.p = dflt; + r.len = strlen(r.p); + return r; +} +#endif + +/* + * Check whether full request is buffered. Return: + * -1 if request is malformed + * 0 if request is not yet fully buffered + * >0 actual request length, including last \r\n\r\n + */ +static int mg_http_get_request_len(const char *s, int buf_len) { + const unsigned char *buf = (unsigned char *) s; + int i; + + for (i = 0; i < buf_len; i++) { + if (!isprint(buf[i]) && buf[i] != '\r' && buf[i] != '\n' && buf[i] < 128) { + return -1; + } else if (buf[i] == '\n' && i + 1 < buf_len && buf[i + 1] == '\n') { + return i + 2; + } else if (buf[i] == '\n' && i + 2 < buf_len && buf[i + 1] == '\r' && + buf[i + 2] == '\n') { + return i + 3; + } + } + + return 0; +} + +static const char *mg_http_parse_headers(const char *s, const char *end, + int len, struct http_message *req) { + int i = 0; + while (i < (int) ARRAY_SIZE(req->header_names) - 1) { + struct mg_str *k = &req->header_names[i], *v = &req->header_values[i]; + + s = mg_skip(s, end, ": ", k); + s = mg_skip(s, end, "\r\n", v); + + while (v->len > 0 && v->p[v->len - 1] == ' ') { + v->len--; /* Trim trailing spaces in header value */ + } + + /* + * If header value is empty - skip it and go to next (if any). + * NOTE: Do not add it to headers_values because such addition changes API + * behaviour + */ + if (k->len != 0 && v->len == 0) { + continue; + } + + if (k->len == 0 || v->len == 0) { + k->p = v->p = NULL; + k->len = v->len = 0; + break; + } + + if (!mg_ncasecmp(k->p, "Content-Length", 14)) { + req->body.len = (size_t) to64(v->p); + req->message.len = len + req->body.len; + } + + i++; + } + + return s; +} + +int mg_parse_http(const char *s, int n, struct http_message *hm, int is_req) { + const char *end, *qs; + int len = mg_http_get_request_len(s, n); + + if (len <= 0) return len; + + memset(hm, 0, sizeof(*hm)); + hm->message.p = s; + hm->body.p = s + len; + hm->message.len = hm->body.len = (size_t) ~0; + end = s + len; + + /* Request is fully buffered. Skip leading whitespaces. */ + while (s < end && isspace(*(unsigned char *) s)) s++; + + if (is_req) { + /* Parse request line: method, URI, proto */ + s = mg_skip(s, end, " ", &hm->method); + s = mg_skip(s, end, " ", &hm->uri); + s = mg_skip(s, end, "\r\n", &hm->proto); + if (hm->uri.p <= hm->method.p || hm->proto.p <= hm->uri.p) return -1; + + /* If URI contains '?' character, initialize query_string */ + if ((qs = (char *) memchr(hm->uri.p, '?', hm->uri.len)) != NULL) { + hm->query_string.p = qs + 1; + hm->query_string.len = &hm->uri.p[hm->uri.len] - (qs + 1); + hm->uri.len = qs - hm->uri.p; + } + } else { + s = mg_skip(s, end, " ", &hm->proto); + if (end - s < 4 || s[3] != ' ') return -1; + hm->resp_code = atoi(s); + if (hm->resp_code < 100 || hm->resp_code >= 600) return -1; + s += 4; + s = mg_skip(s, end, "\r\n", &hm->resp_status_msg); + } + + s = mg_http_parse_headers(s, end, len, hm); + + /* + * mg_parse_http() is used to parse both HTTP requests and HTTP + * responses. If HTTP response does not have Content-Length set, then + * body is read until socket is closed, i.e. body.len is infinite (~0). + * + * For HTTP requests though, according to + * http://tools.ietf.org/html/rfc7231#section-8.1.3, + * only POST and PUT methods have defined body semantics. + * Therefore, if Content-Length is not specified and methods are + * not one of PUT or POST, set body length to 0. + * + * So, + * if it is HTTP request, and Content-Length is not set, + * and method is not (PUT or POST) then reset body length to zero. + */ + if (hm->body.len == (size_t) ~0 && is_req && + mg_vcasecmp(&hm->method, "PUT") != 0 && + mg_vcasecmp(&hm->method, "POST") != 0) { + hm->body.len = 0; + hm->message.len = len; + } + + return len; +} + +struct mg_str *mg_get_http_header(struct http_message *hm, const char *name) { + size_t i, len = strlen(name); + + for (i = 0; hm->header_names[i].len > 0; i++) { + struct mg_str *h = &hm->header_names[i], *v = &hm->header_values[i]; + if (h->p != NULL && h->len == len && !mg_ncasecmp(h->p, name, len)) + return v; + } + + return NULL; +} + +#if MG_ENABLE_FILESYSTEM +static void mg_http_transfer_file_data(struct mg_connection *nc) { + struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); + char buf[MG_MAX_HTTP_SEND_MBUF]; + size_t n = 0, to_read = 0, left = (size_t)(pd->file.cl - pd->file.sent); + + if (pd->file.type == DATA_FILE) { + struct mbuf *io = &nc->send_mbuf; + if (io->len >= MG_MAX_HTTP_SEND_MBUF) { + to_read = 0; + } else { + to_read = MG_MAX_HTTP_SEND_MBUF - io->len; + } + if (to_read > left) { + to_read = left; + } + if (to_read > 0) { + n = mg_fread(buf, 1, to_read, pd->file.fp); + if (n > 0) { + mg_send(nc, buf, n); + pd->file.sent += n; + DBG(("%p sent %d (total %d)", nc, (int) n, (int) pd->file.sent)); + } + } else { + /* Rate-limited */ + } + if (pd->file.sent >= pd->file.cl) { + LOG(LL_DEBUG, ("%p done, %d bytes", nc, (int) pd->file.sent)); + if (!pd->file.keepalive) nc->flags |= MG_F_SEND_AND_CLOSE; + mg_http_free_proto_data_file(&pd->file); + } + } else if (pd->file.type == DATA_PUT) { + struct mbuf *io = &nc->recv_mbuf; + size_t to_write = left <= 0 ? 0 : left < io->len ? (size_t) left : io->len; + size_t n = mg_fwrite(io->buf, 1, to_write, pd->file.fp); + if (n > 0) { + mbuf_remove(io, n); + pd->file.sent += n; + } + if (n == 0 || pd->file.sent >= pd->file.cl) { + if (!pd->file.keepalive) nc->flags |= MG_F_SEND_AND_CLOSE; + mg_http_free_proto_data_file(&pd->file); + } + } +#if MG_ENABLE_HTTP_CGI + else if (pd->cgi.cgi_nc != NULL) { + /* This is POST data that needs to be forwarded to the CGI process */ + if (pd->cgi.cgi_nc != NULL) { + mg_forward(nc, pd->cgi.cgi_nc); + } else { + nc->flags |= MG_F_SEND_AND_CLOSE; + } + } +#endif +} +#endif /* MG_ENABLE_FILESYSTEM */ + +/* + * Parse chunked-encoded buffer. Return 0 if the buffer is not encoded, or + * if it's incomplete. If the chunk is fully buffered, return total number of + * bytes in a chunk, and store data in `data`, `data_len`. + */ +static size_t mg_http_parse_chunk(char *buf, size_t len, char **chunk_data, + size_t *chunk_len) { + unsigned char *s = (unsigned char *) buf; + size_t n = 0; /* scanned chunk length */ + size_t i = 0; /* index in s */ + + /* Scan chunk length. That should be a hexadecimal number. */ + while (i < len && isxdigit(s[i])) { + n *= 16; + n += (s[i] >= '0' && s[i] <= '9') ? s[i] - '0' : tolower(s[i]) - 'a' + 10; + i++; + } + + /* Skip new line */ + if (i == 0 || i + 2 > len || s[i] != '\r' || s[i + 1] != '\n') { + return 0; + } + i += 2; + + /* Record where the data is */ + *chunk_data = (char *) s + i; + *chunk_len = n; + + /* Skip data */ + i += n; + + /* Skip new line */ + if (i == 0 || i + 2 > len || s[i] != '\r' || s[i + 1] != '\n') { + return 0; + } + return i + 2; +} + +MG_INTERNAL size_t mg_handle_chunked(struct mg_connection *nc, + struct http_message *hm, char *buf, + size_t blen) { + struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); + char *data; + size_t i, n, data_len, body_len, zero_chunk_received = 0; + /* Find out piece of received data that is not yet reassembled */ + body_len = (size_t) pd->chunk.body_len; + assert(blen >= body_len); + + /* Traverse all fully buffered chunks */ + for (i = body_len; + (n = mg_http_parse_chunk(buf + i, blen - i, &data, &data_len)) > 0; + i += n) { + /* Collapse chunk data to the rest of HTTP body */ + memmove(buf + body_len, data, data_len); + body_len += data_len; + hm->body.len = body_len; + + if (data_len == 0) { + zero_chunk_received = 1; + i += n; + break; + } + } + + if (i > body_len) { + /* Shift unparsed content to the parsed body */ + assert(i <= blen); + memmove(buf + body_len, buf + i, blen - i); + memset(buf + body_len + blen - i, 0, i - body_len); + nc->recv_mbuf.len -= i - body_len; + pd->chunk.body_len = body_len; + + /* Send MG_EV_HTTP_CHUNK event */ + nc->flags &= ~MG_F_DELETE_CHUNK; + mg_call(nc, nc->handler, nc->user_data, MG_EV_HTTP_CHUNK, hm); + + /* Delete processed data if user set MG_F_DELETE_CHUNK flag */ + if (nc->flags & MG_F_DELETE_CHUNK) { + memset(buf, 0, body_len); + memmove(buf, buf + body_len, blen - i); + nc->recv_mbuf.len -= body_len; + hm->body.len = 0; + pd->chunk.body_len = 0; + } + + if (zero_chunk_received) { + /* Total message size is len(body) + len(headers) */ + hm->message.len = + (size_t) pd->chunk.body_len + blen - i + (hm->body.p - hm->message.p); + } + } + + return body_len; +} + +struct mg_http_endpoint *mg_http_get_endpoint_handler(struct mg_connection *nc, + struct mg_str *uri_path) { + struct mg_http_proto_data *pd; + struct mg_http_endpoint *ret = NULL; + int matched, matched_max = 0; + struct mg_http_endpoint *ep; + + if (nc == NULL) { + return NULL; + } + + pd = mg_http_get_proto_data(nc); + + ep = pd->endpoints; + while (ep != NULL) { + if ((matched = mg_match_prefix_n(ep->uri_pattern, *uri_path)) > 0) { + if (matched > matched_max) { + /* Looking for the longest suitable handler */ + ret = ep; + matched_max = matched; + } + } + + ep = ep->next; + } + + return ret; +} + +#if MG_ENABLE_HTTP_STREAMING_MULTIPART +static void mg_http_multipart_continue(struct mg_connection *nc); + +static void mg_http_multipart_begin(struct mg_connection *nc, + struct http_message *hm, int req_len); + +#endif + +static void mg_http_call_endpoint_handler(struct mg_connection *nc, int ev, + struct http_message *hm); + +static void deliver_chunk(struct mg_connection *c, struct http_message *hm, + int req_len) { + /* Incomplete message received. Send MG_EV_HTTP_CHUNK event */ + hm->body.len = c->recv_mbuf.len - req_len; + c->flags &= ~MG_F_DELETE_CHUNK; + mg_call(c, c->handler, c->user_data, MG_EV_HTTP_CHUNK, hm); + /* Delete processed data if user set MG_F_DELETE_CHUNK flag */ + if (c->flags & MG_F_DELETE_CHUNK) c->recv_mbuf.len = req_len; +} + +/* + * lx106 compiler has a bug (TODO(mkm) report and insert tracking bug here) + * If a big structure is declared in a big function, lx106 gcc will make it + * even bigger (round up to 4k, from 700 bytes of actual size). + */ +#ifdef __xtensa__ +static void mg_http_handler2(struct mg_connection *nc, int ev, + void *ev_data MG_UD_ARG(void *user_data), + struct http_message *hm) __attribute__((noinline)); + +void mg_http_handler(struct mg_connection *nc, int ev, + void *ev_data MG_UD_ARG(void *user_data)) { + struct http_message hm; + mg_http_handler2(nc, ev, ev_data MG_UD_ARG(user_data), &hm); +} + +static void mg_http_handler2(struct mg_connection *nc, int ev, + void *ev_data MG_UD_ARG(void *user_data), + struct http_message *hm) { +#else /* !__XTENSA__ */ +void mg_http_handler(struct mg_connection *nc, int ev, + void *ev_data MG_UD_ARG(void *user_data)) { + struct http_message shm, *hm = &shm; +#endif /* __XTENSA__ */ + struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); + struct mbuf *io = &nc->recv_mbuf; + int req_len; + const int is_req = (nc->listener != NULL); +#if MG_ENABLE_HTTP_WEBSOCKET + struct mg_str *vec; +#endif + if (ev == MG_EV_CLOSE) { +#if MG_ENABLE_HTTP_CGI + /* Close associated CGI forwarder connection */ + if (pd->cgi.cgi_nc != NULL) { + pd->cgi.cgi_nc->user_data = NULL; + pd->cgi.cgi_nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } +#endif +#if MG_ENABLE_HTTP_STREAMING_MULTIPART + if (pd->mp_stream.boundary != NULL) { + /* + * Multipart message is in progress, but connection is closed. + * Finish part and request with an error flag. + */ + struct mg_http_multipart_part mp; + memset(&mp, 0, sizeof(mp)); + mp.status = -1; + mp.var_name = pd->mp_stream.var_name; + mp.file_name = pd->mp_stream.file_name; + mg_call(nc, (pd->endpoint_handler ? pd->endpoint_handler : nc->handler), + nc->user_data, MG_EV_HTTP_PART_END, &mp); + mp.var_name = NULL; + mp.file_name = NULL; + mg_call(nc, (pd->endpoint_handler ? pd->endpoint_handler : nc->handler), + nc->user_data, MG_EV_HTTP_MULTIPART_REQUEST_END, &mp); + } else +#endif + if (io->len > 0 && + (req_len = mg_parse_http(io->buf, io->len, hm, is_req)) > 0) { + /* + * For HTTP messages without Content-Length, always send HTTP message + * before MG_EV_CLOSE message. + */ + int ev2 = is_req ? MG_EV_HTTP_REQUEST : MG_EV_HTTP_REPLY; + hm->message.len = io->len; + hm->body.len = io->buf + io->len - hm->body.p; + deliver_chunk(nc, hm, req_len); + mg_http_call_endpoint_handler(nc, ev2, hm); + } + pd->rcvd = 0; + } + +#if MG_ENABLE_FILESYSTEM + if (pd->file.fp != NULL) { + mg_http_transfer_file_data(nc); + } +#endif + + mg_call(nc, nc->handler, nc->user_data, ev, ev_data); + + if (ev == MG_EV_RECV) { + struct mg_str *s; + pd->rcvd += *(int *) ev_data; + +#if MG_ENABLE_HTTP_STREAMING_MULTIPART + if (pd->mp_stream.boundary != NULL) { + mg_http_multipart_continue(nc); + return; + } +#endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */ + + req_len = mg_parse_http(io->buf, io->len, hm, is_req); + + if (req_len > 0 && + (s = mg_get_http_header(hm, "Transfer-Encoding")) != NULL && + mg_vcasecmp(s, "chunked") == 0) { + mg_handle_chunked(nc, hm, io->buf + req_len, io->len - req_len); + } + +#if MG_ENABLE_HTTP_STREAMING_MULTIPART + if (req_len > 0 && (s = mg_get_http_header(hm, "Content-Type")) != NULL && + s->len >= 9 && strncmp(s->p, "multipart", 9) == 0) { + mg_http_multipart_begin(nc, hm, req_len); + mg_http_multipart_continue(nc); + return; + } +#endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */ + + /* TODO(alashkin): refactor this ifelseifelseifelseifelse */ + if ((req_len < 0 || + (req_len == 0 && io->len >= MG_MAX_HTTP_REQUEST_SIZE))) { + DBG(("invalid request")); + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } else if (req_len == 0) { + /* Do nothing, request is not yet fully buffered */ + } +#if MG_ENABLE_HTTP_WEBSOCKET + else if (nc->listener == NULL && + mg_get_http_header(hm, "Sec-WebSocket-Accept")) { + /* We're websocket client, got handshake response from server. */ + /* TODO(lsm): check the validity of accept Sec-WebSocket-Accept */ + mbuf_remove(io, req_len); + nc->proto_handler = mg_ws_handler; + nc->flags |= MG_F_IS_WEBSOCKET; + mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_HANDSHAKE_DONE, + NULL); + mg_ws_handler(nc, MG_EV_RECV, ev_data MG_UD_ARG(user_data)); + } else if (nc->listener != NULL && + (vec = mg_get_http_header(hm, "Sec-WebSocket-Key")) != NULL) { + struct mg_http_endpoint *ep; + + /* This is a websocket request. Switch protocol handlers. */ + mbuf_remove(io, req_len); + nc->proto_handler = mg_ws_handler; + nc->flags |= MG_F_IS_WEBSOCKET; + + /* + * If we have a handler set up with mg_register_http_endpoint(), + * deliver subsequent websocket events to this handler after the + * protocol switch. + */ + ep = mg_http_get_endpoint_handler(nc->listener, &hm->uri); + if (ep != NULL) { + nc->handler = ep->handler; +#if MG_ENABLE_CALLBACK_USERDATA + nc->user_data = ep->user_data; +#endif + } + + /* Send handshake */ + mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_HANDSHAKE_REQUEST, + hm); + if (!(nc->flags & (MG_F_CLOSE_IMMEDIATELY | MG_F_SEND_AND_CLOSE))) { + if (nc->send_mbuf.len == 0) { + mg_ws_handshake(nc, vec, hm); + } + mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_HANDSHAKE_DONE, + NULL); + mg_ws_handler(nc, MG_EV_RECV, ev_data MG_UD_ARG(user_data)); + } + } +#endif /* MG_ENABLE_HTTP_WEBSOCKET */ + else if (hm->message.len > pd->rcvd) { + /* Not yet received all HTTP body, deliver MG_EV_HTTP_CHUNK */ + deliver_chunk(nc, hm, req_len); + if (nc->recv_mbuf_limit > 0 && nc->recv_mbuf.len >= nc->recv_mbuf_limit) { + LOG(LL_ERROR, ("%p recv buffer (%lu bytes) exceeds the limit " + "%lu bytes, and not drained, closing", + nc, (unsigned long) nc->recv_mbuf.len, + (unsigned long) nc->recv_mbuf_limit)); + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } + } else { + /* We did receive all HTTP body. */ + int trigger_ev = nc->listener ? MG_EV_HTTP_REQUEST : MG_EV_HTTP_REPLY; + char addr[32]; + mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr), + MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT); + DBG(("%p %s %.*s %.*s", nc, addr, (int) hm->method.len, hm->method.p, + (int) hm->uri.len, hm->uri.p)); + deliver_chunk(nc, hm, req_len); + /* Whole HTTP message is fully buffered, call event handler */ + mg_http_call_endpoint_handler(nc, trigger_ev, hm); + mbuf_remove(io, hm->message.len); + pd->rcvd = 0; + } + } +} + +static size_t mg_get_line_len(const char *buf, size_t buf_len) { + size_t len = 0; + while (len < buf_len && buf[len] != '\n') len++; + return len == buf_len ? 0 : len + 1; +} + +#if MG_ENABLE_HTTP_STREAMING_MULTIPART +static void mg_http_multipart_begin(struct mg_connection *nc, + struct http_message *hm, int req_len) { + struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); + struct mg_str *ct; + struct mbuf *io = &nc->recv_mbuf; + + char boundary_buf[100]; + char *boundary = boundary_buf; + int boundary_len; + + ct = mg_get_http_header(hm, "Content-Type"); + if (ct == NULL) { + /* We need more data - or it isn't multipart mesage */ + goto exit_mp; + } + + /* Content-type should start with "multipart" */ + if (ct->len < 9 || strncmp(ct->p, "multipart", 9) != 0) { + goto exit_mp; + } + + boundary_len = + mg_http_parse_header2(ct, "boundary", &boundary, sizeof(boundary_buf)); + if (boundary_len == 0) { + /* + * Content type is multipart, but there is no boundary, + * probably malformed request + */ + nc->flags = MG_F_CLOSE_IMMEDIATELY; + DBG(("invalid request")); + goto exit_mp; + } + + /* If we reach this place - that is multipart request */ + + if (pd->mp_stream.boundary != NULL) { + /* + * Another streaming request was in progress, + * looks like protocol error + */ + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } else { + struct mg_http_endpoint *ep = NULL; + pd->mp_stream.state = MPS_BEGIN; + pd->mp_stream.boundary = strdup(boundary); + pd->mp_stream.boundary_len = strlen(boundary); + pd->mp_stream.var_name = pd->mp_stream.file_name = NULL; + pd->endpoint_handler = nc->handler; + + ep = mg_http_get_endpoint_handler(nc->listener, &hm->uri); + if (ep != NULL) { + pd->endpoint_handler = ep->handler; + } + + mg_http_call_endpoint_handler(nc, MG_EV_HTTP_MULTIPART_REQUEST, hm); + + mbuf_remove(io, req_len); + } +exit_mp: + if (boundary != boundary_buf) MG_FREE(boundary); +} + +#define CONTENT_DISPOSITION "Content-Disposition: " + +static void mg_http_multipart_call_handler(struct mg_connection *c, int ev, + const char *data, size_t data_len) { + struct mg_http_multipart_part mp; + struct mg_http_proto_data *pd = mg_http_get_proto_data(c); + memset(&mp, 0, sizeof(mp)); + + mp.var_name = pd->mp_stream.var_name; + mp.file_name = pd->mp_stream.file_name; + mp.user_data = pd->mp_stream.user_data; + mp.data.p = data; + mp.data.len = data_len; + mg_call(c, pd->endpoint_handler, c->user_data, ev, &mp); + pd->mp_stream.user_data = mp.user_data; +} + +static int mg_http_multipart_got_chunk(struct mg_connection *c) { + struct mg_http_proto_data *pd = mg_http_get_proto_data(c); + struct mbuf *io = &c->recv_mbuf; + + mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_DATA, io->buf, + pd->mp_stream.prev_io_len); + mbuf_remove(io, pd->mp_stream.prev_io_len); + pd->mp_stream.prev_io_len = 0; + pd->mp_stream.state = MPS_WAITING_FOR_CHUNK; + + return 0; +} + +static int mg_http_multipart_finalize(struct mg_connection *c) { + struct mg_http_proto_data *pd = mg_http_get_proto_data(c); + + mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_END, NULL, 0); + MG_FREE((void *) pd->mp_stream.file_name); + pd->mp_stream.file_name = NULL; + MG_FREE((void *) pd->mp_stream.var_name); + pd->mp_stream.var_name = NULL; + mg_http_multipart_call_handler(c, MG_EV_HTTP_MULTIPART_REQUEST_END, NULL, 0); + mg_http_free_proto_data_mp_stream(&pd->mp_stream); + pd->mp_stream.state = MPS_FINISHED; + + return 1; +} + +static int mg_http_multipart_wait_for_boundary(struct mg_connection *c) { + const char *boundary; + struct mbuf *io = &c->recv_mbuf; + struct mg_http_proto_data *pd = mg_http_get_proto_data(c); + + if (pd->mp_stream.boundary == NULL) { + pd->mp_stream.state = MPS_FINALIZE; + DBG(("Invalid request: boundary not initialized")); + return 0; + } + + if ((int) io->len < pd->mp_stream.boundary_len + 2) { + return 0; + } + + boundary = c_strnstr(io->buf, pd->mp_stream.boundary, io->len); + if (boundary != NULL) { + const char *boundary_end = (boundary + pd->mp_stream.boundary_len); + if (io->len - (boundary_end - io->buf) < 4) { + return 0; + } + if (strncmp(boundary_end, "--\r\n", 4) == 0) { + pd->mp_stream.state = MPS_FINALIZE; + mbuf_remove(io, (boundary_end - io->buf) + 4); + } else { + pd->mp_stream.state = MPS_GOT_BOUNDARY; + } + } else { + return 0; + } + + return 1; +} + +static void mg_http_parse_header_internal(struct mg_str *hdr, + const char *var_name, + struct altbuf *ab); + +static int mg_http_multipart_process_boundary(struct mg_connection *c) { + int data_size; + const char *boundary, *block_begin; + struct mbuf *io = &c->recv_mbuf; + struct mg_http_proto_data *pd = mg_http_get_proto_data(c); + struct altbuf ab_file_name, ab_var_name; + int line_len; + boundary = c_strnstr(io->buf, pd->mp_stream.boundary, io->len); + block_begin = boundary + pd->mp_stream.boundary_len + 2; + data_size = io->len - (block_begin - io->buf); + + altbuf_init(&ab_file_name, NULL, 0); + altbuf_init(&ab_var_name, NULL, 0); + + while (data_size > 0 && + (line_len = mg_get_line_len(block_begin, data_size)) != 0) { + if (line_len > (int) sizeof(CONTENT_DISPOSITION) && + mg_ncasecmp(block_begin, CONTENT_DISPOSITION, + sizeof(CONTENT_DISPOSITION) - 1) == 0) { + struct mg_str header; + + header.p = block_begin + sizeof(CONTENT_DISPOSITION) - 1; + header.len = line_len - sizeof(CONTENT_DISPOSITION) - 1; + + altbuf_reset(&ab_var_name); + mg_http_parse_header_internal(&header, "name", &ab_var_name); + + altbuf_reset(&ab_file_name); + mg_http_parse_header_internal(&header, "filename", &ab_file_name); + + block_begin += line_len; + data_size -= line_len; + + continue; + } + + if (line_len == 2 && mg_ncasecmp(block_begin, "\r\n", 2) == 0) { + mbuf_remove(io, block_begin - io->buf + 2); + + if (pd->mp_stream.processing_part != 0) { + mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_END, NULL, 0); + } + + /* Reserve 2 bytes for "\r\n" in file_name and var_name */ + altbuf_append(&ab_file_name, '\0'); + altbuf_append(&ab_file_name, '\0'); + altbuf_append(&ab_var_name, '\0'); + altbuf_append(&ab_var_name, '\0'); + + MG_FREE((void *) pd->mp_stream.file_name); + pd->mp_stream.file_name = altbuf_get_buf(&ab_file_name, 1 /* trim */); + MG_FREE((void *) pd->mp_stream.var_name); + pd->mp_stream.var_name = altbuf_get_buf(&ab_var_name, 1 /* trim */); + + mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_BEGIN, NULL, 0); + pd->mp_stream.state = MPS_WAITING_FOR_CHUNK; + pd->mp_stream.processing_part++; + return 1; + } + + block_begin += line_len; + } + + pd->mp_stream.state = MPS_WAITING_FOR_BOUNDARY; + + altbuf_reset(&ab_var_name); + altbuf_reset(&ab_file_name); + + return 0; +} + +static int mg_http_multipart_continue_wait_for_chunk(struct mg_connection *c) { + struct mg_http_proto_data *pd = mg_http_get_proto_data(c); + struct mbuf *io = &c->recv_mbuf; + + const char *boundary; + if ((int) io->len < pd->mp_stream.boundary_len + 6 /* \r\n, --, -- */) { + return 0; + } + + boundary = c_strnstr(io->buf, pd->mp_stream.boundary, io->len); + if (boundary == NULL && pd->mp_stream.prev_io_len == 0) { + pd->mp_stream.prev_io_len = io->len; + return 0; + } else if (boundary == NULL && + (int) io->len > + pd->mp_stream.prev_io_len + pd->mp_stream.boundary_len + 4) { + pd->mp_stream.state = MPS_GOT_CHUNK; + return 1; + } else if (boundary != NULL) { + int data_size = (boundary - io->buf - 4); + mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_DATA, io->buf, data_size); + mbuf_remove(io, (boundary - io->buf)); + pd->mp_stream.prev_io_len = 0; + pd->mp_stream.state = MPS_WAITING_FOR_BOUNDARY; + return 1; + } else { + return 0; + } +} + +static void mg_http_multipart_continue(struct mg_connection *c) { + struct mg_http_proto_data *pd = mg_http_get_proto_data(c); + while (1) { + switch (pd->mp_stream.state) { + case MPS_BEGIN: { + pd->mp_stream.state = MPS_WAITING_FOR_BOUNDARY; + break; + } + case MPS_WAITING_FOR_BOUNDARY: { + if (mg_http_multipart_wait_for_boundary(c) == 0) { + return; + } + break; + } + case MPS_GOT_BOUNDARY: { + if (mg_http_multipart_process_boundary(c) == 0) { + return; + } + break; + } + case MPS_WAITING_FOR_CHUNK: { + if (mg_http_multipart_continue_wait_for_chunk(c) == 0) { + return; + } + break; + } + case MPS_GOT_CHUNK: { + if (mg_http_multipart_got_chunk(c) == 0) { + return; + } + break; + } + case MPS_FINALIZE: { + if (mg_http_multipart_finalize(c) == 0) { + return; + } + break; + } + case MPS_FINISHED: { + mbuf_remove(&c->recv_mbuf, c->recv_mbuf.len); + return; + } + } + } +} + +struct file_upload_state { + char *lfn; + size_t num_recd; + FILE *fp; +}; + +#endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */ + +void mg_set_protocol_http_websocket(struct mg_connection *nc) { + nc->proto_handler = mg_http_handler; +} + +const char *mg_status_message(int status_code) { + switch (status_code) { + case 206: + return "Partial Content"; + case 301: + return "Moved"; + case 302: + return "Found"; + case 400: + return "Bad Request"; + case 401: + return "Unauthorized"; + case 403: + return "Forbidden"; + case 404: + return "Not Found"; + case 416: + return "Requested Range Not Satisfiable"; + case 418: + return "I'm a teapot"; + case 500: + return "Internal Server Error"; + case 502: + return "Bad Gateway"; + case 503: + return "Service Unavailable"; + +#if MG_ENABLE_EXTRA_ERRORS_DESC + case 100: + return "Continue"; + case 101: + return "Switching Protocols"; + case 102: + return "Processing"; + case 200: + return "OK"; + case 201: + return "Created"; + case 202: + return "Accepted"; + case 203: + return "Non-Authoritative Information"; + case 204: + return "No Content"; + case 205: + return "Reset Content"; + case 207: + return "Multi-Status"; + case 208: + return "Already Reported"; + case 226: + return "IM Used"; + case 300: + return "Multiple Choices"; + case 303: + return "See Other"; + case 304: + return "Not Modified"; + case 305: + return "Use Proxy"; + case 306: + return "Switch Proxy"; + case 307: + return "Temporary Redirect"; + case 308: + return "Permanent Redirect"; + case 402: + return "Payment Required"; + case 405: + return "Method Not Allowed"; + case 406: + return "Not Acceptable"; + case 407: + return "Proxy Authentication Required"; + case 408: + return "Request Timeout"; + case 409: + return "Conflict"; + case 410: + return "Gone"; + case 411: + return "Length Required"; + case 412: + return "Precondition Failed"; + case 413: + return "Payload Too Large"; + case 414: + return "URI Too Long"; + case 415: + return "Unsupported Media Type"; + case 417: + return "Expectation Failed"; + case 422: + return "Unprocessable Entity"; + case 423: + return "Locked"; + case 424: + return "Failed Dependency"; + case 426: + return "Upgrade Required"; + case 428: + return "Precondition Required"; + case 429: + return "Too Many Requests"; + case 431: + return "Request Header Fields Too Large"; + case 451: + return "Unavailable For Legal Reasons"; + case 501: + return "Not Implemented"; + case 504: + return "Gateway Timeout"; + case 505: + return "HTTP Version Not Supported"; + case 506: + return "Variant Also Negotiates"; + case 507: + return "Insufficient Storage"; + case 508: + return "Loop Detected"; + case 510: + return "Not Extended"; + case 511: + return "Network Authentication Required"; +#endif /* MG_ENABLE_EXTRA_ERRORS_DESC */ + + default: + return "OK"; + } +} + +void mg_send_response_line_s(struct mg_connection *nc, int status_code, + const struct mg_str extra_headers) { + mg_printf(nc, "HTTP/1.1 %d %s\r\nServer: %s\r\n", status_code, + mg_status_message(status_code), mg_version_header); + if (extra_headers.len > 0) { + mg_printf(nc, "%.*s\r\n", (int) extra_headers.len, extra_headers.p); + } +} + +void mg_send_response_line(struct mg_connection *nc, int status_code, + const char *extra_headers) { + mg_send_response_line_s(nc, status_code, mg_mk_str(extra_headers)); +} + +void mg_http_send_redirect(struct mg_connection *nc, int status_code, + const struct mg_str location, + const struct mg_str extra_headers) { + char bbody[100], *pbody = bbody; + int bl = mg_asprintf(&pbody, sizeof(bbody), + "

Moved here.\r\n", + (int) location.len, location.p); + char bhead[150], *phead = bhead; + mg_asprintf(&phead, sizeof(bhead), + "Location: %.*s\r\n" + "Content-Type: text/html\r\n" + "Content-Length: %d\r\n" + "Cache-Control: no-cache\r\n" + "%.*s%s", + (int) location.len, location.p, bl, (int) extra_headers.len, + extra_headers.p, (extra_headers.len > 0 ? "\r\n" : "")); + mg_send_response_line(nc, status_code, phead); + if (phead != bhead) MG_FREE(phead); + mg_send(nc, pbody, bl); + if (pbody != bbody) MG_FREE(pbody); +} + +void mg_send_head(struct mg_connection *c, int status_code, + int64_t content_length, const char *extra_headers) { + mg_send_response_line(c, status_code, extra_headers); + if (content_length < 0) { + mg_printf(c, "%s", "Transfer-Encoding: chunked\r\n"); + } else { + mg_printf(c, "Content-Length: %" INT64_FMT "\r\n", content_length); + } + mg_send(c, "\r\n", 2); +} + +void mg_http_send_error(struct mg_connection *nc, int code, + const char *reason) { + if (!reason) reason = mg_status_message(code); + LOG(LL_DEBUG, ("%p %d %s", nc, code, reason)); + mg_send_head(nc, code, strlen(reason), + "Content-Type: text/plain\r\nConnection: close"); + mg_send(nc, reason, strlen(reason)); + nc->flags |= MG_F_SEND_AND_CLOSE; +} + +#if MG_ENABLE_FILESYSTEM +static void mg_http_construct_etag(char *buf, size_t buf_len, + const cs_stat_t *st) { + snprintf(buf, buf_len, "\"%lx.%" INT64_FMT "\"", (unsigned long) st->st_mtime, + (int64_t) st->st_size); +} + +#ifndef WINCE +static void mg_gmt_time_string(char *buf, size_t buf_len, time_t *t) { + strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", gmtime(t)); +} +#else +/* Look wince_lib.c for WindowsCE implementation */ +static void mg_gmt_time_string(char *buf, size_t buf_len, time_t *t); +#endif + +static int mg_http_parse_range_header(const struct mg_str *header, int64_t *a, + int64_t *b) { + /* + * There is no snscanf. Headers are not guaranteed to be NUL-terminated, + * so we have this. Ugh. + */ + int result; + char *p = (char *) MG_MALLOC(header->len + 1); + if (p == NULL) return 0; + memcpy(p, header->p, header->len); + p[header->len] = '\0'; + result = sscanf(p, "bytes=%" INT64_FMT "-%" INT64_FMT, a, b); + MG_FREE(p); + return result; +} + +void mg_http_serve_file(struct mg_connection *nc, struct http_message *hm, + const char *path, const struct mg_str mime_type, + const struct mg_str extra_headers) { + struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); + cs_stat_t st; + LOG(LL_DEBUG, ("%p [%s] %.*s", nc, path, (int) mime_type.len, mime_type.p)); + if (mg_stat(path, &st) != 0 || (pd->file.fp = mg_fopen(path, "rb")) == NULL) { + int code, err = mg_get_errno(); + switch (err) { + case EACCES: + code = 403; + break; + case ENOENT: + code = 404; + break; + default: + code = 500; + }; + mg_http_send_error(nc, code, "Open failed"); + } else { + char etag[50], current_time[50], last_modified[50], range[70]; + time_t t = (time_t) mg_time(); + int64_t r1 = 0, r2 = 0, cl = st.st_size; + struct mg_str *range_hdr = mg_get_http_header(hm, "Range"); + int n, status_code = 200; + + /* Handle Range header */ + range[0] = '\0'; + if (range_hdr != NULL && + (n = mg_http_parse_range_header(range_hdr, &r1, &r2)) > 0 && r1 >= 0 && + r2 >= 0) { + /* If range is specified like "400-", set second limit to content len */ + if (n == 1) { + r2 = cl - 1; + } + if (r1 > r2 || r2 >= cl) { + status_code = 416; + cl = 0; + snprintf(range, sizeof(range), + "Content-Range: bytes */%" INT64_FMT "\r\n", + (int64_t) st.st_size); + } else { + status_code = 206; + cl = r2 - r1 + 1; + snprintf(range, sizeof(range), "Content-Range: bytes %" INT64_FMT + "-%" INT64_FMT "/%" INT64_FMT "\r\n", + r1, r1 + cl - 1, (int64_t) st.st_size); +#if _FILE_OFFSET_BITS == 64 || _POSIX_C_SOURCE >= 200112L || \ + _XOPEN_SOURCE >= 600 + fseeko(pd->file.fp, r1, SEEK_SET); +#else + fseek(pd->file.fp, (long) r1, SEEK_SET); +#endif + } + } + +#if !MG_DISABLE_HTTP_KEEP_ALIVE + { + struct mg_str *conn_hdr = mg_get_http_header(hm, "Connection"); + if (conn_hdr != NULL) { + pd->file.keepalive = (mg_vcasecmp(conn_hdr, "keep-alive") == 0); + } else { + pd->file.keepalive = (mg_vcmp(&hm->proto, "HTTP/1.1") == 0); + } + } +#endif + + mg_http_construct_etag(etag, sizeof(etag), &st); + mg_gmt_time_string(current_time, sizeof(current_time), &t); + mg_gmt_time_string(last_modified, sizeof(last_modified), &st.st_mtime); + /* + * Content length casted to size_t because: + * 1) that's the maximum buffer size anyway + * 2) ESP8266 RTOS SDK newlib vprintf cannot contain a 64bit arg at non-last + * position + * TODO(mkm): fix ESP8266 RTOS SDK + */ + mg_send_response_line_s(nc, status_code, extra_headers); + mg_printf(nc, + "Date: %s\r\n" + "Last-Modified: %s\r\n" + "Accept-Ranges: bytes\r\n" + "Content-Type: %.*s\r\n" + "Connection: %s\r\n" + "Content-Length: %" SIZE_T_FMT + "\r\n" + "%sEtag: %s\r\n\r\n", + current_time, last_modified, (int) mime_type.len, mime_type.p, + (pd->file.keepalive ? "keep-alive" : "close"), (size_t) cl, range, + etag); + + pd->file.cl = cl; + pd->file.type = DATA_FILE; + mg_http_transfer_file_data(nc); + } +} + +static void mg_http_serve_file2(struct mg_connection *nc, const char *path, + struct http_message *hm, + struct mg_serve_http_opts *opts) { +#if MG_ENABLE_HTTP_SSI + if (mg_match_prefix(opts->ssi_pattern, strlen(opts->ssi_pattern), path) > 0) { + mg_handle_ssi_request(nc, hm, path, opts); + return; + } +#endif + mg_http_serve_file(nc, hm, path, mg_get_mime_type(path, "text/plain", opts), + mg_mk_str(opts->extra_headers)); +} + +#endif + +int mg_url_decode(const char *src, int src_len, char *dst, int dst_len, + int is_form_url_encoded) { + int i, j, a, b; +#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W') + + for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) { + if (src[i] == '%') { + if (i < src_len - 2 && isxdigit(*(const unsigned char *) (src + i + 1)) && + isxdigit(*(const unsigned char *) (src + i + 2))) { + a = tolower(*(const unsigned char *) (src + i + 1)); + b = tolower(*(const unsigned char *) (src + i + 2)); + dst[j] = (char) ((HEXTOI(a) << 4) | HEXTOI(b)); + i += 2; + } else { + return -1; + } + } else if (is_form_url_encoded && src[i] == '+') { + dst[j] = ' '; + } else { + dst[j] = src[i]; + } + } + + dst[j] = '\0'; /* Null-terminate the destination */ + + return i >= src_len ? j : -1; +} + +int mg_get_http_var(const struct mg_str *buf, const char *name, char *dst, + size_t dst_len) { + const char *p, *e, *s; + size_t name_len; + int len; + + /* + * According to the documentation function returns negative + * value in case of error. For debug purposes it returns: + * -1 - src is wrong (NUUL) + * -2 - dst is wrong (NULL) + * -3 - failed to decode url or dst is to small + * -4 - name does not exist + */ + if (dst == NULL || dst_len == 0) { + len = -2; + } else if (buf->p == NULL || name == NULL || buf->len == 0) { + len = -1; + dst[0] = '\0'; + } else { + name_len = strlen(name); + e = buf->p + buf->len; + len = -4; + dst[0] = '\0'; + + for (p = buf->p; p + name_len < e; p++) { + if ((p == buf->p || p[-1] == '&') && p[name_len] == '=' && + !mg_ncasecmp(name, p, name_len)) { + p += name_len + 1; + s = (const char *) memchr(p, '&', (size_t)(e - p)); + if (s == NULL) { + s = e; + } + len = mg_url_decode(p, (size_t)(s - p), dst, dst_len, 1); + /* -1 means: failed to decode or dst is too small */ + if (len == -1) { + len = -3; + } + break; + } + } + } + + return len; +} + +void mg_send_http_chunk(struct mg_connection *nc, const char *buf, size_t len) { + char chunk_size[50]; + int n; + + n = snprintf(chunk_size, sizeof(chunk_size), "%lX\r\n", (unsigned long) len); + mg_send(nc, chunk_size, n); + mg_send(nc, buf, len); + mg_send(nc, "\r\n", 2); +} + +void mg_printf_http_chunk(struct mg_connection *nc, const char *fmt, ...) { + char mem[MG_VPRINTF_BUFFER_SIZE], *buf = mem; + int len; + va_list ap; + + va_start(ap, fmt); + len = mg_avprintf(&buf, sizeof(mem), fmt, ap); + va_end(ap); + + if (len >= 0) { + mg_send_http_chunk(nc, buf, len); + } + + /* LCOV_EXCL_START */ + if (buf != mem && buf != NULL) { + MG_FREE(buf); + } + /* LCOV_EXCL_STOP */ +} + +void mg_printf_html_escape(struct mg_connection *nc, const char *fmt, ...) { + char mem[MG_VPRINTF_BUFFER_SIZE], *buf = mem; + int i, j, len; + va_list ap; + + va_start(ap, fmt); + len = mg_avprintf(&buf, sizeof(mem), fmt, ap); + va_end(ap); + + if (len >= 0) { + for (i = j = 0; i < len; i++) { + if (buf[i] == '<' || buf[i] == '>') { + mg_send(nc, buf + j, i - j); + mg_send(nc, buf[i] == '<' ? "<" : ">", 4); + j = i + 1; + } + } + mg_send(nc, buf + j, i - j); + } + + /* LCOV_EXCL_START */ + if (buf != mem && buf != NULL) { + MG_FREE(buf); + } + /* LCOV_EXCL_STOP */ +} + +static void mg_http_parse_header_internal(struct mg_str *hdr, + const char *var_name, + struct altbuf *ab) { + int ch = ' ', ch1 = ',', n = strlen(var_name); + const char *p, *end = hdr ? hdr->p + hdr->len : NULL, *s = NULL; + + /* Find where variable starts */ + for (s = hdr->p; s != NULL && s + n < end; s++) { + if ((s == hdr->p || s[-1] == ch || s[-1] == ch1 || s[-1] == ';') && + s[n] == '=' && !strncmp(s, var_name, n)) + break; + } + + if (s != NULL && &s[n + 1] < end) { + s += n + 1; + if (*s == '"' || *s == '\'') { + ch = ch1 = *s++; + } + p = s; + while (p < end && p[0] != ch && p[0] != ch1) { + if (ch != ' ' && p[0] == '\\' && p[1] == ch) p++; + altbuf_append(ab, *p++); + } + + if (ch != ' ' && *p != ch) { + altbuf_reset(ab); + } + } + + /* If there is some data, append a NUL. */ + if (ab->len > 0) { + altbuf_append(ab, '\0'); + } +} + +int mg_http_parse_header2(struct mg_str *hdr, const char *var_name, char **buf, + size_t buf_size) { + struct altbuf ab; + altbuf_init(&ab, *buf, buf_size); + if (hdr == NULL) return 0; + if (*buf != NULL && buf_size > 0) *buf[0] = '\0'; + + mg_http_parse_header_internal(hdr, var_name, &ab); + + /* + * Get a (trimmed) buffer, and return a len without a NUL byte which might + * have been added. + */ + *buf = altbuf_get_buf(&ab, 1 /* trim */); + return ab.len > 0 ? ab.len - 1 : 0; +} + +int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf, + size_t buf_size) { + char *buf2 = buf; + + int len = mg_http_parse_header2(hdr, var_name, &buf2, buf_size); + + if (buf2 != buf) { + /* Buffer was not enough and was reallocated: free it and just return 0 */ + MG_FREE(buf2); + return 0; + } + + return len; +} + +int mg_get_http_basic_auth(struct http_message *hm, char *user, size_t user_len, + char *pass, size_t pass_len) { + struct mg_str *hdr = mg_get_http_header(hm, "Authorization"); + if (hdr == NULL) return -1; + return mg_parse_http_basic_auth(hdr, user, user_len, pass, pass_len); +} + +int mg_parse_http_basic_auth(struct mg_str *hdr, char *user, size_t user_len, + char *pass, size_t pass_len) { + char *buf = NULL; + char fmt[64]; + int res = 0; + + if (mg_strncmp(*hdr, mg_mk_str("Basic "), 6) != 0) return -1; + + buf = (char *) MG_MALLOC(hdr->len); + cs_base64_decode((unsigned char *) hdr->p + 6, hdr->len, buf, NULL); + + /* e.g. "%123[^:]:%321[^\n]" */ + snprintf(fmt, sizeof(fmt), "%%%" SIZE_T_FMT "[^:]:%%%" SIZE_T_FMT "[^\n]", + user_len - 1, pass_len - 1); + if (sscanf(buf, fmt, user, pass) == 0) { + res = -1; + } + + MG_FREE(buf); + return res; +} + +#if MG_ENABLE_FILESYSTEM +static int mg_is_file_hidden(const char *path, + const struct mg_serve_http_opts *opts, + int exclude_specials) { + const char *p1 = opts->per_directory_auth_file; + const char *p2 = opts->hidden_file_pattern; + + /* Strip directory path from the file name */ + const char *pdir = strrchr(path, DIRSEP); + if (pdir != NULL) { + path = pdir + 1; + } + + return (exclude_specials && (!strcmp(path, ".") || !strcmp(path, ".."))) || + (p1 != NULL && mg_match_prefix(p1, strlen(p1), path) == strlen(p1)) || + (p2 != NULL && mg_match_prefix(p2, strlen(p2), path) > 0); +} + +#if !MG_DISABLE_HTTP_DIGEST_AUTH + +#ifndef MG_EXT_MD5 +void mg_hash_md5_v(size_t num_msgs, const uint8_t *msgs[], + const size_t *msg_lens, uint8_t *digest) { + size_t i; + cs_md5_ctx md5_ctx; + cs_md5_init(&md5_ctx); + for (i = 0; i < num_msgs; i++) { + cs_md5_update(&md5_ctx, msgs[i], msg_lens[i]); + } + cs_md5_final(digest, &md5_ctx); +} +#else +extern void mg_hash_md5_v(size_t num_msgs, const uint8_t *msgs[], + const size_t *msg_lens, uint8_t *digest); +#endif + +void cs_md5(char buf[33], ...) { + unsigned char hash[16]; + const uint8_t *msgs[20], *p; + size_t msg_lens[20]; + size_t num_msgs = 0; + va_list ap; + + va_start(ap, buf); + while ((p = va_arg(ap, const unsigned char *) ) != NULL) { + msgs[num_msgs] = p; + msg_lens[num_msgs] = va_arg(ap, size_t); + num_msgs++; + } + va_end(ap); + + mg_hash_md5_v(num_msgs, msgs, msg_lens, hash); + cs_to_hex(buf, hash, sizeof(hash)); +} + +static void mg_mkmd5resp(const char *method, size_t method_len, const char *uri, + size_t uri_len, const char *ha1, size_t ha1_len, + const char *nonce, size_t nonce_len, const char *nc, + size_t nc_len, const char *cnonce, size_t cnonce_len, + const char *qop, size_t qop_len, char *resp) { + static const char colon[] = ":"; + static const size_t one = 1; + char ha2[33]; + cs_md5(ha2, method, method_len, colon, one, uri, uri_len, NULL); + cs_md5(resp, ha1, ha1_len, colon, one, nonce, nonce_len, colon, one, nc, + nc_len, colon, one, cnonce, cnonce_len, colon, one, qop, qop_len, + colon, one, ha2, sizeof(ha2) - 1, NULL); +} + +int mg_http_create_digest_auth_header(char *buf, size_t buf_len, + const char *method, const char *uri, + const char *auth_domain, const char *user, + const char *passwd, const char *nonce) { + static const char colon[] = ":", qop[] = "auth"; + static const size_t one = 1; + char ha1[33], resp[33], cnonce[40]; + + snprintf(cnonce, sizeof(cnonce), "%lx", (unsigned long) mg_time()); + cs_md5(ha1, user, (size_t) strlen(user), colon, one, auth_domain, + (size_t) strlen(auth_domain), colon, one, passwd, + (size_t) strlen(passwd), NULL); + mg_mkmd5resp(method, strlen(method), uri, strlen(uri), ha1, sizeof(ha1) - 1, + nonce, strlen(nonce), "1", one, cnonce, strlen(cnonce), qop, + sizeof(qop) - 1, resp); + return snprintf(buf, buf_len, + "Authorization: Digest username=\"%s\"," + "realm=\"%s\",uri=\"%s\",qop=%s,nc=1,cnonce=%s," + "nonce=%s,response=%s\r\n", + user, auth_domain, uri, qop, cnonce, nonce, resp); +} + +/* + * Check for authentication timeout. + * Clients send time stamp encoded in nonce. Make sure it is not too old, + * to prevent replay attacks. + * Assumption: nonce is a hexadecimal number of seconds since 1970. + */ +static int mg_check_nonce(const char *nonce) { + unsigned long now = (unsigned long) mg_time(); + unsigned long val = (unsigned long) strtoul(nonce, NULL, 16); + return (now >= val) && (now - val < 60 * 60); +} + +int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain, + FILE *fp) { + int ret = 0; + struct mg_str *hdr; + char username_buf[50], cnonce_buf[64], response_buf[40], uri_buf[200], + qop_buf[20], nc_buf[20], nonce_buf[16]; + + char *username = username_buf, *cnonce = cnonce_buf, *response = response_buf, + *uri = uri_buf, *qop = qop_buf, *nc = nc_buf, *nonce = nonce_buf; + + /* Parse "Authorization:" header, fail fast on parse error */ + if (hm == NULL || fp == NULL || + (hdr = mg_get_http_header(hm, "Authorization")) == NULL || + mg_http_parse_header2(hdr, "username", &username, sizeof(username_buf)) == + 0 || + mg_http_parse_header2(hdr, "cnonce", &cnonce, sizeof(cnonce_buf)) == 0 || + mg_http_parse_header2(hdr, "response", &response, sizeof(response_buf)) == + 0 || + mg_http_parse_header2(hdr, "uri", &uri, sizeof(uri_buf)) == 0 || + mg_http_parse_header2(hdr, "qop", &qop, sizeof(qop_buf)) == 0 || + mg_http_parse_header2(hdr, "nc", &nc, sizeof(nc_buf)) == 0 || + mg_http_parse_header2(hdr, "nonce", &nonce, sizeof(nonce_buf)) == 0 || + mg_check_nonce(nonce) == 0) { + ret = 0; + goto clean; + } + + /* NOTE(lsm): due to a bug in MSIE, we do not compare URIs */ + + ret = mg_check_digest_auth( + hm->method, + mg_mk_str_n( + hm->uri.p, + hm->uri.len + (hm->query_string.len ? hm->query_string.len + 1 : 0)), + mg_mk_str(username), mg_mk_str(cnonce), mg_mk_str(response), + mg_mk_str(qop), mg_mk_str(nc), mg_mk_str(nonce), mg_mk_str(auth_domain), + fp); + +clean: + if (username != username_buf) MG_FREE(username); + if (cnonce != cnonce_buf) MG_FREE(cnonce); + if (response != response_buf) MG_FREE(response); + if (uri != uri_buf) MG_FREE(uri); + if (qop != qop_buf) MG_FREE(qop); + if (nc != nc_buf) MG_FREE(nc); + if (nonce != nonce_buf) MG_FREE(nonce); + + return ret; +} + +int mg_check_digest_auth(struct mg_str method, struct mg_str uri, + struct mg_str username, struct mg_str cnonce, + struct mg_str response, struct mg_str qop, + struct mg_str nc, struct mg_str nonce, + struct mg_str auth_domain, FILE *fp) { + char buf[128], f_user[sizeof(buf)], f_ha1[sizeof(buf)], f_domain[sizeof(buf)]; + char expected_response[33]; + + /* + * Read passwords file line by line. If should have htdigest format, + * i.e. each line should be a colon-separated sequence: + * USER_NAME:DOMAIN_NAME:HA1_HASH_OF_USER_DOMAIN_AND_PASSWORD + */ + while (fgets(buf, sizeof(buf), fp) != NULL) { + if (sscanf(buf, "%[^:]:%[^:]:%s", f_user, f_domain, f_ha1) == 3 && + mg_vcmp(&username, f_user) == 0 && + mg_vcmp(&auth_domain, f_domain) == 0) { + /* Username and domain matched, check the password */ + mg_mkmd5resp(method.p, method.len, uri.p, uri.len, f_ha1, strlen(f_ha1), + nonce.p, nonce.len, nc.p, nc.len, cnonce.p, cnonce.len, + qop.p, qop.len, expected_response); + LOG(LL_DEBUG, + ("%.*s %s %.*s %s", (int) username.len, username.p, f_domain, + (int) response.len, response.p, expected_response)); + return mg_ncasecmp(response.p, expected_response, response.len) == 0; + } + } + + /* None of the entries in the passwords file matched - return failure */ + return 0; +} + +int mg_http_is_authorized(struct http_message *hm, struct mg_str path, + const char *domain, const char *passwords_file, + int flags) { + char buf[MG_MAX_PATH]; + const char *p; + FILE *fp; + int authorized = 1; + + if (domain != NULL && passwords_file != NULL) { + if (flags & MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE) { + fp = mg_fopen(passwords_file, "r"); + } else if (flags & MG_AUTH_FLAG_IS_DIRECTORY) { + snprintf(buf, sizeof(buf), "%.*s%c%s", (int) path.len, path.p, DIRSEP, + passwords_file); + fp = mg_fopen(buf, "r"); + } else { + p = strrchr(path.p, DIRSEP); + if (p == NULL) p = path.p; + snprintf(buf, sizeof(buf), "%.*s%c%s", (int) (p - path.p), path.p, DIRSEP, + passwords_file); + fp = mg_fopen(buf, "r"); + } + + if (fp != NULL) { + authorized = mg_http_check_digest_auth(hm, domain, fp); + fclose(fp); + } else if (!(flags & MG_AUTH_FLAG_ALLOW_MISSING_FILE)) { + authorized = 0; + } + } + + LOG(LL_DEBUG, ("%.*s %s %x %d", (int) path.len, path.p, + passwords_file ? passwords_file : "", flags, authorized)); + return authorized; +} +#else +int mg_http_is_authorized(struct http_message *hm, const struct mg_str path, + const char *domain, const char *passwords_file, + int flags) { + (void) hm; + (void) path; + (void) domain; + (void) passwords_file; + (void) flags; + return 1; +} +#endif + +#if MG_ENABLE_DIRECTORY_LISTING +static void mg_escape(const char *src, char *dst, size_t dst_len) { + size_t n = 0; + while (*src != '\0' && n + 5 < dst_len) { + unsigned char ch = *(unsigned char *) src++; + if (ch == '<') { + n += snprintf(dst + n, dst_len - n, "%s", "<"); + } else { + dst[n++] = ch; + } + } + dst[n] = '\0'; +} + +static void mg_print_dir_entry(struct mg_connection *nc, const char *file_name, + cs_stat_t *stp) { + char size[64], mod[64], path[MG_MAX_PATH]; + int64_t fsize = stp->st_size; + int is_dir = S_ISDIR(stp->st_mode); + const char *slash = is_dir ? "/" : ""; + struct mg_str href; + + if (is_dir) { + snprintf(size, sizeof(size), "%s", "[DIRECTORY]"); + } else { + /* + * We use (double) cast below because MSVC 6 compiler cannot + * convert unsigned __int64 to double. + */ + if (fsize < 1024) { + snprintf(size, sizeof(size), "%d", (int) fsize); + } else if (fsize < 0x100000) { + snprintf(size, sizeof(size), "%.1fk", (double) fsize / 1024.0); + } else if (fsize < 0x40000000) { + snprintf(size, sizeof(size), "%.1fM", (double) fsize / 1048576); + } else { + snprintf(size, sizeof(size), "%.1fG", (double) fsize / 1073741824); + } + } + strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", localtime(&stp->st_mtime)); + mg_escape(file_name, path, sizeof(path)); + href = mg_url_encode(mg_mk_str(file_name)); + mg_printf_http_chunk(nc, + "%s%s" + "%s%s\n", + href.p, slash, path, slash, mod, is_dir ? -1 : fsize, + size); + free((void *) href.p); +} + +static void mg_scan_directory(struct mg_connection *nc, const char *dir, + const struct mg_serve_http_opts *opts, + void (*func)(struct mg_connection *, const char *, + cs_stat_t *)) { + char path[MG_MAX_PATH]; + cs_stat_t st; + struct dirent *dp; + DIR *dirp; + + LOG(LL_DEBUG, ("%p [%s]", nc, dir)); + if ((dirp = (opendir(dir))) != NULL) { + while ((dp = readdir(dirp)) != NULL) { + /* Do not show current dir and hidden files */ + if (mg_is_file_hidden((const char *) dp->d_name, opts, 1)) { + continue; + } + snprintf(path, sizeof(path), "%s/%s", dir, dp->d_name); + if (mg_stat(path, &st) == 0) { + func(nc, (const char *) dp->d_name, &st); + } + } + closedir(dirp); + } else { + LOG(LL_DEBUG, ("%p opendir(%s) -> %d", nc, dir, mg_get_errno())); + } +} + +static void mg_send_directory_listing(struct mg_connection *nc, const char *dir, + struct http_message *hm, + struct mg_serve_http_opts *opts) { + static const char *sort_js_code = + ""; + + mg_send_response_line(nc, 200, opts->extra_headers); + mg_printf(nc, "%s: %s\r\n%s: %s\r\n\r\n", "Transfer-Encoding", "chunked", + "Content-Type", "text/html; charset=utf-8"); + + mg_printf_http_chunk( + nc, + "Index of %.*s%s%s" + "\n" + "

Index of %.*s

\n" + "" + "\n" + "\n" + "", + (int) hm->uri.len, hm->uri.p, sort_js_code, sort_js_code2, + (int) hm->uri.len, hm->uri.p); + mg_scan_directory(nc, dir, opts, mg_print_dir_entry); + mg_printf_http_chunk(nc, + "\n" + "
Name" + "Modified" + "Size


\n" + "
%s
\n" + "", + mg_version_header); + mg_send_http_chunk(nc, "", 0); + /* TODO(rojer): Remove when cesanta/dev/issues/197 is fixed. */ + nc->flags |= MG_F_SEND_AND_CLOSE; +} +#endif /* MG_ENABLE_DIRECTORY_LISTING */ + +/* + * Given a directory path, find one of the files specified in the + * comma-separated list of index files `list`. + * First found index file wins. If an index file is found, then gets + * appended to the `path`, stat-ed, and result of `stat()` passed to `stp`. + * If index file is not found, then `path` and `stp` remain unchanged. + */ +MG_INTERNAL void mg_find_index_file(const char *path, const char *list, + char **index_file, cs_stat_t *stp) { + struct mg_str vec; + size_t path_len = strlen(path); + int found = 0; + *index_file = NULL; + + /* Traverse index files list. For each entry, append it to the given */ + /* path and see if the file exists. If it exists, break the loop */ + while ((list = mg_next_comma_list_entry(list, &vec, NULL)) != NULL) { + cs_stat_t st; + size_t len = path_len + 1 + vec.len + 1; + *index_file = (char *) MG_REALLOC(*index_file, len); + if (*index_file == NULL) break; + snprintf(*index_file, len, "%s%c%.*s", path, DIRSEP, (int) vec.len, vec.p); + + /* Does it exist? Is it a file? */ + if (mg_stat(*index_file, &st) == 0 && S_ISREG(st.st_mode)) { + /* Yes it does, break the loop */ + *stp = st; + found = 1; + break; + } + } + if (!found) { + MG_FREE(*index_file); + *index_file = NULL; + } + LOG(LL_DEBUG, ("[%s] [%s]", path, (*index_file ? *index_file : ""))); +} + +#if MG_ENABLE_HTTP_URL_REWRITES +static int mg_http_send_port_based_redirect( + struct mg_connection *c, struct http_message *hm, + const struct mg_serve_http_opts *opts) { + const char *rewrites = opts->url_rewrites; + struct mg_str a, b; + char local_port[20] = {'%'}; + + mg_conn_addr_to_str(c, local_port + 1, sizeof(local_port) - 1, + MG_SOCK_STRINGIFY_PORT); + + while ((rewrites = mg_next_comma_list_entry(rewrites, &a, &b)) != NULL) { + if (mg_vcmp(&a, local_port) == 0) { + mg_send_response_line(c, 301, NULL); + mg_printf(c, "Content-Length: 0\r\nLocation: %.*s%.*s\r\n\r\n", + (int) b.len, b.p, (int) (hm->proto.p - hm->uri.p - 1), + hm->uri.p); + return 1; + } + } + + return 0; +} + +static void mg_reverse_proxy_handler(struct mg_connection *nc, int ev, + void *ev_data MG_UD_ARG(void *user_data)) { + struct http_message *hm = (struct http_message *) ev_data; + struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); + + if (pd == NULL || pd->reverse_proxy_data.linked_conn == NULL) { + DBG(("%p: upstream closed", nc)); + return; + } + + switch (ev) { + case MG_EV_CONNECT: + if (*(int *) ev_data != 0) { + mg_http_send_error(pd->reverse_proxy_data.linked_conn, 502, NULL); + } + break; + /* TODO(mkm): handle streaming */ + case MG_EV_HTTP_REPLY: + mg_send(pd->reverse_proxy_data.linked_conn, hm->message.p, + hm->message.len); + pd->reverse_proxy_data.linked_conn->flags |= MG_F_SEND_AND_CLOSE; + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + break; + case MG_EV_CLOSE: + pd->reverse_proxy_data.linked_conn->flags |= MG_F_SEND_AND_CLOSE; + break; + } + +#if MG_ENABLE_CALLBACK_USERDATA + (void) user_data; +#endif +} + +void mg_http_reverse_proxy(struct mg_connection *nc, + const struct http_message *hm, struct mg_str mount, + struct mg_str upstream) { + struct mg_connection *be; + char burl[256], *purl = burl; + int i; + const char *error; + struct mg_connect_opts opts; + struct mg_str path = MG_NULL_STR, user_info = MG_NULL_STR, host = MG_NULL_STR; + memset(&opts, 0, sizeof(opts)); + opts.error_string = &error; + + mg_asprintf(&purl, sizeof(burl), "%.*s%.*s", (int) upstream.len, upstream.p, + (int) (hm->uri.len - mount.len), hm->uri.p + mount.len); + + be = mg_connect_http_base(nc->mgr, MG_CB(mg_reverse_proxy_handler, NULL), + opts, "http", NULL, "https", NULL, purl, &path, + &user_info, &host); + LOG(LL_DEBUG, ("Proxying %.*s to %s (rule: %.*s)", (int) hm->uri.len, + hm->uri.p, purl, (int) mount.len, mount.p)); + + if (be == NULL) { + LOG(LL_ERROR, ("Error connecting to %s: %s", purl, error)); + mg_http_send_error(nc, 502, NULL); + goto cleanup; + } + + /* link connections to each other, they must live and die together */ + mg_http_get_proto_data(be)->reverse_proxy_data.linked_conn = nc; + mg_http_get_proto_data(nc)->reverse_proxy_data.linked_conn = be; + + /* send request upstream */ + mg_printf(be, "%.*s %.*s HTTP/1.1\r\n", (int) hm->method.len, hm->method.p, + (int) path.len, path.p); + + mg_printf(be, "Host: %.*s\r\n", (int) host.len, host.p); + for (i = 0; i < MG_MAX_HTTP_HEADERS && hm->header_names[i].len > 0; i++) { + struct mg_str hn = hm->header_names[i]; + struct mg_str hv = hm->header_values[i]; + + /* we rewrite the host header */ + if (mg_vcasecmp(&hn, "Host") == 0) continue; + /* + * Don't pass chunked transfer encoding to the client because hm->body is + * already dechunked when we arrive here. + */ + if (mg_vcasecmp(&hn, "Transfer-encoding") == 0 && + mg_vcasecmp(&hv, "chunked") == 0) { + mg_printf(be, "Content-Length: %" SIZE_T_FMT "\r\n", hm->body.len); + continue; + } + /* We don't support proxying Expect: 100-continue. */ + if (mg_vcasecmp(&hn, "Expect") == 0 && + mg_vcasecmp(&hv, "100-continue") == 0) { + continue; + } + + mg_printf(be, "%.*s: %.*s\r\n", (int) hn.len, hn.p, (int) hv.len, hv.p); + } + + mg_send(be, "\r\n", 2); + mg_send(be, hm->body.p, hm->body.len); + +cleanup: + if (purl != burl) MG_FREE(purl); +} + +static int mg_http_handle_forwarding(struct mg_connection *nc, + struct http_message *hm, + const struct mg_serve_http_opts *opts) { + const char *rewrites = opts->url_rewrites; + struct mg_str a, b; + struct mg_str p1 = MG_MK_STR("http://"), p2 = MG_MK_STR("https://"); + + while ((rewrites = mg_next_comma_list_entry(rewrites, &a, &b)) != NULL) { + if (mg_strncmp(a, hm->uri, a.len) == 0) { + if (mg_strncmp(b, p1, p1.len) == 0 || mg_strncmp(b, p2, p2.len) == 0) { + mg_http_reverse_proxy(nc, hm, a, b); + return 1; + } + } + } + + return 0; +} +#endif /* MG_ENABLE_FILESYSTEM */ + +MG_INTERNAL int mg_uri_to_local_path(struct http_message *hm, + const struct mg_serve_http_opts *opts, + char **local_path, + struct mg_str *remainder) { + int ok = 1; + const char *cp = hm->uri.p, *cp_end = hm->uri.p + hm->uri.len; + struct mg_str root = {NULL, 0}; + const char *file_uri_start = cp; + *local_path = NULL; + remainder->p = NULL; + remainder->len = 0; + + { /* 1. Determine which root to use. */ + +#if MG_ENABLE_HTTP_URL_REWRITES + const char *rewrites = opts->url_rewrites; +#else + const char *rewrites = ""; +#endif + struct mg_str *hh = mg_get_http_header(hm, "Host"); + struct mg_str a, b; + /* Check rewrites first. */ + while ((rewrites = mg_next_comma_list_entry(rewrites, &a, &b)) != NULL) { + if (a.len > 1 && a.p[0] == '@') { + /* Host rewrite. */ + if (hh != NULL && hh->len == a.len - 1 && + mg_ncasecmp(a.p + 1, hh->p, a.len - 1) == 0) { + root = b; + break; + } + } else { + /* Regular rewrite, URI=directory */ + size_t match_len = mg_match_prefix_n(a, hm->uri); + if (match_len > 0) { + file_uri_start = hm->uri.p + match_len; + if (*file_uri_start == '/' || file_uri_start == cp_end) { + /* Match ended at component boundary, ok. */ + } else if (*(file_uri_start - 1) == '/') { + /* Pattern ends with '/', backtrack. */ + file_uri_start--; + } else { + /* No match: must fall on the component boundary. */ + continue; + } + root = b; + break; + } + } + } + /* If no rewrite rules matched, use DAV or regular document root. */ + if (root.p == NULL) { +#if MG_ENABLE_HTTP_WEBDAV + if (opts->dav_document_root != NULL && mg_is_dav_request(&hm->method)) { + root.p = opts->dav_document_root; + root.len = strlen(opts->dav_document_root); + } else +#endif + { + root.p = opts->document_root; + root.len = strlen(opts->document_root); + } + } + assert(root.p != NULL && root.len > 0); + } + + { /* 2. Find where in the canonical URI path the local path ends. */ + const char *u = file_uri_start + 1; + char *lp = (char *) MG_MALLOC(root.len + hm->uri.len + 1); + char *lp_end = lp + root.len + hm->uri.len + 1; + char *p = lp, *ps; + int exists = 1; + if (lp == NULL) { + ok = 0; + goto out; + } + memcpy(p, root.p, root.len); + p += root.len; + if (*(p - 1) == DIRSEP) p--; + *p = '\0'; + ps = p; + + /* Chop off URI path components one by one and build local path. */ + while (u <= cp_end) { + const char *next = u; + struct mg_str component; + if (exists) { + cs_stat_t st; + exists = (mg_stat(lp, &st) == 0); + if (exists && S_ISREG(st.st_mode)) { + /* We found the terminal, the rest of the URI (if any) is path_info. + */ + if (*(u - 1) == '/') u--; + break; + } + } + if (u >= cp_end) break; + parse_uri_component((const char **) &next, cp_end, "/", &component); + if (component.len > 0) { + int len; + memmove(p + 1, component.p, component.len); + len = mg_url_decode(p + 1, component.len, p + 1, lp_end - p - 1, 0); + if (len <= 0) { + ok = 0; + break; + } + component.p = p + 1; + component.len = len; + if (mg_vcmp(&component, ".") == 0) { + /* Yum. */ + } else if (mg_vcmp(&component, "..") == 0) { + while (p > ps && *p != DIRSEP) p--; + *p = '\0'; + } else { + size_t i; +#ifdef _WIN32 + /* On Windows, make sure it's valid Unicode (no funny stuff). */ + wchar_t buf[MG_MAX_PATH * 2]; + if (to_wchar(component.p, buf, MG_MAX_PATH) == 0) { + DBG(("[%.*s] smells funny", (int) component.len, component.p)); + ok = 0; + break; + } +#endif + *p++ = DIRSEP; + /* No NULs and DIRSEPs in the component (percent-encoded). */ + for (i = 0; i < component.len; i++, p++) { + if (*p == '\0' || *p == DIRSEP +#ifdef _WIN32 + /* On Windows, "/" is also accepted, so check for that too. */ + || + *p == '/' +#endif + ) { + ok = 0; + break; + } + } + } + } + u = next; + } + if (ok) { + *local_path = lp; + if (u > cp_end) u = cp_end; + remainder->p = u; + remainder->len = cp_end - u; + } else { + MG_FREE(lp); + } + } + +out: + LOG(LL_DEBUG, + ("'%.*s' -> '%s' + '%.*s'", (int) hm->uri.len, hm->uri.p, + *local_path ? *local_path : "", (int) remainder->len, remainder->p)); + return ok; +} + +static int mg_get_month_index(const char *s) { + static const char *month_names[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + size_t i; + + for (i = 0; i < ARRAY_SIZE(month_names); i++) + if (!strcmp(s, month_names[i])) return (int) i; + + return -1; +} + +static int mg_num_leap_years(int year) { + return year / 4 - year / 100 + year / 400; +} + +/* Parse UTC date-time string, and return the corresponding time_t value. */ +MG_INTERNAL time_t mg_parse_date_string(const char *datetime) { + static const unsigned short days_before_month[] = { + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; + char month_str[32]; + int second, minute, hour, day, month, year, leap_days, days; + time_t result = (time_t) 0; + + if (((sscanf(datetime, "%d/%3s/%d %d:%d:%d", &day, month_str, &year, &hour, + &minute, &second) == 6) || + (sscanf(datetime, "%d %3s %d %d:%d:%d", &day, month_str, &year, &hour, + &minute, &second) == 6) || + (sscanf(datetime, "%*3s, %d %3s %d %d:%d:%d", &day, month_str, &year, + &hour, &minute, &second) == 6) || + (sscanf(datetime, "%d-%3s-%d %d:%d:%d", &day, month_str, &year, &hour, + &minute, &second) == 6)) && + year > 1970 && (month = mg_get_month_index(month_str)) != -1) { + leap_days = mg_num_leap_years(year) - mg_num_leap_years(1970); + year -= 1970; + days = year * 365 + days_before_month[month] + (day - 1) + leap_days; + result = days * 24 * 3600 + hour * 3600 + minute * 60 + second; + } + + return result; +} + +MG_INTERNAL int mg_is_not_modified(struct http_message *hm, cs_stat_t *st) { + struct mg_str *hdr; + if ((hdr = mg_get_http_header(hm, "If-None-Match")) != NULL) { + char etag[64]; + mg_http_construct_etag(etag, sizeof(etag), st); + return mg_vcasecmp(hdr, etag) == 0; + } else if ((hdr = mg_get_http_header(hm, "If-Modified-Since")) != NULL) { + return st->st_mtime <= mg_parse_date_string(hdr->p); + } else { + return 0; + } +} + +void mg_http_send_digest_auth_request(struct mg_connection *c, + const char *domain) { + mg_printf(c, + "HTTP/1.1 401 Unauthorized\r\n" + "WWW-Authenticate: Digest qop=\"auth\", " + "realm=\"%s\", nonce=\"%lx\"\r\n" + "Content-Length: 0\r\n\r\n", + domain, (unsigned long) mg_time()); +} + +static void mg_http_send_options(struct mg_connection *nc) { + mg_printf(nc, "%s", + "HTTP/1.1 200 OK\r\nAllow: GET, POST, HEAD, CONNECT, OPTIONS" +#if MG_ENABLE_HTTP_WEBDAV + ", MKCOL, PUT, DELETE, PROPFIND, MOVE\r\nDAV: 1,2" +#endif + "\r\n\r\n"); + nc->flags |= MG_F_SEND_AND_CLOSE; +} + +static int mg_is_creation_request(const struct http_message *hm) { + return mg_vcmp(&hm->method, "MKCOL") == 0 || mg_vcmp(&hm->method, "PUT") == 0; +} + +MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path, + const struct mg_str *path_info, + struct http_message *hm, + struct mg_serve_http_opts *opts) { + int exists, is_directory, is_cgi; +#if MG_ENABLE_HTTP_WEBDAV + int is_dav = mg_is_dav_request(&hm->method); +#else + int is_dav = 0; +#endif + char *index_file = NULL; + cs_stat_t st; + + exists = (mg_stat(path, &st) == 0); + is_directory = exists && S_ISDIR(st.st_mode); + + if (is_directory) + mg_find_index_file(path, opts->index_files, &index_file, &st); + + is_cgi = + (mg_match_prefix(opts->cgi_file_pattern, strlen(opts->cgi_file_pattern), + index_file ? index_file : path) > 0); + + LOG(LL_DEBUG, + ("%p %.*s [%s] exists=%d is_dir=%d is_dav=%d is_cgi=%d index=%s", nc, + (int) hm->method.len, hm->method.p, path, exists, is_directory, is_dav, + is_cgi, index_file ? index_file : "")); + + if (is_directory && hm->uri.p[hm->uri.len - 1] != '/' && !is_dav) { + mg_printf(nc, + "HTTP/1.1 301 Moved\r\nLocation: %.*s/\r\n" + "Content-Length: 0\r\n\r\n", + (int) hm->uri.len, hm->uri.p); + MG_FREE(index_file); + return; + } + + /* If we have path_info, the only way to handle it is CGI. */ + if (path_info->len > 0 && !is_cgi) { + mg_http_send_error(nc, 501, NULL); + MG_FREE(index_file); + return; + } + + if (is_dav && opts->dav_document_root == NULL) { + mg_http_send_error(nc, 501, NULL); + } else if (!mg_http_is_authorized( + hm, mg_mk_str(path), opts->auth_domain, opts->global_auth_file, + ((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) | + MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE | + MG_AUTH_FLAG_ALLOW_MISSING_FILE)) || + !mg_http_is_authorized( + hm, mg_mk_str(path), opts->auth_domain, + opts->per_directory_auth_file, + ((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) | + MG_AUTH_FLAG_ALLOW_MISSING_FILE))) { + mg_http_send_digest_auth_request(nc, opts->auth_domain); + } else if (is_cgi) { +#if MG_ENABLE_HTTP_CGI + mg_handle_cgi(nc, index_file ? index_file : path, path_info, hm, opts); +#else + mg_http_send_error(nc, 501, NULL); +#endif /* MG_ENABLE_HTTP_CGI */ + } else if ((!exists || + mg_is_file_hidden(path, opts, 0 /* specials are ok */)) && + !mg_is_creation_request(hm)) { + mg_http_send_error(nc, 404, NULL); +#if MG_ENABLE_HTTP_WEBDAV + } else if (!mg_vcmp(&hm->method, "PROPFIND")) { + mg_handle_propfind(nc, path, &st, hm, opts); +#if !MG_DISABLE_DAV_AUTH + } else if (is_dav && + (opts->dav_auth_file == NULL || + (strcmp(opts->dav_auth_file, "-") != 0 && + !mg_http_is_authorized( + hm, mg_mk_str(path), opts->auth_domain, opts->dav_auth_file, + ((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) | + MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE | + MG_AUTH_FLAG_ALLOW_MISSING_FILE))))) { + mg_http_send_digest_auth_request(nc, opts->auth_domain); +#endif + } else if (!mg_vcmp(&hm->method, "MKCOL")) { + mg_handle_mkcol(nc, path, hm); + } else if (!mg_vcmp(&hm->method, "DELETE")) { + mg_handle_delete(nc, opts, path); + } else if (!mg_vcmp(&hm->method, "PUT")) { + mg_handle_put(nc, path, hm); + } else if (!mg_vcmp(&hm->method, "MOVE")) { + mg_handle_move(nc, opts, path, hm); +#if MG_ENABLE_FAKE_DAVLOCK + } else if (!mg_vcmp(&hm->method, "LOCK")) { + mg_handle_lock(nc, path); +#endif +#endif /* MG_ENABLE_HTTP_WEBDAV */ + } else if (!mg_vcmp(&hm->method, "OPTIONS")) { + mg_http_send_options(nc); + } else if (is_directory && index_file == NULL) { +#if MG_ENABLE_DIRECTORY_LISTING + if (strcmp(opts->enable_directory_listing, "yes") == 0) { + mg_send_directory_listing(nc, path, hm, opts); + } else { + mg_http_send_error(nc, 403, NULL); + } +#else + mg_http_send_error(nc, 501, NULL); +#endif + } else if (mg_is_not_modified(hm, &st)) { + mg_http_send_error(nc, 304, "Not Modified"); + } else { + mg_http_serve_file2(nc, index_file ? index_file : path, hm, opts); + } + MG_FREE(index_file); +} + +void mg_serve_http(struct mg_connection *nc, struct http_message *hm, + struct mg_serve_http_opts opts) { + char *path = NULL; + struct mg_str *hdr, path_info; + uint32_t remote_ip = ntohl(*(uint32_t *) &nc->sa.sin.sin_addr); + + if (mg_check_ip_acl(opts.ip_acl, remote_ip) != 1) { + /* Not allowed to connect */ + mg_http_send_error(nc, 403, NULL); + nc->flags |= MG_F_SEND_AND_CLOSE; + return; + } + +#if MG_ENABLE_HTTP_URL_REWRITES + if (mg_http_handle_forwarding(nc, hm, &opts)) { + return; + } + + if (mg_http_send_port_based_redirect(nc, hm, &opts)) { + return; + } +#endif + + if (opts.document_root == NULL) { + opts.document_root = "."; + } + if (opts.per_directory_auth_file == NULL) { + opts.per_directory_auth_file = ".htpasswd"; + } + if (opts.enable_directory_listing == NULL) { + opts.enable_directory_listing = "yes"; + } + if (opts.cgi_file_pattern == NULL) { + opts.cgi_file_pattern = "**.cgi$|**.php$"; + } + if (opts.ssi_pattern == NULL) { + opts.ssi_pattern = "**.shtml$|**.shtm$"; + } + if (opts.index_files == NULL) { + opts.index_files = "index.html,index.htm,index.shtml,index.cgi,index.php"; + } + /* Normalize path - resolve "." and ".." (in-place). */ + if (!mg_normalize_uri_path(&hm->uri, &hm->uri)) { + mg_http_send_error(nc, 400, NULL); + return; + } + if (mg_uri_to_local_path(hm, &opts, &path, &path_info) == 0) { + mg_http_send_error(nc, 404, NULL); + return; + } + mg_send_http_file(nc, path, &path_info, hm, &opts); + + MG_FREE(path); + path = NULL; + + /* Close connection for non-keep-alive requests */ + if (mg_vcmp(&hm->proto, "HTTP/1.1") != 0 || + ((hdr = mg_get_http_header(hm, "Connection")) != NULL && + mg_vcmp(hdr, "keep-alive") != 0)) { +#if 0 + nc->flags |= MG_F_SEND_AND_CLOSE; +#endif + } +} + +#if MG_ENABLE_HTTP_STREAMING_MULTIPART +void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data, + mg_fu_fname_fn local_name_fn + MG_UD_ARG(void *user_data)) { + switch (ev) { + case MG_EV_HTTP_PART_BEGIN: { + struct mg_http_multipart_part *mp = + (struct mg_http_multipart_part *) ev_data; + struct file_upload_state *fus = + (struct file_upload_state *) MG_CALLOC(1, sizeof(*fus)); + struct mg_str lfn = local_name_fn(nc, mg_mk_str(mp->file_name)); + mp->user_data = NULL; + if (lfn.p == NULL || lfn.len == 0) { + LOG(LL_ERROR, ("%p Not allowed to upload %s", nc, mp->file_name)); + mg_printf(nc, + "HTTP/1.1 403 Not Allowed\r\n" + "Content-Type: text/plain\r\n" + "Connection: close\r\n\r\n" + "Not allowed to upload %s\r\n", + mp->file_name); + nc->flags |= MG_F_SEND_AND_CLOSE; + return; + } + fus->lfn = (char *) MG_MALLOC(lfn.len + 1); + memcpy(fus->lfn, lfn.p, lfn.len); + fus->lfn[lfn.len] = '\0'; + if (lfn.p != mp->file_name) MG_FREE((char *) lfn.p); + LOG(LL_DEBUG, + ("%p Receiving file %s -> %s", nc, mp->file_name, fus->lfn)); + fus->fp = mg_fopen(fus->lfn, "w"); + if (fus->fp == NULL) { + mg_printf(nc, + "HTTP/1.1 500 Internal Server Error\r\n" + "Content-Type: text/plain\r\n" + "Connection: close\r\n\r\n"); + LOG(LL_ERROR, ("Failed to open %s: %d\n", fus->lfn, mg_get_errno())); + mg_printf(nc, "Failed to open %s: %d\n", fus->lfn, mg_get_errno()); + /* Do not close the connection just yet, discard remainder of the data. + * This is because at the time of writing some browsers (Chrome) fail to + * render response before all the data is sent. */ + } + mp->user_data = (void *) fus; + break; + } + case MG_EV_HTTP_PART_DATA: { + struct mg_http_multipart_part *mp = + (struct mg_http_multipart_part *) ev_data; + struct file_upload_state *fus = + (struct file_upload_state *) mp->user_data; + if (fus == NULL || fus->fp == NULL) break; + if (mg_fwrite(mp->data.p, 1, mp->data.len, fus->fp) != mp->data.len) { + LOG(LL_ERROR, ("Failed to write to %s: %d, wrote %d", fus->lfn, + mg_get_errno(), (int) fus->num_recd)); + if (mg_get_errno() == ENOSPC +#ifdef SPIFFS_ERR_FULL + || mg_get_errno() == SPIFFS_ERR_FULL +#endif + ) { + mg_printf(nc, + "HTTP/1.1 413 Payload Too Large\r\n" + "Content-Type: text/plain\r\n" + "Connection: close\r\n\r\n"); + mg_printf(nc, "Failed to write to %s: no space left; wrote %d\r\n", + fus->lfn, (int) fus->num_recd); + } else { + mg_printf(nc, + "HTTP/1.1 500 Internal Server Error\r\n" + "Content-Type: text/plain\r\n" + "Connection: close\r\n\r\n"); + mg_printf(nc, "Failed to write to %s: %d, wrote %d", mp->file_name, + mg_get_errno(), (int) fus->num_recd); + } + fclose(fus->fp); + remove(fus->lfn); + fus->fp = NULL; + /* Do not close the connection just yet, discard remainder of the data. + * This is because at the time of writing some browsers (Chrome) fail to + * render response before all the data is sent. */ + return; + } + fus->num_recd += mp->data.len; + LOG(LL_DEBUG, ("%p rec'd %d bytes, %d total", nc, (int) mp->data.len, + (int) fus->num_recd)); + break; + } + case MG_EV_HTTP_PART_END: { + struct mg_http_multipart_part *mp = + (struct mg_http_multipart_part *) ev_data; + struct file_upload_state *fus = + (struct file_upload_state *) mp->user_data; + if (fus == NULL) break; + if (mp->status >= 0 && fus->fp != NULL) { + LOG(LL_DEBUG, ("%p Uploaded %s (%s), %d bytes", nc, mp->file_name, + fus->lfn, (int) fus->num_recd)); + mg_printf(nc, + "HTTP/1.1 200 OK\r\n" + "Content-Type: text/plain\r\n" + "Connection: close\r\n\r\n" + "Ok, %s - %d bytes.\r\n", + mp->file_name, (int) fus->num_recd); + } else { + LOG(LL_ERROR, ("Failed to store %s (%s)", mp->file_name, fus->lfn)); + /* + * mp->status < 0 means connection was terminated, so no reason to send + * HTTP reply + */ + } + if (fus->fp != NULL) fclose(fus->fp); + MG_FREE(fus->lfn); + MG_FREE(fus); + mp->user_data = NULL; + nc->flags |= MG_F_SEND_AND_CLOSE; + break; + } + } + +#if MG_ENABLE_CALLBACK_USERDATA + (void) user_data; +#endif +} + +#endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */ +#endif /* MG_ENABLE_FILESYSTEM */ + +struct mg_connection *mg_connect_http_base( + struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data), + struct mg_connect_opts opts, const char *scheme1, const char *scheme2, + const char *scheme_ssl1, const char *scheme_ssl2, const char *url, + struct mg_str *path, struct mg_str *user_info, struct mg_str *host) { + struct mg_connection *nc = NULL; + unsigned int port_i = 0; + int use_ssl = 0; + struct mg_str scheme, query, fragment; + char conn_addr_buf[2]; + char *conn_addr = conn_addr_buf; + + if (mg_parse_uri(mg_mk_str(url), &scheme, user_info, host, &port_i, path, + &query, &fragment) != 0) { + MG_SET_PTRPTR(opts.error_string, "cannot parse url"); + goto out; + } + + /* If query is present, do not strip it. Pass to the caller. */ + if (query.len > 0) path->len += query.len + 1; + + if (scheme.len == 0 || mg_vcmp(&scheme, scheme1) == 0 || + (scheme2 != NULL && mg_vcmp(&scheme, scheme2) == 0)) { + use_ssl = 0; + if (port_i == 0) port_i = 80; + } else if (mg_vcmp(&scheme, scheme_ssl1) == 0 || + (scheme2 != NULL && mg_vcmp(&scheme, scheme_ssl2) == 0)) { + use_ssl = 1; + if (port_i == 0) port_i = 443; + } else { + goto out; + } + + mg_asprintf(&conn_addr, sizeof(conn_addr_buf), "tcp://%.*s:%u", + (int) host->len, host->p, port_i); + if (conn_addr == NULL) goto out; + + LOG(LL_DEBUG, ("%s use_ssl? %d %s", url, use_ssl, conn_addr)); + if (use_ssl) { +#if MG_ENABLE_SSL + /* + * Schema requires SSL, but no SSL parameters were provided in opts. + * In order to maintain backward compatibility, use a faux-SSL with no + * verification. + */ + if (opts.ssl_ca_cert == NULL) { + opts.ssl_ca_cert = "*"; + } +#else + MG_SET_PTRPTR(opts.error_string, "ssl is disabled"); + goto out; +#endif + } + + if ((nc = mg_connect_opt(mgr, conn_addr, MG_CB(ev_handler, user_data), + opts)) != NULL) { + mg_set_protocol_http_websocket(nc); + } + +out: + if (conn_addr != NULL && conn_addr != conn_addr_buf) MG_FREE(conn_addr); + return nc; +} + +struct mg_connection *mg_connect_http_opt( + struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data), + struct mg_connect_opts opts, const char *url, const char *extra_headers, + const char *post_data) { + struct mg_str user = MG_NULL_STR, null_str = MG_NULL_STR; + struct mg_str host = MG_NULL_STR, path = MG_NULL_STR; + struct mbuf auth; + struct mg_connection *nc = + mg_connect_http_base(mgr, MG_CB(ev_handler, user_data), opts, "http", + NULL, "https", NULL, url, &path, &user, &host); + + if (nc == NULL) { + return NULL; + } + + mbuf_init(&auth, 0); + if (user.len > 0) { + mg_basic_auth_header(user, null_str, &auth); + } + + if (post_data == NULL) post_data = ""; + if (extra_headers == NULL) extra_headers = ""; + if (path.len == 0) path = mg_mk_str("/"); + if (host.len == 0) host = mg_mk_str(""); + + mg_printf(nc, "%s %.*s HTTP/1.1\r\nHost: %.*s\r\nContent-Length: %" SIZE_T_FMT + "\r\n%.*s%s\r\n%s", + (post_data[0] == '\0' ? "GET" : "POST"), (int) path.len, path.p, + (int) (path.p - host.p), host.p, strlen(post_data), (int) auth.len, + (auth.buf == NULL ? "" : auth.buf), extra_headers, post_data); + + mbuf_free(&auth); + return nc; +} + +struct mg_connection *mg_connect_http( + struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data), + const char *url, const char *extra_headers, const char *post_data) { + struct mg_connect_opts opts; + memset(&opts, 0, sizeof(opts)); + return mg_connect_http_opt(mgr, MG_CB(ev_handler, user_data), opts, url, + extra_headers, post_data); +} + +size_t mg_parse_multipart(const char *buf, size_t buf_len, char *var_name, + size_t var_name_len, char *file_name, + size_t file_name_len, const char **data, + size_t *data_len) { + static const char cd[] = "Content-Disposition: "; + size_t hl, bl, n, ll, pos, cdl = sizeof(cd) - 1; + int shl; + + if (buf == NULL || buf_len <= 0) return 0; + if ((shl = mg_http_get_request_len(buf, buf_len)) <= 0) return 0; + hl = shl; + if (buf[0] != '-' || buf[1] != '-' || buf[2] == '\n') return 0; + + /* Get boundary length */ + bl = mg_get_line_len(buf, buf_len); + + /* Loop through headers, fetch variable name and file name */ + var_name[0] = file_name[0] = '\0'; + for (n = bl; (ll = mg_get_line_len(buf + n, hl - n)) > 0; n += ll) { + if (mg_ncasecmp(cd, buf + n, cdl) == 0) { + struct mg_str header; + header.p = buf + n + cdl; + header.len = ll - (cdl + 2); + { + char *var_name2 = var_name; + mg_http_parse_header2(&header, "name", &var_name2, var_name_len); + /* TODO: handle reallocated buffer correctly */ + if (var_name2 != var_name) { + MG_FREE(var_name2); + var_name[0] = '\0'; + } + } + { + char *file_name2 = file_name; + mg_http_parse_header2(&header, "filename", &file_name2, file_name_len); + /* TODO: handle reallocated buffer correctly */ + if (file_name2 != file_name) { + MG_FREE(file_name2); + file_name[0] = '\0'; + } + } + } + } + + /* Scan through the body, search for terminating boundary */ + for (pos = hl; pos + (bl - 2) < buf_len; pos++) { + if (buf[pos] == '-' && !strncmp(buf, &buf[pos], bl - 2)) { + if (data_len != NULL) *data_len = (pos - 2) - hl; + if (data != NULL) *data = buf + hl; + return pos; + } + } + + return 0; +} + +void mg_register_http_endpoint_opt(struct mg_connection *nc, + const char *uri_path, + mg_event_handler_t handler, + struct mg_http_endpoint_opts opts) { + struct mg_http_proto_data *pd = NULL; + struct mg_http_endpoint *new_ep = NULL; + + if (nc == NULL) return; + new_ep = (struct mg_http_endpoint *) MG_CALLOC(1, sizeof(*new_ep)); + if (new_ep == NULL) return; + + pd = mg_http_get_proto_data(nc); + new_ep->uri_pattern = mg_strdup(mg_mk_str(uri_path)); + if (opts.auth_domain != NULL && opts.auth_file != NULL) { + new_ep->auth_domain = strdup(opts.auth_domain); + new_ep->auth_file = strdup(opts.auth_file); + } + new_ep->handler = handler; +#if MG_ENABLE_CALLBACK_USERDATA + new_ep->user_data = opts.user_data; +#endif + new_ep->next = pd->endpoints; + pd->endpoints = new_ep; +} + +static void mg_http_call_endpoint_handler(struct mg_connection *nc, int ev, + struct http_message *hm) { + struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); + void *user_data = nc->user_data; + + if (ev == MG_EV_HTTP_REQUEST +#if MG_ENABLE_HTTP_STREAMING_MULTIPART + || ev == MG_EV_HTTP_MULTIPART_REQUEST +#endif + ) { + struct mg_http_endpoint *ep = + mg_http_get_endpoint_handler(nc->listener, &hm->uri); + if (ep != NULL) { +#if MG_ENABLE_FILESYSTEM && !MG_DISABLE_HTTP_DIGEST_AUTH + if (!mg_http_is_authorized(hm, hm->uri, ep->auth_domain, ep->auth_file, + MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE)) { + mg_http_send_digest_auth_request(nc, ep->auth_domain); + return; + } +#endif + pd->endpoint_handler = ep->handler; +#if MG_ENABLE_CALLBACK_USERDATA + user_data = ep->user_data; +#endif + } + } + mg_call(nc, pd->endpoint_handler ? pd->endpoint_handler : nc->handler, + user_data, ev, hm); +} + +void mg_register_http_endpoint(struct mg_connection *nc, const char *uri_path, + MG_CB(mg_event_handler_t handler, + void *user_data)) { + struct mg_http_endpoint_opts opts; + memset(&opts, 0, sizeof(opts)); +#if MG_ENABLE_CALLBACK_USERDATA + opts.user_data = user_data; +#endif + mg_register_http_endpoint_opt(nc, uri_path, handler, opts); +} + +#endif /* MG_ENABLE_HTTP */ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_http_cgi.c" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef _WIN32 +#include +#endif + +#if MG_ENABLE_HTTP && MG_ENABLE_HTTP_CGI + +#ifndef MG_MAX_CGI_ENVIR_VARS +#define MG_MAX_CGI_ENVIR_VARS 64 +#endif + +#ifndef MG_ENV_EXPORT_TO_CGI +#define MG_ENV_EXPORT_TO_CGI "MONGOOSE_CGI" +#endif + +#define MG_F_HTTP_CGI_PARSE_HEADERS MG_F_USER_1 + +/* + * This structure helps to create an environment for the spawned CGI program. + * Environment is an array of "VARIABLE=VALUE\0" ASCIIZ strings, + * last element must be NULL. + * However, on Windows there is a requirement that all these VARIABLE=VALUE\0 + * strings must reside in a contiguous buffer. The end of the buffer is + * marked by two '\0' characters. + * We satisfy both worlds: we create an envp array (which is vars), all + * entries are actually pointers inside buf. + */ +struct mg_cgi_env_block { + struct mg_connection *nc; + char buf[MG_CGI_ENVIRONMENT_SIZE]; /* Environment buffer */ + const char *vars[MG_MAX_CGI_ENVIR_VARS]; /* char *envp[] */ + int len; /* Space taken */ + int nvars; /* Number of variables in envp[] */ +}; + +#ifdef _WIN32 +struct mg_threadparam { + sock_t s; + HANDLE hPipe; +}; + +static int mg_wait_until_ready(sock_t sock, int for_read) { + fd_set set; + FD_ZERO(&set); + FD_SET(sock, &set); + return select(sock + 1, for_read ? &set : 0, for_read ? 0 : &set, 0, 0) == 1; +} + +static void *mg_push_to_stdin(void *arg) { + struct mg_threadparam *tp = (struct mg_threadparam *) arg; + int n, sent, stop = 0; + DWORD k; + char buf[BUFSIZ]; + + while (!stop && mg_wait_until_ready(tp->s, 1) && + (n = recv(tp->s, buf, sizeof(buf), 0)) > 0) { + if (n == -1 && GetLastError() == WSAEWOULDBLOCK) continue; + for (sent = 0; !stop && sent < n; sent += k) { + if (!WriteFile(tp->hPipe, buf + sent, n - sent, &k, 0)) stop = 1; + } + } + DBG(("%s", "FORWARED EVERYTHING TO CGI")); + CloseHandle(tp->hPipe); + MG_FREE(tp); + return NULL; +} + +static void *mg_pull_from_stdout(void *arg) { + struct mg_threadparam *tp = (struct mg_threadparam *) arg; + int k = 0, stop = 0; + DWORD n, sent; + char buf[BUFSIZ]; + + while (!stop && ReadFile(tp->hPipe, buf, sizeof(buf), &n, NULL)) { + for (sent = 0; !stop && sent < n; sent += k) { + if (mg_wait_until_ready(tp->s, 0) && + (k = send(tp->s, buf + sent, n - sent, 0)) <= 0) + stop = 1; + } + } + DBG(("%s", "EOF FROM CGI")); + CloseHandle(tp->hPipe); + shutdown(tp->s, 2); // Without this, IO thread may get truncated data + closesocket(tp->s); + MG_FREE(tp); + return NULL; +} + +static void mg_spawn_stdio_thread(sock_t sock, HANDLE hPipe, + void *(*func)(void *)) { + struct mg_threadparam *tp = (struct mg_threadparam *) MG_MALLOC(sizeof(*tp)); + if (tp != NULL) { + tp->s = sock; + tp->hPipe = hPipe; + mg_start_thread(func, tp); + } +} + +static void mg_abs_path(const char *utf8_path, char *abs_path, size_t len) { + wchar_t buf[MG_MAX_PATH], buf2[MG_MAX_PATH]; + to_wchar(utf8_path, buf, ARRAY_SIZE(buf)); + GetFullPathNameW(buf, ARRAY_SIZE(buf2), buf2, NULL); + WideCharToMultiByte(CP_UTF8, 0, buf2, wcslen(buf2) + 1, abs_path, len, 0, 0); +} + +static int mg_start_process(const char *interp, const char *cmd, + const char *env, const char *envp[], + const char *dir, sock_t sock) { + STARTUPINFOW si; + PROCESS_INFORMATION pi; + HANDLE a[2], b[2], me = GetCurrentProcess(); + wchar_t wcmd[MG_MAX_PATH], full_dir[MG_MAX_PATH]; + char buf[MG_MAX_PATH], buf2[MG_MAX_PATH], buf5[MG_MAX_PATH], + buf4[MG_MAX_PATH], cmdline[MG_MAX_PATH]; + DWORD flags = DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS; + FILE *fp; + + memset(&si, 0, sizeof(si)); + memset(&pi, 0, sizeof(pi)); + + si.cb = sizeof(si); + si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + si.hStdError = GetStdHandle(STD_ERROR_HANDLE); + + CreatePipe(&a[0], &a[1], NULL, 0); + CreatePipe(&b[0], &b[1], NULL, 0); + DuplicateHandle(me, a[0], me, &si.hStdInput, 0, TRUE, flags); + DuplicateHandle(me, b[1], me, &si.hStdOutput, 0, TRUE, flags); + + if (interp == NULL && (fp = mg_fopen(cmd, "r")) != NULL) { + buf[0] = buf[1] = '\0'; + fgets(buf, sizeof(buf), fp); + buf[sizeof(buf) - 1] = '\0'; + if (buf[0] == '#' && buf[1] == '!') { + interp = buf + 2; + /* Trim leading spaces: https://github.com/cesanta/mongoose/issues/489 */ + while (*interp != '\0' && isspace(*(unsigned char *) interp)) { + interp++; + } + } + fclose(fp); + } + + snprintf(buf, sizeof(buf), "%s/%s", dir, cmd); + mg_abs_path(buf, buf2, ARRAY_SIZE(buf2)); + + mg_abs_path(dir, buf5, ARRAY_SIZE(buf5)); + to_wchar(dir, full_dir, ARRAY_SIZE(full_dir)); + + if (interp != NULL) { + mg_abs_path(interp, buf4, ARRAY_SIZE(buf4)); + snprintf(cmdline, sizeof(cmdline), "%s \"%s\"", buf4, buf2); + } else { + snprintf(cmdline, sizeof(cmdline), "\"%s\"", buf2); + } + to_wchar(cmdline, wcmd, ARRAY_SIZE(wcmd)); + + if (CreateProcessW(NULL, wcmd, NULL, NULL, TRUE, CREATE_NEW_PROCESS_GROUP, + (void *) env, full_dir, &si, &pi) != 0) { + mg_spawn_stdio_thread(sock, a[1], mg_push_to_stdin); + mg_spawn_stdio_thread(sock, b[0], mg_pull_from_stdout); + + CloseHandle(si.hStdOutput); + CloseHandle(si.hStdInput); + + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + } else { + CloseHandle(a[1]); + CloseHandle(b[0]); + closesocket(sock); + } + DBG(("CGI command: [%ls] -> %p", wcmd, pi.hProcess)); + + /* Not closing a[0] and b[1] because we've used DUPLICATE_CLOSE_SOURCE */ + (void) envp; + return (pi.hProcess != NULL); +} +#else +static int mg_start_process(const char *interp, const char *cmd, + const char *env, const char *envp[], + const char *dir, sock_t sock) { + char buf[500]; + pid_t pid = fork(); + (void) env; + + if (pid == 0) { + /* + * In Linux `chdir` declared with `warn_unused_result` attribute + * To shutup compiler we have yo use result in some way + */ + int tmp = chdir(dir); + (void) tmp; + (void) dup2(sock, 0); + (void) dup2(sock, 1); + closesocket(sock); + + /* + * After exec, all signal handlers are restored to their default values, + * with one exception of SIGCHLD. According to POSIX.1-2001 and Linux's + * implementation, SIGCHLD's handler will leave unchanged after exec + * if it was set to be ignored. Restore it to default action. + */ + signal(SIGCHLD, SIG_DFL); + + if (interp == NULL) { + execle(cmd, cmd, (char *) 0, envp); /* (char *) 0 to squash warning */ + } else { + execle(interp, interp, cmd, (char *) 0, envp); + } + snprintf(buf, sizeof(buf), + "Status: 500\r\n\r\n" + "500 Server Error: %s%s%s: %s", + interp == NULL ? "" : interp, interp == NULL ? "" : " ", cmd, + strerror(errno)); + send(1, buf, strlen(buf), 0); + _exit(EXIT_FAILURE); /* exec call failed */ + } + + return (pid != 0); +} +#endif /* _WIN32 */ + +/* + * Append VARIABLE=VALUE\0 string to the buffer, and add a respective + * pointer into the vars array. + */ +static char *mg_addenv(struct mg_cgi_env_block *block, const char *fmt, ...) { + int n, space; + char *added = block->buf + block->len; + va_list ap; + + /* Calculate how much space is left in the buffer */ + space = sizeof(block->buf) - (block->len + 2); + if (space > 0) { + /* Copy VARIABLE=VALUE\0 string into the free space */ + va_start(ap, fmt); + n = vsnprintf(added, (size_t) space, fmt, ap); + va_end(ap); + + /* Make sure we do not overflow buffer and the envp array */ + if (n > 0 && n + 1 < space && + block->nvars < (int) ARRAY_SIZE(block->vars) - 2) { + /* Append a pointer to the added string into the envp array */ + block->vars[block->nvars++] = added; + /* Bump up used length counter. Include \0 terminator */ + block->len += n + 1; + } + } + + return added; +} + +static void mg_addenv2(struct mg_cgi_env_block *blk, const char *name) { + const char *s; + if ((s = getenv(name)) != NULL) mg_addenv(blk, "%s=%s", name, s); +} + +static void mg_prepare_cgi_environment(struct mg_connection *nc, + const char *prog, + const struct mg_str *path_info, + const struct http_message *hm, + const struct mg_serve_http_opts *opts, + struct mg_cgi_env_block *blk) { + const char *s; + struct mg_str *h; + char *p; + size_t i; + char buf[100]; + size_t path_info_len = path_info != NULL ? path_info->len : 0; + + blk->len = blk->nvars = 0; + blk->nc = nc; + + if ((s = getenv("SERVER_NAME")) != NULL) { + mg_addenv(blk, "SERVER_NAME=%s", s); + } else { + mg_sock_to_str(nc->sock, buf, sizeof(buf), 3); + mg_addenv(blk, "SERVER_NAME=%s", buf); + } + mg_addenv(blk, "SERVER_ROOT=%s", opts->document_root); + mg_addenv(blk, "DOCUMENT_ROOT=%s", opts->document_root); + mg_addenv(blk, "SERVER_SOFTWARE=%s/%s", "Mongoose", MG_VERSION); + + /* Prepare the environment block */ + mg_addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1"); + mg_addenv(blk, "%s", "SERVER_PROTOCOL=HTTP/1.1"); + mg_addenv(blk, "%s", "REDIRECT_STATUS=200"); /* For PHP */ + + mg_addenv(blk, "REQUEST_METHOD=%.*s", (int) hm->method.len, hm->method.p); + + mg_addenv(blk, "REQUEST_URI=%.*s%s%.*s", (int) hm->uri.len, hm->uri.p, + hm->query_string.len == 0 ? "" : "?", (int) hm->query_string.len, + hm->query_string.p); + + mg_conn_addr_to_str(nc, buf, sizeof(buf), + MG_SOCK_STRINGIFY_REMOTE | MG_SOCK_STRINGIFY_IP); + mg_addenv(blk, "REMOTE_ADDR=%s", buf); + mg_conn_addr_to_str(nc, buf, sizeof(buf), MG_SOCK_STRINGIFY_PORT); + mg_addenv(blk, "SERVER_PORT=%s", buf); + + s = hm->uri.p + hm->uri.len - path_info_len - 1; + if (*s == '/') { + const char *base_name = strrchr(prog, DIRSEP); + mg_addenv(blk, "SCRIPT_NAME=%.*s/%s", (int) (s - hm->uri.p), hm->uri.p, + (base_name != NULL ? base_name + 1 : prog)); + } else { + mg_addenv(blk, "SCRIPT_NAME=%.*s", (int) (s - hm->uri.p + 1), hm->uri.p); + } + mg_addenv(blk, "SCRIPT_FILENAME=%s", prog); + + if (path_info != NULL && path_info->len > 0) { + mg_addenv(blk, "PATH_INFO=%.*s", (int) path_info->len, path_info->p); + /* Not really translated... */ + mg_addenv(blk, "PATH_TRANSLATED=%.*s", (int) path_info->len, path_info->p); + } + +#if MG_ENABLE_SSL + mg_addenv(blk, "HTTPS=%s", (nc->flags & MG_F_SSL ? "on" : "off")); +#else + mg_addenv(blk, "HTTPS=off"); +#endif + + if ((h = mg_get_http_header((struct http_message *) hm, "Content-Type")) != + NULL) { + mg_addenv(blk, "CONTENT_TYPE=%.*s", (int) h->len, h->p); + } + + if (hm->query_string.len > 0) { + mg_addenv(blk, "QUERY_STRING=%.*s", (int) hm->query_string.len, + hm->query_string.p); + } + + if ((h = mg_get_http_header((struct http_message *) hm, "Content-Length")) != + NULL) { + mg_addenv(blk, "CONTENT_LENGTH=%.*s", (int) h->len, h->p); + } + + mg_addenv2(blk, "PATH"); + mg_addenv2(blk, "TMP"); + mg_addenv2(blk, "TEMP"); + mg_addenv2(blk, "TMPDIR"); + mg_addenv2(blk, "PERLLIB"); + mg_addenv2(blk, MG_ENV_EXPORT_TO_CGI); + +#ifdef _WIN32 + mg_addenv2(blk, "COMSPEC"); + mg_addenv2(blk, "SYSTEMROOT"); + mg_addenv2(blk, "SystemDrive"); + mg_addenv2(blk, "ProgramFiles"); + mg_addenv2(blk, "ProgramFiles(x86)"); + mg_addenv2(blk, "CommonProgramFiles(x86)"); +#else + mg_addenv2(blk, "LD_LIBRARY_PATH"); +#endif /* _WIN32 */ + + /* Add all headers as HTTP_* variables */ + for (i = 0; hm->header_names[i].len > 0; i++) { + p = mg_addenv(blk, "HTTP_%.*s=%.*s", (int) hm->header_names[i].len, + hm->header_names[i].p, (int) hm->header_values[i].len, + hm->header_values[i].p); + + /* Convert variable name into uppercase, and change - to _ */ + for (; *p != '=' && *p != '\0'; p++) { + if (*p == '-') *p = '_'; + *p = (char) toupper(*(unsigned char *) p); + } + } + + blk->vars[blk->nvars++] = NULL; + blk->buf[blk->len++] = '\0'; +} + +static void mg_cgi_ev_handler(struct mg_connection *cgi_nc, int ev, + void *ev_data MG_UD_ARG(void *user_data)) { +#if !MG_ENABLE_CALLBACK_USERDATA + void *user_data = cgi_nc->user_data; +#endif + struct mg_connection *nc = (struct mg_connection *) user_data; + (void) ev_data; + + if (nc == NULL) { + /* The corresponding network connection was closed. */ + cgi_nc->flags |= MG_F_CLOSE_IMMEDIATELY; + return; + } + + switch (ev) { + case MG_EV_RECV: + /* + * CGI script does not output reply line, like "HTTP/1.1 CODE XXXXX\n" + * It outputs headers, then body. Headers might include "Status" + * header, which changes CODE, and it might include "Location" header + * which changes CODE to 302. + * + * Therefore we do not send the output from the CGI script to the user + * until all CGI headers are received. + * + * Here we parse the output from the CGI script, and if all headers has + * been received, send appropriate reply line, and forward all + * received headers to the client. + */ + if (nc->flags & MG_F_HTTP_CGI_PARSE_HEADERS) { + struct mbuf *io = &cgi_nc->recv_mbuf; + int len = mg_http_get_request_len(io->buf, io->len); + + if (len == 0) break; + if (len < 0 || io->len > MG_MAX_HTTP_REQUEST_SIZE) { + cgi_nc->flags |= MG_F_CLOSE_IMMEDIATELY; + mg_http_send_error(nc, 500, "Bad headers"); + } else { + struct http_message hm; + struct mg_str *h; + mg_http_parse_headers(io->buf, io->buf + io->len, io->len, &hm); + if (mg_get_http_header(&hm, "Location") != NULL) { + mg_printf(nc, "%s", "HTTP/1.1 302 Moved\r\n"); + } else if ((h = mg_get_http_header(&hm, "Status")) != NULL) { + mg_printf(nc, "HTTP/1.1 %.*s\r\n", (int) h->len, h->p); + } else { + mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\n"); + } + } + nc->flags &= ~MG_F_HTTP_CGI_PARSE_HEADERS; + } + if (!(nc->flags & MG_F_HTTP_CGI_PARSE_HEADERS)) { + mg_forward(cgi_nc, nc); + } + break; + case MG_EV_CLOSE: + DBG(("%p CLOSE", cgi_nc)); + mg_http_free_proto_data_cgi(&mg_http_get_proto_data(nc)->cgi); + nc->flags |= MG_F_SEND_AND_CLOSE; + break; + } +} + +MG_INTERNAL void mg_handle_cgi(struct mg_connection *nc, const char *prog, + const struct mg_str *path_info, + const struct http_message *hm, + const struct mg_serve_http_opts *opts) { + struct mg_cgi_env_block blk; + char dir[MG_MAX_PATH]; + const char *p; + sock_t fds[2]; + + DBG(("%p [%s]", nc, prog)); + mg_prepare_cgi_environment(nc, prog, path_info, hm, opts, &blk); + /* + * CGI must be executed in its own directory. 'dir' must point to the + * directory containing executable program, 'p' must point to the + * executable program name relative to 'dir'. + */ + if ((p = strrchr(prog, DIRSEP)) == NULL) { + snprintf(dir, sizeof(dir), "%s", "."); + } else { + snprintf(dir, sizeof(dir), "%.*s", (int) (p - prog), prog); + prog = p + 1; + } + + if (!mg_socketpair(fds, SOCK_STREAM)) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + return; + } + +#ifndef _WIN32 + struct sigaction sa; + + sigemptyset(&sa.sa_mask); + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + sigaction(SIGCHLD, &sa, NULL); +#endif + + if (mg_start_process(opts->cgi_interpreter, prog, blk.buf, blk.vars, dir, + fds[1]) != 0) { + size_t n = nc->recv_mbuf.len - (hm->message.len - hm->body.len); + struct mg_connection *cgi_nc = + mg_add_sock(nc->mgr, fds[0], mg_cgi_ev_handler MG_UD_ARG(nc)); + struct mg_http_proto_data *cgi_pd = mg_http_get_proto_data(nc); + cgi_pd->cgi.cgi_nc = cgi_nc; +#if !MG_ENABLE_CALLBACK_USERDATA + cgi_pd->cgi.cgi_nc->user_data = nc; +#endif + nc->flags |= MG_F_HTTP_CGI_PARSE_HEADERS; + /* Push POST data to the CGI */ + if (n > 0 && n < nc->recv_mbuf.len) { + mg_send(cgi_pd->cgi.cgi_nc, hm->body.p, n); + } + mbuf_remove(&nc->recv_mbuf, nc->recv_mbuf.len); + } else { + closesocket(fds[0]); + mg_http_send_error(nc, 500, "CGI failure"); + } + +#ifndef _WIN32 + closesocket(fds[1]); /* On Windows, CGI stdio thread closes that socket */ +#endif +} + +MG_INTERNAL void mg_http_free_proto_data_cgi(struct mg_http_proto_data_cgi *d) { + if (d == NULL) return; + if (d->cgi_nc != NULL) { + d->cgi_nc->flags |= MG_F_CLOSE_IMMEDIATELY; + d->cgi_nc->user_data = NULL; + } + memset(d, 0, sizeof(*d)); +} + +#endif /* MG_ENABLE_HTTP && MG_ENABLE_HTTP_CGI */ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_http_ssi.c" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_HTTP && MG_ENABLE_HTTP_SSI && MG_ENABLE_FILESYSTEM + +static void mg_send_ssi_file(struct mg_connection *nc, struct http_message *hm, + const char *path, FILE *fp, int include_level, + const struct mg_serve_http_opts *opts); + +static void mg_send_file_data(struct mg_connection *nc, FILE *fp) { + char buf[BUFSIZ]; + size_t n; + while ((n = mg_fread(buf, 1, sizeof(buf), fp)) > 0) { + mg_send(nc, buf, n); + } +} + +static void mg_do_ssi_include(struct mg_connection *nc, struct http_message *hm, + const char *ssi, char *tag, int include_level, + const struct mg_serve_http_opts *opts) { + char file_name[MG_MAX_PATH], path[MG_MAX_PATH], *p; + FILE *fp; + + /* + * sscanf() is safe here, since send_ssi_file() also uses buffer + * of size MG_BUF_LEN to get the tag. So strlen(tag) is always < MG_BUF_LEN. + */ + if (sscanf(tag, " virtual=\"%[^\"]\"", file_name) == 1) { + /* File name is relative to the webserver root */ + snprintf(path, sizeof(path), "%s/%s", opts->document_root, file_name); + } else if (sscanf(tag, " abspath=\"%[^\"]\"", file_name) == 1) { + /* + * File name is relative to the webserver working directory + * or it is absolute system path + */ + snprintf(path, sizeof(path), "%s", file_name); + } else if (sscanf(tag, " file=\"%[^\"]\"", file_name) == 1 || + sscanf(tag, " \"%[^\"]\"", file_name) == 1) { + /* File name is relative to the currect document */ + snprintf(path, sizeof(path), "%s", ssi); + if ((p = strrchr(path, DIRSEP)) != NULL) { + p[1] = '\0'; + } + snprintf(path + strlen(path), sizeof(path) - strlen(path), "%s", file_name); + } else { + mg_printf(nc, "Bad SSI #include: [%s]", tag); + return; + } + + if ((fp = mg_fopen(path, "rb")) == NULL) { + mg_printf(nc, "SSI include error: mg_fopen(%s): %s", path, + strerror(mg_get_errno())); + } else { + mg_set_close_on_exec((sock_t) fileno(fp)); + if (mg_match_prefix(opts->ssi_pattern, strlen(opts->ssi_pattern), path) > + 0) { + mg_send_ssi_file(nc, hm, path, fp, include_level + 1, opts); + } else { + mg_send_file_data(nc, fp); + } + fclose(fp); + } +} + +#if MG_ENABLE_HTTP_SSI_EXEC +static void do_ssi_exec(struct mg_connection *nc, char *tag) { + char cmd[BUFSIZ]; + FILE *fp; + + if (sscanf(tag, " \"%[^\"]\"", cmd) != 1) { + mg_printf(nc, "Bad SSI #exec: [%s]", tag); + } else if ((fp = popen(cmd, "r")) == NULL) { + mg_printf(nc, "Cannot SSI #exec: [%s]: %s", cmd, strerror(mg_get_errno())); + } else { + mg_send_file_data(nc, fp); + pclose(fp); + } +} +#endif /* MG_ENABLE_HTTP_SSI_EXEC */ + +/* + * SSI directive has the following format: + * + */ +static void mg_send_ssi_file(struct mg_connection *nc, struct http_message *hm, + const char *path, FILE *fp, int include_level, + const struct mg_serve_http_opts *opts) { + static const struct mg_str btag = MG_MK_STR(" */ + buf[i--] = '\0'; + while (i > 0 && buf[i] == ' ') { + buf[i--] = '\0'; + } + + /* Handle known SSI directives */ + if (strncmp(p, d_include.p, d_include.len) == 0) { + mg_do_ssi_include(nc, hm, path, p + d_include.len + 1, include_level, + opts); + } else if (strncmp(p, d_call.p, d_call.len) == 0) { + struct mg_ssi_call_ctx cctx; + memset(&cctx, 0, sizeof(cctx)); + cctx.req = hm; + cctx.file = mg_mk_str(path); + cctx.arg = mg_mk_str(p + d_call.len + 1); + mg_call(nc, NULL, nc->user_data, MG_EV_SSI_CALL, + (void *) cctx.arg.p); /* NUL added above */ + mg_call(nc, NULL, nc->user_data, MG_EV_SSI_CALL_CTX, &cctx); +#if MG_ENABLE_HTTP_SSI_EXEC + } else if (strncmp(p, d_exec.p, d_exec.len) == 0) { + do_ssi_exec(nc, p + d_exec.len + 1); +#endif + } else { + /* Silently ignore unknown SSI directive. */ + } + len = 0; + } else if (ch == '<') { + in_ssi_tag = 1; + if (len > 0) { + mg_send(nc, buf, (size_t) len); + } + len = 0; + buf[len++] = ch & 0xff; + } else if (in_ssi_tag) { + if (len == (int) btag.len && strncmp(buf, btag.p, btag.len) != 0) { + /* Not an SSI tag */ + in_ssi_tag = 0; + } else if (len == (int) sizeof(buf) - 2) { + mg_printf(nc, "%s: SSI tag is too large", path); + len = 0; + } + buf[len++] = ch & 0xff; + } else { + buf[len++] = ch & 0xff; + if (len == (int) sizeof(buf)) { + mg_send(nc, buf, (size_t) len); + len = 0; + } + } + } + + /* Send the rest of buffered data */ + if (len > 0) { + mg_send(nc, buf, (size_t) len); + } +} + +MG_INTERNAL void mg_handle_ssi_request(struct mg_connection *nc, + struct http_message *hm, + const char *path, + const struct mg_serve_http_opts *opts) { + FILE *fp; + struct mg_str mime_type; + DBG(("%p %s", nc, path)); + + if ((fp = mg_fopen(path, "rb")) == NULL) { + mg_http_send_error(nc, 404, NULL); + } else { + mg_set_close_on_exec((sock_t) fileno(fp)); + + mime_type = mg_get_mime_type(path, "text/plain", opts); + mg_send_response_line(nc, 200, opts->extra_headers); + mg_printf(nc, + "Content-Type: %.*s\r\n" + "Connection: close\r\n\r\n", + (int) mime_type.len, mime_type.p); + mg_send_ssi_file(nc, hm, path, fp, 0, opts); + fclose(fp); + nc->flags |= MG_F_SEND_AND_CLOSE; + } +} + +#endif /* MG_ENABLE_HTTP_SSI && MG_ENABLE_HTTP && MG_ENABLE_FILESYSTEM */ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_http_webdav.c" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_HTTP && MG_ENABLE_HTTP_WEBDAV + +MG_INTERNAL int mg_is_dav_request(const struct mg_str *s) { + static const char *methods[] = { + "PUT", + "DELETE", + "MKCOL", + "PROPFIND", + "MOVE" +#if MG_ENABLE_FAKE_DAVLOCK + , + "LOCK", + "UNLOCK" +#endif + }; + size_t i; + + for (i = 0; i < ARRAY_SIZE(methods); i++) { + if (mg_vcmp(s, methods[i]) == 0) { + return 1; + } + } + + return 0; +} + +static int mg_mkdir(const char *path, uint32_t mode) { +#ifndef _WIN32 + return mkdir(path, mode); +#else + (void) mode; + return _mkdir(path); +#endif +} + +static void mg_print_props(struct mg_connection *nc, const char *name, + cs_stat_t *stp) { + char mtime[64]; + time_t t = stp->st_mtime; /* store in local variable for NDK compile */ + struct mg_str name_esc = mg_url_encode(mg_mk_str(name)); + mg_gmt_time_string(mtime, sizeof(mtime), &t); + mg_printf(nc, + "" + "%s" + "" + "" + "%s" + "%" INT64_FMT + "" + "%s" + "" + "HTTP/1.1 200 OK" + "" + "\n", + name_esc.p, S_ISDIR(stp->st_mode) ? "" : "", + (int64_t) stp->st_size, mtime); + free((void *) name_esc.p); +} + +MG_INTERNAL void mg_handle_propfind(struct mg_connection *nc, const char *path, + cs_stat_t *stp, struct http_message *hm, + struct mg_serve_http_opts *opts) { + static const char header[] = + "HTTP/1.1 207 Multi-Status\r\n" + "Connection: close\r\n" + "Content-Type: text/xml; charset=utf-8\r\n\r\n" + "" + "\n"; + static const char footer[] = "\n"; + const struct mg_str *depth = mg_get_http_header(hm, "Depth"); + + /* Print properties for the requested resource itself */ + if (S_ISDIR(stp->st_mode) && + strcmp(opts->enable_directory_listing, "yes") != 0) { + mg_printf(nc, "%s", "HTTP/1.1 403 Directory Listing Denied\r\n\r\n"); + } else { + char uri[MG_MAX_PATH]; + mg_send(nc, header, sizeof(header) - 1); + snprintf(uri, sizeof(uri), "%.*s", (int) hm->uri.len, hm->uri.p); + mg_print_props(nc, uri, stp); + if (S_ISDIR(stp->st_mode) && (depth == NULL || mg_vcmp(depth, "0") != 0)) { + mg_scan_directory(nc, path, opts, mg_print_props); + } + mg_send(nc, footer, sizeof(footer) - 1); + nc->flags |= MG_F_SEND_AND_CLOSE; + } +} + +#if MG_ENABLE_FAKE_DAVLOCK +/* + * Windows explorer (probably there are another WebDav clients like it) + * requires LOCK support in webdav. W/out this, it still works, but fails + * to save file: shows error message and offers "Save As". + * "Save as" works, but this message is very annoying. + * This is fake lock, which doesn't lock something, just returns LOCK token, + * UNLOCK always answers "OK". + * With this fake LOCK Windows Explorer looks happy and saves file. + * NOTE: that is not DAV LOCK imlementation, it is just a way to shut up + * Windows native DAV client. This is why FAKE LOCK is not enabed by default + */ +MG_INTERNAL void mg_handle_lock(struct mg_connection *nc, const char *path) { + static const char *reply = + "HTTP/1.1 207 Multi-Status\r\n" + "Connection: close\r\n" + "Content-Type: text/xml; charset=utf-8\r\n\r\n" + "" + "\n" + "\n" + "\n" + "\n" + "\n" + "opaquelocktoken:%s%u" + "" + "" + "\n" + "" + "\n"; + mg_printf(nc, reply, path, (unsigned int) mg_time()); + nc->flags |= MG_F_SEND_AND_CLOSE; +} +#endif + +MG_INTERNAL void mg_handle_mkcol(struct mg_connection *nc, const char *path, + struct http_message *hm) { + int status_code = 500; + if (hm->body.len != (size_t) ~0 && hm->body.len > 0) { + status_code = 415; + } else if (!mg_mkdir(path, 0755)) { + status_code = 201; + } else if (errno == EEXIST) { + status_code = 405; + } else if (errno == EACCES) { + status_code = 403; + } else if (errno == ENOENT) { + status_code = 409; + } else { + status_code = 500; + } + mg_http_send_error(nc, status_code, NULL); +} + +static int mg_remove_directory(const struct mg_serve_http_opts *opts, + const char *dir) { + char path[MG_MAX_PATH]; + struct dirent *dp; + cs_stat_t st; + DIR *dirp; + + if ((dirp = opendir(dir)) == NULL) return 0; + + while ((dp = readdir(dirp)) != NULL) { + if (mg_is_file_hidden((const char *) dp->d_name, opts, 1)) { + continue; + } + snprintf(path, sizeof(path), "%s%c%s", dir, '/', dp->d_name); + mg_stat(path, &st); + if (S_ISDIR(st.st_mode)) { + mg_remove_directory(opts, path); + } else { + remove(path); + } + } + closedir(dirp); + rmdir(dir); + + return 1; +} + +MG_INTERNAL void mg_handle_move(struct mg_connection *c, + const struct mg_serve_http_opts *opts, + const char *path, struct http_message *hm) { + const struct mg_str *dest = mg_get_http_header(hm, "Destination"); + if (dest == NULL) { + mg_http_send_error(c, 411, NULL); + } else { + const char *p = (char *) memchr(dest->p, '/', dest->len); + if (p != NULL && p[1] == '/' && + (p = (char *) memchr(p + 2, '/', dest->p + dest->len - p)) != NULL) { + char buf[MG_MAX_PATH]; + snprintf(buf, sizeof(buf), "%s%.*s", opts->dav_document_root, + (int) (dest->p + dest->len - p), p); + if (rename(path, buf) == 0) { + mg_http_send_error(c, 200, NULL); + } else { + mg_http_send_error(c, 418, NULL); + } + } else { + mg_http_send_error(c, 500, NULL); + } + } +} + +MG_INTERNAL void mg_handle_delete(struct mg_connection *nc, + const struct mg_serve_http_opts *opts, + const char *path) { + cs_stat_t st; + if (mg_stat(path, &st) != 0) { + mg_http_send_error(nc, 404, NULL); + } else if (S_ISDIR(st.st_mode)) { + mg_remove_directory(opts, path); + mg_http_send_error(nc, 204, NULL); + } else if (remove(path) == 0) { + mg_http_send_error(nc, 204, NULL); + } else { + mg_http_send_error(nc, 423, NULL); + } +} + +/* Return -1 on error, 1 on success. */ +static int mg_create_itermediate_directories(const char *path) { + const char *s; + + /* Create intermediate directories if they do not exist */ + for (s = path + 1; *s != '\0'; s++) { + if (*s == '/') { + char buf[MG_MAX_PATH]; + cs_stat_t st; + snprintf(buf, sizeof(buf), "%.*s", (int) (s - path), path); + buf[sizeof(buf) - 1] = '\0'; + if (mg_stat(buf, &st) != 0 && mg_mkdir(buf, 0755) != 0) { + return -1; + } + } + } + + return 1; +} + +MG_INTERNAL void mg_handle_put(struct mg_connection *nc, const char *path, + struct http_message *hm) { + struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); + cs_stat_t st; + const struct mg_str *cl_hdr = mg_get_http_header(hm, "Content-Length"); + int rc, status_code = mg_stat(path, &st) == 0 ? 200 : 201; + + mg_http_free_proto_data_file(&pd->file); + if ((rc = mg_create_itermediate_directories(path)) == 0) { + mg_printf(nc, "HTTP/1.1 %d OK\r\nContent-Length: 0\r\n\r\n", status_code); + } else if (rc == -1) { + mg_http_send_error(nc, 500, NULL); + } else if (cl_hdr == NULL) { + mg_http_send_error(nc, 411, NULL); + } else if ((pd->file.fp = mg_fopen(path, "w+b")) == NULL) { + mg_http_send_error(nc, 500, NULL); + } else { + const struct mg_str *range_hdr = mg_get_http_header(hm, "Content-Range"); + int64_t r1 = 0, r2 = 0; + pd->file.type = DATA_PUT; + mg_set_close_on_exec((sock_t) fileno(pd->file.fp)); + pd->file.cl = to64(cl_hdr->p); + if (range_hdr != NULL && + mg_http_parse_range_header(range_hdr, &r1, &r2) > 0) { + status_code = 206; + fseeko(pd->file.fp, r1, SEEK_SET); + pd->file.cl = r2 > r1 ? r2 - r1 + 1 : pd->file.cl - r1; + } + mg_printf(nc, "HTTP/1.1 %d OK\r\nContent-Length: 0\r\n\r\n", status_code); + /* Remove HTTP request from the mbuf, leave only payload */ + mbuf_remove(&nc->recv_mbuf, hm->message.len - hm->body.len); + mg_http_transfer_file_data(nc); + } +} + +#endif /* MG_ENABLE_HTTP && MG_ENABLE_HTTP_WEBDAV */ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_http_websocket.c" +#endif +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_HTTP && MG_ENABLE_HTTP_WEBSOCKET + +/* Amalgamated: #include "common/cs_sha1.h" */ + +#ifndef MG_WEBSOCKET_PING_INTERVAL_SECONDS +#define MG_WEBSOCKET_PING_INTERVAL_SECONDS 5 +#endif + +#define FLAGS_MASK_FIN (1 << 7) +#define FLAGS_MASK_OP 0x0f + +static int mg_is_ws_fragment(unsigned char flags) { + return (flags & FLAGS_MASK_FIN) == 0 || + (flags & FLAGS_MASK_OP) == WEBSOCKET_OP_CONTINUE; +} + +static int mg_is_ws_first_fragment(unsigned char flags) { + return (flags & FLAGS_MASK_FIN) == 0 && + (flags & FLAGS_MASK_OP) != WEBSOCKET_OP_CONTINUE; +} + +static int mg_is_ws_control_frame(unsigned char flags) { + unsigned char op = (flags & FLAGS_MASK_OP); + return op == WEBSOCKET_OP_CLOSE || op == WEBSOCKET_OP_PING || + op == WEBSOCKET_OP_PONG; +} + +static void mg_handle_incoming_websocket_frame(struct mg_connection *nc, + struct websocket_message *wsm) { + if (wsm->flags & 0x8) { + mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_CONTROL_FRAME, wsm); + } else { + mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_FRAME, wsm); + } +} + +static struct mg_ws_proto_data *mg_ws_get_proto_data(struct mg_connection *nc) { + struct mg_http_proto_data *htd = mg_http_get_proto_data(nc); + return (htd != NULL ? &htd->ws_data : NULL); +} + +/* + * Sends a Close websocket frame with the given data, and closes the underlying + * connection. If `len` is ~0, strlen(data) is used. + */ +static void mg_ws_close(struct mg_connection *nc, const void *data, + size_t len) { + if ((int) len == ~0) { + len = strlen((const char *) data); + } + mg_send_websocket_frame(nc, WEBSOCKET_OP_CLOSE, data, len); + nc->flags |= MG_F_SEND_AND_CLOSE; +} + +static int mg_deliver_websocket_data(struct mg_connection *nc) { + /* Using unsigned char *, cause of integer arithmetic below */ + uint64_t i, data_len = 0, frame_len = 0, new_data_len = nc->recv_mbuf.len, + len, mask_len = 0, header_len = 0; + struct mg_ws_proto_data *wsd = mg_ws_get_proto_data(nc); + unsigned char *new_data = (unsigned char *) nc->recv_mbuf.buf, + *e = (unsigned char *) nc->recv_mbuf.buf + nc->recv_mbuf.len; + uint8_t flags; + int ok, reass; + + if (wsd->reass_len > 0) { + /* + * We already have some previously received data which we need to + * reassemble and deliver to the client code when we get the final + * fragment. + * + * NOTE: it doesn't mean that the current message must be a continuation: + * it might be a control frame (Close, Ping or Pong), which should be + * handled without breaking the fragmented message. + */ + + size_t existing_len = wsd->reass_len; + assert(new_data_len >= existing_len); + + new_data += existing_len; + new_data_len -= existing_len; + } + + flags = new_data[0]; + + reass = new_data_len > 0 && mg_is_ws_fragment(flags) && + !(nc->flags & MG_F_WEBSOCKET_NO_DEFRAG); + + if (reass && mg_is_ws_control_frame(flags)) { + /* + * Control frames can't be fragmented, so if we encounter fragmented + * control frame, close connection immediately. + */ + mg_ws_close(nc, "fragmented control frames are illegal", ~0); + return 0; + } else if (new_data_len > 0 && !reass && !mg_is_ws_control_frame(flags) && + wsd->reass_len > 0) { + /* + * When in the middle of a fragmented message, only the continuations + * and control frames are allowed. + */ + mg_ws_close(nc, "non-continuation in the middle of a fragmented message", + ~0); + return 0; + } + + if (new_data_len >= 2) { + len = new_data[1] & 0x7f; + mask_len = new_data[1] & FLAGS_MASK_FIN ? 4 : 0; + if (len < 126 && new_data_len >= mask_len) { + data_len = len; + header_len = 2 + mask_len; + } else if (len == 126 && new_data_len >= 4 + mask_len) { + header_len = 4 + mask_len; + data_len = ntohs(*(uint16_t *) &new_data[2]); + } else if (new_data_len >= 10 + mask_len) { + header_len = 10 + mask_len; + data_len = (((uint64_t) ntohl(*(uint32_t *) &new_data[2])) << 32) + + ntohl(*(uint32_t *) &new_data[6]); + } + } + + frame_len = header_len + data_len; + ok = (frame_len > 0 && frame_len <= new_data_len); + + /* Check for overflow */ + if (frame_len < header_len || frame_len < data_len) { + ok = 0; + mg_ws_close(nc, "overflowed message", ~0); + } + + if (ok) { + size_t cleanup_len = 0; + struct websocket_message wsm; + + wsm.size = (size_t) data_len; + wsm.data = new_data + header_len; + wsm.flags = flags; + + /* Apply mask if necessary */ + if (mask_len > 0) { + for (i = 0; i < data_len; i++) { + new_data[i + header_len] ^= (new_data + header_len - mask_len)[i % 4]; + } + } + + if (reass) { + /* This is a message fragment */ + + if (mg_is_ws_first_fragment(flags)) { + /* + * On the first fragmented frame, skip the first byte (op) and also + * reset size to 1 (op), it'll be incremented with the data len below. + */ + new_data += 1; + wsd->reass_len = 1 /* op */; + } + + /* Append this frame to the reassembled buffer */ + memmove(new_data, wsm.data, e - wsm.data); + wsd->reass_len += wsm.size; + nc->recv_mbuf.len -= wsm.data - new_data; + + if (flags & FLAGS_MASK_FIN) { + /* On last fragmented frame - call user handler and remove data */ + wsm.flags = FLAGS_MASK_FIN | nc->recv_mbuf.buf[0]; + wsm.data = (unsigned char *) nc->recv_mbuf.buf + 1 /* op */; + wsm.size = wsd->reass_len - 1 /* op */; + cleanup_len = wsd->reass_len; + wsd->reass_len = 0; + + /* Pass reassembled message to the client code. */ + mg_handle_incoming_websocket_frame(nc, &wsm); + mbuf_remove(&nc->recv_mbuf, cleanup_len); /* Cleanup frame */ + } + } else { + /* + * This is a complete message, not a fragment. It might happen in between + * of a fragmented message (in this case, WebSocket protocol requires + * current message to be a control frame). + */ + cleanup_len = (size_t) frame_len; + + /* First of all, check if we need to react on a control frame. */ + switch (flags & FLAGS_MASK_OP) { + case WEBSOCKET_OP_PING: + mg_send_websocket_frame(nc, WEBSOCKET_OP_PONG, wsm.data, wsm.size); + break; + + case WEBSOCKET_OP_CLOSE: + mg_ws_close(nc, wsm.data, wsm.size); + break; + } + + /* Pass received message to the client code. */ + mg_handle_incoming_websocket_frame(nc, &wsm); + + /* Cleanup frame */ + memmove(nc->recv_mbuf.buf + wsd->reass_len, + nc->recv_mbuf.buf + wsd->reass_len + cleanup_len, + nc->recv_mbuf.len - wsd->reass_len - cleanup_len); + nc->recv_mbuf.len -= cleanup_len; + } + } + + return ok; +} + +struct ws_mask_ctx { + size_t pos; /* zero means unmasked */ + uint32_t mask; +}; + +static uint32_t mg_ws_random_mask(void) { + uint32_t mask; +/* + * The spec requires WS client to generate hard to + * guess mask keys. From RFC6455, Section 5.3: + * + * The unpredictability of the masking key is essential to prevent + * authors of malicious applications from selecting the bytes that appear on + * the wire. + * + * Hence this feature is essential when the actual end user of this API + * is untrusted code that wouldn't have access to a lower level net API + * anyway (e.g. web browsers). Hence this feature is low prio for most + * mongoose use cases and thus can be disabled, e.g. when porting to a platform + * that lacks rand(). + */ +#if MG_DISABLE_WS_RANDOM_MASK + mask = 0xefbeadde; /* generated with a random number generator, I swear */ +#else + if (sizeof(long) >= 4) { + mask = (uint32_t) rand(); + } else if (sizeof(long) == 2) { + mask = (uint32_t) rand() << 16 | (uint32_t) rand(); + } +#endif + return mask; +} + +static void mg_send_ws_header(struct mg_connection *nc, int op, size_t len, + struct ws_mask_ctx *ctx) { + int header_len; + unsigned char header[10]; + + header[0] = + (op & WEBSOCKET_DONT_FIN ? 0x0 : FLAGS_MASK_FIN) | (op & FLAGS_MASK_OP); + if (len < 126) { + header[1] = (unsigned char) len; + header_len = 2; + } else if (len < 65535) { + uint16_t tmp = htons((uint16_t) len); + header[1] = 126; + memcpy(&header[2], &tmp, sizeof(tmp)); + header_len = 4; + } else { + uint32_t tmp; + header[1] = 127; + tmp = htonl((uint32_t)((uint64_t) len >> 32)); + memcpy(&header[2], &tmp, sizeof(tmp)); + tmp = htonl((uint32_t)(len & 0xffffffff)); + memcpy(&header[6], &tmp, sizeof(tmp)); + header_len = 10; + } + + /* client connections enable masking */ + if (nc->listener == NULL) { + header[1] |= 1 << 7; /* set masking flag */ + mg_send(nc, header, header_len); + ctx->mask = mg_ws_random_mask(); + mg_send(nc, &ctx->mask, sizeof(ctx->mask)); + ctx->pos = nc->send_mbuf.len; + } else { + mg_send(nc, header, header_len); + ctx->pos = 0; + } +} + +static void mg_ws_mask_frame(struct mbuf *mbuf, struct ws_mask_ctx *ctx) { + size_t i; + if (ctx->pos == 0) return; + for (i = 0; i < (mbuf->len - ctx->pos); i++) { + mbuf->buf[ctx->pos + i] ^= ((char *) &ctx->mask)[i % 4]; + } +} + +void mg_send_websocket_frame(struct mg_connection *nc, int op, const void *data, + size_t len) { + struct ws_mask_ctx ctx; + DBG(("%p %d %d", nc, op, (int) len)); + mg_send_ws_header(nc, op, len, &ctx); + mg_send(nc, data, len); + + mg_ws_mask_frame(&nc->send_mbuf, &ctx); + + if (op == WEBSOCKET_OP_CLOSE) { + nc->flags |= MG_F_SEND_AND_CLOSE; + } +} + +void mg_send_websocket_framev(struct mg_connection *nc, int op, + const struct mg_str *strv, int strvcnt) { + struct ws_mask_ctx ctx; + int i; + int len = 0; + for (i = 0; i < strvcnt; i++) { + len += strv[i].len; + } + + mg_send_ws_header(nc, op, len, &ctx); + + for (i = 0; i < strvcnt; i++) { + mg_send(nc, strv[i].p, strv[i].len); + } + + mg_ws_mask_frame(&nc->send_mbuf, &ctx); + + if (op == WEBSOCKET_OP_CLOSE) { + nc->flags |= MG_F_SEND_AND_CLOSE; + } +} + +void mg_printf_websocket_frame(struct mg_connection *nc, int op, + const char *fmt, ...) { + char mem[MG_VPRINTF_BUFFER_SIZE], *buf = mem; + va_list ap; + int len; + + va_start(ap, fmt); + if ((len = mg_avprintf(&buf, sizeof(mem), fmt, ap)) > 0) { + mg_send_websocket_frame(nc, op, buf, len); + } + va_end(ap); + + if (buf != mem && buf != NULL) { + MG_FREE(buf); + } +} + +MG_INTERNAL void mg_ws_handler(struct mg_connection *nc, int ev, + void *ev_data MG_UD_ARG(void *user_data)) { + mg_call(nc, nc->handler, nc->user_data, ev, ev_data); + + switch (ev) { + case MG_EV_RECV: + do { + } while (mg_deliver_websocket_data(nc)); + break; + case MG_EV_POLL: + /* Ping idle websocket connections */ + { + time_t now = *(time_t *) ev_data; + if (nc->flags & MG_F_IS_WEBSOCKET && + now > nc->last_io_time + MG_WEBSOCKET_PING_INTERVAL_SECONDS) { + mg_send_websocket_frame(nc, WEBSOCKET_OP_PING, "", 0); + } + } + break; + default: + break; + } +#if MG_ENABLE_CALLBACK_USERDATA + (void) user_data; +#endif +} + +#ifndef MG_EXT_SHA1 +void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[], + const size_t *msg_lens, uint8_t *digest) { + size_t i; + cs_sha1_ctx sha_ctx; + cs_sha1_init(&sha_ctx); + for (i = 0; i < num_msgs; i++) { + cs_sha1_update(&sha_ctx, msgs[i], msg_lens[i]); + } + cs_sha1_final(digest, &sha_ctx); +} +#else +extern void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[], + const size_t *msg_lens, uint8_t *digest); +#endif + +MG_INTERNAL void mg_ws_handshake(struct mg_connection *nc, + const struct mg_str *key, + struct http_message *hm) { + static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + const uint8_t *msgs[2] = {(const uint8_t *) key->p, (const uint8_t *) magic}; + const size_t msg_lens[2] = {key->len, 36}; + unsigned char sha[20]; + char b64_sha[30]; + struct mg_str *s; + + mg_hash_sha1_v(2, msgs, msg_lens, sha); + mg_base64_encode(sha, sizeof(sha), b64_sha); + mg_printf(nc, "%s", + "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n"); + + s = mg_get_http_header(hm, "Sec-WebSocket-Protocol"); + if (s != NULL) { + mg_printf(nc, "Sec-WebSocket-Protocol: %.*s\r\n", (int) s->len, s->p); + } + mg_printf(nc, "Sec-WebSocket-Accept: %s%s", b64_sha, "\r\n\r\n"); + + DBG(("%p %.*s %s", nc, (int) key->len, key->p, b64_sha)); +} + +void mg_send_websocket_handshake2(struct mg_connection *nc, const char *path, + const char *host, const char *protocol, + const char *extra_headers) { + mg_send_websocket_handshake3(nc, path, host, protocol, extra_headers, NULL, + NULL); +} + +void mg_send_websocket_handshake3(struct mg_connection *nc, const char *path, + const char *host, const char *protocol, + const char *extra_headers, const char *user, + const char *pass) { + mg_send_websocket_handshake3v(nc, mg_mk_str(path), mg_mk_str(host), + mg_mk_str(protocol), mg_mk_str(extra_headers), + mg_mk_str(user), mg_mk_str(pass)); +} + +void mg_send_websocket_handshake3v(struct mg_connection *nc, + const struct mg_str path, + const struct mg_str host, + const struct mg_str protocol, + const struct mg_str extra_headers, + const struct mg_str user, + const struct mg_str pass) { + struct mbuf auth; + char key[25]; + uint32_t nonce[4]; + nonce[0] = mg_ws_random_mask(); + nonce[1] = mg_ws_random_mask(); + nonce[2] = mg_ws_random_mask(); + nonce[3] = mg_ws_random_mask(); + mg_base64_encode((unsigned char *) &nonce, sizeof(nonce), key); + + mbuf_init(&auth, 0); + if (user.len > 0) { + mg_basic_auth_header(user, pass, &auth); + } + + /* + * NOTE: the (auth.buf == NULL ? "" : auth.buf) is because cc3200 libc is + * broken: it doesn't like zero length to be passed to %.*s + * i.e. sprintf("f%.*so", (int)0, NULL), yields `f\0o`. + * because it handles NULL specially (and incorrectly). + */ + mg_printf(nc, + "GET %.*s HTTP/1.1\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "%.*s" + "Sec-WebSocket-Version: 13\r\n" + "Sec-WebSocket-Key: %s\r\n", + (int) path.len, path.p, (int) auth.len, + (auth.buf == NULL ? "" : auth.buf), key); + + /* TODO(mkm): take default hostname from http proto data if host == NULL */ + if (host.len > 0) { + int host_len = (int) (path.p - host.p); /* Account for possible :PORT */ + mg_printf(nc, "Host: %.*s\r\n", host_len, host.p); + } + if (protocol.len > 0) { + mg_printf(nc, "Sec-WebSocket-Protocol: %.*s\r\n", (int) protocol.len, + protocol.p); + } + if (extra_headers.len > 0) { + mg_printf(nc, "%.*s", (int) extra_headers.len, extra_headers.p); + } + mg_printf(nc, "\r\n"); + + mbuf_free(&auth); +} + +void mg_send_websocket_handshake(struct mg_connection *nc, const char *path, + const char *extra_headers) { + struct mg_str null_str = MG_NULL_STR; + mg_send_websocket_handshake3v( + nc, mg_mk_str(path), null_str /* host */, null_str /* protocol */, + mg_mk_str(extra_headers), null_str /* user */, null_str /* pass */); +} + +struct mg_connection *mg_connect_ws_opt( + struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data), + struct mg_connect_opts opts, const char *url, const char *protocol, + const char *extra_headers) { + struct mg_str null_str = MG_NULL_STR; + struct mg_str host = MG_NULL_STR, path = MG_NULL_STR, user_info = MG_NULL_STR; + struct mg_connection *nc = + mg_connect_http_base(mgr, MG_CB(ev_handler, user_data), opts, "http", + "ws", "https", "wss", url, &path, &user_info, &host); + if (nc != NULL) { + mg_send_websocket_handshake3v(nc, path, host, mg_mk_str(protocol), + mg_mk_str(extra_headers), user_info, + null_str); + } + return nc; +} + +struct mg_connection *mg_connect_ws( + struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data), + const char *url, const char *protocol, const char *extra_headers) { + struct mg_connect_opts opts; + memset(&opts, 0, sizeof(opts)); + return mg_connect_ws_opt(mgr, MG_CB(ev_handler, user_data), opts, url, + protocol, extra_headers); +} +#endif /* MG_ENABLE_HTTP && MG_ENABLE_HTTP_WEBSOCKET */ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_util.c" +#endif +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +/* Amalgamated: #include "common/cs_base64.h" */ +/* Amalgamated: #include "mg_internal.h" */ +/* Amalgamated: #include "mg_util.h" */ + +/* For platforms with limited libc */ +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +const char *mg_skip(const char *s, const char *end, const char *delims, + struct mg_str *v) { + v->p = s; + while (s < end && strchr(delims, *(unsigned char *) s) == NULL) s++; + v->len = s - v->p; + while (s < end && strchr(delims, *(unsigned char *) s) != NULL) s++; + return s; +} + +#if MG_ENABLE_FILESYSTEM && !defined(MG_USER_FILE_FUNCTIONS) +int mg_stat(const char *path, cs_stat_t *st) { +#ifdef _WIN32 + wchar_t wpath[MG_MAX_PATH]; + to_wchar(path, wpath, ARRAY_SIZE(wpath)); + DBG(("[%ls] -> %d", wpath, _wstati64(wpath, st))); + return _wstati64(wpath, st); +#else + return stat(path, st); +#endif +} + +FILE *mg_fopen(const char *path, const char *mode) { +#ifdef _WIN32 + wchar_t wpath[MG_MAX_PATH], wmode[10]; + to_wchar(path, wpath, ARRAY_SIZE(wpath)); + to_wchar(mode, wmode, ARRAY_SIZE(wmode)); + return _wfopen(wpath, wmode); +#else + return fopen(path, mode); +#endif +} + +int mg_open(const char *path, int flag, int mode) { /* LCOV_EXCL_LINE */ +#if defined(_WIN32) && !defined(WINCE) + wchar_t wpath[MG_MAX_PATH]; + to_wchar(path, wpath, ARRAY_SIZE(wpath)); + return _wopen(wpath, flag, mode); +#else + return open(path, flag, mode); /* LCOV_EXCL_LINE */ +#endif +} + +size_t mg_fread(void *ptr, size_t size, size_t count, FILE *f) { + return fread(ptr, size, count, f); +} + +size_t mg_fwrite(const void *ptr, size_t size, size_t count, FILE *f) { + return fwrite(ptr, size, count, f); +} +#endif + +void mg_base64_encode(const unsigned char *src, int src_len, char *dst) { + cs_base64_encode(src, src_len, dst); +} + +int mg_base64_decode(const unsigned char *s, int len, char *dst) { + return cs_base64_decode(s, len, dst, NULL); +} + +#if MG_ENABLE_THREADS +void *mg_start_thread(void *(*f)(void *), void *p) { +#ifdef WINCE + return (void *) CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) f, p, 0, NULL); +#elif defined(_WIN32) + return (void *) _beginthread((void(__cdecl *) (void *) ) f, 0, p); +#else + pthread_t thread_id = (pthread_t) 0; + pthread_attr_t attr; + + (void) pthread_attr_init(&attr); + (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + +#if defined(MG_STACK_SIZE) && MG_STACK_SIZE > 1 + (void) pthread_attr_setstacksize(&attr, MG_STACK_SIZE); +#endif + + pthread_create(&thread_id, &attr, f, p); + pthread_attr_destroy(&attr); + + return (void *) thread_id; +#endif +} +#endif /* MG_ENABLE_THREADS */ + +/* Set close-on-exec bit for a given socket. */ +void mg_set_close_on_exec(sock_t sock) { +#if defined(_WIN32) && !defined(WINCE) + (void) SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0); +#elif defined(__unix__) + fcntl(sock, F_SETFD, FD_CLOEXEC); +#else + (void) sock; +#endif +} + +int mg_sock_addr_to_str(const union socket_address *sa, char *buf, size_t len, + int flags) { + int is_v6; + if (buf == NULL || len <= 0) return 0; + memset(buf, 0, len); +#if MG_ENABLE_IPV6 + is_v6 = sa->sa.sa_family == AF_INET6; +#else + is_v6 = 0; +#endif + if (flags & MG_SOCK_STRINGIFY_IP) { +#if MG_ENABLE_IPV6 + const void *addr = NULL; + char *start = buf; + socklen_t capacity = len; + if (!is_v6) { + addr = &sa->sin.sin_addr; + } else { + addr = (void *) &sa->sin6.sin6_addr; + if (flags & MG_SOCK_STRINGIFY_PORT) { + *buf = '['; + start++; + capacity--; + } + } + if (inet_ntop(sa->sa.sa_family, addr, start, capacity) == NULL) { + goto cleanup; + } +#elif defined(_WIN32) || MG_LWIP || (MG_NET_IF == MG_NET_IF_PIC32) + /* Only Windoze Vista (and newer) have inet_ntop() */ + char *addr_str = inet_ntoa(sa->sin.sin_addr); + if (addr_str != NULL) { + strncpy(buf, inet_ntoa(sa->sin.sin_addr), len - 1); + } else { + goto cleanup; + } +#else + if (inet_ntop(AF_INET, (void *) &sa->sin.sin_addr, buf, len - 1) == NULL) { + goto cleanup; + } +#endif + } + if (flags & MG_SOCK_STRINGIFY_PORT) { + int port = ntohs(sa->sin.sin_port); + if (flags & MG_SOCK_STRINGIFY_IP) { + int buf_len = strlen(buf); + snprintf(buf + buf_len, len - (buf_len + 1), "%s:%d", (is_v6 ? "]" : ""), + port); + } else { + snprintf(buf, len, "%d", port); + } + } + + return strlen(buf); + +cleanup: + *buf = '\0'; + return 0; +} + +int mg_conn_addr_to_str(struct mg_connection *nc, char *buf, size_t len, + int flags) { + union socket_address sa; + memset(&sa, 0, sizeof(sa)); + mg_if_get_conn_addr(nc, flags & MG_SOCK_STRINGIFY_REMOTE, &sa); + return mg_sock_addr_to_str(&sa, buf, len, flags); +} + +#if MG_ENABLE_HEXDUMP +static int mg_hexdump_n(const void *buf, int len, char *dst, int dst_len, + int offset) { + const unsigned char *p = (const unsigned char *) buf; + char ascii[17] = ""; + int i, idx, n = 0; + + for (i = 0; i < len; i++) { + idx = i % 16; + if (idx == 0) { + if (i > 0) n += snprintf(dst + n, MAX(dst_len - n, 0), " %s\n", ascii); + n += snprintf(dst + n, MAX(dst_len - n, 0), "%04x ", i + offset); + } + if (dst_len - n < 0) { + return n; + } + n += snprintf(dst + n, MAX(dst_len - n, 0), " %02x", p[i]); + ascii[idx] = p[i] < 0x20 || p[i] > 0x7e ? '.' : p[i]; + ascii[idx + 1] = '\0'; + } + + while (i++ % 16) n += snprintf(dst + n, MAX(dst_len - n, 0), "%s", " "); + n += snprintf(dst + n, MAX(dst_len - n, 0), " %s\n", ascii); + + return n; +} + +int mg_hexdump(const void *buf, int len, char *dst, int dst_len) { + return mg_hexdump_n(buf, len, dst, dst_len, 0); +} + +void mg_hexdumpf(FILE *fp, const void *buf, int len) { + char tmp[80]; + int offset = 0, n; + while (len > 0) { + n = (len < 16 ? len : 16); + mg_hexdump_n(((const char *) buf) + offset, n, tmp, sizeof(tmp), offset); + fputs(tmp, fp); + offset += n; + len -= n; + } +} + +void mg_hexdump_connection(struct mg_connection *nc, const char *path, + const void *buf, int num_bytes, int ev) { + FILE *fp = NULL; + char src[60], dst[60]; + const char *tag = NULL; + switch (ev) { + case MG_EV_RECV: + tag = "<-"; + break; + case MG_EV_SEND: + tag = "->"; + break; + case MG_EV_ACCEPT: + tag = " 0) { + mg_hexdumpf(fp, buf, num_bytes); + } + if (fp != stdout && fp != stderr) fclose(fp); +} +#endif + +int mg_is_big_endian(void) { + static const int n = 1; + /* TODO(mkm) use compiletime check with 4-byte char literal */ + return ((char *) &n)[0] == 0; +} + +DO_NOT_WARN_UNUSED MG_INTERNAL int mg_get_errno(void) { +#ifndef WINCE + return errno; +#else + /* TODO(alashkin): translate error codes? */ + return GetLastError(); +#endif +} + +void mg_mbuf_append_base64_putc(char ch, void *user_data) { + struct mbuf *mbuf = (struct mbuf *) user_data; + mbuf_append(mbuf, &ch, sizeof(ch)); +} + +void mg_mbuf_append_base64(struct mbuf *mbuf, const void *data, size_t len) { + struct cs_base64_ctx ctx; + cs_base64_init(&ctx, mg_mbuf_append_base64_putc, mbuf); + cs_base64_update(&ctx, (const char *) data, len); + cs_base64_finish(&ctx); +} + +void mg_basic_auth_header(const struct mg_str user, const struct mg_str pass, + struct mbuf *buf) { + const char *header_prefix = "Authorization: Basic "; + const char *header_suffix = "\r\n"; + + struct cs_base64_ctx ctx; + cs_base64_init(&ctx, mg_mbuf_append_base64_putc, buf); + + mbuf_append(buf, header_prefix, strlen(header_prefix)); + + cs_base64_update(&ctx, user.p, user.len); + if (pass.len > 0) { + cs_base64_update(&ctx, ":", 1); + cs_base64_update(&ctx, pass.p, pass.len); + } + cs_base64_finish(&ctx); + mbuf_append(buf, header_suffix, strlen(header_suffix)); +} + +struct mg_str mg_url_encode(const struct mg_str src) { + static const char *dont_escape = "._-$,;~()/"; + static const char *hex = "0123456789abcdef"; + size_t i = 0; + struct mbuf mb; + mbuf_init(&mb, src.len); + + for (i = 0; i < src.len; i++) { + const unsigned char c = *((const unsigned char *) src.p + i); + if (isalnum(c) || strchr(dont_escape, c) != NULL) { + mbuf_append(&mb, &c, 1); + } else { + mbuf_append(&mb, "%", 1); + mbuf_append(&mb, &hex[c >> 4], 1); + mbuf_append(&mb, &hex[c & 15], 1); + } + } + mbuf_append(&mb, "", 1); + mbuf_trim(&mb); + return mg_mk_str_n(mb.buf, mb.len - 1); +} +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_mqtt.c" +#endif +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_MQTT + +#include + +/* Amalgamated: #include "mg_internal.h" */ +/* Amalgamated: #include "mg_mqtt.h" */ + +static uint16_t getu16(const char *p) { + const uint8_t *up = (const uint8_t *) p; + return (up[0] << 8) + up[1]; +} + +static const char *scanto(const char *p, struct mg_str *s) { + s->len = getu16(p); + s->p = p + 2; + return s->p + s->len; +} + +MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm) { + uint8_t header; + size_t len = 0, len_len = 0; + const char *p, *end; + unsigned char lc = 0; + int cmd; + + if (io->len < 2) return MG_MQTT_ERROR_INCOMPLETE_MSG; + header = io->buf[0]; + cmd = header >> 4; + + /* decode mqtt variable length */ + len = len_len = 0; + p = io->buf + 1; + while ((size_t)(p - io->buf) < io->len) { + lc = *((const unsigned char *) p++); + len += (lc & 0x7f) << 7 * len_len; + len_len++; + if (!(lc & 0x80)) break; + if (len_len > 4) return MG_MQTT_ERROR_MALFORMED_MSG; + } + + end = p + len; + if (lc & 0x80 || len > (io->len - (p - io->buf))) { + return MG_MQTT_ERROR_INCOMPLETE_MSG; + } + + mm->cmd = cmd; + mm->qos = MG_MQTT_GET_QOS(header); + + switch (cmd) { + case MG_MQTT_CMD_CONNECT: { + p = scanto(p, &mm->protocol_name); + if (p > end - 4) return MG_MQTT_ERROR_MALFORMED_MSG; + mm->protocol_version = *(uint8_t *) p++; + mm->connect_flags = *(uint8_t *) p++; + mm->keep_alive_timer = getu16(p); + p += 2; + if (p >= end) return MG_MQTT_ERROR_MALFORMED_MSG; + p = scanto(p, &mm->client_id); + if (p > end) return MG_MQTT_ERROR_MALFORMED_MSG; + if (mm->connect_flags & MG_MQTT_HAS_WILL) { + if (p >= end) return MG_MQTT_ERROR_MALFORMED_MSG; + p = scanto(p, &mm->will_topic); + } + if (mm->connect_flags & MG_MQTT_HAS_WILL) { + if (p >= end) return MG_MQTT_ERROR_MALFORMED_MSG; + p = scanto(p, &mm->will_message); + } + if (mm->connect_flags & MG_MQTT_HAS_USER_NAME) { + if (p >= end) return MG_MQTT_ERROR_MALFORMED_MSG; + p = scanto(p, &mm->user_name); + } + if (mm->connect_flags & MG_MQTT_HAS_PASSWORD) { + if (p >= end) return MG_MQTT_ERROR_MALFORMED_MSG; + p = scanto(p, &mm->password); + } + if (p != end) return MG_MQTT_ERROR_MALFORMED_MSG; + + LOG(LL_DEBUG, + ("%d %2x %d proto [%.*s] client_id [%.*s] will_topic [%.*s] " + "will_msg [%.*s] user_name [%.*s] password [%.*s]", + (int) len, (int) mm->connect_flags, (int) mm->keep_alive_timer, + (int) mm->protocol_name.len, mm->protocol_name.p, + (int) mm->client_id.len, mm->client_id.p, (int) mm->will_topic.len, + mm->will_topic.p, (int) mm->will_message.len, mm->will_message.p, + (int) mm->user_name.len, mm->user_name.p, (int) mm->password.len, + mm->password.p)); + break; + } + case MG_MQTT_CMD_CONNACK: + if (end - p < 2) return MG_MQTT_ERROR_MALFORMED_MSG; + mm->connack_ret_code = p[1]; + break; + case MG_MQTT_CMD_PUBACK: + case MG_MQTT_CMD_PUBREC: + case MG_MQTT_CMD_PUBREL: + case MG_MQTT_CMD_PUBCOMP: + case MG_MQTT_CMD_SUBACK: + mm->message_id = getu16(p); + break; + case MG_MQTT_CMD_PUBLISH: { + p = scanto(p, &mm->topic); + if (p > end) return MG_MQTT_ERROR_MALFORMED_MSG; + if (mm->qos > 0) { + if (end - p < 2) return MG_MQTT_ERROR_MALFORMED_MSG; + mm->message_id = getu16(p); + p += 2; + } + mm->payload.p = p; + mm->payload.len = end - p; + break; + } + case MG_MQTT_CMD_SUBSCRIBE: + if (end - p < 2) return MG_MQTT_ERROR_MALFORMED_MSG; + mm->message_id = getu16(p); + p += 2; + /* + * topic expressions are left in the payload and can be parsed with + * `mg_mqtt_next_subscribe_topic` + */ + mm->payload.p = p; + mm->payload.len = end - p; + break; + default: + /* Unhandled command */ + break; + } + + mm->len = end - io->buf; + return mm->len; +} + +static void mqtt_handler(struct mg_connection *nc, int ev, + void *ev_data MG_UD_ARG(void *user_data)) { + struct mbuf *io = &nc->recv_mbuf; + struct mg_mqtt_message mm; + memset(&mm, 0, sizeof(mm)); + + nc->handler(nc, ev, ev_data MG_UD_ARG(user_data)); + + switch (ev) { + case MG_EV_ACCEPT: + if (nc->proto_data == NULL) mg_set_protocol_mqtt(nc); + break; + case MG_EV_RECV: { + /* There can be multiple messages in the buffer, process them all. */ + while (1) { + int len = parse_mqtt(io, &mm); + if (len < 0) { + if (len == MG_MQTT_ERROR_MALFORMED_MSG) { + /* Protocol error. */ + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } else if (len == MG_MQTT_ERROR_INCOMPLETE_MSG) { + /* Not fully buffered, let's check if we have a chance to get more + * data later */ + if (nc->recv_mbuf_limit > 0 && + nc->recv_mbuf.len >= nc->recv_mbuf_limit) { + LOG(LL_ERROR, ("%p recv buffer (%lu bytes) exceeds the limit " + "%lu bytes, and not drained, closing", + nc, (unsigned long) nc->recv_mbuf.len, + (unsigned long) nc->recv_mbuf_limit)); + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } + } else { + /* Should never be here */ + LOG(LL_ERROR, ("%p invalid len: %d, closing", nc, len)); + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } + break; + } + + nc->handler(nc, MG_MQTT_EVENT_BASE + mm.cmd, &mm MG_UD_ARG(user_data)); + mbuf_remove(io, len); + } + break; + } + case MG_EV_POLL: { + struct mg_mqtt_proto_data *pd = + (struct mg_mqtt_proto_data *) nc->proto_data; + double now = mg_time(); + if (pd->keep_alive > 0 && pd->last_control_time > 0 && + (now - pd->last_control_time) > pd->keep_alive) { + LOG(LL_DEBUG, ("Send PINGREQ")); + mg_mqtt_ping(nc); + } + break; + } + } +} + +static void mg_mqtt_proto_data_destructor(void *proto_data) { + MG_FREE(proto_data); +} + +int mg_mqtt_match_topic_expression(struct mg_str exp, struct mg_str topic) { + /* TODO(mkm): implement real matching */ + if (memchr(exp.p, '#', exp.len)) { + /* exp `foo/#` will become `foo/` */ + exp.len -= 1; + /* + * topic should be longer than the expression: e.g. topic `foo/bar` does + * match `foo/#`, but neither `foo` nor `foo/` do. + */ + if (topic.len <= exp.len) { + return 0; + } + + /* Truncate topic so that it'll pass the next length check */ + topic.len = exp.len; + } + if (topic.len != exp.len) { + return 0; + } + return strncmp(topic.p, exp.p, exp.len) == 0; +} + +int mg_mqtt_vmatch_topic_expression(const char *exp, struct mg_str topic) { + return mg_mqtt_match_topic_expression(mg_mk_str(exp), topic); +} + +void mg_set_protocol_mqtt(struct mg_connection *nc) { + nc->proto_handler = mqtt_handler; + nc->proto_data = MG_CALLOC(1, sizeof(struct mg_mqtt_proto_data)); + nc->proto_data_destructor = mg_mqtt_proto_data_destructor; +} + +static void mg_mqtt_prepend_header(struct mg_connection *nc, uint8_t cmd, + uint8_t flags, size_t len) { + struct mg_mqtt_proto_data *pd = (struct mg_mqtt_proto_data *) nc->proto_data; + size_t off = nc->send_mbuf.len - len; + uint8_t header = cmd << 4 | (uint8_t) flags; + + uint8_t buf[1 + sizeof(size_t)]; + uint8_t *vlen = &buf[1]; + + assert(nc->send_mbuf.len >= len); + + buf[0] = header; + + /* mqtt variable length encoding */ + do { + *vlen = len % 0x80; + len /= 0x80; + if (len > 0) *vlen |= 0x80; + vlen++; + } while (len > 0); + + mbuf_insert(&nc->send_mbuf, off, buf, vlen - buf); + pd->last_control_time = mg_time(); +} + +void mg_send_mqtt_handshake(struct mg_connection *nc, const char *client_id) { + static struct mg_send_mqtt_handshake_opts opts; + mg_send_mqtt_handshake_opt(nc, client_id, opts); +} + +void mg_send_mqtt_handshake_opt(struct mg_connection *nc, const char *client_id, + struct mg_send_mqtt_handshake_opts opts) { + uint16_t hlen, nlen, rem_len = 0; + struct mg_mqtt_proto_data *pd = (struct mg_mqtt_proto_data *) nc->proto_data; + + mg_send(nc, "\00\04MQTT\04", 7); + rem_len += 7; + + if (opts.user_name != NULL) { + opts.flags |= MG_MQTT_HAS_USER_NAME; + } + if (opts.password != NULL) { + opts.flags |= MG_MQTT_HAS_PASSWORD; + } + if (opts.will_topic != NULL && opts.will_message != NULL) { + opts.flags |= MG_MQTT_HAS_WILL; + } + if (opts.keep_alive == 0) { + opts.keep_alive = 60; + } + + mg_send(nc, &opts.flags, 1); + rem_len += 1; + + nlen = htons(opts.keep_alive); + mg_send(nc, &nlen, 2); + rem_len += 2; + + hlen = strlen(client_id); + nlen = htons((uint16_t) hlen); + mg_send(nc, &nlen, 2); + mg_send(nc, client_id, hlen); + rem_len += 2 + hlen; + + if (opts.flags & MG_MQTT_HAS_WILL) { + hlen = strlen(opts.will_topic); + nlen = htons((uint16_t) hlen); + mg_send(nc, &nlen, 2); + mg_send(nc, opts.will_topic, hlen); + rem_len += 2 + hlen; + + hlen = strlen(opts.will_message); + nlen = htons((uint16_t) hlen); + mg_send(nc, &nlen, 2); + mg_send(nc, opts.will_message, hlen); + rem_len += 2 + hlen; + } + + if (opts.flags & MG_MQTT_HAS_USER_NAME) { + hlen = strlen(opts.user_name); + nlen = htons((uint16_t) hlen); + mg_send(nc, &nlen, 2); + mg_send(nc, opts.user_name, hlen); + rem_len += 2 + hlen; + } + if (opts.flags & MG_MQTT_HAS_PASSWORD) { + hlen = strlen(opts.password); + nlen = htons((uint16_t) hlen); + mg_send(nc, &nlen, 2); + mg_send(nc, opts.password, hlen); + rem_len += 2 + hlen; + } + + mg_mqtt_prepend_header(nc, MG_MQTT_CMD_CONNECT, 0, rem_len); + + if (pd != NULL) { + pd->keep_alive = opts.keep_alive; + } +} + +void mg_mqtt_publish(struct mg_connection *nc, const char *topic, + uint16_t message_id, int flags, const void *data, + size_t len) { + size_t old_len = nc->send_mbuf.len; + + uint16_t topic_len = htons((uint16_t) strlen(topic)); + uint16_t message_id_net = htons(message_id); + + mg_send(nc, &topic_len, 2); + mg_send(nc, topic, strlen(topic)); + if (MG_MQTT_GET_QOS(flags) > 0) { + mg_send(nc, &message_id_net, 2); + } + mg_send(nc, data, len); + + mg_mqtt_prepend_header(nc, MG_MQTT_CMD_PUBLISH, flags, + nc->send_mbuf.len - old_len); +} + +void mg_mqtt_subscribe(struct mg_connection *nc, + const struct mg_mqtt_topic_expression *topics, + size_t topics_len, uint16_t message_id) { + size_t old_len = nc->send_mbuf.len; + + uint16_t message_id_n = htons(message_id); + size_t i; + + mg_send(nc, (char *) &message_id_n, 2); + for (i = 0; i < topics_len; i++) { + uint16_t topic_len_n = htons((uint16_t) strlen(topics[i].topic)); + mg_send(nc, &topic_len_n, 2); + mg_send(nc, topics[i].topic, strlen(topics[i].topic)); + mg_send(nc, &topics[i].qos, 1); + } + + mg_mqtt_prepend_header(nc, MG_MQTT_CMD_SUBSCRIBE, MG_MQTT_QOS(1), + nc->send_mbuf.len - old_len); +} + +int mg_mqtt_next_subscribe_topic(struct mg_mqtt_message *msg, + struct mg_str *topic, uint8_t *qos, int pos) { + unsigned char *buf = (unsigned char *) msg->payload.p + pos; + int new_pos; + + if ((size_t) pos >= msg->payload.len) return -1; + + topic->len = buf[0] << 8 | buf[1]; + topic->p = (char *) buf + 2; + new_pos = pos + 2 + topic->len + 1; + if ((size_t) new_pos > msg->payload.len) return -1; + *qos = buf[2 + topic->len]; + return new_pos; +} + +void mg_mqtt_unsubscribe(struct mg_connection *nc, char **topics, + size_t topics_len, uint16_t message_id) { + size_t old_len = nc->send_mbuf.len; + + uint16_t message_id_n = htons(message_id); + size_t i; + + mg_send(nc, (char *) &message_id_n, 2); + for (i = 0; i < topics_len; i++) { + uint16_t topic_len_n = htons((uint16_t) strlen(topics[i])); + mg_send(nc, &topic_len_n, 2); + mg_send(nc, topics[i], strlen(topics[i])); + } + + mg_mqtt_prepend_header(nc, MG_MQTT_CMD_UNSUBSCRIBE, MG_MQTT_QOS(1), + nc->send_mbuf.len - old_len); +} + +void mg_mqtt_connack(struct mg_connection *nc, uint8_t return_code) { + uint8_t unused = 0; + mg_send(nc, &unused, 1); + mg_send(nc, &return_code, 1); + mg_mqtt_prepend_header(nc, MG_MQTT_CMD_CONNACK, 0, 2); +} + +/* + * Sends a command which contains only a `message_id` and a QoS level of 1. + * + * Helper function. + */ +static void mg_send_mqtt_short_command(struct mg_connection *nc, uint8_t cmd, + uint16_t message_id) { + uint16_t message_id_net = htons(message_id); + uint8_t flags = (cmd == MG_MQTT_CMD_PUBREL ? 2 : 0); + mg_send(nc, &message_id_net, 2); + mg_mqtt_prepend_header(nc, cmd, flags, 2 /* len */); +} + +void mg_mqtt_puback(struct mg_connection *nc, uint16_t message_id) { + mg_send_mqtt_short_command(nc, MG_MQTT_CMD_PUBACK, message_id); +} + +void mg_mqtt_pubrec(struct mg_connection *nc, uint16_t message_id) { + mg_send_mqtt_short_command(nc, MG_MQTT_CMD_PUBREC, message_id); +} + +void mg_mqtt_pubrel(struct mg_connection *nc, uint16_t message_id) { + mg_send_mqtt_short_command(nc, MG_MQTT_CMD_PUBREL, message_id); +} + +void mg_mqtt_pubcomp(struct mg_connection *nc, uint16_t message_id) { + mg_send_mqtt_short_command(nc, MG_MQTT_CMD_PUBCOMP, message_id); +} + +void mg_mqtt_suback(struct mg_connection *nc, uint8_t *qoss, size_t qoss_len, + uint16_t message_id) { + size_t i; + uint16_t message_id_net = htons(message_id); + mg_send(nc, &message_id_net, 2); + for (i = 0; i < qoss_len; i++) { + mg_send(nc, &qoss[i], 1); + } + mg_mqtt_prepend_header(nc, MG_MQTT_CMD_SUBACK, MG_MQTT_QOS(1), 2 + qoss_len); +} + +void mg_mqtt_unsuback(struct mg_connection *nc, uint16_t message_id) { + mg_send_mqtt_short_command(nc, MG_MQTT_CMD_UNSUBACK, message_id); +} + +void mg_mqtt_ping(struct mg_connection *nc) { + mg_mqtt_prepend_header(nc, MG_MQTT_CMD_PINGREQ, 0, 0); +} + +void mg_mqtt_pong(struct mg_connection *nc) { + mg_mqtt_prepend_header(nc, MG_MQTT_CMD_PINGRESP, 0, 0); +} + +void mg_mqtt_disconnect(struct mg_connection *nc) { + mg_mqtt_prepend_header(nc, MG_MQTT_CMD_DISCONNECT, 0, 0); +} + +#endif /* MG_ENABLE_MQTT */ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_mqtt_server.c" +#endif +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +/* Amalgamated: #include "mg_internal.h" */ +/* Amalgamated: #include "mg_mqtt_server.h" */ + +#if MG_ENABLE_MQTT_BROKER + +static void mg_mqtt_session_init(struct mg_mqtt_broker *brk, + struct mg_mqtt_session *s, + struct mg_connection *nc) { + s->brk = brk; + s->subscriptions = NULL; + s->num_subscriptions = 0; + s->nc = nc; +} + +static void mg_mqtt_add_session(struct mg_mqtt_session *s) { + LIST_INSERT_HEAD(&s->brk->sessions, s, link); +} + +static void mg_mqtt_remove_session(struct mg_mqtt_session *s) { + LIST_REMOVE(s, link); +} + +static void mg_mqtt_destroy_session(struct mg_mqtt_session *s) { + size_t i; + for (i = 0; i < s->num_subscriptions; i++) { + MG_FREE((void *) s->subscriptions[i].topic); + } + MG_FREE(s->subscriptions); + MG_FREE(s); +} + +static void mg_mqtt_close_session(struct mg_mqtt_session *s) { + mg_mqtt_remove_session(s); + mg_mqtt_destroy_session(s); +} + +void mg_mqtt_broker_init(struct mg_mqtt_broker *brk, void *user_data) { + LIST_INIT(&brk->sessions); + brk->user_data = user_data; +} + +static void mg_mqtt_broker_handle_connect(struct mg_mqtt_broker *brk, + struct mg_connection *nc) { + struct mg_mqtt_session *s = + (struct mg_mqtt_session *) MG_CALLOC(1, sizeof *s); + if (s == NULL) { + /* LCOV_EXCL_START */ + mg_mqtt_connack(nc, MG_EV_MQTT_CONNACK_SERVER_UNAVAILABLE); + return; + /* LCOV_EXCL_STOP */ + } + + /* TODO(mkm): check header (magic and version) */ + + mg_mqtt_session_init(brk, s, nc); + nc->priv_2 = s; + mg_mqtt_add_session(s); + + mg_mqtt_connack(nc, MG_EV_MQTT_CONNACK_ACCEPTED); +} + +static void mg_mqtt_broker_handle_subscribe(struct mg_connection *nc, + struct mg_mqtt_message *msg) { + struct mg_mqtt_session *ss = (struct mg_mqtt_session *) nc->priv_2; + uint8_t qoss[MG_MQTT_MAX_SESSION_SUBSCRIPTIONS]; + size_t num_subs = 0; + struct mg_str topic; + uint8_t qos; + int pos; + struct mg_mqtt_topic_expression *te; + + for (pos = 0; + (pos = mg_mqtt_next_subscribe_topic(msg, &topic, &qos, pos)) != -1;) { + if (num_subs >= sizeof(MG_MQTT_MAX_SESSION_SUBSCRIPTIONS) || + (ss->num_subscriptions + num_subs >= + MG_MQTT_MAX_SESSION_SUBSCRIPTIONS)) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + return; + } + qoss[num_subs++] = qos; + } + + if (num_subs > 0) { + te = (struct mg_mqtt_topic_expression *) MG_REALLOC( + ss->subscriptions, + sizeof(*ss->subscriptions) * (ss->num_subscriptions + num_subs)); + if (te == NULL) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + return; + } + ss->subscriptions = te; + for (pos = 0; + pos < (int) msg->payload.len && + (pos = mg_mqtt_next_subscribe_topic(msg, &topic, &qos, pos)) != -1; + ss->num_subscriptions++) { + te = &ss->subscriptions[ss->num_subscriptions]; + te->topic = (char *) MG_MALLOC(topic.len + 1); + te->qos = qos; + memcpy((char *) te->topic, topic.p, topic.len); + ((char *) te->topic)[topic.len] = '\0'; + } + } + + if (pos == (int) msg->payload.len) { + mg_mqtt_suback(nc, qoss, num_subs, msg->message_id); + } else { + /* We did not fully parse the payload, something must be wrong. */ + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } +} + +static void mg_mqtt_broker_handle_publish(struct mg_mqtt_broker *brk, + struct mg_mqtt_message *msg) { + struct mg_mqtt_session *s; + size_t i; + + for (s = mg_mqtt_next(brk, NULL); s != NULL; s = mg_mqtt_next(brk, s)) { + for (i = 0; i < s->num_subscriptions; i++) { + if (mg_mqtt_vmatch_topic_expression(s->subscriptions[i].topic, + msg->topic)) { + char buf[100], *p = buf; + mg_asprintf(&p, sizeof(buf), "%.*s", (int) msg->topic.len, + msg->topic.p); + if (p == NULL) { + return; + } + mg_mqtt_publish(s->nc, p, 0, 0, msg->payload.p, msg->payload.len); + if (p != buf) { + MG_FREE(p); + } + break; + } + } + } +} + +void mg_mqtt_broker(struct mg_connection *nc, int ev, void *data) { + struct mg_mqtt_message *msg = (struct mg_mqtt_message *) data; + struct mg_mqtt_broker *brk; + + if (nc->listener) { + brk = (struct mg_mqtt_broker *) nc->listener->priv_2; + } else { + brk = (struct mg_mqtt_broker *) nc->priv_2; + } + + switch (ev) { + case MG_EV_ACCEPT: + if (nc->proto_data == NULL) mg_set_protocol_mqtt(nc); + nc->priv_2 = NULL; /* Clear up the inherited pointer to broker */ + break; + case MG_EV_MQTT_CONNECT: + if (nc->priv_2 == NULL) { + mg_mqtt_broker_handle_connect(brk, nc); + } else { + /* Repeated CONNECT */ + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } + break; + case MG_EV_MQTT_SUBSCRIBE: + if (nc->priv_2 != NULL) { + mg_mqtt_broker_handle_subscribe(nc, msg); + } else { + /* Subscribe before CONNECT */ + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } + break; + case MG_EV_MQTT_PUBLISH: + if (nc->priv_2 != NULL) { + mg_mqtt_broker_handle_publish(brk, msg); + } else { + /* Publish before CONNECT */ + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } + break; + case MG_EV_CLOSE: + if (nc->listener && nc->priv_2 != NULL) { + mg_mqtt_close_session((struct mg_mqtt_session *) nc->priv_2); + } + break; + } +} + +struct mg_mqtt_session *mg_mqtt_next(struct mg_mqtt_broker *brk, + struct mg_mqtt_session *s) { + return s == NULL ? LIST_FIRST(&brk->sessions) : LIST_NEXT(s, link); +} + +#endif /* MG_ENABLE_MQTT_BROKER */ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_dns.c" +#endif +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_DNS + +/* Amalgamated: #include "mg_internal.h" */ +/* Amalgamated: #include "mg_dns.h" */ + +static int mg_dns_tid = 0xa0; + +struct mg_dns_header { + uint16_t transaction_id; + uint16_t flags; + uint16_t num_questions; + uint16_t num_answers; + uint16_t num_authority_prs; + uint16_t num_other_prs; +}; + +struct mg_dns_resource_record *mg_dns_next_record( + struct mg_dns_message *msg, int query, + struct mg_dns_resource_record *prev) { + struct mg_dns_resource_record *rr; + + for (rr = (prev == NULL ? msg->answers : prev + 1); + rr - msg->answers < msg->num_answers; rr++) { + if (rr->rtype == query) { + return rr; + } + } + return NULL; +} + +int mg_dns_parse_record_data(struct mg_dns_message *msg, + struct mg_dns_resource_record *rr, void *data, + size_t data_len) { + switch (rr->rtype) { + case MG_DNS_A_RECORD: + if (data_len < sizeof(struct in_addr)) { + return -1; + } + if (rr->rdata.p + data_len > msg->pkt.p + msg->pkt.len) { + return -1; + } + memcpy(data, rr->rdata.p, data_len); + return 0; +#if MG_ENABLE_IPV6 + case MG_DNS_AAAA_RECORD: + if (data_len < sizeof(struct in6_addr)) { + return -1; /* LCOV_EXCL_LINE */ + } + memcpy(data, rr->rdata.p, data_len); + return 0; +#endif + case MG_DNS_CNAME_RECORD: + mg_dns_uncompress_name(msg, &rr->rdata, (char *) data, data_len); + return 0; + } + + return -1; +} + +int mg_dns_insert_header(struct mbuf *io, size_t pos, + struct mg_dns_message *msg) { + struct mg_dns_header header; + + memset(&header, 0, sizeof(header)); + header.transaction_id = msg->transaction_id; + header.flags = htons(msg->flags); + header.num_questions = htons(msg->num_questions); + header.num_answers = htons(msg->num_answers); + + return mbuf_insert(io, pos, &header, sizeof(header)); +} + +int mg_dns_copy_questions(struct mbuf *io, struct mg_dns_message *msg) { + unsigned char *begin, *end; + struct mg_dns_resource_record *last_q; + if (msg->num_questions <= 0) return 0; + begin = (unsigned char *) msg->pkt.p + sizeof(struct mg_dns_header); + last_q = &msg->questions[msg->num_questions - 1]; + end = (unsigned char *) last_q->name.p + last_q->name.len + 4; + return mbuf_append(io, begin, end - begin); +} + +int mg_dns_encode_name(struct mbuf *io, const char *name, size_t len) { + const char *s; + unsigned char n; + size_t pos = io->len; + + do { + if ((s = strchr(name, '.')) == NULL) { + s = name + len; + } + + if (s - name > 127) { + return -1; /* TODO(mkm) cover */ + } + n = s - name; /* chunk length */ + mbuf_append(io, &n, 1); /* send length */ + mbuf_append(io, name, n); + + if (*s == '.') { + n++; + } + + name += n; + len -= n; + } while (*s != '\0'); + mbuf_append(io, "\0", 1); /* Mark end of host name */ + + return io->len - pos; +} + +int mg_dns_encode_record(struct mbuf *io, struct mg_dns_resource_record *rr, + const char *name, size_t nlen, const void *rdata, + size_t rlen) { + size_t pos = io->len; + uint16_t u16; + uint32_t u32; + + if (rr->kind == MG_DNS_INVALID_RECORD) { + return -1; /* LCOV_EXCL_LINE */ + } + + if (mg_dns_encode_name(io, name, nlen) == -1) { + return -1; + } + + u16 = htons(rr->rtype); + mbuf_append(io, &u16, 2); + u16 = htons(rr->rclass); + mbuf_append(io, &u16, 2); + + if (rr->kind == MG_DNS_ANSWER) { + u32 = htonl(rr->ttl); + mbuf_append(io, &u32, 4); + + if (rr->rtype == MG_DNS_CNAME_RECORD) { + int clen; + /* fill size after encoding */ + size_t off = io->len; + mbuf_append(io, &u16, 2); + if ((clen = mg_dns_encode_name(io, (const char *) rdata, rlen)) == -1) { + return -1; + } + u16 = clen; + io->buf[off] = u16 >> 8; + io->buf[off + 1] = u16 & 0xff; + } else { + u16 = htons((uint16_t) rlen); + mbuf_append(io, &u16, 2); + mbuf_append(io, rdata, rlen); + } + } + + return io->len - pos; +} + +void mg_send_dns_query(struct mg_connection *nc, const char *name, + int query_type) { + struct mg_dns_message *msg = + (struct mg_dns_message *) MG_CALLOC(1, sizeof(*msg)); + struct mbuf pkt; + struct mg_dns_resource_record *rr = &msg->questions[0]; + + DBG(("%s %d", name, query_type)); + + mbuf_init(&pkt, 64 /* Start small, it'll grow as needed. */); + + msg->transaction_id = ++mg_dns_tid; + msg->flags = 0x100; + msg->num_questions = 1; + + mg_dns_insert_header(&pkt, 0, msg); + + rr->rtype = query_type; + rr->rclass = 1; /* Class: inet */ + rr->kind = MG_DNS_QUESTION; + + if (mg_dns_encode_record(&pkt, rr, name, strlen(name), NULL, 0) == -1) { + /* TODO(mkm): return an error code */ + goto cleanup; /* LCOV_EXCL_LINE */ + } + + /* TCP DNS requires messages to be prefixed with len */ + if (!(nc->flags & MG_F_UDP)) { + uint16_t len = htons((uint16_t) pkt.len); + mbuf_insert(&pkt, 0, &len, 2); + } + + mg_send(nc, pkt.buf, pkt.len); + mbuf_free(&pkt); + +cleanup: + MG_FREE(msg); +} + +static unsigned char *mg_parse_dns_resource_record( + unsigned char *data, unsigned char *end, struct mg_dns_resource_record *rr, + int reply) { + unsigned char *name = data; + int chunk_len, data_len; + + while (data < end && (chunk_len = *data)) { + if (((unsigned char *) data)[0] & 0xc0) { + data += 1; + break; + } + data += chunk_len + 1; + } + + if (data > end - 5) { + return NULL; + } + + rr->name.p = (char *) name; + rr->name.len = data - name + 1; + data++; + + rr->rtype = data[0] << 8 | data[1]; + data += 2; + + rr->rclass = data[0] << 8 | data[1]; + data += 2; + + rr->kind = reply ? MG_DNS_ANSWER : MG_DNS_QUESTION; + if (reply) { + if (data >= end - 6) { + return NULL; + } + + rr->ttl = (uint32_t) data[0] << 24 | (uint32_t) data[1] << 16 | + data[2] << 8 | data[3]; + data += 4; + + data_len = *data << 8 | *(data + 1); + data += 2; + + rr->rdata.p = (char *) data; + rr->rdata.len = data_len; + data += data_len; + } + return data; +} + +int mg_parse_dns(const char *buf, int len, struct mg_dns_message *msg) { + struct mg_dns_header *header = (struct mg_dns_header *) buf; + unsigned char *data = (unsigned char *) buf + sizeof(*header); + unsigned char *end = (unsigned char *) buf + len; + int i; + + memset(msg, 0, sizeof(*msg)); + msg->pkt.p = buf; + msg->pkt.len = len; + + if (len < (int) sizeof(*header)) return -1; + + msg->transaction_id = header->transaction_id; + msg->flags = ntohs(header->flags); + msg->num_questions = ntohs(header->num_questions); + if (msg->num_questions > (int) ARRAY_SIZE(msg->questions)) { + msg->num_questions = (int) ARRAY_SIZE(msg->questions); + } + msg->num_answers = ntohs(header->num_answers); + if (msg->num_answers > (int) ARRAY_SIZE(msg->answers)) { + msg->num_answers = (int) ARRAY_SIZE(msg->answers); + } + + for (i = 0; i < msg->num_questions; i++) { + data = mg_parse_dns_resource_record(data, end, &msg->questions[i], 0); + if (data == NULL) return -1; + } + + for (i = 0; i < msg->num_answers; i++) { + data = mg_parse_dns_resource_record(data, end, &msg->answers[i], 1); + if (data == NULL) return -1; + } + + return 0; +} + +size_t mg_dns_uncompress_name(struct mg_dns_message *msg, struct mg_str *name, + char *dst, int dst_len) { + int chunk_len, num_ptrs = 0; + char *old_dst = dst; + const unsigned char *data = (unsigned char *) name->p; + const unsigned char *end = (unsigned char *) msg->pkt.p + msg->pkt.len; + + if (data >= end) { + return 0; + } + + while ((chunk_len = *data++)) { + int leeway = dst_len - (dst - old_dst); + if (data >= end) { + return 0; + } + + if ((chunk_len & 0xc0) == 0xc0) { + uint16_t off = (data[-1] & (~0xc0)) << 8 | data[0]; + if (off >= msg->pkt.len) { + return 0; + } + /* Basic circular loop avoidance: allow up to 16 pointer hops. */ + if (++num_ptrs > 15) { + return 0; + } + data = (unsigned char *) msg->pkt.p + off; + continue; + } + if (chunk_len > 63) { + return 0; + } + if (chunk_len > leeway) { + chunk_len = leeway; + } + + if (data + chunk_len >= end) { + return 0; + } + + memcpy(dst, data, chunk_len); + data += chunk_len; + dst += chunk_len; + leeway -= chunk_len; + if (leeway == 0) { + return dst - old_dst; + } + *dst++ = '.'; + } + + if (dst != old_dst) { + *--dst = 0; + } + return dst - old_dst; +} + +static void dns_handler(struct mg_connection *nc, int ev, + void *ev_data MG_UD_ARG(void *user_data)) { + struct mbuf *io = &nc->recv_mbuf; + struct mg_dns_message msg; + + /* Pass low-level events to the user handler */ + nc->handler(nc, ev, ev_data MG_UD_ARG(user_data)); + + switch (ev) { + case MG_EV_RECV: + if (!(nc->flags & MG_F_UDP)) { + mbuf_remove(&nc->recv_mbuf, 2); + } + if (mg_parse_dns(nc->recv_mbuf.buf, nc->recv_mbuf.len, &msg) == -1) { + /* reply + recursion allowed + format error */ + memset(&msg, 0, sizeof(msg)); + msg.flags = 0x8081; + mg_dns_insert_header(io, 0, &msg); + if (!(nc->flags & MG_F_UDP)) { + uint16_t len = htons((uint16_t) io->len); + mbuf_insert(io, 0, &len, 2); + } + mg_send(nc, io->buf, io->len); + } else { + /* Call user handler with parsed message */ + nc->handler(nc, MG_DNS_MESSAGE, &msg MG_UD_ARG(user_data)); + } + mbuf_remove(io, io->len); + break; + } +} + +void mg_set_protocol_dns(struct mg_connection *nc) { + nc->proto_handler = dns_handler; +} + +#endif /* MG_ENABLE_DNS */ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_dns_server.c" +#endif +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_DNS_SERVER + +/* Amalgamated: #include "mg_internal.h" */ +/* Amalgamated: #include "dns-server.h" */ + +struct mg_dns_reply mg_dns_create_reply(struct mbuf *io, + struct mg_dns_message *msg) { + struct mg_dns_reply rep; + rep.msg = msg; + rep.io = io; + rep.start = io->len; + + /* reply + recursion allowed */ + msg->flags |= 0x8080; + mg_dns_copy_questions(io, msg); + + msg->num_answers = 0; + return rep; +} + +void mg_dns_send_reply(struct mg_connection *nc, struct mg_dns_reply *r) { + size_t sent = r->io->len - r->start; + mg_dns_insert_header(r->io, r->start, r->msg); + if (!(nc->flags & MG_F_UDP)) { + uint16_t len = htons((uint16_t) sent); + mbuf_insert(r->io, r->start, &len, 2); + } + + if (&nc->send_mbuf != r->io) { + mg_send(nc, r->io->buf + r->start, r->io->len - r->start); + r->io->len = r->start; + } +} + +int mg_dns_reply_record(struct mg_dns_reply *reply, + struct mg_dns_resource_record *question, + const char *name, int rtype, int ttl, const void *rdata, + size_t rdata_len) { + struct mg_dns_message *msg = (struct mg_dns_message *) reply->msg; + char rname[512]; + struct mg_dns_resource_record *ans = &msg->answers[msg->num_answers]; + if (msg->num_answers >= MG_MAX_DNS_ANSWERS) { + return -1; /* LCOV_EXCL_LINE */ + } + + if (name == NULL) { + name = rname; + rname[511] = 0; + mg_dns_uncompress_name(msg, &question->name, rname, sizeof(rname) - 1); + } + + *ans = *question; + ans->kind = MG_DNS_ANSWER; + ans->rtype = rtype; + ans->ttl = ttl; + + if (mg_dns_encode_record(reply->io, ans, name, strlen(name), rdata, + rdata_len) == -1) { + return -1; /* LCOV_EXCL_LINE */ + }; + + msg->num_answers++; + return 0; +} + +#endif /* MG_ENABLE_DNS_SERVER */ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_resolv.c" +#endif +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_ASYNC_RESOLVER + +/* Amalgamated: #include "mg_internal.h" */ +/* Amalgamated: #include "mg_resolv.h" */ + +#ifndef MG_DEFAULT_NAMESERVER +#define MG_DEFAULT_NAMESERVER "8.8.8.8" +#endif + +struct mg_resolve_async_request { + char name[1024]; + int query; + mg_resolve_callback_t callback; + void *data; + time_t timeout; + int max_retries; + enum mg_resolve_err err; + + /* state */ + time_t last_time; + int retries; +}; + +/* + * Find what nameserver to use. + * + * Return 0 if OK, -1 if error + */ +static int mg_get_ip_address_of_nameserver(char *name, size_t name_len) { + int ret = -1; + +#ifdef _WIN32 + int i; + LONG err; + HKEY hKey, hSub; + wchar_t subkey[512], value[128], + *key = L"SYSTEM\\ControlSet001\\Services\\Tcpip\\Parameters\\Interfaces"; + + if ((err = RegOpenKeyExW(HKEY_LOCAL_MACHINE, key, 0, KEY_READ, &hKey)) != + ERROR_SUCCESS) { + fprintf(stderr, "cannot open reg key %S: %ld\n", key, err); + ret = -1; + } else { + for (ret = -1, i = 0; 1; i++) { + DWORD subkey_size = sizeof(subkey), type, len = sizeof(value); + if (RegEnumKeyExW(hKey, i, subkey, &subkey_size, NULL, NULL, NULL, + NULL) != ERROR_SUCCESS) { + break; + } + if (RegOpenKeyExW(hKey, subkey, 0, KEY_READ, &hSub) == ERROR_SUCCESS && + ((RegQueryValueExW(hSub, L"NameServer", 0, &type, (void *) value, + &len) == ERROR_SUCCESS && + value[0] != '\0') || + (RegQueryValueExW(hSub, L"DhcpNameServer", 0, &type, (void *) value, + &len) == ERROR_SUCCESS && + value[0] != '\0'))) { + /* + * See https://github.com/cesanta/mongoose/issues/176 + * The value taken from the registry can be empty, a single + * IP address, or multiple IP addresses separated by comma. + * If it's empty, check the next interface. + * If it's multiple IP addresses, take the first one. + */ + wchar_t *comma = wcschr(value, ','); + if (comma != NULL) { + *comma = '\0'; + } + /* %S will convert wchar_t -> char */ + snprintf(name, name_len, "%S", value); + ret = 0; + RegCloseKey(hSub); + break; + } + } + RegCloseKey(hKey); + } +#elif MG_ENABLE_FILESYSTEM && defined(MG_RESOLV_CONF_FILE_NAME) + FILE *fp; + char line[512]; + + if ((fp = mg_fopen(MG_RESOLV_CONF_FILE_NAME, "r")) == NULL) { + ret = -1; + } else { + /* Try to figure out what nameserver to use */ + for (ret = -1; fgets(line, sizeof(line), fp) != NULL;) { + unsigned int a, b, c, d; + if (sscanf(line, "nameserver %u.%u.%u.%u", &a, &b, &c, &d) == 4) { + snprintf(name, name_len, "%u.%u.%u.%u", a, b, c, d); + ret = 0; + break; + } + } + (void) fclose(fp); + } +#else + snprintf(name, name_len, "%s", MG_DEFAULT_NAMESERVER); +#endif /* _WIN32 */ + + return ret; +} + +int mg_resolve_from_hosts_file(const char *name, union socket_address *usa) { +#if MG_ENABLE_FILESYSTEM && defined(MG_HOSTS_FILE_NAME) + /* TODO(mkm) cache /etc/hosts */ + FILE *fp; + char line[1024]; + char *p; + char alias[256]; + unsigned int a, b, c, d; + int len = 0; + + if ((fp = mg_fopen(MG_HOSTS_FILE_NAME, "r")) == NULL) { + return -1; + } + + for (; fgets(line, sizeof(line), fp) != NULL;) { + if (line[0] == '#') continue; + + if (sscanf(line, "%u.%u.%u.%u%n", &a, &b, &c, &d, &len) == 0) { + /* TODO(mkm): handle ipv6 */ + continue; + } + for (p = line + len; sscanf(p, "%s%n", alias, &len) == 1; p += len) { + if (strcmp(alias, name) == 0) { + usa->sin.sin_addr.s_addr = htonl(a << 24 | b << 16 | c << 8 | d); + fclose(fp); + return 0; + } + } + } + + fclose(fp); +#else + (void) name; + (void) usa; +#endif + + return -1; +} + +static void mg_resolve_async_eh(struct mg_connection *nc, int ev, + void *data MG_UD_ARG(void *user_data)) { + time_t now = (time_t) mg_time(); + struct mg_resolve_async_request *req; + struct mg_dns_message *msg; + int first = 0; +#if !MG_ENABLE_CALLBACK_USERDATA + void *user_data = nc->user_data; +#endif + + if (ev != MG_EV_POLL) DBG(("ev=%d user_data=%p", ev, user_data)); + + req = (struct mg_resolve_async_request *) user_data; + + if (req == NULL) { + return; + } + + switch (ev) { + case MG_EV_CONNECT: + /* don't depend on timer not being at epoch for sending out first req */ + first = 1; + /* fallthrough */ + case MG_EV_POLL: + if (req->retries > req->max_retries) { + req->err = MG_RESOLVE_EXCEEDED_RETRY_COUNT; + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + break; + } + if (first || now - req->last_time >= req->timeout) { + mg_send_dns_query(nc, req->name, req->query); + req->last_time = now; + req->retries++; + } + break; + case MG_EV_RECV: + msg = (struct mg_dns_message *) MG_MALLOC(sizeof(*msg)); + if (mg_parse_dns(nc->recv_mbuf.buf, *(int *) data, msg) == 0 && + msg->num_answers > 0) { + req->callback(msg, req->data, MG_RESOLVE_OK); + nc->user_data = NULL; + MG_FREE(req); + } else { + req->err = MG_RESOLVE_NO_ANSWERS; + } + MG_FREE(msg); + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + break; + case MG_EV_SEND: + /* + * If a send error occurs, prevent closing of the connection by the core. + * We will retry after timeout. + */ + nc->flags &= ~MG_F_CLOSE_IMMEDIATELY; + mbuf_remove(&nc->send_mbuf, nc->send_mbuf.len); + break; + case MG_EV_TIMER: + req->err = MG_RESOLVE_TIMEOUT; + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + break; + case MG_EV_CLOSE: + /* If we got here with request still not done, fire an error callback. */ + if (req != NULL) { + char addr[32]; + mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr), MG_SOCK_STRINGIFY_IP); +#ifdef MG_LOG_DNS_FAILURES + LOG(LL_ERROR, ("Failed to resolve '%s', server %s", req->name, addr)); +#endif + req->callback(NULL, req->data, req->err); + nc->user_data = NULL; + MG_FREE(req); + } + break; + } +} + +int mg_resolve_async(struct mg_mgr *mgr, const char *name, int query, + mg_resolve_callback_t cb, void *data) { + struct mg_resolve_async_opts opts; + memset(&opts, 0, sizeof(opts)); + return mg_resolve_async_opt(mgr, name, query, cb, data, opts); +} + +int mg_resolve_async_opt(struct mg_mgr *mgr, const char *name, int query, + mg_resolve_callback_t cb, void *data, + struct mg_resolve_async_opts opts) { + struct mg_resolve_async_request *req; + struct mg_connection *dns_nc; + const char *nameserver = opts.nameserver; + char dns_server_buff[17], nameserver_url[26]; + + if (nameserver == NULL) { + nameserver = mgr->nameserver; + } + + DBG(("%s %d %p", name, query, opts.dns_conn)); + + /* resolve with DNS */ + req = (struct mg_resolve_async_request *) MG_CALLOC(1, sizeof(*req)); + if (req == NULL) { + return -1; + } + + strncpy(req->name, name, sizeof(req->name)); + req->name[sizeof(req->name) - 1] = '\0'; + + req->query = query; + req->callback = cb; + req->data = data; + /* TODO(mkm): parse defaults out of resolve.conf */ + req->max_retries = opts.max_retries ? opts.max_retries : 2; + req->timeout = opts.timeout ? opts.timeout : 5; + + /* Lazily initialize dns server */ + if (nameserver == NULL) { + if (mg_get_ip_address_of_nameserver(dns_server_buff, + sizeof(dns_server_buff)) != -1) { + nameserver = dns_server_buff; + } else { + nameserver = MG_DEFAULT_NAMESERVER; + } + } + + snprintf(nameserver_url, sizeof(nameserver_url), "udp://%s:53", nameserver); + + dns_nc = mg_connect(mgr, nameserver_url, MG_CB(mg_resolve_async_eh, NULL)); + if (dns_nc == NULL) { + MG_FREE(req); + return -1; + } + dns_nc->user_data = req; + if (opts.dns_conn != NULL) { + *opts.dns_conn = dns_nc; + } + + return 0; +} + +void mg_set_nameserver(struct mg_mgr *mgr, const char *nameserver) { + MG_FREE((char *) mgr->nameserver); + mgr->nameserver = NULL; + if (nameserver != NULL) { + mgr->nameserver = strdup(nameserver); + } +} + +#endif /* MG_ENABLE_ASYNC_RESOLVER */ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_coap.c" +#endif +/* + * Copyright (c) 2015 Cesanta Software Limited + * All rights reserved + * This software is dual-licensed: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. For the terms of this + * license, see . + * + * You are free to use this software under the terms of the GNU General + * Public License, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * Alternatively, you can license this software under a commercial + * license, as set out in . + */ + +/* Amalgamated: #include "mg_internal.h" */ +/* Amalgamated: #include "mg_coap.h" */ + +#if MG_ENABLE_COAP + +void mg_coap_free_options(struct mg_coap_message *cm) { + while (cm->options != NULL) { + struct mg_coap_option *next = cm->options->next; + MG_FREE(cm->options); + cm->options = next; + } +} + +struct mg_coap_option *mg_coap_add_option(struct mg_coap_message *cm, + uint32_t number, char *value, + size_t len) { + struct mg_coap_option *new_option = + (struct mg_coap_option *) MG_CALLOC(1, sizeof(*new_option)); + + new_option->number = number; + new_option->value.p = value; + new_option->value.len = len; + + if (cm->options == NULL) { + cm->options = cm->optiomg_tail = new_option; + } else { + /* + * A very simple attention to help clients to compose options: + * CoAP wants to see options ASC ordered. + * Could be change by using sort in coap_compose + */ + if (cm->optiomg_tail->number <= new_option->number) { + /* if option is already ordered just add it */ + cm->optiomg_tail = cm->optiomg_tail->next = new_option; + } else { + /* looking for appropriate position */ + struct mg_coap_option *current_opt = cm->options; + struct mg_coap_option *prev_opt = 0; + + while (current_opt != NULL) { + if (current_opt->number > new_option->number) { + break; + } + prev_opt = current_opt; + current_opt = current_opt->next; + } + + if (prev_opt != NULL) { + prev_opt->next = new_option; + new_option->next = current_opt; + } else { + /* insert new_option to the beginning */ + new_option->next = cm->options; + cm->options = new_option; + } + } + } + + return new_option; +} + +/* + * Fills CoAP header in mg_coap_message. + * + * Helper function. + */ +static char *coap_parse_header(char *ptr, struct mbuf *io, + struct mg_coap_message *cm) { + if (io->len < sizeof(uint32_t)) { + cm->flags |= MG_COAP_NOT_ENOUGH_DATA; + return NULL; + } + + /* + * Version (Ver): 2-bit unsigned integer. Indicates the CoAP version + * number. Implementations of this specification MUST set this field + * to 1 (01 binary). Other values are reserved for future versions. + * Messages with unknown version numbers MUST be silently ignored. + */ + if (((uint8_t) *ptr >> 6) != 1) { + cm->flags |= MG_COAP_IGNORE; + return NULL; + } + + /* + * Type (T): 2-bit unsigned integer. Indicates if this message is of + * type Confirmable (0), Non-confirmable (1), Acknowledgement (2), or + * Reset (3). + */ + cm->msg_type = ((uint8_t) *ptr & 0x30) >> 4; + cm->flags |= MG_COAP_MSG_TYPE_FIELD; + + /* + * Token Length (TKL): 4-bit unsigned integer. Indicates the length of + * the variable-length Token field (0-8 bytes). Lengths 9-15 are + * reserved, MUST NOT be sent, and MUST be processed as a message + * format error. + */ + cm->token.len = *ptr & 0x0F; + if (cm->token.len > 8) { + cm->flags |= MG_COAP_FORMAT_ERROR; + return NULL; + } + + ptr++; + + /* + * Code: 8-bit unsigned integer, split into a 3-bit class (most + * significant bits) and a 5-bit detail (least significant bits) + */ + cm->code_class = (uint8_t) *ptr >> 5; + cm->code_detail = *ptr & 0x1F; + cm->flags |= (MG_COAP_CODE_CLASS_FIELD | MG_COAP_CODE_DETAIL_FIELD); + + ptr++; + + /* Message ID: 16-bit unsigned integer in network byte order. */ + cm->msg_id = (uint8_t) *ptr << 8 | (uint8_t) * (ptr + 1); + cm->flags |= MG_COAP_MSG_ID_FIELD; + + ptr += 2; + + return ptr; +} + +/* + * Fills token information in mg_coap_message. + * + * Helper function. + */ +static char *coap_get_token(char *ptr, struct mbuf *io, + struct mg_coap_message *cm) { + if (cm->token.len != 0) { + if (ptr + cm->token.len > io->buf + io->len) { + cm->flags |= MG_COAP_NOT_ENOUGH_DATA; + return NULL; + } else { + cm->token.p = ptr; + ptr += cm->token.len; + cm->flags |= MG_COAP_TOKEN_FIELD; + } + } + + return ptr; +} + +/* + * Returns Option Delta or Length. + * + * Helper function. + */ +static int coap_get_ext_opt(char *ptr, struct mbuf *io, uint16_t *opt_info) { + int ret = 0; + + if (*opt_info == 13) { + /* + * 13: An 8-bit unsigned integer follows the initial byte and + * indicates the Option Delta/Length minus 13. + */ + if (ptr < io->buf + io->len) { + *opt_info = (uint8_t) *ptr + 13; + ret = sizeof(uint8_t); + } else { + ret = -1; /* LCOV_EXCL_LINE */ + } + } else if (*opt_info == 14) { + /* + * 14: A 16-bit unsigned integer in network byte order follows the + * initial byte and indicates the Option Delta/Length minus 269. + */ + if (ptr + sizeof(uint8_t) < io->buf + io->len) { + *opt_info = ((uint8_t) *ptr << 8 | (uint8_t) * (ptr + 1)) + 269; + ret = sizeof(uint16_t); + } else { + ret = -1; /* LCOV_EXCL_LINE */ + } + } + + return ret; +} + +/* + * Fills options in mg_coap_message. + * + * Helper function. + * + * General options format: + * +---------------+---------------+ + * | Option Delta | Option Length | 1 byte + * +---------------+---------------+ + * \ Option Delta (extended) \ 0-2 bytes + * +-------------------------------+ + * / Option Length (extended) \ 0-2 bytes + * +-------------------------------+ + * \ Option Value \ 0 or more bytes + * +-------------------------------+ + */ +static char *coap_get_options(char *ptr, struct mbuf *io, + struct mg_coap_message *cm) { + uint16_t prev_opt = 0; + + if (ptr == io->buf + io->len) { + /* end of packet, ok */ + return NULL; + } + + /* 0xFF is payload marker */ + while (ptr < io->buf + io->len && (uint8_t) *ptr != 0xFF) { + uint16_t option_delta, option_lenght; + int optinfo_len; + + /* Option Delta: 4-bit unsigned integer */ + option_delta = ((uint8_t) *ptr & 0xF0) >> 4; + /* Option Length: 4-bit unsigned integer */ + option_lenght = *ptr & 0x0F; + + if (option_delta == 15 || option_lenght == 15) { + /* + * 15: Reserved for future use. If the field is set to this value, + * it MUST be processed as a message format error + */ + cm->flags |= MG_COAP_FORMAT_ERROR; + break; + } + + ptr++; + + /* check for extended option delta */ + optinfo_len = coap_get_ext_opt(ptr, io, &option_delta); + if (optinfo_len == -1) { + cm->flags |= MG_COAP_NOT_ENOUGH_DATA; /* LCOV_EXCL_LINE */ + break; /* LCOV_EXCL_LINE */ + } + + ptr += optinfo_len; + + /* check or extended option lenght */ + optinfo_len = coap_get_ext_opt(ptr, io, &option_lenght); + if (optinfo_len == -1) { + cm->flags |= MG_COAP_NOT_ENOUGH_DATA; /* LCOV_EXCL_LINE */ + break; /* LCOV_EXCL_LINE */ + } + + ptr += optinfo_len; + + /* + * Instead of specifying the Option Number directly, the instances MUST + * appear in order of their Option Numbers and a delta encoding is used + * between them. + */ + option_delta += prev_opt; + + mg_coap_add_option(cm, option_delta, ptr, option_lenght); + + prev_opt = option_delta; + + if (ptr + option_lenght > io->buf + io->len) { + cm->flags |= MG_COAP_NOT_ENOUGH_DATA; /* LCOV_EXCL_LINE */ + break; /* LCOV_EXCL_LINE */ + } + + ptr += option_lenght; + } + + if ((cm->flags & MG_COAP_ERROR) != 0) { + mg_coap_free_options(cm); + return NULL; + } + + cm->flags |= MG_COAP_OPTIOMG_FIELD; + + if (ptr == io->buf + io->len) { + /* end of packet, ok */ + return NULL; + } + + ptr++; + + return ptr; +} + +uint32_t mg_coap_parse(struct mbuf *io, struct mg_coap_message *cm) { + char *ptr; + + memset(cm, 0, sizeof(*cm)); + + if ((ptr = coap_parse_header(io->buf, io, cm)) == NULL) { + return cm->flags; + } + + if ((ptr = coap_get_token(ptr, io, cm)) == NULL) { + return cm->flags; + } + + if ((ptr = coap_get_options(ptr, io, cm)) == NULL) { + return cm->flags; + } + + /* the rest is payload */ + cm->payload.len = io->len - (ptr - io->buf); + if (cm->payload.len != 0) { + cm->payload.p = ptr; + cm->flags |= MG_COAP_PAYLOAD_FIELD; + } + + return cm->flags; +} + +/* + * Calculates extended size of given Opt Number/Length in coap message. + * + * Helper function. + */ +static size_t coap_get_ext_opt_size(uint32_t value) { + int ret = 0; + + if (value >= 13 && value <= 0xFF + 13) { + ret = sizeof(uint8_t); + } else if (value > 0xFF + 13 && value <= 0xFFFF + 269) { + ret = sizeof(uint16_t); + } + + return ret; +} + +/* + * Splits given Opt Number/Length into base and ext values. + * + * Helper function. + */ +static int coap_split_opt(uint32_t value, uint8_t *base, uint16_t *ext) { + int ret = 0; + + if (value < 13) { + *base = value; + } else if (value >= 13 && value <= 0xFF + 13) { + *base = 13; + *ext = value - 13; + ret = sizeof(uint8_t); + } else if (value > 0xFF + 13 && value <= 0xFFFF + 269) { + *base = 14; + *ext = value - 269; + ret = sizeof(uint16_t); + } + + return ret; +} + +/* + * Puts uint16_t (in network order) into given char stream. + * + * Helper function. + */ +static char *coap_add_uint16(char *ptr, uint16_t val) { + *ptr = val >> 8; + ptr++; + *ptr = val & 0x00FF; + ptr++; + return ptr; +} + +/* + * Puts extended value of Opt Number/Length into given char stream. + * + * Helper function. + */ +static char *coap_add_opt_info(char *ptr, uint16_t val, size_t len) { + if (len == sizeof(uint8_t)) { + *ptr = (char) val; + ptr++; + } else if (len == sizeof(uint16_t)) { + ptr = coap_add_uint16(ptr, val); + } + + return ptr; +} + +/* + * Verifies given mg_coap_message and calculates message size for it. + * + * Helper function. + */ +static uint32_t coap_calculate_packet_size(struct mg_coap_message *cm, + size_t *len) { + struct mg_coap_option *opt; + uint32_t prev_opt_number; + + *len = 4; /* header */ + if (cm->msg_type > MG_COAP_MSG_MAX) { + return MG_COAP_ERROR | MG_COAP_MSG_TYPE_FIELD; + } + if (cm->token.len > 8) { + return MG_COAP_ERROR | MG_COAP_TOKEN_FIELD; + } + if (cm->code_class > 7) { + return MG_COAP_ERROR | MG_COAP_CODE_CLASS_FIELD; + } + if (cm->code_detail > 31) { + return MG_COAP_ERROR | MG_COAP_CODE_DETAIL_FIELD; + } + + *len += cm->token.len; + if (cm->payload.len != 0) { + *len += cm->payload.len + 1; /* ... + 1; add payload marker */ + } + + opt = cm->options; + prev_opt_number = 0; + while (opt != NULL) { + *len += 1; /* basic delta/length */ + *len += coap_get_ext_opt_size(opt->number - prev_opt_number); + *len += coap_get_ext_opt_size((uint32_t) opt->value.len); + /* + * Current implementation performs check if + * option_number > previous option_number and produces an error + * TODO(alashkin): write design doc with limitations + * May be resorting is more suitable solution. + */ + if ((opt->next != NULL && opt->number > opt->next->number) || + opt->value.len > 0xFFFF + 269 || + opt->number - prev_opt_number > 0xFFFF + 269) { + return MG_COAP_ERROR | MG_COAP_OPTIOMG_FIELD; + } + *len += opt->value.len; + prev_opt_number = opt->number; + opt = opt->next; + } + + return 0; +} + +uint32_t mg_coap_compose(struct mg_coap_message *cm, struct mbuf *io) { + struct mg_coap_option *opt; + uint32_t res, prev_opt_number; + size_t prev_io_len, packet_size; + char *ptr; + + res = coap_calculate_packet_size(cm, &packet_size); + if (res != 0) { + return res; + } + + /* saving previous lenght to handle non-empty mbuf */ + prev_io_len = io->len; + if (mbuf_append(io, NULL, packet_size) == 0) return MG_COAP_ERROR; + ptr = io->buf + prev_io_len; + + /* + * since cm is verified, it is possible to use bits shift operator + * without additional zeroing of unused bits + */ + + /* ver: 2 bits, msg_type: 2 bits, toklen: 4 bits */ + *ptr = (1 << 6) | (cm->msg_type << 4) | (uint8_t)(cm->token.len); + ptr++; + + /* code class: 3 bits, code detail: 5 bits */ + *ptr = (cm->code_class << 5) | (cm->code_detail); + ptr++; + + ptr = coap_add_uint16(ptr, cm->msg_id); + + if (cm->token.len != 0) { + memcpy(ptr, cm->token.p, cm->token.len); + ptr += cm->token.len; + } + + opt = cm->options; + prev_opt_number = 0; + while (opt != NULL) { + uint8_t delta_base = 0, length_base = 0; + uint16_t delta_ext = 0, length_ext = 0; + + size_t opt_delta_len = + coap_split_opt(opt->number - prev_opt_number, &delta_base, &delta_ext); + size_t opt_lenght_len = + coap_split_opt((uint32_t) opt->value.len, &length_base, &length_ext); + + *ptr = (delta_base << 4) | length_base; + ptr++; + + ptr = coap_add_opt_info(ptr, delta_ext, opt_delta_len); + ptr = coap_add_opt_info(ptr, length_ext, opt_lenght_len); + + if (opt->value.len != 0) { + memcpy(ptr, opt->value.p, opt->value.len); + ptr += opt->value.len; + } + + prev_opt_number = opt->number; + opt = opt->next; + } + + if (cm->payload.len != 0) { + *ptr = (char) -1; + ptr++; + memcpy(ptr, cm->payload.p, cm->payload.len); + } + + return 0; +} + +uint32_t mg_coap_send_message(struct mg_connection *nc, + struct mg_coap_message *cm) { + struct mbuf packet_out; + uint32_t compose_res; + + mbuf_init(&packet_out, 0); + compose_res = mg_coap_compose(cm, &packet_out); + if (compose_res != 0) { + return compose_res; /* LCOV_EXCL_LINE */ + } + + mg_send(nc, packet_out.buf, (int) packet_out.len); + mbuf_free(&packet_out); + + return 0; +} + +uint32_t mg_coap_send_ack(struct mg_connection *nc, uint16_t msg_id) { + struct mg_coap_message cm; + memset(&cm, 0, sizeof(cm)); + cm.msg_type = MG_COAP_MSG_ACK; + cm.msg_id = msg_id; + + return mg_coap_send_message(nc, &cm); +} + +static void coap_handler(struct mg_connection *nc, int ev, + void *ev_data MG_UD_ARG(void *user_data)) { + struct mbuf *io = &nc->recv_mbuf; + struct mg_coap_message cm; + uint32_t parse_res; + + memset(&cm, 0, sizeof(cm)); + + nc->handler(nc, ev, ev_data MG_UD_ARG(user_data)); + + switch (ev) { + case MG_EV_RECV: + parse_res = mg_coap_parse(io, &cm); + if ((parse_res & MG_COAP_IGNORE) == 0) { + if ((cm.flags & MG_COAP_NOT_ENOUGH_DATA) != 0) { + /* + * Since we support UDP only + * MG_COAP_NOT_ENOUGH_DATA == MG_COAP_FORMAT_ERROR + */ + cm.flags |= MG_COAP_FORMAT_ERROR; /* LCOV_EXCL_LINE */ + } /* LCOV_EXCL_LINE */ + nc->handler(nc, MG_COAP_EVENT_BASE + cm.msg_type, + &cm MG_UD_ARG(user_data)); + } + + mg_coap_free_options(&cm); + mbuf_remove(io, io->len); + break; + } +} +/* + * Attach built-in CoAP event handler to the given connection. + * + * The user-defined event handler will receive following extra events: + * + * - MG_EV_COAP_CON + * - MG_EV_COAP_NOC + * - MG_EV_COAP_ACK + * - MG_EV_COAP_RST + */ +int mg_set_protocol_coap(struct mg_connection *nc) { + /* supports UDP only */ + if ((nc->flags & MG_F_UDP) == 0) { + return -1; + } + + nc->proto_handler = coap_handler; + + return 0; +} + +#endif /* MG_ENABLE_COAP */ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_sntp.c" +#endif +/* + * Copyright (c) 2016 Cesanta Software Limited + * All rights reserved + */ + +/* Amalgamated: #include "mg_internal.h" */ +/* Amalgamated: #include "mg_sntp.h" */ +/* Amalgamated: #include "mg_util.h" */ + +#if MG_ENABLE_SNTP + +#define SNTP_TIME_OFFSET 2208988800 + +#ifndef SNTP_TIMEOUT +#define SNTP_TIMEOUT 10 +#endif + +#ifndef SNTP_ATTEMPTS +#define SNTP_ATTEMPTS 3 +#endif + +static uint64_t mg_get_sec(uint64_t val) { + return (val & 0xFFFFFFFF00000000) >> 32; +} + +static uint64_t mg_get_usec(uint64_t val) { + uint64_t tmp = (val & 0x00000000FFFFFFFF); + tmp *= 1000000; + tmp >>= 32; + return tmp; +} + +static void mg_ntp_to_tv(uint64_t val, struct timeval *tv) { + uint64_t tmp; + tmp = mg_get_sec(val); + tmp -= SNTP_TIME_OFFSET; + tv->tv_sec = tmp; + tv->tv_usec = mg_get_usec(val); +} + +static void mg_get_ntp_ts(const char *ntp, uint64_t *val) { + uint32_t tmp; + memcpy(&tmp, ntp, sizeof(tmp)); + tmp = ntohl(tmp); + *val = (uint64_t) tmp << 32; + memcpy(&tmp, ntp + 4, sizeof(tmp)); + tmp = ntohl(tmp); + *val |= tmp; +} + +void mg_sntp_send_request(struct mg_connection *c) { + uint8_t buf[48] = {0}; + /* + * header - 8 bit: + * LI (2 bit) - 3 (not in sync), VN (3 bit) - 4 (version), + * mode (3 bit) - 3 (client) + */ + buf[0] = (3 << 6) | (4 << 3) | 3; + +/* + * Next fields should be empty in client request + * stratum, 8 bit + * poll interval, 8 bit + * rrecision, 8 bit + * root delay, 32 bit + * root dispersion, 32 bit + * ref id, 32 bit + * ref timestamp, 64 bit + * originate Timestamp, 64 bit + * receive Timestamp, 64 bit +*/ + +/* + * convert time to sntp format (sntp starts from 00:00:00 01.01.1900) + * according to rfc868 it is 2208988800L sec + * this information is used to correct roundtrip delay + * but if local clock is absolutely broken (and doesn't work even + * as simple timer), it is better to disable it +*/ +#ifndef MG_SNTP_NO_DELAY_CORRECTION + uint32_t sec; + sec = htonl((uint32_t)(mg_time() + SNTP_TIME_OFFSET)); + memcpy(&buf[40], &sec, sizeof(sec)); +#endif + + mg_send(c, buf, sizeof(buf)); +} + +#ifndef MG_SNTP_NO_DELAY_CORRECTION +static uint64_t mg_calculate_delay(uint64_t t1, uint64_t t2, uint64_t t3) { + /* roundloop delay = (T4 - T1) - (T3 - T2) */ + uint64_t d1 = ((mg_time() + SNTP_TIME_OFFSET) * 1000000) - + (mg_get_sec(t1) * 1000000 + mg_get_usec(t1)); + uint64_t d2 = (mg_get_sec(t3) * 1000000 + mg_get_usec(t3)) - + (mg_get_sec(t2) * 1000000 + mg_get_usec(t2)); + + return (d1 > d2) ? d1 - d2 : 0; +} +#endif + +MG_INTERNAL int mg_sntp_parse_reply(const char *buf, int len, + struct mg_sntp_message *msg) { + uint8_t hdr; + uint64_t trsm_ts_T3, delay = 0; + int mode; + struct timeval tv; + + if (len < 48) { + return -1; + } + + hdr = buf[0]; + + if ((hdr & 0x38) >> 3 != 4) { + /* Wrong version */ + return -1; + } + + mode = hdr & 0x7; + if (mode != 4 && mode != 5) { + /* Not a server reply */ + return -1; + } + + memset(msg, 0, sizeof(*msg)); + + msg->kiss_of_death = (buf[1] == 0); /* Server asks to not send requests */ + + mg_get_ntp_ts(&buf[40], &trsm_ts_T3); + +#ifndef MG_SNTP_NO_DELAY_CORRECTION + { + uint64_t orig_ts_T1, recv_ts_T2; + mg_get_ntp_ts(&buf[24], &orig_ts_T1); + mg_get_ntp_ts(&buf[32], &recv_ts_T2); + delay = mg_calculate_delay(orig_ts_T1, recv_ts_T2, trsm_ts_T3); + } +#endif + + mg_ntp_to_tv(trsm_ts_T3, &tv); + + msg->time = (double) tv.tv_sec + (((double) tv.tv_usec + delay) / 1000000.0); + + return 0; +} + +static void mg_sntp_handler(struct mg_connection *c, int ev, + void *ev_data MG_UD_ARG(void *user_data)) { + struct mbuf *io = &c->recv_mbuf; + struct mg_sntp_message msg; + + c->handler(c, ev, ev_data MG_UD_ARG(user_data)); + + switch (ev) { + case MG_EV_RECV: { + if (mg_sntp_parse_reply(io->buf, io->len, &msg) < 0) { + DBG(("Invalid SNTP packet received (%d)", (int) io->len)); + c->handler(c, MG_SNTP_MALFORMED_REPLY, NULL MG_UD_ARG(user_data)); + } else { + c->handler(c, MG_SNTP_REPLY, (void *) &msg MG_UD_ARG(user_data)); + } + + mbuf_remove(io, io->len); + break; + } + } +} + +int mg_set_protocol_sntp(struct mg_connection *c) { + if ((c->flags & MG_F_UDP) == 0) { + return -1; + } + + c->proto_handler = mg_sntp_handler; + + return 0; +} + +struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, + MG_CB(mg_event_handler_t event_handler, + void *user_data), + const char *sntp_server_name) { + struct mg_connection *c = NULL; + char url[100], *p_url = url; + const char *proto = "", *port = "", *tmp; + + /* If port is not specified, use default (123) */ + tmp = strchr(sntp_server_name, ':'); + if (tmp != NULL && *(tmp + 1) == '/') { + tmp = strchr(tmp + 1, ':'); + } + + if (tmp == NULL) { + port = ":123"; + } + + /* Add udp:// if needed */ + if (strncmp(sntp_server_name, "udp://", 6) != 0) { + proto = "udp://"; + } + + mg_asprintf(&p_url, sizeof(url), "%s%s%s", proto, sntp_server_name, port); + + c = mg_connect(mgr, p_url, event_handler MG_UD_ARG(user_data)); + + if (c == NULL) { + goto cleanup; + } + + mg_set_protocol_sntp(c); + +cleanup: + if (p_url != url) { + MG_FREE(p_url); + } + + return c; +} + +struct sntp_data { + mg_event_handler_t hander; + int count; +}; + +static void mg_sntp_util_ev_handler(struct mg_connection *c, int ev, + void *ev_data MG_UD_ARG(void *user_data)) { +#if !MG_ENABLE_CALLBACK_USERDATA + void *user_data = c->user_data; +#endif + struct sntp_data *sd = (struct sntp_data *) user_data; + + switch (ev) { + case MG_EV_CONNECT: + if (*(int *) ev_data != 0) { + mg_call(c, sd->hander, c->user_data, MG_SNTP_FAILED, NULL); + break; + } + /* fallthrough */ + case MG_EV_TIMER: + if (sd->count <= SNTP_ATTEMPTS) { + mg_sntp_send_request(c); + mg_set_timer(c, mg_time() + 10); + sd->count++; + } else { + mg_call(c, sd->hander, c->user_data, MG_SNTP_FAILED, NULL); + c->flags |= MG_F_CLOSE_IMMEDIATELY; + } + break; + case MG_SNTP_MALFORMED_REPLY: + mg_call(c, sd->hander, c->user_data, MG_SNTP_FAILED, NULL); + c->flags |= MG_F_CLOSE_IMMEDIATELY; + break; + case MG_SNTP_REPLY: + mg_call(c, sd->hander, c->user_data, MG_SNTP_REPLY, ev_data); + c->flags |= MG_F_CLOSE_IMMEDIATELY; + break; + case MG_EV_CLOSE: + MG_FREE(user_data); + c->user_data = NULL; + break; + } +} + +struct mg_connection *mg_sntp_get_time(struct mg_mgr *mgr, + mg_event_handler_t event_handler, + const char *sntp_server_name) { + struct mg_connection *c; + struct sntp_data *sd = (struct sntp_data *) MG_CALLOC(1, sizeof(*sd)); + if (sd == NULL) { + return NULL; + } + + c = mg_sntp_connect(mgr, MG_CB(mg_sntp_util_ev_handler, sd), + sntp_server_name); + if (c == NULL) { + MG_FREE(sd); + return NULL; + } + + sd->hander = event_handler; +#if !MG_ENABLE_CALLBACK_USERDATA + c->user_data = sd; +#endif + + return c; +} + +#endif /* MG_ENABLE_SNTP */ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_socks.c" +#endif +/* + * Copyright (c) 2017 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_SOCKS + +/* Amalgamated: #include "mg_socks.h" */ +/* Amalgamated: #include "mg_internal.h" */ + +/* + * https://www.ietf.org/rfc/rfc1928.txt paragraph 3, handle client handshake + * + * +----+----------+----------+ + * |VER | NMETHODS | METHODS | + * +----+----------+----------+ + * | 1 | 1 | 1 to 255 | + * +----+----------+----------+ + */ +static void mg_socks5_handshake(struct mg_connection *c) { + struct mbuf *r = &c->recv_mbuf; + if (r->buf[0] != MG_SOCKS_VERSION) { + c->flags |= MG_F_CLOSE_IMMEDIATELY; + } else if (r->len > 2 && (size_t) r->buf[1] + 2 <= r->len) { + /* https://www.ietf.org/rfc/rfc1928.txt paragraph 3 */ + unsigned char reply[2] = {MG_SOCKS_VERSION, MG_SOCKS_HANDSHAKE_FAILURE}; + int i; + for (i = 2; i < r->buf[1] + 2; i++) { + /* TODO(lsm): support other auth methods */ + if (r->buf[i] == MG_SOCKS_HANDSHAKE_NOAUTH) reply[1] = r->buf[i]; + } + mbuf_remove(r, 2 + r->buf[1]); + mg_send(c, reply, sizeof(reply)); + c->flags |= MG_SOCKS_HANDSHAKE_DONE; /* Mark handshake done */ + } +} + +static void disband(struct mg_connection *c) { + struct mg_connection *c2 = (struct mg_connection *) c->user_data; + if (c2 != NULL) { + c2->flags |= MG_F_SEND_AND_CLOSE; + c2->user_data = NULL; + } + c->flags |= MG_F_SEND_AND_CLOSE; + c->user_data = NULL; +} + +static void relay_data(struct mg_connection *c) { + struct mg_connection *c2 = (struct mg_connection *) c->user_data; + if (c2 != NULL) { + mg_send(c2, c->recv_mbuf.buf, c->recv_mbuf.len); + mbuf_remove(&c->recv_mbuf, c->recv_mbuf.len); + } else { + c->flags |= MG_F_SEND_AND_CLOSE; + } +} + +static void serv_ev_handler(struct mg_connection *c, int ev, void *ev_data) { + if (ev == MG_EV_CLOSE) { + disband(c); + } else if (ev == MG_EV_RECV) { + relay_data(c); + } else if (ev == MG_EV_CONNECT) { + int res = *(int *) ev_data; + if (res != 0) LOG(LL_ERROR, ("connect error: %d", res)); + } +} + +static void mg_socks5_connect(struct mg_connection *c, const char *addr) { + struct mg_connection *serv = mg_connect(c->mgr, addr, serv_ev_handler); + serv->user_data = c; + c->user_data = serv; +} + +/* + * Request, https://www.ietf.org/rfc/rfc1928.txt paragraph 4 + * + * +----+-----+-------+------+----------+----------+ + * |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | + * +----+-----+-------+------+----------+----------+ + * | 1 | 1 | X'00' | 1 | Variable | 2 | + * +----+-----+-------+------+----------+----------+ + */ +static void mg_socks5_handle_request(struct mg_connection *c) { + struct mbuf *r = &c->recv_mbuf; + unsigned char *p = (unsigned char *) r->buf; + unsigned char addr_len = 4, reply = MG_SOCKS_SUCCESS; + int ver, cmd, atyp; + char addr[300]; + + if (r->len < 8) return; /* return if not fully buffered. min DST.ADDR is 2 */ + ver = p[0]; + cmd = p[1]; + atyp = p[3]; + + /* TODO(lsm): support other commands */ + if (ver != MG_SOCKS_VERSION || cmd != MG_SOCKS_CMD_CONNECT) { + reply = MG_SOCKS_CMD_NOT_SUPPORTED; + } else if (atyp == MG_SOCKS_ADDR_IPV4) { + addr_len = 4; + if (r->len < (size_t) addr_len + 6) return; /* return if not buffered */ + snprintf(addr, sizeof(addr), "%d.%d.%d.%d:%d", p[4], p[5], p[6], p[7], + p[8] << 8 | p[9]); + mg_socks5_connect(c, addr); + } else if (atyp == MG_SOCKS_ADDR_IPV6) { + addr_len = 16; + if (r->len < (size_t) addr_len + 6) return; /* return if not buffered */ + snprintf(addr, sizeof(addr), "[%x:%x:%x:%x:%x:%x:%x:%x]:%d", + p[4] << 8 | p[5], p[6] << 8 | p[7], p[8] << 8 | p[9], + p[10] << 8 | p[11], p[12] << 8 | p[13], p[14] << 8 | p[15], + p[16] << 8 | p[17], p[18] << 8 | p[19], p[20] << 8 | p[21]); + mg_socks5_connect(c, addr); + } else if (atyp == MG_SOCKS_ADDR_DOMAIN) { + addr_len = p[4] + 1; + if (r->len < (size_t) addr_len + 6) return; /* return if not buffered */ + snprintf(addr, sizeof(addr), "%.*s:%d", p[4], p + 5, + p[4 + addr_len] << 8 | p[4 + addr_len + 1]); + mg_socks5_connect(c, addr); + } else { + reply = MG_SOCKS_ADDR_NOT_SUPPORTED; + } + + /* + * Reply, https://www.ietf.org/rfc/rfc1928.txt paragraph 5 + * + * +----+-----+-------+------+----------+----------+ + * |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | + * +----+-----+-------+------+----------+----------+ + * | 1 | 1 | X'00' | 1 | Variable | 2 | + * +----+-----+-------+------+----------+----------+ + */ + { + unsigned char buf[] = {MG_SOCKS_VERSION, reply, 0}; + mg_send(c, buf, sizeof(buf)); + } + mg_send(c, r->buf + 3, addr_len + 1 + 2); + + mbuf_remove(r, 6 + addr_len); /* Remove request from the input stream */ + c->flags |= MG_SOCKS_CONNECT_DONE; /* Mark ourselves as connected */ +} + +static void socks_handler(struct mg_connection *c, int ev, void *ev_data) { + if (ev == MG_EV_RECV) { + if (!(c->flags & MG_SOCKS_HANDSHAKE_DONE)) mg_socks5_handshake(c); + if (c->flags & MG_SOCKS_HANDSHAKE_DONE && + !(c->flags & MG_SOCKS_CONNECT_DONE)) { + mg_socks5_handle_request(c); + } + if (c->flags & MG_SOCKS_CONNECT_DONE) relay_data(c); + } else if (ev == MG_EV_CLOSE) { + disband(c); + } + (void) ev_data; +} + +void mg_set_protocol_socks(struct mg_connection *c) { + c->proto_handler = socks_handler; +} +#endif +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/cc3200/cc3200_libc.c" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if CS_PLATFORM == CS_P_CC3200 + +/* Amalgamated: #include "common/mg_mem.h" */ +#include +#include + +#ifndef __TI_COMPILER_VERSION__ +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#define CONSOLE_UART UARTA0_BASE + +#ifdef __TI_COMPILER_VERSION__ +int asprintf(char **strp, const char *fmt, ...) { + va_list ap; + int len; + + *strp = MG_MALLOC(BUFSIZ); + if (*strp == NULL) return -1; + + va_start(ap, fmt); + len = vsnprintf(*strp, BUFSIZ, fmt, ap); + va_end(ap); + + if (len > 0) { + *strp = MG_REALLOC(*strp, len + 1); + if (*strp == NULL) return -1; + } + + if (len >= BUFSIZ) { + va_start(ap, fmt); + len = vsnprintf(*strp, len + 1, fmt, ap); + va_end(ap); + } + + return len; +} + +#if MG_TI_NO_HOST_INTERFACE +time_t HOSTtime() { + struct timeval tp; + gettimeofday(&tp, NULL); + return tp.tv_sec; +} +#endif + +#endif /* __TI_COMPILER_VERSION__ */ + +void fprint_str(FILE *fp, const char *str) { + while (*str != '\0') { + if (*str == '\n') MAP_UARTCharPut(CONSOLE_UART, '\r'); + MAP_UARTCharPut(CONSOLE_UART, *str++); + } +} + +void _exit(int status) { + fprint_str(stderr, "_exit\n"); + /* cause an unaligned access exception, that will drop you into gdb */ + *(int *) 1 = status; + while (1) + ; /* avoid gcc warning because stdlib abort() has noreturn attribute */ +} + +void _not_implemented(const char *what) { + fprint_str(stderr, what); + fprint_str(stderr, " is not implemented\n"); + _exit(42); +} + +int _kill(int pid, int sig) { + (void) pid; + (void) sig; + _not_implemented("_kill"); + return -1; +} + +int _getpid() { + fprint_str(stderr, "_getpid is not implemented\n"); + return 42; +} + +int _isatty(int fd) { + /* 0, 1 and 2 are TTYs. */ + return fd < 2; +} + +#endif /* CS_PLATFORM == CS_P_CC3200 */ +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/msp432/msp432_libc.c" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if CS_PLATFORM == CS_P_MSP432 + +#include +#include + +int gettimeofday(struct timeval *tp, void *tzp) { + uint32_t ticks = Clock_getTicks(); + tp->tv_sec = ticks / 1000; + tp->tv_usec = (ticks % 1000) * 1000; + return 0; +} + +#endif /* CS_PLATFORM == CS_P_MSP432 */ +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/nrf5/nrf5_libc.c" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if (CS_PLATFORM == CS_P_NRF51 || CS_PLATFORM == CS_P_NRF52) && \ + defined(__ARMCC_VERSION) +int gettimeofday(struct timeval *tp, void *tzp) { + /* TODO */ + tp->tv_sec = 0; + tp->tv_usec = 0; + return 0; +} +#endif +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/simplelink/sl_fs_slfs.h" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_SIMPLELINK_SL_FS_SLFS_H_ +#define CS_COMMON_PLATFORMS_SIMPLELINK_SL_FS_SLFS_H_ + +#if defined(MG_FS_SLFS) + +#include +#ifndef __TI_COMPILER_VERSION__ +#include +#include +#endif + +#define MAX_OPEN_SLFS_FILES 8 + +/* Indirect libc interface - same functions, different names. */ +int fs_slfs_open(const char *pathname, int flags, mode_t mode); +int fs_slfs_close(int fd); +ssize_t fs_slfs_read(int fd, void *buf, size_t count); +ssize_t fs_slfs_write(int fd, const void *buf, size_t count); +int fs_slfs_stat(const char *pathname, struct stat *s); +int fs_slfs_fstat(int fd, struct stat *s); +off_t fs_slfs_lseek(int fd, off_t offset, int whence); +int fs_slfs_unlink(const char *filename); +int fs_slfs_rename(const char *from, const char *to); + +void fs_slfs_set_new_file_size(const char *name, size_t size); + +#endif /* defined(MG_FS_SLFS) */ + +#endif /* CS_COMMON_PLATFORMS_SIMPLELINK_SL_FS_SLFS_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/simplelink/sl_fs_slfs.c" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +/* Standard libc interface to TI SimpleLink FS. */ + +#if defined(MG_FS_SLFS) || defined(CC3200_FS_SLFS) + +/* Amalgamated: #include "common/platforms/simplelink/sl_fs_slfs.h" */ + +#include + +#if CS_PLATFORM == CS_P_CC3200 +#include +#endif + +/* Amalgamated: #include "common/cs_dbg.h" */ +/* Amalgamated: #include "common/mg_mem.h" */ + +#if SL_MAJOR_VERSION_NUM < 2 +int slfs_open(const unsigned char *fname, uint32_t flags) { + _i32 fh; + _i32 r = sl_FsOpen(fname, flags, NULL /* token */, &fh); + return (r < 0 ? r : fh); +} +#else /* SL_MAJOR_VERSION_NUM >= 2 */ +int slfs_open(const unsigned char *fname, uint32_t flags) { + return sl_FsOpen(fname, flags, NULL /* token */); +} +#endif + +/* From sl_fs.c */ +int set_errno(int e); +const char *drop_dir(const char *fname, bool *is_slfs); + +/* + * With SLFS, you have to pre-declare max file size. Yes. Really. + * 64K should be enough for everyone. Right? + */ +#ifndef FS_SLFS_MAX_FILE_SIZE +#define FS_SLFS_MAX_FILE_SIZE (64 * 1024) +#endif + +struct sl_file_size_hint { + char *name; + size_t size; +}; + +struct sl_fd_info { + _i32 fh; + _off_t pos; + size_t size; +}; + +static struct sl_fd_info s_sl_fds[MAX_OPEN_SLFS_FILES]; +static struct sl_file_size_hint s_sl_file_size_hints[MAX_OPEN_SLFS_FILES]; + +static int sl_fs_to_errno(_i32 r) { + DBG(("SL error: %d", (int) r)); + switch (r) { + case SL_FS_OK: + return 0; + case SL_ERROR_FS_FILE_NAME_EXIST: + return EEXIST; + case SL_ERROR_FS_WRONG_FILE_NAME: + return EINVAL; + case SL_ERROR_FS_NO_AVAILABLE_NV_INDEX: + case SL_ERROR_FS_NOT_ENOUGH_STORAGE_SPACE: + return ENOSPC; + case SL_ERROR_FS_FAILED_TO_ALLOCATE_MEM: + return ENOMEM; + case SL_ERROR_FS_FILE_NOT_EXISTS: + return ENOENT; + case SL_ERROR_FS_NOT_SUPPORTED: + return ENOTSUP; + } + return ENXIO; +} + +int fs_slfs_open(const char *pathname, int flags, mode_t mode) { + int fd; + for (fd = 0; fd < MAX_OPEN_SLFS_FILES; fd++) { + if (s_sl_fds[fd].fh <= 0) break; + } + if (fd >= MAX_OPEN_SLFS_FILES) return set_errno(ENOMEM); + struct sl_fd_info *fi = &s_sl_fds[fd]; + + /* + * Apply path manipulations again, in case we got here directly + * (via TI libc's "add_device"). + */ + pathname = drop_dir(pathname, NULL); + + _u32 am = 0; + fi->size = (size_t) -1; + int rw = (flags & 3); + size_t new_size = FS_SLFS_MAX_FILE_SIZE; + if (rw == O_RDONLY) { + SlFsFileInfo_t sl_fi; + _i32 r = sl_FsGetInfo((const _u8 *) pathname, 0, &sl_fi); + if (r == SL_FS_OK) { + fi->size = SL_FI_FILE_SIZE(sl_fi); + } + am = SL_FS_READ; + } else { + if (!(flags & O_TRUNC) || (flags & O_APPEND)) { + // FailFS files cannot be opened for append and will be truncated + // when opened for write. + return set_errno(ENOTSUP); + } + if (flags & O_CREAT) { + size_t i; + for (i = 0; i < MAX_OPEN_SLFS_FILES; i++) { + if (s_sl_file_size_hints[i].name != NULL && + strcmp(s_sl_file_size_hints[i].name, pathname) == 0) { + new_size = s_sl_file_size_hints[i].size; + MG_FREE(s_sl_file_size_hints[i].name); + s_sl_file_size_hints[i].name = NULL; + break; + } + } + am = FS_MODE_OPEN_CREATE(new_size, 0); + } else { + am = SL_FS_WRITE; + } + } + fi->fh = slfs_open((_u8 *) pathname, am); + LOG(LL_DEBUG, ("sl_FsOpen(%s, 0x%x) sz %u = %d", pathname, (int) am, + (unsigned int) new_size, (int) fi->fh)); + int r; + if (fi->fh >= 0) { + fi->pos = 0; + r = fd; + } else { + r = set_errno(sl_fs_to_errno(fi->fh)); + } + return r; +} + +int fs_slfs_close(int fd) { + struct sl_fd_info *fi = &s_sl_fds[fd]; + if (fi->fh <= 0) return set_errno(EBADF); + _i32 r = sl_FsClose(fi->fh, NULL, NULL, 0); + LOG(LL_DEBUG, ("sl_FsClose(%d) = %d", (int) fi->fh, (int) r)); + s_sl_fds[fd].fh = -1; + return set_errno(sl_fs_to_errno(r)); +} + +ssize_t fs_slfs_read(int fd, void *buf, size_t count) { + struct sl_fd_info *fi = &s_sl_fds[fd]; + if (fi->fh <= 0) return set_errno(EBADF); + /* Simulate EOF. sl_FsRead @ file_size return SL_FS_ERR_OFFSET_OUT_OF_RANGE. + */ + if (fi->pos == fi->size) return 0; + _i32 r = sl_FsRead(fi->fh, fi->pos, buf, count); + DBG(("sl_FsRead(%d, %d, %d) = %d", (int) fi->fh, (int) fi->pos, (int) count, + (int) r)); + if (r >= 0) { + fi->pos += r; + return r; + } + return set_errno(sl_fs_to_errno(r)); +} + +ssize_t fs_slfs_write(int fd, const void *buf, size_t count) { + struct sl_fd_info *fi = &s_sl_fds[fd]; + if (fi->fh <= 0) return set_errno(EBADF); + _i32 r = sl_FsWrite(fi->fh, fi->pos, (_u8 *) buf, count); + DBG(("sl_FsWrite(%d, %d, %d) = %d", (int) fi->fh, (int) fi->pos, (int) count, + (int) r)); + if (r >= 0) { + fi->pos += r; + return r; + } + return set_errno(sl_fs_to_errno(r)); +} + +int fs_slfs_stat(const char *pathname, struct stat *s) { + SlFsFileInfo_t sl_fi; + /* + * Apply path manipulations again, in case we got here directly + * (via TI libc's "add_device"). + */ + pathname = drop_dir(pathname, NULL); + _i32 r = sl_FsGetInfo((const _u8 *) pathname, 0, &sl_fi); + if (r == SL_FS_OK) { + s->st_mode = S_IFREG | 0666; + s->st_nlink = 1; + s->st_size = SL_FI_FILE_SIZE(sl_fi); + return 0; + } + return set_errno(sl_fs_to_errno(r)); +} + +int fs_slfs_fstat(int fd, struct stat *s) { + struct sl_fd_info *fi = &s_sl_fds[fd]; + if (fi->fh <= 0) return set_errno(EBADF); + s->st_mode = 0666; + s->st_mode = S_IFREG | 0666; + s->st_nlink = 1; + s->st_size = fi->size; + return 0; +} + +off_t fs_slfs_lseek(int fd, off_t offset, int whence) { + if (s_sl_fds[fd].fh <= 0) return set_errno(EBADF); + switch (whence) { + case SEEK_SET: + s_sl_fds[fd].pos = offset; + break; + case SEEK_CUR: + s_sl_fds[fd].pos += offset; + break; + case SEEK_END: + return set_errno(ENOTSUP); + } + return 0; +} + +int fs_slfs_unlink(const char *pathname) { + /* + * Apply path manipulations again, in case we got here directly + * (via TI libc's "add_device"). + */ + pathname = drop_dir(pathname, NULL); + return set_errno(sl_fs_to_errno(sl_FsDel((const _u8 *) pathname, 0))); +} + +int fs_slfs_rename(const char *from, const char *to) { + return set_errno(ENOTSUP); +} + +void fs_slfs_set_new_file_size(const char *name, size_t size) { + int i; + for (i = 0; i < MAX_OPEN_SLFS_FILES; i++) { + if (s_sl_file_size_hints[i].name == NULL) { + DBG(("File size hint: %s %d", name, (int) size)); + s_sl_file_size_hints[i].name = strdup(name); + s_sl_file_size_hints[i].size = size; + break; + } + } +} + +#endif /* defined(MG_FS_SLFS) || defined(CC3200_FS_SLFS) */ +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/simplelink/sl_fs.c" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_NET_IF == MG_NET_IF_SIMPLELINK && \ + (defined(MG_FS_SLFS) || defined(MG_FS_SPIFFS)) + +int set_errno(int e) { + errno = e; + return (e == 0 ? 0 : -1); +} + +const char *drop_dir(const char *fname, bool *is_slfs) { + if (is_slfs != NULL) { + *is_slfs = (strncmp(fname, "SL:", 3) == 0); + if (*is_slfs) fname += 3; + } + /* Drop "./", if any */ + if (fname[0] == '.' && fname[1] == '/') { + fname += 2; + } + /* + * Drop / if it is the only one in the path. + * This allows use of /pretend/directories but serves /file.txt as normal. + */ + if (fname[0] == '/' && strchr(fname + 1, '/') == NULL) { + fname++; + } + return fname; +} + +#if !defined(MG_FS_NO_VFS) + +#include +#include +#include +#include +#include +#ifdef __TI_COMPILER_VERSION__ +#include +#endif + +/* Amalgamated: #include "common/cs_dbg.h" */ +/* Amalgamated: #include "common/platform.h" */ + +#ifdef CC3200_FS_SPIFFS +/* Amalgamated: #include "cc3200_fs_spiffs.h" */ +#endif + +#ifdef MG_FS_SLFS +/* Amalgamated: #include "sl_fs_slfs.h" */ +#endif + +#define NUM_SYS_FDS 3 +#define SPIFFS_FD_BASE 10 +#define SLFS_FD_BASE 100 + +#if !defined(MG_UART_CHAR_PUT) && !defined(MG_UART_WRITE) +#if CS_PLATFORM == CS_P_CC3200 +#include +#include +#include +#include +#include +#define MG_UART_CHAR_PUT(fd, c) MAP_UARTCharPut(UARTA0_BASE, c); +#else +#define MG_UART_WRITE(fd, buf, len) +#endif /* CS_PLATFORM == CS_P_CC3200 */ +#endif /* !MG_UART_CHAR_PUT */ + +enum fd_type { + FD_INVALID, + FD_SYS, +#ifdef CC3200_FS_SPIFFS + FD_SPIFFS, +#endif +#ifdef MG_FS_SLFS + FD_SLFS +#endif +}; +static int fd_type(int fd) { + if (fd >= 0 && fd < NUM_SYS_FDS) return FD_SYS; +#ifdef CC3200_FS_SPIFFS + if (fd >= SPIFFS_FD_BASE && fd < SPIFFS_FD_BASE + MAX_OPEN_SPIFFS_FILES) { + return FD_SPIFFS; + } +#endif +#ifdef MG_FS_SLFS + if (fd >= SLFS_FD_BASE && fd < SLFS_FD_BASE + MAX_OPEN_SLFS_FILES) { + return FD_SLFS; + } +#endif + return FD_INVALID; +} + +#if MG_TI_NO_HOST_INTERFACE +int open(const char *pathname, unsigned flags, int mode) { +#else +int _open(const char *pathname, int flags, mode_t mode) { +#endif + int fd = -1; + bool is_sl; + const char *fname = drop_dir(pathname, &is_sl); + if (is_sl) { +#ifdef MG_FS_SLFS + fd = fs_slfs_open(fname, flags, mode); + if (fd >= 0) fd += SLFS_FD_BASE; +#endif + } else { +#ifdef CC3200_FS_SPIFFS + fd = fs_spiffs_open(fname, flags, mode); + if (fd >= 0) fd += SPIFFS_FD_BASE; +#endif + } + LOG(LL_DEBUG, + ("open(%s, 0x%x) = %d, fname = %s", pathname, flags, fd, fname)); + return fd; +} + +int _stat(const char *pathname, struct stat *st) { + int res = -1; + bool is_sl; + const char *fname = drop_dir(pathname, &is_sl); + memset(st, 0, sizeof(*st)); + /* Simulate statting the root directory. */ + if (fname[0] == '\0' || strcmp(fname, ".") == 0) { + st->st_ino = 0; + st->st_mode = S_IFDIR | 0777; + st->st_nlink = 1; + st->st_size = 0; + return 0; + } + if (is_sl) { +#ifdef MG_FS_SLFS + res = fs_slfs_stat(fname, st); +#endif + } else { +#ifdef CC3200_FS_SPIFFS + res = fs_spiffs_stat(fname, st); +#endif + } + LOG(LL_DEBUG, ("stat(%s) = %d; fname = %s", pathname, res, fname)); + return res; +} + +#if MG_TI_NO_HOST_INTERFACE +int close(int fd) { +#else +int _close(int fd) { +#endif + int r = -1; + switch (fd_type(fd)) { + case FD_INVALID: + r = set_errno(EBADF); + break; + case FD_SYS: + r = set_errno(EACCES); + break; +#ifdef CC3200_FS_SPIFFS + case FD_SPIFFS: + r = fs_spiffs_close(fd - SPIFFS_FD_BASE); + break; +#endif +#ifdef MG_FS_SLFS + case FD_SLFS: + r = fs_slfs_close(fd - SLFS_FD_BASE); + break; +#endif + } + DBG(("close(%d) = %d", fd, r)); + return r; +} + +#if MG_TI_NO_HOST_INTERFACE +off_t lseek(int fd, off_t offset, int whence) { +#else +off_t _lseek(int fd, off_t offset, int whence) { +#endif + int r = -1; + switch (fd_type(fd)) { + case FD_INVALID: + r = set_errno(EBADF); + break; + case FD_SYS: + r = set_errno(ESPIPE); + break; +#ifdef CC3200_FS_SPIFFS + case FD_SPIFFS: + r = fs_spiffs_lseek(fd - SPIFFS_FD_BASE, offset, whence); + break; +#endif +#ifdef MG_FS_SLFS + case FD_SLFS: + r = fs_slfs_lseek(fd - SLFS_FD_BASE, offset, whence); + break; +#endif + } + DBG(("lseek(%d, %d, %d) = %d", fd, (int) offset, whence, r)); + return r; +} + +int _fstat(int fd, struct stat *s) { + int r = -1; + memset(s, 0, sizeof(*s)); + switch (fd_type(fd)) { + case FD_INVALID: + r = set_errno(EBADF); + break; + case FD_SYS: { + /* Create barely passable stats for STD{IN,OUT,ERR}. */ + memset(s, 0, sizeof(*s)); + s->st_ino = fd; + s->st_mode = S_IFCHR | 0666; + r = 0; + break; + } +#ifdef CC3200_FS_SPIFFS + case FD_SPIFFS: + r = fs_spiffs_fstat(fd - SPIFFS_FD_BASE, s); + break; +#endif +#ifdef MG_FS_SLFS + case FD_SLFS: + r = fs_slfs_fstat(fd - SLFS_FD_BASE, s); + break; +#endif + } + DBG(("fstat(%d) = %d", fd, r)); + return r; +} + +#if MG_TI_NO_HOST_INTERFACE +int read(int fd, char *buf, unsigned count) { +#else +ssize_t _read(int fd, void *buf, size_t count) { +#endif + int r = -1; + switch (fd_type(fd)) { + case FD_INVALID: + r = set_errno(EBADF); + break; + case FD_SYS: { + if (fd != 0) { + r = set_errno(EACCES); + break; + } + /* Should we allow reading from stdin = uart? */ + r = set_errno(ENOTSUP); + break; + } +#ifdef CC3200_FS_SPIFFS + case FD_SPIFFS: + r = fs_spiffs_read(fd - SPIFFS_FD_BASE, buf, count); + break; +#endif +#ifdef MG_FS_SLFS + case FD_SLFS: + r = fs_slfs_read(fd - SLFS_FD_BASE, buf, count); + break; +#endif + } + DBG(("read(%d, %u) = %d", fd, count, r)); + return r; +} + +#if MG_TI_NO_HOST_INTERFACE +int write(int fd, const char *buf, unsigned count) { +#else +ssize_t _write(int fd, const void *buf, size_t count) { +#endif + int r = -1; + switch (fd_type(fd)) { + case FD_INVALID: + r = set_errno(EBADF); + break; + case FD_SYS: { + if (fd == 0) { + r = set_errno(EACCES); + break; + } +#ifdef MG_UART_WRITE + MG_UART_WRITE(fd, buf, count); +#elif defined(MG_UART_CHAR_PUT) + { + size_t i; + for (i = 0; i < count; i++) { + const char c = ((const char *) buf)[i]; + if (c == '\n') MG_UART_CHAR_PUT(fd, '\r'); + MG_UART_CHAR_PUT(fd, c); + } + } +#endif + r = count; + break; + } +#ifdef CC3200_FS_SPIFFS + case FD_SPIFFS: + r = fs_spiffs_write(fd - SPIFFS_FD_BASE, buf, count); + break; +#endif +#ifdef MG_FS_SLFS + case FD_SLFS: + r = fs_slfs_write(fd - SLFS_FD_BASE, buf, count); + break; +#endif + } + return r; +} + +/* + * On Newlib we override rename directly too, because the default + * implementation using _link and _unlink doesn't work for us. + */ +#if MG_TI_NO_HOST_INTERFACE || defined(_NEWLIB_VERSION) +int rename(const char *frompath, const char *topath) { + int r = -1; + bool is_sl_from, is_sl_to; + const char *from = drop_dir(frompath, &is_sl_from); + const char *to = drop_dir(topath, &is_sl_to); + if (is_sl_from || is_sl_to) { + set_errno(ENOTSUP); + } else { +#ifdef CC3200_FS_SPIFFS + r = fs_spiffs_rename(from, to); +#endif + } + DBG(("rename(%s, %s) = %d", from, to, r)); + return r; +} +#endif /* MG_TI_NO_HOST_INTERFACE || defined(_NEWLIB_VERSION) */ + +#if MG_TI_NO_HOST_INTERFACE +int unlink(const char *pathname) { +#else +int _unlink(const char *pathname) { +#endif + int r = -1; + bool is_sl; + const char *fname = drop_dir(pathname, &is_sl); + if (is_sl) { +#ifdef MG_FS_SLFS + r = fs_slfs_unlink(fname); +#endif + } else { +#ifdef CC3200_FS_SPIFFS + r = fs_spiffs_unlink(fname); +#endif + } + DBG(("unlink(%s) = %d, fname = %s", pathname, r, fname)); + return r; +} + +#ifdef CC3200_FS_SPIFFS /* FailFS does not support listing files. */ +DIR *opendir(const char *dir_name) { + DIR *r = NULL; + bool is_sl; + drop_dir(dir_name, &is_sl); + if (is_sl) { + r = NULL; + set_errno(ENOTSUP); + } else { + r = fs_spiffs_opendir(dir_name); + } + DBG(("opendir(%s) = %p", dir_name, r)); + return r; +} + +struct dirent *readdir(DIR *dir) { + struct dirent *res = fs_spiffs_readdir(dir); + DBG(("readdir(%p) = %p", dir, res)); + return res; +} + +int closedir(DIR *dir) { + int res = fs_spiffs_closedir(dir); + DBG(("closedir(%p) = %d", dir, res)); + return res; +} + +int rmdir(const char *path) { + return fs_spiffs_rmdir(path); +} + +int mkdir(const char *path, mode_t mode) { + (void) path; + (void) mode; + /* for spiffs supports only root dir, which comes from mongoose as '.' */ + return (strlen(path) == 1 && *path == '.') ? 0 : ENOTDIR; +} +#endif + +int sl_fs_init(void) { + int ret = 1; +#ifdef __TI_COMPILER_VERSION__ +#ifdef MG_FS_SLFS +#pragma diag_push +#pragma diag_suppress 169 /* Nothing we can do about the prototype mismatch. \ + */ + ret = (add_device("SL", _MSA, fs_slfs_open, fs_slfs_close, fs_slfs_read, + fs_slfs_write, fs_slfs_lseek, fs_slfs_unlink, + fs_slfs_rename) == 0); +#pragma diag_pop +#endif +#endif + return ret; +} + +#endif /* !defined(MG_FS_NO_VFS) */ +#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK && (defined(MG_FS_SLFS) || \ + defined(MG_FS_SPIFFS)) */ +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/simplelink/sl_socket.c" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_NET_IF == MG_NET_IF_SIMPLELINK + +#include +#include + +/* Amalgamated: #include "common/platform.h" */ + +const char *inet_ntop(int af, const void *src, char *dst, socklen_t size) { + int res; + struct in_addr *in = (struct in_addr *) src; + if (af != AF_INET) { + errno = ENOTSUP; + return NULL; + } + res = snprintf(dst, size, "%lu.%lu.%lu.%lu", SL_IPV4_BYTE(in->s_addr, 0), + SL_IPV4_BYTE(in->s_addr, 1), SL_IPV4_BYTE(in->s_addr, 2), + SL_IPV4_BYTE(in->s_addr, 3)); + return res > 0 ? dst : NULL; +} + +char *inet_ntoa(struct in_addr n) { + static char a[16]; + return (char *) inet_ntop(AF_INET, &n, a, sizeof(a)); +} + +int inet_pton(int af, const char *src, void *dst) { + uint32_t a0, a1, a2, a3; + uint8_t *db = (uint8_t *) dst; + if (af != AF_INET) { + errno = ENOTSUP; + return 0; + } + if (sscanf(src, "%lu.%lu.%lu.%lu", &a0, &a1, &a2, &a3) != 4) { + return 0; + } + *db = a3; + *(db + 1) = a2; + *(db + 2) = a1; + *(db + 3) = a0; + return 1; +} + +#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK */ +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/simplelink/sl_mg_task.c" +#endif +#if MG_NET_IF == MG_NET_IF_SIMPLELINK && !defined(MG_SIMPLELINK_NO_OSI) + +/* Amalgamated: #include "mg_task.h" */ + +#include + +enum mg_q_msg_type { + MG_Q_MSG_CB, +}; +struct mg_q_msg { + enum mg_q_msg_type type; + void (*cb)(struct mg_mgr *mgr, void *arg); + void *arg; +}; +static OsiMsgQ_t s_mg_q; +static void mg_task(void *arg); + +bool mg_start_task(int priority, int stack_size, mg_init_cb mg_init) { + if (osi_MsgQCreate(&s_mg_q, "MG", sizeof(struct mg_q_msg), 16) != OSI_OK) { + return false; + } + if (osi_TaskCreate(mg_task, (const signed char *) "MG", stack_size, + (void *) mg_init, priority, NULL) != OSI_OK) { + return false; + } + return true; +} + +static void mg_task(void *arg) { + struct mg_mgr mgr; + mg_init_cb mg_init = (mg_init_cb) arg; + mg_mgr_init(&mgr, NULL); + mg_init(&mgr); + while (1) { + struct mg_q_msg msg; + mg_mgr_poll(&mgr, 1); + if (osi_MsgQRead(&s_mg_q, &msg, 1) != OSI_OK) continue; + switch (msg.type) { + case MG_Q_MSG_CB: { + msg.cb(&mgr, msg.arg); + } + } + } +} + +void mg_run_in_task(void (*cb)(struct mg_mgr *mgr, void *arg), void *cb_arg) { + struct mg_q_msg msg = {MG_Q_MSG_CB, cb, cb_arg}; + osi_MsgQWrite(&s_mg_q, &msg, OSI_NO_WAIT); +} + +#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK && !defined(MG_SIMPLELINK_NO_OSI) \ + */ +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/simplelink/sl_net_if.h" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_SIMPLELINK_SL_NET_IF_H_ +#define CS_COMMON_PLATFORMS_SIMPLELINK_SL_NET_IF_H_ + +/* Amalgamated: #include "mongoose/src/net_if.h" */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef MG_ENABLE_NET_IF_SIMPLELINK +#define MG_ENABLE_NET_IF_SIMPLELINK MG_NET_IF == MG_NET_IF_SIMPLELINK +#endif + +extern const struct mg_iface_vtable mg_simplelink_iface_vtable; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CS_COMMON_PLATFORMS_SIMPLELINK_SL_NET_IF_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/simplelink/sl_net_if.c" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +/* Amalgamated: #include "common/platforms/simplelink/sl_net_if.h" */ + +#if MG_ENABLE_NET_IF_SIMPLELINK + +/* Amalgamated: #include "mongoose/src/internal.h" */ +/* Amalgamated: #include "mongoose/src/util.h" */ + +#define MG_TCP_RECV_BUFFER_SIZE 1024 +#define MG_UDP_RECV_BUFFER_SIZE 1500 + +static sock_t mg_open_listening_socket(struct mg_connection *nc, + union socket_address *sa, int type, + int proto); + +void mg_set_non_blocking_mode(sock_t sock) { + SlSockNonblocking_t opt; +#if SL_MAJOR_VERSION_NUM < 2 + opt.NonblockingEnabled = 1; +#else + opt.NonBlockingEnabled = 1; +#endif + sl_SetSockOpt(sock, SL_SOL_SOCKET, SL_SO_NONBLOCKING, &opt, sizeof(opt)); +} + +static int mg_is_error(int n) { + return (n < 0 && n != SL_ERROR_BSD_EALREADY && n != SL_ERROR_BSD_EAGAIN); +} + +void mg_sl_if_connect_tcp(struct mg_connection *nc, + const union socket_address *sa) { + int proto = 0; + if (nc->flags & MG_F_SSL) proto = SL_SEC_SOCKET; + sock_t sock = sl_Socket(AF_INET, SOCK_STREAM, proto); + if (sock < 0) { + nc->err = sock; + goto out; + } + mg_sock_set(nc, sock); +#if MG_ENABLE_SSL + nc->err = sl_set_ssl_opts(sock, nc); + if (nc->err != 0) goto out; +#endif + nc->err = sl_Connect(sock, &sa->sa, sizeof(sa->sin)); +out: + DBG(("%p to %s:%d sock %d %d err %d", nc, inet_ntoa(sa->sin.sin_addr), + ntohs(sa->sin.sin_port), nc->sock, proto, nc->err)); +} + +void mg_sl_if_connect_udp(struct mg_connection *nc) { + sock_t sock = sl_Socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) { + nc->err = sock; + return; + } + mg_sock_set(nc, sock); + nc->err = 0; +} + +int mg_sl_if_listen_tcp(struct mg_connection *nc, union socket_address *sa) { + int proto = 0; + if (nc->flags & MG_F_SSL) proto = SL_SEC_SOCKET; + sock_t sock = mg_open_listening_socket(nc, sa, SOCK_STREAM, proto); + if (sock < 0) return sock; + mg_sock_set(nc, sock); + return 0; +} + +int mg_sl_if_listen_udp(struct mg_connection *nc, union socket_address *sa) { + sock_t sock = mg_open_listening_socket(nc, sa, SOCK_DGRAM, 0); + if (sock == INVALID_SOCKET) return (errno ? errno : 1); + mg_sock_set(nc, sock); + return 0; +} + +void mg_sl_if_tcp_send(struct mg_connection *nc, const void *buf, size_t len) { + mbuf_append(&nc->send_mbuf, buf, len); +} + +void mg_sl_if_udp_send(struct mg_connection *nc, const void *buf, size_t len) { + mbuf_append(&nc->send_mbuf, buf, len); +} + +void mg_sl_if_recved(struct mg_connection *nc, size_t len) { + (void) nc; + (void) len; +} + +int mg_sl_if_create_conn(struct mg_connection *nc) { + (void) nc; + return 1; +} + +void mg_sl_if_destroy_conn(struct mg_connection *nc) { + if (nc->sock == INVALID_SOCKET) return; + /* For UDP, only close outgoing sockets or listeners. */ + if (!(nc->flags & MG_F_UDP) || nc->listener == NULL) { + sl_Close(nc->sock); + } + nc->sock = INVALID_SOCKET; +} + +static int mg_accept_conn(struct mg_connection *lc) { + struct mg_connection *nc; + union socket_address sa; + socklen_t sa_len = sizeof(sa); + sock_t sock = sl_Accept(lc->sock, &sa.sa, &sa_len); + if (sock < 0) { + DBG(("%p: failed to accept: %d", lc, sock)); + return 0; + } + nc = mg_if_accept_new_conn(lc); + if (nc == NULL) { + sl_Close(sock); + return 0; + } + DBG(("%p conn from %s:%d", nc, inet_ntoa(sa.sin.sin_addr), + ntohs(sa.sin.sin_port))); + mg_sock_set(nc, sock); + if (nc->flags & MG_F_SSL) nc->flags |= MG_F_SSL_HANDSHAKE_DONE; + mg_if_accept_tcp_cb(nc, &sa, sa_len); + return 1; +} + +/* 'sa' must be an initialized address to bind to */ +static sock_t mg_open_listening_socket(struct mg_connection *nc, + union socket_address *sa, int type, + int proto) { + int r; + socklen_t sa_len = + (sa->sa.sa_family == AF_INET) ? sizeof(sa->sin) : sizeof(sa->sin6); + sock_t sock = sl_Socket(sa->sa.sa_family, type, proto); + if (sock < 0) return sock; + if ((r = sl_Bind(sock, &sa->sa, sa_len)) < 0) goto clean; + if (type != SOCK_DGRAM) { +#if MG_ENABLE_SSL + if ((r = sl_set_ssl_opts(sock, nc)) < 0) goto clean; +#endif + if ((r = sl_Listen(sock, SOMAXCONN)) < 0) goto clean; + } + mg_set_non_blocking_mode(sock); +clean: + if (r < 0) { + sl_Close(sock); + sock = r; + } + return sock; +} + +static void mg_write_to_socket(struct mg_connection *nc) { + struct mbuf *io = &nc->send_mbuf; + int n = 0; + + if (nc->flags & MG_F_UDP) { + n = sl_SendTo(nc->sock, io->buf, io->len, 0, &nc->sa.sa, + sizeof(nc->sa.sin)); + DBG(("%p %d %d %d %s:%hu", nc, nc->sock, n, errno, + inet_ntoa(nc->sa.sin.sin_addr), ntohs(nc->sa.sin.sin_port))); + } else { + n = (int) sl_Send(nc->sock, io->buf, io->len, 0); + DBG(("%p %d bytes -> %d", nc, n, nc->sock)); + } + + if (n > 0) { + mg_if_sent_cb(nc, n); + } else if (n < 0 && mg_is_error(n)) { + /* Something went wrong, drop the connection. */ + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } +} + +MG_INTERNAL size_t recv_avail_size(struct mg_connection *conn, size_t max) { + size_t avail; + if (conn->recv_mbuf_limit < conn->recv_mbuf.len) return 0; + avail = conn->recv_mbuf_limit - conn->recv_mbuf.len; + return avail > max ? max : avail; +} + +static void mg_handle_tcp_read(struct mg_connection *conn) { + int n = 0; + char *buf = (char *) MG_MALLOC(MG_TCP_RECV_BUFFER_SIZE); + + if (buf == NULL) { + DBG(("OOM")); + return; + } + + n = (int) sl_Recv(conn->sock, buf, + recv_avail_size(conn, MG_TCP_RECV_BUFFER_SIZE), 0); + DBG(("%p %d bytes <- %d", conn, n, conn->sock)); + if (n > 0) { + mg_if_recv_tcp_cb(conn, buf, n, 1 /* own */); + } else { + MG_FREE(buf); + } + if (n == 0) { + /* Orderly shutdown of the socket, try flushing output. */ + conn->flags |= MG_F_SEND_AND_CLOSE; + } else if (mg_is_error(n)) { + conn->flags |= MG_F_CLOSE_IMMEDIATELY; + } +} + +static void mg_handle_udp_read(struct mg_connection *nc) { + char *buf = (char *) MG_MALLOC(MG_UDP_RECV_BUFFER_SIZE); + if (buf == NULL) return; + union socket_address sa; + socklen_t sa_len = sizeof(sa); + int n = sl_RecvFrom(nc->sock, buf, MG_UDP_RECV_BUFFER_SIZE, 0, + (SlSockAddr_t *) &sa, &sa_len); + DBG(("%p %d bytes from %s:%d", nc, n, inet_ntoa(nc->sa.sin.sin_addr), + ntohs(nc->sa.sin.sin_port))); + if (n > 0) { + mg_if_recv_udp_cb(nc, buf, n, &sa, sa_len); + } else { + MG_FREE(buf); + } +} + +#define _MG_F_FD_CAN_READ 1 +#define _MG_F_FD_CAN_WRITE 1 << 1 +#define _MG_F_FD_ERROR 1 << 2 + +void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) { + DBG(("%p fd=%d fd_flags=%d nc_flags=%lu rmbl=%d smbl=%d", nc, nc->sock, + fd_flags, nc->flags, (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); + + if (nc->flags & MG_F_CONNECTING) { + if (nc->flags & MG_F_UDP || nc->err != SL_ERROR_BSD_EALREADY) { + mg_if_connect_cb(nc, nc->err); + } else { + /* In SimpleLink, to get status of non-blocking connect() we need to wait + * until socket is writable and repeat the call to sl_Connect again, + * which will now return the real status. */ + if (fd_flags & _MG_F_FD_CAN_WRITE) { + nc->err = sl_Connect(nc->sock, &nc->sa.sa, sizeof(nc->sa.sin)); + DBG(("%p conn res=%d", nc, nc->err)); + if (nc->err == SL_ERROR_BSD_ESECSNOVERIFY || + /* TODO(rojer): Provide API to set the date for verification. */ + nc->err == SL_ERROR_BSD_ESECDATEERROR +#if SL_MAJOR_VERSION_NUM >= 2 + /* Per SWRU455, this error does not mean verification failed, + * it only means that the cert used is not present in the trusted + * root CA catalog. Which is perfectly fine. */ + || + nc->err == SL_ERROR_BSD_ESECUNKNOWNROOTCA +#endif + ) { + nc->err = 0; + } + if (nc->flags & MG_F_SSL && nc->err == 0) { + nc->flags |= MG_F_SSL_HANDSHAKE_DONE; + } + mg_if_connect_cb(nc, nc->err); + } + } + /* Ignore read/write in further processing, we've handled it. */ + fd_flags &= ~(_MG_F_FD_CAN_READ | _MG_F_FD_CAN_WRITE); + } + + if (fd_flags & _MG_F_FD_CAN_READ) { + if (nc->flags & MG_F_UDP) { + mg_handle_udp_read(nc); + } else { + if (nc->flags & MG_F_LISTENING) { + mg_accept_conn(nc); + } else { + mg_handle_tcp_read(nc); + } + } + } + + if (!(nc->flags & MG_F_CLOSE_IMMEDIATELY)) { + if ((fd_flags & _MG_F_FD_CAN_WRITE) && nc->send_mbuf.len > 0) { + mg_write_to_socket(nc); + } + + if (!(fd_flags & (_MG_F_FD_CAN_READ | _MG_F_FD_CAN_WRITE))) { + mg_if_poll(nc, now); + } + mg_if_timer(nc, now); + } + + DBG(("%p after fd=%d nc_flags=%lu rmbl=%d smbl=%d", nc, nc->sock, nc->flags, + (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); +} + +/* Associate a socket to a connection. */ +void mg_sl_if_sock_set(struct mg_connection *nc, sock_t sock) { + mg_set_non_blocking_mode(sock); + nc->sock = sock; + DBG(("%p %d", nc, sock)); +} + +void mg_sl_if_init(struct mg_iface *iface) { + (void) iface; + DBG(("%p using sl_Select()", iface->mgr)); +} + +void mg_sl_if_free(struct mg_iface *iface) { + (void) iface; +} + +void mg_sl_if_add_conn(struct mg_connection *nc) { + (void) nc; +} + +void mg_sl_if_remove_conn(struct mg_connection *nc) { + (void) nc; +} + +time_t mg_sl_if_poll(struct mg_iface *iface, int timeout_ms) { + struct mg_mgr *mgr = iface->mgr; + double now = mg_time(); + double min_timer; + struct mg_connection *nc, *tmp; + struct SlTimeval_t tv; + SlFdSet_t read_set, write_set, err_set; + sock_t max_fd = INVALID_SOCKET; + int num_fds, num_ev = 0, num_timers = 0; + + SL_SOCKET_FD_ZERO(&read_set); + SL_SOCKET_FD_ZERO(&write_set); + SL_SOCKET_FD_ZERO(&err_set); + + /* + * Note: it is ok to have connections with sock == INVALID_SOCKET in the list, + * e.g. timer-only "connections". + */ + min_timer = 0; + for (nc = mgr->active_connections, num_fds = 0; nc != NULL; nc = tmp) { + tmp = nc->next; + + if (nc->sock != INVALID_SOCKET) { + num_fds++; + + if (!(nc->flags & MG_F_WANT_WRITE) && + nc->recv_mbuf.len < nc->recv_mbuf_limit && + (!(nc->flags & MG_F_UDP) || nc->listener == NULL)) { + SL_SOCKET_FD_SET(nc->sock, &read_set); + if (max_fd == INVALID_SOCKET || nc->sock > max_fd) max_fd = nc->sock; + } + + if (((nc->flags & MG_F_CONNECTING) && !(nc->flags & MG_F_WANT_READ)) || + (nc->send_mbuf.len > 0 && !(nc->flags & MG_F_CONNECTING))) { + SL_SOCKET_FD_SET(nc->sock, &write_set); + SL_SOCKET_FD_SET(nc->sock, &err_set); + if (max_fd == INVALID_SOCKET || nc->sock > max_fd) max_fd = nc->sock; + } + } + + if (nc->ev_timer_time > 0) { + if (num_timers == 0 || nc->ev_timer_time < min_timer) { + min_timer = nc->ev_timer_time; + } + num_timers++; + } + } + + /* + * If there is a timer to be fired earlier than the requested timeout, + * adjust the timeout. + */ + if (num_timers > 0) { + double timer_timeout_ms = (min_timer - mg_time()) * 1000 + 1 /* rounding */; + if (timer_timeout_ms < timeout_ms) { + timeout_ms = timer_timeout_ms; + } + } + if (timeout_ms < 0) timeout_ms = 0; + + tv.tv_sec = timeout_ms / 1000; + tv.tv_usec = (timeout_ms % 1000) * 1000; + + if (num_fds > 0) { + num_ev = sl_Select((int) max_fd + 1, &read_set, &write_set, &err_set, &tv); + } + + now = mg_time(); + DBG(("sl_Select @ %ld num_ev=%d of %d, timeout=%d", (long) now, num_ev, + num_fds, timeout_ms)); + + for (nc = mgr->active_connections; nc != NULL; nc = tmp) { + int fd_flags = 0; + if (nc->sock != INVALID_SOCKET) { + if (num_ev > 0) { + fd_flags = + (SL_SOCKET_FD_ISSET(nc->sock, &read_set) && + (!(nc->flags & MG_F_UDP) || nc->listener == NULL) + ? _MG_F_FD_CAN_READ + : 0) | + (SL_SOCKET_FD_ISSET(nc->sock, &write_set) ? _MG_F_FD_CAN_WRITE + : 0) | + (SL_SOCKET_FD_ISSET(nc->sock, &err_set) ? _MG_F_FD_ERROR : 0); + } + /* SimpleLink does not report UDP sockets as writable. */ + if (nc->flags & MG_F_UDP && nc->send_mbuf.len > 0) { + fd_flags |= _MG_F_FD_CAN_WRITE; + } + } + tmp = nc->next; + mg_mgr_handle_conn(nc, fd_flags, now); + } + + for (nc = mgr->active_connections; nc != NULL; nc = tmp) { + tmp = nc->next; + if ((nc->flags & MG_F_CLOSE_IMMEDIATELY) || + (nc->send_mbuf.len == 0 && (nc->flags & MG_F_SEND_AND_CLOSE))) { + mg_close_conn(nc); + } + } + + return now; +} + +void mg_sl_if_get_conn_addr(struct mg_connection *nc, int remote, + union socket_address *sa) { + /* SimpleLink does not provide a way to get socket's peer address after + * accept or connect. Address should have been preserved in the connection, + * so we do our best here by using it. */ + if (remote) memcpy(sa, &nc->sa, sizeof(*sa)); +} + +void sl_restart_cb(struct mg_mgr *mgr) { + /* + * SimpleLink has been restarted, meaning all sockets have been invalidated. + * We try our best - we'll restart the listeners, but for outgoing + * connections we have no option but to terminate. + */ + struct mg_connection *nc; + for (nc = mg_next(mgr, NULL); nc != NULL; nc = mg_next(mgr, nc)) { + if (nc->sock == INVALID_SOCKET) continue; /* Could be a timer */ + if (nc->flags & MG_F_LISTENING) { + DBG(("restarting %p %s:%d", nc, inet_ntoa(nc->sa.sin.sin_addr), + ntohs(nc->sa.sin.sin_port))); + int res = (nc->flags & MG_F_UDP ? mg_sl_if_listen_udp(nc, &nc->sa) + : mg_sl_if_listen_tcp(nc, &nc->sa)); + if (res == 0) continue; + /* Well, we tried and failed. Fall through to closing. */ + } + nc->sock = INVALID_SOCKET; + DBG(("terminating %p %s:%d", nc, inet_ntoa(nc->sa.sin.sin_addr), + ntohs(nc->sa.sin.sin_port))); + /* TODO(rojer): Outgoing UDP? */ + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } +} + +/* clang-format off */ +#define MG_SL_IFACE_VTABLE \ + { \ + mg_sl_if_init, \ + mg_sl_if_free, \ + mg_sl_if_add_conn, \ + mg_sl_if_remove_conn, \ + mg_sl_if_poll, \ + mg_sl_if_listen_tcp, \ + mg_sl_if_listen_udp, \ + mg_sl_if_connect_tcp, \ + mg_sl_if_connect_udp, \ + mg_sl_if_tcp_send, \ + mg_sl_if_udp_send, \ + mg_sl_if_recved, \ + mg_sl_if_create_conn, \ + mg_sl_if_destroy_conn, \ + mg_sl_if_sock_set, \ + mg_sl_if_get_conn_addr, \ + } +/* clang-format on */ + +const struct mg_iface_vtable mg_simplelink_iface_vtable = MG_SL_IFACE_VTABLE; +#if MG_NET_IF == MG_NET_IF_SIMPLELINK +const struct mg_iface_vtable mg_default_iface_vtable = MG_SL_IFACE_VTABLE; +#endif + +#endif /* MG_ENABLE_NET_IF_SIMPLELINK */ +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/simplelink/sl_ssl_if.c" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_SIMPLELINK + +/* Amalgamated: #include "common/mg_mem.h" */ + +#ifndef MG_SSL_IF_SIMPLELINK_SLFS_PREFIX +#define MG_SSL_IF_SIMPLELINK_SLFS_PREFIX "SL:" +#endif + +#define MG_SSL_IF_SIMPLELINK_SLFS_PREFIX_LEN \ + (sizeof(MG_SSL_IF_SIMPLELINK_SLFS_PREFIX) - 1) + +struct mg_ssl_if_ctx { + char *ssl_cert; + char *ssl_key; + char *ssl_ca_cert; + char *ssl_server_name; +}; + +void mg_ssl_if_init() { +} + +enum mg_ssl_if_result mg_ssl_if_conn_init( + struct mg_connection *nc, const struct mg_ssl_if_conn_params *params, + const char **err_msg) { + struct mg_ssl_if_ctx *ctx = + (struct mg_ssl_if_ctx *) MG_CALLOC(1, sizeof(*ctx)); + if (ctx == NULL) { + MG_SET_PTRPTR(err_msg, "Out of memory"); + return MG_SSL_ERROR; + } + nc->ssl_if_data = ctx; + + if (params->cert != NULL || params->key != NULL) { + if (params->cert != NULL && params->key != NULL) { + ctx->ssl_cert = strdup(params->cert); + ctx->ssl_key = strdup(params->key); + } else { + MG_SET_PTRPTR(err_msg, "Both cert and key are required."); + return MG_SSL_ERROR; + } + } + if (params->ca_cert != NULL && strcmp(params->ca_cert, "*") != 0) { + ctx->ssl_ca_cert = strdup(params->ca_cert); + } + /* TODO(rojer): cipher_suites. */ + if (params->server_name != NULL) { + ctx->ssl_server_name = strdup(params->server_name); + } + return MG_SSL_OK; +} + +void mg_ssl_if_conn_close_notify(struct mg_connection *nc) { + /* Nothing to do */ + (void) nc; +} + +void mg_ssl_if_conn_free(struct mg_connection *nc) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + if (ctx == NULL) return; + nc->ssl_if_data = NULL; + MG_FREE(ctx->ssl_cert); + MG_FREE(ctx->ssl_key); + MG_FREE(ctx->ssl_ca_cert); + MG_FREE(ctx->ssl_server_name); + memset(ctx, 0, sizeof(*ctx)); + MG_FREE(ctx); +} + +bool pem_to_der(const char *pem_file, const char *der_file) { + bool ret = false; + FILE *pf = NULL, *df = NULL; + bool writing = false; + pf = fopen(pem_file, "r"); + if (pf == NULL) goto clean; + remove(der_file); + fs_slfs_set_new_file_size(der_file + MG_SSL_IF_SIMPLELINK_SLFS_PREFIX_LEN, + 2048); + df = fopen(der_file, "w"); + if (df == NULL) goto clean; + while (1) { + char pem_buf[70]; + char der_buf[48]; + if (!fgets(pem_buf, sizeof(pem_buf), pf)) break; + if (writing) { + if (strstr(pem_buf, "-----END ") != NULL) { + ret = true; + break; + } + int l = 0; + while (!isspace((unsigned int) pem_buf[l])) l++; + int der_len = 0; + cs_base64_decode((const unsigned char *) pem_buf, sizeof(pem_buf), + der_buf, &der_len); + if (der_len <= 0) break; + if (fwrite(der_buf, 1, der_len, df) != der_len) break; + } else if (strstr(pem_buf, "-----BEGIN ") != NULL) { + writing = true; + } + } + +clean: + if (pf != NULL) fclose(pf); + if (df != NULL) { + fclose(df); + if (!ret) remove(der_file); + } + return ret; +} + +#if MG_ENABLE_FILESYSTEM && defined(MG_FS_SLFS) +/* If the file's extension is .pem, convert it to DER format and put on SLFS. */ +static char *sl_pem2der(const char *pem_file) { + const char *pem_ext = strstr(pem_file, ".pem"); + if (pem_ext == NULL || *(pem_ext + 4) != '\0') { + return strdup(pem_file); + } + char *der_file = NULL; + /* DER file must be located on SLFS, add prefix. */ + int l = mg_asprintf(&der_file, 0, MG_SSL_IF_SIMPLELINK_SLFS_PREFIX "%.*s.der", + (int) (pem_ext - pem_file), pem_file); + if (der_file == NULL) return NULL; + bool result = false; + cs_stat_t st; + if (mg_stat(der_file, &st) != 0) { + result = pem_to_der(pem_file, der_file); + LOG(LL_DEBUG, ("%s -> %s = %d", pem_file, der_file, result)); + } else { + /* File exists, assume it's already been converted. */ + result = true; + } + if (result) { + /* Strip the SL: prefix we added since NWP does not expect it. */ + memmove(der_file, der_file + MG_SSL_IF_SIMPLELINK_SLFS_PREFIX_LEN, + l - 2 /* including \0 */); + } else { + MG_FREE(der_file); + der_file = NULL; + } + return der_file; +} +#else +static char *sl_pem2der(const char *pem_file) { + return strdup(pem_file); +} +#endif + +int sl_set_ssl_opts(int sock, struct mg_connection *nc) { + int err; + const struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + DBG(("%p ssl ctx: %p", nc, ctx)); + + if (ctx != NULL) { + DBG(("%p %s,%s,%s,%s", nc, (ctx->ssl_cert ? ctx->ssl_cert : "-"), + (ctx->ssl_key ? ctx->ssl_cert : "-"), + (ctx->ssl_ca_cert ? ctx->ssl_ca_cert : "-"), + (ctx->ssl_server_name ? ctx->ssl_server_name : "-"))); + if (ctx->ssl_cert != NULL && ctx->ssl_key != NULL) { + char *ssl_cert = sl_pem2der(ctx->ssl_cert); + char *ssl_key = sl_pem2der(ctx->ssl_key); + if (ssl_cert != NULL && ssl_key != NULL) { + err = sl_SetSockOpt(sock, SL_SOL_SOCKET, + SL_SO_SECURE_FILES_CERTIFICATE_FILE_NAME, ssl_cert, + strlen(ssl_cert)); + LOG(LL_INFO, ("CERTIFICATE_FILE_NAME %s -> %d", ssl_cert, err)); + err = sl_SetSockOpt(sock, SL_SOL_SOCKET, + SL_SO_SECURE_FILES_PRIVATE_KEY_FILE_NAME, ssl_key, + strlen(ssl_key)); + LOG(LL_INFO, ("PRIVATE_KEY_FILE_NAME %s -> %d", ssl_key, err)); + } else { + err = -1; + } + MG_FREE(ssl_cert); + MG_FREE(ssl_key); + if (err != 0) return err; + } + if (ctx->ssl_ca_cert != NULL) { + if (ctx->ssl_ca_cert[0] != '\0') { + char *ssl_ca_cert = sl_pem2der(ctx->ssl_ca_cert); + if (ssl_ca_cert != NULL) { + err = sl_SetSockOpt(sock, SL_SOL_SOCKET, + SL_SO_SECURE_FILES_CA_FILE_NAME, ssl_ca_cert, + strlen(ssl_ca_cert)); + LOG(LL_INFO, ("CA_FILE_NAME %s -> %d", ssl_ca_cert, err)); + } else { + err = -1; + } + MG_FREE(ssl_ca_cert); + if (err != 0) return err; + } + } + if (ctx->ssl_server_name != NULL) { + err = sl_SetSockOpt(sock, SL_SOL_SOCKET, + SL_SO_SECURE_DOMAIN_NAME_VERIFICATION, + ctx->ssl_server_name, strlen(ctx->ssl_server_name)); + DBG(("DOMAIN_NAME_VERIFICATION %s -> %d", ctx->ssl_server_name, err)); + /* Domain name verificationw as added in a NWP service pack, older + * versions return SL_ERROR_BSD_ENOPROTOOPT. There isn't much we can do + * about it, + * so we ignore the error. */ + if (err != 0 && err != SL_ERROR_BSD_ENOPROTOOPT) return err; + } + } + return 0; +} + +#endif /* MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_SIMPLELINK */ +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/lwip/mg_lwip_net_if.h" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_LWIP_MG_NET_IF_LWIP_H_ +#define CS_COMMON_PLATFORMS_LWIP_MG_NET_IF_LWIP_H_ + +#ifndef MG_ENABLE_NET_IF_LWIP_LOW_LEVEL +#define MG_ENABLE_NET_IF_LWIP_LOW_LEVEL MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL +#endif + +#if MG_ENABLE_NET_IF_LWIP_LOW_LEVEL + +#include + +extern const struct mg_iface_vtable mg_lwip_iface_vtable; + +struct mg_lwip_conn_state { + struct mg_connection *nc; + struct mg_connection *lc; + union { + struct tcp_pcb *tcp; + struct udp_pcb *udp; + } pcb; + err_t err; + size_t num_sent; /* Number of acknowledged bytes to be reported to the core */ + struct pbuf *rx_chain; /* Chain of incoming data segments. */ + size_t rx_offset; /* Offset within the first pbuf (if partially consumed) */ + /* Last SSL write size, for retries. */ + int last_ssl_write_size; + /* Whether MG_SIG_RECV is already pending for this connection */ + int recv_pending : 1; + /* Whether the connection is about to close, just `rx_chain` needs to drain */ + int draining_rx_chain : 1; +}; + +enum mg_sig_type { + MG_SIG_CONNECT_RESULT = 1, + MG_SIG_RECV = 2, + MG_SIG_CLOSE_CONN = 3, + MG_SIG_TOMBSTONE = 4, + MG_SIG_ACCEPT = 5, +}; + +void mg_lwip_post_signal(enum mg_sig_type sig, struct mg_connection *nc); + +/* To be implemented by the platform. */ +void mg_lwip_mgr_schedule_poll(struct mg_mgr *mgr); + +#endif /* MG_ENABLE_NET_IF_LWIP_LOW_LEVEL */ + +#endif /* CS_COMMON_PLATFORMS_LWIP_MG_NET_IF_LWIP_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/lwip/mg_lwip_net_if.c" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_NET_IF_LWIP_LOW_LEVEL + +/* Amalgamated: #include "common/mg_mem.h" */ + +#include +#include +#include +#include +#if ((LWIP_VERSION_MAJOR << 8) | LWIP_VERSION_MINOR) >= 0x0105 +#include /* For tcp_seg */ +#else +#include +#endif +#include + +/* Amalgamated: #include "common/cs_dbg.h" */ + +/* + * Newest versions of LWIP have ip_2_ip4, older have ipX_2_ip, + * even older have nothing. + */ +#ifndef ip_2_ip4 +#ifdef ipX_2_ip +#define ip_2_ip4(addr) ipX_2_ip(addr) +#else +#define ip_2_ip4(addr) (addr) +#endif +#endif + +/* + * Depending on whether Mongoose is compiled with ipv6 support, use right + * lwip functions + */ +#if MG_ENABLE_IPV6 +#define TCP_NEW tcp_new_ip6 +#define TCP_BIND tcp_bind_ip6 +#define UDP_BIND udp_bind_ip6 +#define IPADDR_NTOA(x) ip6addr_ntoa((const ip6_addr_t *)(x)) +#define SET_ADDR(dst, src) \ + memcpy((dst)->sin6.sin6_addr.s6_addr, (src)->ip6.addr, \ + sizeof((dst)->sin6.sin6_addr.s6_addr)) +#else +#define TCP_NEW tcp_new +#define TCP_BIND tcp_bind +#define UDP_BIND udp_bind +#define IPADDR_NTOA ipaddr_ntoa +#define SET_ADDR(dst, src) (dst)->sin.sin_addr.s_addr = ip_2_ip4(src)->addr +#endif + +#if NO_SYS +#define tcpip_callback(fn, arg) (fn)(arg) +typedef void (*tcpip_callback_fn)(void *arg); +#endif + +void mg_lwip_ssl_do_hs(struct mg_connection *nc); +void mg_lwip_ssl_send(struct mg_connection *nc); +void mg_lwip_ssl_recv(struct mg_connection *nc); + +void mg_lwip_if_init(struct mg_iface *iface); +void mg_lwip_if_free(struct mg_iface *iface); +void mg_lwip_if_add_conn(struct mg_connection *nc); +void mg_lwip_if_remove_conn(struct mg_connection *nc); +time_t mg_lwip_if_poll(struct mg_iface *iface, int timeout_ms); + +#if defined(RTOS_SDK) || defined(ESP_PLATFORM) +extern void mgos_lock(); +extern void mgos_unlock(); +#else +#define mgos_lock() +#define mgos_unlock() +#endif + +static void mg_lwip_recv_common(struct mg_connection *nc, struct pbuf *p); + +#if LWIP_TCP_KEEPALIVE +void mg_lwip_set_keepalive_params(struct mg_connection *nc, int idle, + int interval, int count) { + if (nc->sock == INVALID_SOCKET || nc->flags & MG_F_UDP) { + return; + } + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + struct tcp_pcb *tpcb = cs->pcb.tcp; + if (idle > 0 && interval > 0 && count > 0) { + tpcb->keep_idle = idle * 1000; + tpcb->keep_intvl = interval * 1000; + tpcb->keep_cnt = count; + tpcb->so_options |= SOF_KEEPALIVE; + } else { + tpcb->so_options &= ~SOF_KEEPALIVE; + } +} +#elif !defined(MG_NO_LWIP_TCP_KEEPALIVE) +#warning LWIP TCP keepalive is disabled. Please consider enabling it. +#endif /* LWIP_TCP_KEEPALIVE */ + +static err_t mg_lwip_tcp_conn_cb(void *arg, struct tcp_pcb *tpcb, err_t err) { + struct mg_connection *nc = (struct mg_connection *) arg; + DBG(("%p connect to %s:%u = %d", nc, IPADDR_NTOA(ipX_2_ip(&tpcb->remote_ip)), + tpcb->remote_port, err)); + if (nc == NULL) { + tcp_abort(tpcb); + return ERR_ARG; + } + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + cs->err = err; +#if LWIP_TCP_KEEPALIVE + if (err == 0) mg_lwip_set_keepalive_params(nc, 60, 10, 6); +#endif + mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); + return ERR_OK; +} + +static void mg_lwip_tcp_error_cb(void *arg, err_t err) { + struct mg_connection *nc = (struct mg_connection *) arg; + DBG(("%p conn error %d", nc, err)); + if (nc == NULL) return; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + cs->pcb.tcp = NULL; /* Has already been deallocated */ + if (nc->flags & MG_F_CONNECTING) { + cs->err = err; + mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); + } else { + mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); + } +} + +static err_t mg_lwip_tcp_recv_cb(void *arg, struct tcp_pcb *tpcb, + struct pbuf *p, err_t err) { + struct mg_connection *nc = (struct mg_connection *) arg; + DBG(("%p %p %u %d", nc, tpcb, (p != NULL ? p->tot_len : 0), err)); + if (p == NULL) { + if (nc != NULL && !(nc->flags & MG_F_CLOSE_IMMEDIATELY)) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + if (cs->rx_chain != NULL) { + /* + * rx_chain still contains non-consumed data, don't close the + * connection + */ + cs->draining_rx_chain = 1; + } else { + mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); + } + } else { + /* Tombstoned connection, do nothing. */ + } + return ERR_OK; + } else if (nc == NULL) { + tcp_abort(tpcb); + return ERR_ARG; + } + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + /* + * If we get a chain of more than one segment at once, we need to bump + * refcount on the subsequent bufs to make them independent. + */ + if (p->next != NULL) { + struct pbuf *q = p->next; + for (; q != NULL; q = q->next) pbuf_ref(q); + } + mgos_lock(); + if (cs->rx_chain == NULL) { + cs->rx_offset = 0; + } else if (pbuf_clen(cs->rx_chain) >= 4) { + /* ESP SDK has a limited pool of 5 pbufs. We must not hog them all or RX + * will be completely blocked. We already have at least 4 in the chain, + * this one is, so we have to make a copy and release this one. */ + struct pbuf *np = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); + if (np != NULL) { + pbuf_copy(np, p); + pbuf_free(p); + p = np; + } + } + mgos_unlock(); + mg_lwip_recv_common(nc, p); + return ERR_OK; +} + +static void mg_lwip_consume_rx_chain_tcp(struct mg_connection *nc) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + if (cs->rx_chain == NULL) return; +#if MG_ENABLE_SSL + if (nc->flags & MG_F_SSL) { + if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) { + mg_lwip_ssl_recv(nc); + } else { + mg_lwip_ssl_do_hs(nc); + } + return; + } +#endif + mgos_lock(); + while (cs->rx_chain != NULL && nc->recv_mbuf.len < nc->recv_mbuf_limit) { + struct pbuf *seg = cs->rx_chain; + + size_t seg_len = (seg->len - cs->rx_offset); + size_t buf_avail = (nc->recv_mbuf_limit - nc->recv_mbuf.len); + size_t len = MIN(seg_len, buf_avail); + + char *data = (char *) MG_MALLOC(len); + if (data == NULL) { + mgos_unlock(); + DBG(("OOM")); + return; + } + pbuf_copy_partial(seg, data, len, cs->rx_offset); + cs->rx_offset += len; + if (cs->rx_offset == cs->rx_chain->len) { + cs->rx_chain = pbuf_dechain(cs->rx_chain); + pbuf_free(seg); + cs->rx_offset = 0; + } + mgos_unlock(); + mg_if_recv_tcp_cb(nc, data, len, 1 /* own */); + mgos_lock(); + } + mgos_unlock(); +} + +static void mg_lwip_handle_recv_tcp(struct mg_connection *nc) { + mg_lwip_consume_rx_chain_tcp(nc); + + if (nc->send_mbuf.len > 0) { + mg_lwip_mgr_schedule_poll(nc->mgr); + } +} + +static err_t mg_lwip_tcp_sent_cb(void *arg, struct tcp_pcb *tpcb, + u16_t num_sent) { + struct mg_connection *nc = (struct mg_connection *) arg; + DBG(("%p %p %u %p %p", nc, tpcb, num_sent, tpcb->unsent, tpcb->unacked)); + if (nc == NULL) return ERR_OK; + if ((nc->flags & MG_F_SEND_AND_CLOSE) && !(nc->flags & MG_F_WANT_WRITE) && + nc->send_mbuf.len == 0 && tpcb->unsent == NULL && tpcb->unacked == NULL) { + mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); + } + return ERR_OK; +} + +struct mg_lwip_if_connect_tcp_ctx { + struct mg_connection *nc; + const union socket_address *sa; +}; + +static void mg_lwip_if_connect_tcp_tcpip(void *arg) { + struct mg_lwip_if_connect_tcp_ctx *ctx = + (struct mg_lwip_if_connect_tcp_ctx *) arg; + struct mg_connection *nc = ctx->nc; + const union socket_address *sa = ctx->sa; + + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + struct tcp_pcb *tpcb = TCP_NEW(); + cs->pcb.tcp = tpcb; + ip_addr_t *ip = (ip_addr_t *) &sa->sin.sin_addr.s_addr; + u16_t port = ntohs(sa->sin.sin_port); + tcp_arg(tpcb, nc); + tcp_err(tpcb, mg_lwip_tcp_error_cb); + tcp_sent(tpcb, mg_lwip_tcp_sent_cb); + tcp_recv(tpcb, mg_lwip_tcp_recv_cb); + cs->err = TCP_BIND(tpcb, IP_ADDR_ANY, 0 /* any port */); + DBG(("%p tcp_bind = %d", nc, cs->err)); + if (cs->err != ERR_OK) { + mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); + return; + } + cs->err = tcp_connect(tpcb, ip, port, mg_lwip_tcp_conn_cb); + DBG(("%p tcp_connect %p = %d", nc, tpcb, cs->err)); + if (cs->err != ERR_OK) { + mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); + return; + } +} + +void mg_lwip_if_connect_tcp(struct mg_connection *nc, + const union socket_address *sa) { + struct mg_lwip_if_connect_tcp_ctx ctx = {.nc = nc, .sa = sa}; + tcpip_callback(mg_lwip_if_connect_tcp_tcpip, &ctx); +} + +/* + * Lwip included in the SDKs for nRF5x chips has different type for the + * callback of `udp_recv()` + */ +#if ((LWIP_VERSION_MAJOR << 8) | LWIP_VERSION_MINOR) >= 0x0105 +static void mg_lwip_udp_recv_cb(void *arg, struct udp_pcb *pcb, struct pbuf *p, + const ip_addr_t *addr, u16_t port) +#else +static void mg_lwip_udp_recv_cb(void *arg, struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *addr, u16_t port) +#endif +{ + struct mg_connection *nc = (struct mg_connection *) arg; + DBG(("%p %s:%u %p %u %u", nc, IPADDR_NTOA(addr), port, p, p->ref, p->len)); + /* Put address in a separate pbuf and tack it onto the packet. */ + struct pbuf *sap = + pbuf_alloc(PBUF_RAW, sizeof(union socket_address), PBUF_RAM); + if (sap == NULL) { + pbuf_free(p); + return; + } + union socket_address *sa = (union socket_address *) sap->payload; +#if ((LWIP_VERSION_MAJOR << 8) | LWIP_VERSION_MINOR) >= 0x0105 + sa->sin.sin_addr.s_addr = ip_2_ip4(addr)->addr; +#else + sa->sin.sin_addr.s_addr = addr->addr; +#endif + sa->sin.sin_port = htons(port); + /* Logic in the recv handler requires that there be exactly one data pbuf. */ + p = pbuf_coalesce(p, PBUF_RAW); + pbuf_chain(sap, p); + mg_lwip_recv_common(nc, sap); + (void) pcb; +} + +static void mg_lwip_recv_common(struct mg_connection *nc, struct pbuf *p) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + mgos_lock(); + if (cs->rx_chain == NULL) { + cs->rx_chain = p; + } else { + pbuf_chain(cs->rx_chain, p); + } + if (!cs->recv_pending) { + cs->recv_pending = 1; + mg_lwip_post_signal(MG_SIG_RECV, nc); + } + mgos_unlock(); +} + +static void mg_lwip_handle_recv_udp(struct mg_connection *nc) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + /* + * For UDP, RX chain consists of interleaved address and packet bufs: + * Address pbuf followed by exactly one data pbuf (recv_cb took care of that). + */ + while (cs->rx_chain != NULL) { + struct pbuf *sap = cs->rx_chain; + struct pbuf *p = sap->next; + cs->rx_chain = pbuf_dechain(p); + size_t data_len = p->len; + char *data = (char *) MG_MALLOC(data_len); + if (data != NULL) { + pbuf_copy_partial(p, data, data_len, 0); + pbuf_free(p); + mg_if_recv_udp_cb(nc, data, data_len, + (union socket_address *) sap->payload, sap->len); + pbuf_free(sap); + } else { + pbuf_free(p); + pbuf_free(sap); + } + } +} + +static void mg_lwip_if_connect_udp_tcpip(void *arg) { + struct mg_connection *nc = (struct mg_connection *) arg; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + struct udp_pcb *upcb = udp_new(); + cs->err = UDP_BIND(upcb, IP_ADDR_ANY, 0 /* any port */); + DBG(("%p udp_bind %p = %d", nc, upcb, cs->err)); + if (cs->err == ERR_OK) { + udp_recv(upcb, mg_lwip_udp_recv_cb, nc); + cs->pcb.udp = upcb; + } else { + udp_remove(upcb); + } + mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); +} + +void mg_lwip_if_connect_udp(struct mg_connection *nc) { + tcpip_callback(mg_lwip_if_connect_udp_tcpip, nc); +} + +void mg_lwip_accept_conn(struct mg_connection *nc, struct tcp_pcb *tpcb) { + union socket_address sa; + SET_ADDR(&sa, &tpcb->remote_ip); + sa.sin.sin_port = htons(tpcb->remote_port); + mg_if_accept_tcp_cb(nc, &sa, sizeof(sa.sin)); +} + +static void tcp_close_tcpip(void *arg) { + tcp_close((struct tcp_pcb *) arg); +} + +void mg_lwip_handle_accept(struct mg_connection *nc) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + if (cs->pcb.tcp == NULL) return; +#if MG_ENABLE_SSL + if (cs->lc->flags & MG_F_SSL) { + if (mg_ssl_if_conn_accept(nc, cs->lc) != MG_SSL_OK) { + LOG(LL_ERROR, ("SSL error")); + tcpip_callback(tcp_close_tcpip, cs->pcb.tcp); + } + } else +#endif + { + mg_lwip_accept_conn(nc, cs->pcb.tcp); + } +} + +static err_t mg_lwip_accept_cb(void *arg, struct tcp_pcb *newtpcb, err_t err) { + struct mg_connection *lc = (struct mg_connection *) arg, *nc; + struct mg_lwip_conn_state *lcs, *cs; + struct tcp_pcb_listen *lpcb; + LOG(LL_DEBUG, + ("%p conn %p from %s:%u", lc, newtpcb, + IPADDR_NTOA(ipX_2_ip(&newtpcb->remote_ip)), newtpcb->remote_port)); + if (lc == NULL) { + tcp_abort(newtpcb); + return ERR_ABRT; + } + lcs = (struct mg_lwip_conn_state *) lc->sock; + lpcb = (struct tcp_pcb_listen *) lcs->pcb.tcp; +#if TCP_LISTEN_BACKLOG + tcp_accepted(lpcb); +#endif + nc = mg_if_accept_new_conn(lc); + if (nc == NULL) { + tcp_abort(newtpcb); + return ERR_ABRT; + } + cs = (struct mg_lwip_conn_state *) nc->sock; + cs->lc = lc; + cs->pcb.tcp = newtpcb; + /* We need to set up callbacks before returning because data may start + * arriving immediately. */ + tcp_arg(newtpcb, nc); + tcp_err(newtpcb, mg_lwip_tcp_error_cb); + tcp_sent(newtpcb, mg_lwip_tcp_sent_cb); + tcp_recv(newtpcb, mg_lwip_tcp_recv_cb); +#if LWIP_TCP_KEEPALIVE + mg_lwip_set_keepalive_params(nc, 60, 10, 6); +#endif + mg_lwip_post_signal(MG_SIG_ACCEPT, nc); + (void) err; + (void) lpcb; + return ERR_OK; +} + +struct mg_lwip_if_listen_ctx { + struct mg_connection *nc; + union socket_address *sa; + int ret; +}; + +static void mg_lwip_if_listen_tcp_tcpip(void *arg) { + struct mg_lwip_if_listen_ctx *ctx = (struct mg_lwip_if_listen_ctx *) arg; + struct mg_connection *nc = ctx->nc; + union socket_address *sa = ctx->sa; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + struct tcp_pcb *tpcb = TCP_NEW(); + ip_addr_t *ip = (ip_addr_t *) &sa->sin.sin_addr.s_addr; + u16_t port = ntohs(sa->sin.sin_port); + cs->err = TCP_BIND(tpcb, ip, port); + DBG(("%p tcp_bind(%s:%u) = %d", nc, IPADDR_NTOA(ip), port, cs->err)); + if (cs->err != ERR_OK) { + tcp_close(tpcb); + ctx->ret = -1; + return; + } + tcp_arg(tpcb, nc); + tpcb = tcp_listen(tpcb); + cs->pcb.tcp = tpcb; + tcp_accept(tpcb, mg_lwip_accept_cb); + ctx->ret = 0; +} + +int mg_lwip_if_listen_tcp(struct mg_connection *nc, union socket_address *sa) { + struct mg_lwip_if_listen_ctx ctx = {.nc = nc, .sa = sa}; + tcpip_callback(mg_lwip_if_listen_tcp_tcpip, &ctx); + return ctx.ret; +} + +static void mg_lwip_if_listen_udp_tcpip(void *arg) { + struct mg_lwip_if_listen_ctx *ctx = (struct mg_lwip_if_listen_ctx *) arg; + struct mg_connection *nc = ctx->nc; + union socket_address *sa = ctx->sa; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + struct udp_pcb *upcb = udp_new(); + ip_addr_t *ip = (ip_addr_t *) &sa->sin.sin_addr.s_addr; + u16_t port = ntohs(sa->sin.sin_port); + cs->err = UDP_BIND(upcb, ip, port); + DBG(("%p udb_bind(%s:%u) = %d", nc, IPADDR_NTOA(ip), port, cs->err)); + if (cs->err != ERR_OK) { + udp_remove(upcb); + ctx->ret = -1; + } else { + udp_recv(upcb, mg_lwip_udp_recv_cb, nc); + cs->pcb.udp = upcb; + ctx->ret = 0; + } +} + +int mg_lwip_if_listen_udp(struct mg_connection *nc, union socket_address *sa) { + struct mg_lwip_if_listen_ctx ctx = {.nc = nc, .sa = sa}; + tcpip_callback(mg_lwip_if_listen_udp_tcpip, &ctx); + return ctx.ret; +} + +struct mg_lwip_tcp_write_ctx { + struct mg_connection *nc; + const void *data; + uint16_t len; + int ret; +}; + +static void tcp_output_tcpip(void *arg) { + tcp_output((struct tcp_pcb *) arg); +} + +static void mg_lwip_tcp_write_tcpip(void *arg) { + struct mg_lwip_tcp_write_ctx *ctx = (struct mg_lwip_tcp_write_ctx *) arg; + struct mg_connection *nc = ctx->nc; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + struct tcp_pcb *tpcb = cs->pcb.tcp; + size_t len = MIN(tpcb->mss, MIN(ctx->len, tpcb->snd_buf)); + size_t unsent, unacked; + if (len == 0) { + DBG(("%p no buf avail %u %u %p %p", tpcb, tpcb->snd_buf, tpcb->snd_queuelen, + tpcb->unsent, tpcb->unacked)); + tcpip_callback(tcp_output_tcpip, tpcb); + ctx->ret = 0; + return; + } + unsent = (tpcb->unsent != NULL ? tpcb->unsent->len : 0); + unacked = (tpcb->unacked != NULL ? tpcb->unacked->len : 0); +/* + * On ESP8266 we only allow one TCP segment in flight at any given time. + * This may increase latency and reduce efficiency of tcp windowing, + * but memory is scarce and precious on that platform so we do this to + * reduce footprint. + */ +#if CS_PLATFORM == CS_P_ESP8266 + if (unacked > 0) { + ctx->ret = 0; + return; + } + len = MIN(len, (TCP_MSS - unsent)); +#endif + cs->err = tcp_write(tpcb, ctx->data, len, TCP_WRITE_FLAG_COPY); + unsent = (tpcb->unsent != NULL ? tpcb->unsent->len : 0); + unacked = (tpcb->unacked != NULL ? tpcb->unacked->len : 0); + DBG(("%p tcp_write %u = %d, %u %u", tpcb, len, cs->err, unsent, unacked)); + if (cs->err != ERR_OK) { + /* + * We ignore ERR_MEM because memory will be freed up when the data is sent + * and we'll retry. + */ + ctx->ret = (cs->err == ERR_MEM ? 0 : -1); + return; + } + ctx->ret = len; +} + +static int mg_lwip_tcp_write(struct mg_connection *nc, const void *data, + uint16_t len) { + struct mg_lwip_tcp_write_ctx ctx = {.nc = nc, .data = data, .len = len}; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + struct tcp_pcb *tpcb = cs->pcb.tcp; + if (tpcb == NULL) { + return -1; + } + tcpip_callback(mg_lwip_tcp_write_tcpip, &ctx); + return ctx.ret; +} + +struct udp_sendto_ctx { + struct udp_pcb *upcb; + struct pbuf *p; + ip_addr_t *ip; + uint16_t port; + int ret; +}; + +static void udp_sendto_tcpip(void *arg) { + struct udp_sendto_ctx *ctx = (struct udp_sendto_ctx *) arg; + ctx->ret = udp_sendto(ctx->upcb, ctx->p, ctx->ip, ctx->port); +} + +static int mg_lwip_udp_send(struct mg_connection *nc, const void *data, + uint16_t len) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + if (cs->pcb.udp == NULL) { + /* + * In case of UDP, this usually means, what + * async DNS resolve is still in progress and connection + * is not ready yet + */ + DBG(("%p socket is not connected", nc)); + return -1; + } + struct udp_pcb *upcb = cs->pcb.udp; + struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM); +#if defined(LWIP_IPV4) && LWIP_IPV4 && defined(LWIP_IPV6) && LWIP_IPV6 + ip_addr_t ip = {.u_addr.ip4.addr = nc->sa.sin.sin_addr.s_addr, .type = 0}; +#else + ip_addr_t ip = {.addr = nc->sa.sin.sin_addr.s_addr}; +#endif + u16_t port = ntohs(nc->sa.sin.sin_port); + if (p == NULL) { + DBG(("OOM")); + return 0; + } + memcpy(p->payload, data, len); + struct udp_sendto_ctx ctx = {.upcb = upcb, .p = p, .ip = &ip, .port = port}; + tcpip_callback(udp_sendto_tcpip, &ctx); + cs->err = ctx.ret; + pbuf_free(p); + return (cs->err == ERR_OK ? len : -1); +} + +static void mg_lwip_send_more(struct mg_connection *nc) { + int num_sent = 0; + if (nc->sock == INVALID_SOCKET) return; + if (nc->flags & MG_F_UDP) { + num_sent = mg_lwip_udp_send(nc, nc->send_mbuf.buf, nc->send_mbuf.len); + DBG(("%p mg_lwip_udp_send %u = %d", nc, nc->send_mbuf.len, num_sent)); + } else { + num_sent = mg_lwip_tcp_write(nc, nc->send_mbuf.buf, nc->send_mbuf.len); + DBG(("%p mg_lwip_tcp_write %u = %d", nc, nc->send_mbuf.len, num_sent)); + } + if (num_sent == 0) return; + if (num_sent > 0) { + mg_if_sent_cb(nc, num_sent); + } else { + mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); + } +} + +void mg_lwip_if_tcp_send(struct mg_connection *nc, const void *buf, + size_t len) { + mbuf_append(&nc->send_mbuf, buf, len); + mg_lwip_mgr_schedule_poll(nc->mgr); +} + +void mg_lwip_if_udp_send(struct mg_connection *nc, const void *buf, + size_t len) { + mbuf_append(&nc->send_mbuf, buf, len); + mg_lwip_mgr_schedule_poll(nc->mgr); +} + +struct tcp_recved_ctx { + struct tcp_pcb *tpcb; + size_t len; +}; + +void tcp_recved_tcpip(void *arg) { + struct tcp_recved_ctx *ctx = (struct tcp_recved_ctx *) arg; + tcp_recved(ctx->tpcb, ctx->len); +} + +void mg_lwip_if_recved(struct mg_connection *nc, size_t len) { + if (nc->flags & MG_F_UDP) return; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + if (nc->sock == INVALID_SOCKET || cs->pcb.tcp == NULL) { + DBG(("%p invalid socket", nc)); + return; + } + DBG(("%p %p %u %u", nc, cs->pcb.tcp, len, + (cs->rx_chain ? cs->rx_chain->tot_len : 0))); + struct tcp_recved_ctx ctx = {.tpcb = cs->pcb.tcp, .len = len}; +#if MG_ENABLE_SSL + if (!(nc->flags & MG_F_SSL)) { + tcpip_callback(tcp_recved_tcpip, &ctx); + } else { + /* Currently SSL acknowledges data immediately. + * TODO(rojer): Find a way to propagate mg_lwip_if_recved. */ + } +#else + tcpip_callback(tcp_recved_tcpip, &ctx); +#endif + mbuf_trim(&nc->recv_mbuf); +} + +int mg_lwip_if_create_conn(struct mg_connection *nc) { + struct mg_lwip_conn_state *cs = + (struct mg_lwip_conn_state *) MG_CALLOC(1, sizeof(*cs)); + if (cs == NULL) return 0; + cs->nc = nc; + nc->sock = (intptr_t) cs; + return 1; +} + +static void udp_remove_tcpip(void *arg) { + udp_remove((struct udp_pcb *) arg); +} + +void mg_lwip_if_destroy_conn(struct mg_connection *nc) { + if (nc->sock == INVALID_SOCKET) return; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + if (!(nc->flags & MG_F_UDP)) { + struct tcp_pcb *tpcb = cs->pcb.tcp; + if (tpcb != NULL) { + tcp_arg(tpcb, NULL); + DBG(("%p tcp_close %p", nc, tpcb)); + tcp_arg(tpcb, NULL); + tcpip_callback(tcp_close_tcpip, tpcb); + } + while (cs->rx_chain != NULL) { + struct pbuf *seg = cs->rx_chain; + cs->rx_chain = pbuf_dechain(cs->rx_chain); + pbuf_free(seg); + } + memset(cs, 0, sizeof(*cs)); + MG_FREE(cs); + } else if (nc->listener == NULL) { + /* Only close outgoing UDP pcb or listeners. */ + struct udp_pcb *upcb = cs->pcb.udp; + if (upcb != NULL) { + DBG(("%p udp_remove %p", nc, upcb)); + tcpip_callback(udp_remove_tcpip, upcb); + } + memset(cs, 0, sizeof(*cs)); + MG_FREE(cs); + } + nc->sock = INVALID_SOCKET; +} + +void mg_lwip_if_get_conn_addr(struct mg_connection *nc, int remote, + union socket_address *sa) { + memset(sa, 0, sizeof(*sa)); + if (nc == NULL || nc->sock == INVALID_SOCKET) return; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + if (nc->flags & MG_F_UDP) { + struct udp_pcb *upcb = cs->pcb.udp; + if (remote) { + memcpy(sa, &nc->sa, sizeof(*sa)); + } else if (upcb != NULL) { + sa->sin.sin_port = htons(upcb->local_port); + SET_ADDR(sa, &upcb->local_ip); + } + } else { + struct tcp_pcb *tpcb = cs->pcb.tcp; + if (remote) { + memcpy(sa, &nc->sa, sizeof(*sa)); + } else if (tpcb != NULL) { + sa->sin.sin_port = htons(tpcb->local_port); + SET_ADDR(sa, &tpcb->local_ip); + } + } +} + +void mg_lwip_if_sock_set(struct mg_connection *nc, sock_t sock) { + nc->sock = sock; +} + +/* clang-format off */ +#define MG_LWIP_IFACE_VTABLE \ + { \ + mg_lwip_if_init, \ + mg_lwip_if_free, \ + mg_lwip_if_add_conn, \ + mg_lwip_if_remove_conn, \ + mg_lwip_if_poll, \ + mg_lwip_if_listen_tcp, \ + mg_lwip_if_listen_udp, \ + mg_lwip_if_connect_tcp, \ + mg_lwip_if_connect_udp, \ + mg_lwip_if_tcp_send, \ + mg_lwip_if_udp_send, \ + mg_lwip_if_recved, \ + mg_lwip_if_create_conn, \ + mg_lwip_if_destroy_conn, \ + mg_lwip_if_sock_set, \ + mg_lwip_if_get_conn_addr, \ + } +/* clang-format on */ + +const struct mg_iface_vtable mg_lwip_iface_vtable = MG_LWIP_IFACE_VTABLE; +#if MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL +const struct mg_iface_vtable mg_default_iface_vtable = MG_LWIP_IFACE_VTABLE; +#endif + +#endif /* MG_ENABLE_NET_IF_LWIP_LOW_LEVEL */ +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/lwip/mg_lwip_ev_mgr.c" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL + +#ifndef MG_SIG_QUEUE_LEN +#define MG_SIG_QUEUE_LEN 32 +#endif + +struct mg_ev_mgr_lwip_signal { + int sig; + struct mg_connection *nc; +}; + +struct mg_ev_mgr_lwip_data { + struct mg_ev_mgr_lwip_signal sig_queue[MG_SIG_QUEUE_LEN]; + int sig_queue_len; + int start_index; +}; + +void mg_lwip_post_signal(enum mg_sig_type sig, struct mg_connection *nc) { + struct mg_ev_mgr_lwip_data *md = + (struct mg_ev_mgr_lwip_data *) nc->iface->data; + mgos_lock(); + if (md->sig_queue_len >= MG_SIG_QUEUE_LEN) { + mgos_unlock(); + return; + } + int end_index = (md->start_index + md->sig_queue_len) % MG_SIG_QUEUE_LEN; + md->sig_queue[end_index].sig = sig; + md->sig_queue[end_index].nc = nc; + md->sig_queue_len++; + mg_lwip_mgr_schedule_poll(nc->mgr); + mgos_unlock(); +} + +void mg_ev_mgr_lwip_process_signals(struct mg_mgr *mgr) { + struct mg_ev_mgr_lwip_data *md = + (struct mg_ev_mgr_lwip_data *) mgr->ifaces[MG_MAIN_IFACE]->data; + while (md->sig_queue_len > 0) { + mgos_lock(); + int sig = md->sig_queue[md->start_index].sig; + struct mg_connection *nc = md->sig_queue[md->start_index].nc; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + md->start_index = (md->start_index + 1) % MG_SIG_QUEUE_LEN; + md->sig_queue_len--; + mgos_unlock(); + if (nc->iface == NULL || nc->mgr == NULL) continue; + switch (sig) { + case MG_SIG_CONNECT_RESULT: { +#if MG_ENABLE_SSL + if (cs->err == 0 && (nc->flags & MG_F_SSL) && + !(nc->flags & MG_F_SSL_HANDSHAKE_DONE)) { + mg_lwip_ssl_do_hs(nc); + } else +#endif + { + mg_if_connect_cb(nc, cs->err); + } + break; + } + case MG_SIG_CLOSE_CONN: { + nc->flags |= MG_F_SEND_AND_CLOSE; + mg_close_conn(nc); + break; + } + case MG_SIG_RECV: { + cs->recv_pending = 0; + if (nc->flags & MG_F_UDP) { + mg_lwip_handle_recv_udp(nc); + } else { + mg_lwip_handle_recv_tcp(nc); + } + break; + } + case MG_SIG_TOMBSTONE: { + break; + } + case MG_SIG_ACCEPT: { + mg_lwip_handle_accept(nc); + break; + } + } + } +} + +void mg_lwip_if_init(struct mg_iface *iface) { + LOG(LL_INFO, ("%p Mongoose init", iface)); + iface->data = MG_CALLOC(1, sizeof(struct mg_ev_mgr_lwip_data)); +} + +void mg_lwip_if_free(struct mg_iface *iface) { + MG_FREE(iface->data); + iface->data = NULL; +} + +void mg_lwip_if_add_conn(struct mg_connection *nc) { + (void) nc; +} + +void mg_lwip_if_remove_conn(struct mg_connection *nc) { + struct mg_ev_mgr_lwip_data *md = + (struct mg_ev_mgr_lwip_data *) nc->iface->data; + /* Walk the queue and null-out further signals for this conn. */ + for (int i = 0; i < MG_SIG_QUEUE_LEN; i++) { + if (md->sig_queue[i].nc == nc) { + md->sig_queue[i].sig = MG_SIG_TOMBSTONE; + } + } +} + +time_t mg_lwip_if_poll(struct mg_iface *iface, int timeout_ms) { + struct mg_mgr *mgr = iface->mgr; + int n = 0; + double now = mg_time(); + struct mg_connection *nc, *tmp; + double min_timer = 0; + int num_timers = 0; +#if 0 + DBG(("begin poll @%u", (unsigned int) (now * 1000))); +#endif + mg_ev_mgr_lwip_process_signals(mgr); + for (nc = mgr->active_connections; nc != NULL; nc = tmp) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + tmp = nc->next; + n++; + if ((nc->flags & MG_F_CLOSE_IMMEDIATELY) || + ((nc->flags & MG_F_SEND_AND_CLOSE) && (nc->flags & MG_F_UDP) && + (nc->send_mbuf.len == 0))) { + mg_close_conn(nc); + continue; + } + mg_if_poll(nc, now); + mg_if_timer(nc, now); +#if MG_ENABLE_SSL + if ((nc->flags & MG_F_SSL) && cs != NULL && cs->pcb.tcp != NULL && + cs->pcb.tcp->state == ESTABLISHED) { + if (((nc->flags & MG_F_WANT_WRITE) || + ((nc->send_mbuf.len > 0) && + (nc->flags & MG_F_SSL_HANDSHAKE_DONE))) && + cs->pcb.tcp->snd_buf > 0) { + /* Can write more. */ + if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) { + if (!(nc->flags & MG_F_CONNECTING)) mg_lwip_ssl_send(nc); + } else { + mg_lwip_ssl_do_hs(nc); + } + } + if (cs->rx_chain != NULL || (nc->flags & MG_F_WANT_READ)) { + if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) { + if (!(nc->flags & MG_F_CONNECTING)) mg_lwip_ssl_recv(nc); + } else { + mg_lwip_ssl_do_hs(nc); + } + } + } else +#endif /* MG_ENABLE_SSL */ + { + if (nc->send_mbuf.len > 0 && !(nc->flags & MG_F_CONNECTING)) { + mg_lwip_send_more(nc); + } + } + if (nc->sock != INVALID_SOCKET && + !(nc->flags & (MG_F_UDP | MG_F_LISTENING)) && cs->pcb.tcp != NULL && + cs->pcb.tcp->unsent != NULL) { + tcpip_callback(tcp_output_tcpip, cs->pcb.tcp); + } + if (nc->ev_timer_time > 0) { + if (num_timers == 0 || nc->ev_timer_time < min_timer) { + min_timer = nc->ev_timer_time; + } + num_timers++; + } + + if (nc->sock != INVALID_SOCKET) { + /* Try to consume data from cs->rx_chain */ + mg_lwip_consume_rx_chain_tcp(nc); + + /* + * If the connection is about to close, and rx_chain is finally empty, + * send the MG_SIG_CLOSE_CONN signal + */ + if (cs->draining_rx_chain && cs->rx_chain == NULL) { + mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); + } + } + } +#if 0 + DBG(("end poll @%u, %d conns, %d timers (min %u), next in %d ms", + (unsigned int) (now * 1000), n, num_timers, + (unsigned int) (min_timer * 1000), timeout_ms)); +#endif + (void) timeout_ms; + return now; +} + +uint32_t mg_lwip_get_poll_delay_ms(struct mg_mgr *mgr) { + struct mg_connection *nc; + double now; + double min_timer = 0; + int num_timers = 0; + mg_ev_mgr_lwip_process_signals(mgr); + for (nc = mg_next(mgr, NULL); nc != NULL; nc = mg_next(mgr, nc)) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + if (nc->ev_timer_time > 0) { + if (num_timers == 0 || nc->ev_timer_time < min_timer) { + min_timer = nc->ev_timer_time; + } + num_timers++; + } + if (nc->send_mbuf.len > 0 +#if MG_ENABLE_SSL + || (nc->flags & MG_F_WANT_WRITE) +#endif + ) { + int can_send = 0; + /* We have stuff to send, but can we? */ + if (nc->flags & MG_F_UDP) { + /* UDP is always ready for sending. */ + can_send = (cs->pcb.udp != NULL); + } else { + can_send = (cs->pcb.tcp != NULL && cs->pcb.tcp->snd_buf > 0); + } + /* We want and can send, request a poll immediately. */ + if (can_send) return 0; + } + } + uint32_t timeout_ms = ~0; + now = mg_time(); + if (num_timers > 0) { + /* If we have a timer that is past due, do a poll ASAP. */ + if (min_timer < now) return 0; + double timer_timeout_ms = (min_timer - now) * 1000 + 1 /* rounding */; + if (timer_timeout_ms < timeout_ms) { + timeout_ms = timer_timeout_ms; + } + } + return timeout_ms; +} + +#endif /* MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL */ +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/lwip/mg_lwip_ssl_if.c" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_SSL && MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL + +/* Amalgamated: #include "common/mg_mem.h" */ +/* Amalgamated: #include "common/cs_dbg.h" */ + +#include +#include + +#ifndef MG_LWIP_SSL_IO_SIZE +#define MG_LWIP_SSL_IO_SIZE 1024 +#endif + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +void mg_lwip_ssl_do_hs(struct mg_connection *nc) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + int server_side = (nc->listener != NULL); + enum mg_ssl_if_result res; + if (nc->flags & MG_F_CLOSE_IMMEDIATELY) return; + res = mg_ssl_if_handshake(nc); + DBG(("%p %lu %d %d", nc, nc->flags, server_side, res)); + if (res != MG_SSL_OK) { + if (res == MG_SSL_WANT_WRITE) { + nc->flags |= MG_F_WANT_WRITE; + cs->err = 0; + } else if (res == MG_SSL_WANT_READ) { + /* + * Nothing to do in particular, we are callback-driven. + * What we definitely do not need anymore is SSL reading (nothing left). + */ + nc->flags &= ~MG_F_WANT_READ; + cs->err = 0; + } else { + cs->err = res; + if (server_side) { + mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); + } else { + mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); + } + } + } else { + cs->err = 0; + nc->flags &= ~MG_F_WANT_WRITE; + /* + * Handshake is done. Schedule a read immediately to consume app data + * which may already be waiting. + */ + nc->flags |= (MG_F_SSL_HANDSHAKE_DONE | MG_F_WANT_READ); + if (server_side) { + mg_lwip_accept_conn(nc, cs->pcb.tcp); + } else { + mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); + } + } +} + +void mg_lwip_ssl_send(struct mg_connection *nc) { + if (nc->sock == INVALID_SOCKET) { + DBG(("%p invalid socket", nc)); + return; + } + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + /* It's ok if the buffer is empty. Return value of 0 may also be valid. */ + int len = cs->last_ssl_write_size; + if (len == 0) { + len = MIN(MG_LWIP_SSL_IO_SIZE, nc->send_mbuf.len); + } + int ret = mg_ssl_if_write(nc, nc->send_mbuf.buf, len); + DBG(("%p SSL_write %u = %d", nc, len, ret)); + if (ret > 0) { + mg_if_sent_cb(nc, ret); + cs->last_ssl_write_size = 0; + } else if (ret < 0) { + /* This is tricky. We must remember the exact data we were sending to retry + * exactly the same send next time. */ + cs->last_ssl_write_size = len; + } + if (ret == len) { + nc->flags &= ~MG_F_WANT_WRITE; + } else if (ret == MG_SSL_WANT_WRITE) { + nc->flags |= MG_F_WANT_WRITE; + } else { + mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); + } +} + +void mg_lwip_ssl_recv(struct mg_connection *nc) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + /* Don't deliver data before connect callback */ + if (nc->flags & MG_F_CONNECTING) return; + while (nc->recv_mbuf.len < nc->recv_mbuf_limit) { + char *buf = (char *) MG_MALLOC(MG_LWIP_SSL_IO_SIZE); + if (buf == NULL) return; + int ret = mg_ssl_if_read(nc, buf, MG_LWIP_SSL_IO_SIZE); + DBG(("%p %p SSL_read %u = %d", nc, cs->rx_chain, MG_LWIP_SSL_IO_SIZE, ret)); + if (ret <= 0) { + MG_FREE(buf); + if (ret == MG_SSL_WANT_WRITE) { + nc->flags |= MG_F_WANT_WRITE; + return; + } else if (ret == MG_SSL_WANT_READ) { + /* + * Nothing to do in particular, we are callback-driven. + * What we definitely do not need anymore is SSL reading (nothing left). + */ + nc->flags &= ~MG_F_WANT_READ; + cs->err = 0; + return; + } else { + mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); + return; + } + } else { + mg_if_recv_tcp_cb(nc, buf, ret, 1 /* own */); + } + } +} + +#ifdef KR_VERSION + +ssize_t kr_send(int fd, const void *buf, size_t len) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) fd; + int ret = mg_lwip_tcp_write(cs->nc, buf, len); + DBG(("%p mg_lwip_tcp_write %u = %d", cs->nc, len, ret)); + if (ret == 0) ret = KR_IO_WOULDBLOCK; + return ret; +} + +ssize_t kr_recv(int fd, void *buf, size_t len) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) fd; + struct pbuf *seg = cs->rx_chain; + if (seg == NULL) { + DBG(("%u - nothing to read", len)); + return KR_IO_WOULDBLOCK; + } + size_t seg_len = (seg->len - cs->rx_offset); + DBG(("%u %u %u %u", len, cs->rx_chain->len, seg_len, cs->rx_chain->tot_len)); + len = MIN(len, seg_len); + pbuf_copy_partial(seg, buf, len, cs->rx_offset); + cs->rx_offset += len; + tcp_recved(cs->pcb.tcp, len); + if (cs->rx_offset == cs->rx_chain->len) { + cs->rx_chain = pbuf_dechain(cs->rx_chain); + pbuf_free(seg); + cs->rx_offset = 0; + } + return len; +} + +#elif MG_SSL_IF == MG_SSL_IF_MBEDTLS + +int ssl_socket_send(void *ctx, const unsigned char *buf, size_t len) { + struct mg_connection *nc = (struct mg_connection *) ctx; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + int ret = mg_lwip_tcp_write(cs->nc, buf, len); + if (ret == 0) ret = MBEDTLS_ERR_SSL_WANT_WRITE; + LOG(LL_DEBUG, ("%p %d -> %d", nc, len, ret)); + return ret; +} + +int ssl_socket_recv(void *ctx, unsigned char *buf, size_t len) { + struct mg_connection *nc = (struct mg_connection *) ctx; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + struct pbuf *seg = cs->rx_chain; + if (seg == NULL) { + DBG(("%u - nothing to read", len)); + return MBEDTLS_ERR_SSL_WANT_READ; + } + size_t seg_len = (seg->len - cs->rx_offset); + DBG(("%u %u %u %u", len, cs->rx_chain->len, seg_len, cs->rx_chain->tot_len)); + mgos_lock(); + len = MIN(len, seg_len); + pbuf_copy_partial(seg, buf, len, cs->rx_offset); + cs->rx_offset += len; + /* TCP PCB may be NULL if connection has already been closed + * but we still have data to deliver to SSL. */ + if (cs->pcb.tcp != NULL) tcp_recved(cs->pcb.tcp, len); + if (cs->rx_offset == cs->rx_chain->len) { + cs->rx_chain = pbuf_dechain(cs->rx_chain); + pbuf_free(seg); + cs->rx_offset = 0; + } + mgos_unlock(); + LOG(LL_DEBUG, ("%p <- %d", nc, (int) len)); + return len; +} + +#endif + +#endif /* MG_ENABLE_SSL && MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL */ +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/wince/wince_libc.c" +#endif +/* + * Copyright (c) 2016 Cesanta Software Limited + * All rights reserved + */ + +#ifdef WINCE + +const char *strerror(int err) { + /* + * TODO(alashkin): there is no strerror on WinCE; + * look for similar wce_xxxx function + */ + static char buf[10]; + snprintf(buf, sizeof(buf), "%d", err); + return buf; +} + +int open(const char *filename, int oflag, int pmode) { + /* + * TODO(alashkin): mg_open function is not used in mongoose + * but exists in documentation as utility function + * Shall we delete it at all or implement for WinCE as well? + */ + DebugBreak(); + return 0; /* for compiler */ +} + +int _wstati64(const wchar_t *path, cs_stat_t *st) { + DWORD fa = GetFileAttributesW(path); + if (fa == INVALID_FILE_ATTRIBUTES) { + return -1; + } + memset(st, 0, sizeof(*st)); + if ((fa & FILE_ATTRIBUTE_DIRECTORY) == 0) { + HANDLE h; + FILETIME ftime; + st->st_mode |= _S_IFREG; + h = CreateFileW(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + if (h == INVALID_HANDLE_VALUE) { + return -1; + } + st->st_size = GetFileSize(h, NULL); + GetFileTime(h, NULL, NULL, &ftime); + st->st_mtime = (uint32_t)((((uint64_t) ftime.dwLowDateTime + + ((uint64_t) ftime.dwHighDateTime << 32)) / + 10000000.0) - + 11644473600); + CloseHandle(h); + } else { + st->st_mode |= _S_IFDIR; + } + return 0; +} + +/* Windows CE doesn't have neither gmtime nor strftime */ +static void mg_gmt_time_string(char *buf, size_t buf_len, time_t *t) { + FILETIME ft; + SYSTEMTIME systime; + if (t != NULL) { + uint64_t filetime = (*t + 11644473600) * 10000000; + ft.dwLowDateTime = filetime & 0xFFFFFFFF; + ft.dwHighDateTime = (filetime & 0xFFFFFFFF00000000) >> 32; + FileTimeToSystemTime(&ft, &systime); + } else { + GetSystemTime(&systime); + } + /* There is no PRIu16 in WinCE SDK */ + snprintf(buf, buf_len, "%d.%d.%d %d:%d:%d GMT", (int) systime.wYear, + (int) systime.wMonth, (int) systime.wDay, (int) systime.wHour, + (int) systime.wMinute, (int) systime.wSecond); +} + +#endif +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/pic32/pic32_net_if.h" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PIC32_NET_IF_H_ +#define CS_COMMON_PLATFORMS_PIC32_NET_IF_H_ + +/* Amalgamated: #include "mongoose/src/net_if.h" */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef MG_ENABLE_NET_IF_PIC32 +#define MG_ENABLE_NET_IF_PIC32 MG_NET_IF == MG_NET_IF_PIC32 +#endif + +extern const struct mg_iface_vtable mg_pic32_iface_vtable; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CS_COMMON_PLATFORMS_PIC32_NET_IF_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/pic32/pic32_net_if.c" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_NET_IF_PIC32 + +int mg_pic32_if_create_conn(struct mg_connection *nc) { + (void) nc; + return 1; +} + +void mg_pic32_if_recved(struct mg_connection *nc, size_t len) { + (void) nc; + (void) len; +} + +void mg_pic32_if_add_conn(struct mg_connection *nc) { + (void) nc; +} + +void mg_pic32_if_init(struct mg_iface *iface) { + (void) iface; + (void) mg_get_errno(); /* Shutup compiler */ +} + +void mg_pic32_if_free(struct mg_iface *iface) { + (void) iface; +} + +void mg_pic32_if_remove_conn(struct mg_connection *nc) { + (void) nc; +} + +void mg_pic32_if_destroy_conn(struct mg_connection *nc) { + if (nc->sock == INVALID_SOCKET) return; + /* For UDP, only close outgoing sockets or listeners. */ + if (!(nc->flags & MG_F_UDP)) { + /* Close TCP */ + TCPIP_TCP_Close((TCP_SOCKET) nc->sock); + } else if (nc->listener == NULL) { + /* Only close outgoing UDP or listeners. */ + TCPIP_UDP_Close((UDP_SOCKET) nc->sock); + } + + nc->sock = INVALID_SOCKET; +} + +int mg_pic32_if_listen_udp(struct mg_connection *nc, union socket_address *sa) { + nc->sock = TCPIP_UDP_ServerOpen( + sa->sin.sin_family == AF_INET ? IP_ADDRESS_TYPE_IPV4 + : IP_ADDRESS_TYPE_IPV6, + ntohs(sa->sin.sin_port), + sa->sin.sin_addr.s_addr == 0 ? 0 : (IP_MULTI_ADDRESS *) &sa->sin); + if (nc->sock == INVALID_SOCKET) { + return -1; + } + return 0; +} + +void mg_pic32_if_udp_send(struct mg_connection *nc, const void *buf, + size_t len) { + mbuf_append(&nc->send_mbuf, buf, len); +} + +void mg_pic32_if_tcp_send(struct mg_connection *nc, const void *buf, + size_t len) { + mbuf_append(&nc->send_mbuf, buf, len); +} + +int mg_pic32_if_listen_tcp(struct mg_connection *nc, union socket_address *sa) { + nc->sock = TCPIP_TCP_ServerOpen( + sa->sin.sin_family == AF_INET ? IP_ADDRESS_TYPE_IPV4 + : IP_ADDRESS_TYPE_IPV6, + ntohs(sa->sin.sin_port), + sa->sin.sin_addr.s_addr == 0 ? 0 : (IP_MULTI_ADDRESS *) &sa->sin); + memcpy(&nc->sa, sa, sizeof(*sa)); + if (nc->sock == INVALID_SOCKET) { + return -1; + } + return 0; +} + +static int mg_accept_conn(struct mg_connection *lc) { + struct mg_connection *nc; + TCP_SOCKET_INFO si; + union socket_address sa; + + nc = mg_if_accept_new_conn(lc); + + if (nc == NULL) { + return 0; + } + + nc->sock = lc->sock; + nc->flags &= ~MG_F_LISTENING; + + if (!TCPIP_TCP_SocketInfoGet((TCP_SOCKET) nc->sock, &si)) { + return 0; + } + + if (si.addressType == IP_ADDRESS_TYPE_IPV4) { + sa.sin.sin_family = AF_INET; + sa.sin.sin_port = htons(si.remotePort); + sa.sin.sin_addr.s_addr = si.remoteIPaddress.v4Add.Val; + } else { + /* TODO(alashkin): do something with _potential_ IPv6 */ + memset(&sa, 0, sizeof(sa)); + } + + mg_if_accept_tcp_cb(nc, (union socket_address *) &sa, sizeof(sa)); + + return mg_pic32_if_listen_tcp(lc, &lc->sa) >= 0; +} + +char *inet_ntoa(struct in_addr in) { + static char addr[17]; + snprintf(addr, sizeof(addr), "%d.%d.%d.%d", (int) in.S_un.S_un_b.s_b1, + (int) in.S_un.S_un_b.s_b2, (int) in.S_un.S_un_b.s_b3, + (int) in.S_un.S_un_b.s_b4); + return addr; +} + +static void mg_handle_send(struct mg_connection *nc) { + uint16_t bytes_written = 0; + if (nc->flags & MG_F_UDP) { + if (!TCPIP_UDP_RemoteBind( + (UDP_SOCKET) nc->sock, + nc->sa.sin.sin_family == AF_INET ? IP_ADDRESS_TYPE_IPV4 + : IP_ADDRESS_TYPE_IPV6, + ntohs(nc->sa.sin.sin_port), (IP_MULTI_ADDRESS *) &nc->sa.sin)) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + return; + } + bytes_written = TCPIP_UDP_TxPutIsReady((UDP_SOCKET) nc->sock, 0); + if (bytes_written >= nc->send_mbuf.len) { + if (TCPIP_UDP_ArrayPut((UDP_SOCKET) nc->sock, + (uint8_t *) nc->send_mbuf.buf, + nc->send_mbuf.len) != nc->send_mbuf.len) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + bytes_written = 0; + } + } + } else { + bytes_written = TCPIP_TCP_FifoTxFreeGet((TCP_SOCKET) nc->sock); + if (bytes_written != 0) { + if (bytes_written > nc->send_mbuf.len) { + bytes_written = nc->send_mbuf.len; + } + if (TCPIP_TCP_ArrayPut((TCP_SOCKET) nc->sock, + (uint8_t *) nc->send_mbuf.buf, + bytes_written) != bytes_written) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + bytes_written = 0; + } + } + } + + mg_if_sent_cb(nc, bytes_written); +} + +static void mg_handle_recv(struct mg_connection *nc) { + uint16_t bytes_read = 0; + uint8_t *buf = NULL; + if (nc->flags & MG_F_UDP) { + bytes_read = TCPIP_UDP_GetIsReady((UDP_SOCKET) nc->sock); + if (bytes_read != 0 && + (nc->recv_mbuf_limit == -1 || + nc->recv_mbuf.len + bytes_read < nc->recv_mbuf_limit)) { + buf = (uint8_t *) MG_MALLOC(bytes_read); + if (TCPIP_UDP_ArrayGet((UDP_SOCKET) nc->sock, buf, bytes_read) != + bytes_read) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + bytes_read = 0; + MG_FREE(buf); + } + } + } else { + bytes_read = TCPIP_TCP_GetIsReady((TCP_SOCKET) nc->sock); + if (bytes_read != 0) { + if (nc->recv_mbuf_limit != -1 && + nc->recv_mbuf_limit - nc->recv_mbuf.len > bytes_read) { + bytes_read = nc->recv_mbuf_limit - nc->recv_mbuf.len; + } + buf = (uint8_t *) MG_MALLOC(bytes_read); + if (TCPIP_TCP_ArrayGet((TCP_SOCKET) nc->sock, buf, bytes_read) != + bytes_read) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + MG_FREE(buf); + bytes_read = 0; + } + } + } + + if (bytes_read != 0) { + mg_if_recv_tcp_cb(nc, buf, bytes_read, 1 /* own */); + } +} + +time_t mg_pic32_if_poll(struct mg_iface *iface, int timeout_ms) { + struct mg_mgr *mgr = iface->mgr; + double now = mg_time(); + struct mg_connection *nc, *tmp; + + for (nc = mgr->active_connections; nc != NULL; nc = tmp) { + tmp = nc->next; + + if (nc->flags & MG_F_CONNECTING) { + /* processing connections */ + if (nc->flags & MG_F_UDP || + TCPIP_TCP_IsConnected((TCP_SOCKET) nc->sock)) { + mg_if_connect_cb(nc, 0); + } + } else if (nc->flags & MG_F_LISTENING) { + if (TCPIP_TCP_IsConnected((TCP_SOCKET) nc->sock)) { + /* accept new connections */ + mg_accept_conn(nc); + } + } else { + if (nc->send_mbuf.len != 0) { + mg_handle_send(nc); + } + + if (nc->recv_mbuf_limit == -1 || + nc->recv_mbuf.len < nc->recv_mbuf_limit) { + mg_handle_recv(nc); + } + } + } + + for (nc = mgr->active_connections; nc != NULL; nc = tmp) { + tmp = nc->next; + if ((nc->flags & MG_F_CLOSE_IMMEDIATELY) || + (nc->send_mbuf.len == 0 && (nc->flags & MG_F_SEND_AND_CLOSE))) { + mg_close_conn(nc); + } + } + + return now; +} + +void mg_pic32_if_sock_set(struct mg_connection *nc, sock_t sock) { + nc->sock = sock; +} + +void mg_pic32_if_get_conn_addr(struct mg_connection *nc, int remote, + union socket_address *sa) { + /* TODO(alaskin): not implemented yet */ +} + +void mg_pic32_if_connect_tcp(struct mg_connection *nc, + const union socket_address *sa) { + nc->sock = TCPIP_TCP_ClientOpen( + sa->sin.sin_family == AF_INET ? IP_ADDRESS_TYPE_IPV4 + : IP_ADDRESS_TYPE_IPV6, + ntohs(sa->sin.sin_port), (IP_MULTI_ADDRESS *) &sa->sin); + nc->err = (nc->sock == INVALID_SOCKET) ? -1 : 0; +} + +void mg_pic32_if_connect_udp(struct mg_connection *nc) { + nc->sock = TCPIP_UDP_ClientOpen(IP_ADDRESS_TYPE_ANY, 0, NULL); + nc->err = (nc->sock == INVALID_SOCKET) ? -1 : 0; +} + +/* clang-format off */ +#define MG_PIC32_IFACE_VTABLE \ + { \ + mg_pic32_if_init, \ + mg_pic32_if_free, \ + mg_pic32_if_add_conn, \ + mg_pic32_if_remove_conn, \ + mg_pic32_if_poll, \ + mg_pic32_if_listen_tcp, \ + mg_pic32_if_listen_udp, \ + mg_pic32_if_connect_tcp, \ + mg_pic32_if_connect_udp, \ + mg_pic32_if_tcp_send, \ + mg_pic32_if_udp_send, \ + mg_pic32_if_recved, \ + mg_pic32_if_create_conn, \ + mg_pic32_if_destroy_conn, \ + mg_pic32_if_sock_set, \ + mg_pic32_if_get_conn_addr, \ + } +/* clang-format on */ + +const struct mg_iface_vtable mg_pic32_iface_vtable = MG_PIC32_IFACE_VTABLE; +#if MG_NET_IF == MG_NET_IF_PIC32 +const struct mg_iface_vtable mg_default_iface_vtable = MG_PIC32_IFACE_VTABLE; +#endif + +#endif /* MG_ENABLE_NET_IF_PIC32 */ +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/windows/windows_direct.c" +#endif +/* + * Copyright (c) 2017 Cesanta Software Limited + * All rights reserved + */ + +#ifdef _WIN32 + +int rmdir(const char *dirname) { + return _rmdir(dirname); +} + +unsigned int sleep(unsigned int seconds) { + Sleep(seconds * 1000); + return 0; +} + +#endif /* _WIN32 */ diff --git a/src/mongoose-6.11/mongoose.h b/src/mongoose-6.11/mongoose.h new file mode 100644 index 0000000..eecbbef --- /dev/null +++ b/src/mongoose-6.11/mongoose.h @@ -0,0 +1,6222 @@ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_common.h" +#endif +/* + * Copyright (c) 2004-2013 Sergey Lyubka + * Copyright (c) 2013-2015 Cesanta Software Limited + * All rights reserved + * + * This software is dual-licensed: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. For the terms of this + * license, see . + * + * You are free to use this software under the terms of the GNU General + * Public License, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * Alternatively, you can license this software under a commercial + * license, as set out in . + */ + +#ifndef CS_MONGOOSE_SRC_COMMON_H_ +#define CS_MONGOOSE_SRC_COMMON_H_ + +#define MG_VERSION "6.11" + +/* Local tweaks, applied before any of Mongoose's own headers. */ +#ifdef MG_LOCALS +#include +#endif + +#endif /* CS_MONGOOSE_SRC_COMMON_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/platform.h" +#endif +#ifndef CS_COMMON_PLATFORM_H_ +#define CS_COMMON_PLATFORM_H_ + +/* + * For the "custom" platform, includes and dependencies can be + * provided through mg_locals.h. + */ +#define CS_P_CUSTOM 0 +#define CS_P_UNIX 1 +#define CS_P_WINDOWS 2 +#define CS_P_ESP32 15 +#define CS_P_ESP8266 3 +#define CS_P_CC3100 6 +#define CS_P_CC3200 4 +#define CS_P_CC3220 17 +#define CS_P_MSP432 5 +#define CS_P_TM4C129 14 +#define CS_P_MBED 7 +#define CS_P_WINCE 8 +#define CS_P_NXP_LPC 13 +#define CS_P_NXP_KINETIS 9 +#define CS_P_NRF51 12 +#define CS_P_NRF52 10 +#define CS_P_PIC32 11 +#define CS_P_STM32 16 +/* Next id: 18 */ + +/* If not specified explicitly, we guess platform by defines. */ +#ifndef CS_PLATFORM + +#if defined(TARGET_IS_MSP432P4XX) || defined(__MSP432P401R__) +#define CS_PLATFORM CS_P_MSP432 +#elif defined(cc3200) || defined(TARGET_IS_CC3200) +#define CS_PLATFORM CS_P_CC3200 +#elif defined(cc3220) || defined(TARGET_IS_CC3220) +#define CS_PLATFORM CS_P_CC3220 +#elif defined(__unix__) || defined(__APPLE__) +#define CS_PLATFORM CS_P_UNIX +#elif defined(WINCE) +#define CS_PLATFORM CS_P_WINCE +#elif defined(_WIN32) +#define CS_PLATFORM CS_P_WINDOWS +#elif defined(__MBED__) +#define CS_PLATFORM CS_P_MBED +#elif defined(__USE_LPCOPEN) +#define CS_PLATFORM CS_P_NXP_LPC +#elif defined(FRDM_K64F) || defined(FREEDOM) +#define CS_PLATFORM CS_P_NXP_KINETIS +#elif defined(PIC32) +#define CS_PLATFORM CS_P_PIC32 +#elif defined(ESP_PLATFORM) +#define CS_PLATFORM CS_P_ESP32 +#elif defined(ICACHE_FLASH) +#define CS_PLATFORM CS_P_ESP8266 +#elif defined(TARGET_IS_TM4C129_RA0) || defined(TARGET_IS_TM4C129_RA1) || \ + defined(TARGET_IS_TM4C129_RA2) +#define CS_PLATFORM CS_P_TM4C129 +#elif defined(STM32) +#define CS_PLATFORM CS_P_STM32 +#endif + +#ifndef CS_PLATFORM +#error "CS_PLATFORM is not specified and we couldn't guess it." +#endif + +#endif /* !defined(CS_PLATFORM) */ + +#define MG_NET_IF_SOCKET 1 +#define MG_NET_IF_SIMPLELINK 2 +#define MG_NET_IF_LWIP_LOW_LEVEL 3 +#define MG_NET_IF_PIC32 4 + +#define MG_SSL_IF_OPENSSL 1 +#define MG_SSL_IF_MBEDTLS 2 +#define MG_SSL_IF_SIMPLELINK 3 + +/* Amalgamated: #include "common/platforms/platform_unix.h" */ +/* Amalgamated: #include "common/platforms/platform_windows.h" */ +/* Amalgamated: #include "common/platforms/platform_esp32.h" */ +/* Amalgamated: #include "common/platforms/platform_esp8266.h" */ +/* Amalgamated: #include "common/platforms/platform_cc3100.h" */ +/* Amalgamated: #include "common/platforms/platform_cc3200.h" */ +/* Amalgamated: #include "common/platforms/platform_cc3220.h" */ +/* Amalgamated: #include "common/platforms/platform_mbed.h" */ +/* Amalgamated: #include "common/platforms/platform_nrf51.h" */ +/* Amalgamated: #include "common/platforms/platform_nrf52.h" */ +/* Amalgamated: #include "common/platforms/platform_wince.h" */ +/* Amalgamated: #include "common/platforms/platform_nxp_lpc.h" */ +/* Amalgamated: #include "common/platforms/platform_nxp_kinetis.h" */ +/* Amalgamated: #include "common/platforms/platform_pic32.h" */ +/* Amalgamated: #include "common/platforms/platform_stm32.h" */ + +/* Common stuff */ + +#if !defined(WEAK) +#if (defined(__GNUC__) || defined(__TI_COMPILER_VERSION__)) && !defined(_WIN32) +#define WEAK __attribute__((weak)) +#else +#define WEAK +#endif +#endif + +#ifdef __GNUC__ +#define NORETURN __attribute__((noreturn)) +#define NOINLINE __attribute__((noinline)) +#define WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#define NOINSTR __attribute__((no_instrument_function)) +#define DO_NOT_WARN_UNUSED __attribute__((unused)) +#else +#define NORETURN +#define NOINLINE +#define WARN_UNUSED_RESULT +#define NOINSTR +#define DO_NOT_WARN_UNUSED +#endif /* __GNUC__ */ + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) +#endif + +#endif /* CS_COMMON_PLATFORM_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/platform_windows.h" +#endif +#ifndef CS_COMMON_PLATFORMS_PLATFORM_WINDOWS_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_WINDOWS_H_ +#if CS_PLATFORM == CS_P_WINDOWS + +/* + * MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015) + * MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013) + * MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012) + * MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010) + * MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008) + * MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005) + * MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio 2003) + * MSVC++ 7.0 _MSC_VER == 1300 + * MSVC++ 6.0 _MSC_VER == 1200 + * MSVC++ 5.0 _MSC_VER == 1100 + */ +#ifdef _MSC_VER +#pragma warning(disable : 4127) /* FD_SET() emits warning, disable it */ +#pragma warning(disable : 4204) /* missing c99 support */ +#endif + +#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS +#define _WINSOCK_DEPRECATED_NO_WARNINGS 1 +#endif + +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") /* Linking with winsock library */ +#endif + +#include +#include +#include +#include + +#if _MSC_VER < 1700 +typedef int bool; +#else +#include +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1800 +#define strdup _strdup +#endif + +#ifndef EINPROGRESS +#define EINPROGRESS WSAEINPROGRESS +#endif +#ifndef EWOULDBLOCK +#define EWOULDBLOCK WSAEWOULDBLOCK +#endif +#ifndef __func__ +#define STRX(x) #x +#define STR(x) STRX(x) +#define __func__ __FILE__ ":" STR(__LINE__) +#endif +#define snprintf _snprintf +#define vsnprintf _vsnprintf +#define to64(x) _atoi64(x) +#if !defined(__MINGW32__) && !defined(__MINGW64__) +#define popen(x, y) _popen((x), (y)) +#define pclose(x) _pclose(x) +#define fileno _fileno +#endif +#if defined(_MSC_VER) && _MSC_VER >= 1400 +#define fseeko(x, y, z) _fseeki64((x), (y), (z)) +#else +#define fseeko(x, y, z) fseek((x), (y), (z)) +#endif +#if defined(_MSC_VER) && _MSC_VER <= 1200 +typedef unsigned long uintptr_t; +typedef long intptr_t; +#endif +typedef int socklen_t; +#if _MSC_VER >= 1700 +#include +#else +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef int int32_t; +typedef unsigned int uint32_t; +typedef short int16_t; +typedef unsigned short uint16_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#endif +typedef SOCKET sock_t; +typedef uint32_t in_addr_t; +#ifndef UINT16_MAX +#define UINT16_MAX 65535 +#endif +#ifndef UINT32_MAX +#define UINT32_MAX 4294967295 +#endif +#ifndef pid_t +#define pid_t HANDLE +#endif +#define INT64_FMT "I64d" +#define INT64_X_FMT "I64x" +#define SIZE_T_FMT "Iu" +typedef struct _stati64 cs_stat_t; +#ifndef S_ISDIR +#define S_ISDIR(x) (((x) &_S_IFMT) == _S_IFDIR) +#endif +#ifndef S_ISREG +#define S_ISREG(x) (((x) &_S_IFMT) == _S_IFREG) +#endif +#define DIRSEP '\\' +#define CS_DEFINE_DIRENT + +#ifndef va_copy +#ifdef __va_copy +#define va_copy __va_copy +#else +#define va_copy(x, y) (x) = (y) +#endif +#endif + +#ifndef MG_MAX_HTTP_REQUEST_SIZE +#define MG_MAX_HTTP_REQUEST_SIZE 8192 +#endif + +#ifndef MG_MAX_HTTP_SEND_MBUF +#define MG_MAX_HTTP_SEND_MBUF 4096 +#endif + +#ifndef MG_MAX_HTTP_HEADERS +#define MG_MAX_HTTP_HEADERS 40 +#endif + +#ifndef CS_ENABLE_STDIO +#define CS_ENABLE_STDIO 1 +#endif + +#ifndef MG_ENABLE_BROADCAST +#define MG_ENABLE_BROADCAST 1 +#endif + +#ifndef MG_ENABLE_DIRECTORY_LISTING +#define MG_ENABLE_DIRECTORY_LISTING 1 +#endif + +#ifndef MG_ENABLE_FILESYSTEM +#define MG_ENABLE_FILESYSTEM 1 +#endif + +#ifndef MG_ENABLE_HTTP_CGI +#define MG_ENABLE_HTTP_CGI MG_ENABLE_FILESYSTEM +#endif + +#ifndef MG_NET_IF +#define MG_NET_IF MG_NET_IF_SOCKET +#endif + +unsigned int sleep(unsigned int seconds); + +/* https://stackoverflow.com/questions/16647819/timegm-cross-platform */ +#define timegm _mkgmtime + +#define gmtime_r(a, b) \ + do { \ + *(b) = *gmtime(a); \ + } while (0) + +#endif /* CS_PLATFORM == CS_P_WINDOWS */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_WINDOWS_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/platform_unix.h" +#endif +#ifndef CS_COMMON_PLATFORMS_PLATFORM_UNIX_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_UNIX_H_ +#if CS_PLATFORM == CS_P_UNIX + +#ifndef _XOPEN_SOURCE +#define _XOPEN_SOURCE 600 +#endif + +/* wants this for C++ */ +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif + +/* C++ wants that for INT64_MAX */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS +#endif + +/* Enable fseeko() and ftello() functions */ +#ifndef _LARGEFILE_SOURCE +#define _LARGEFILE_SOURCE +#endif + +/* Enable 64-bit file offsets */ +#ifndef _FILE_OFFSET_BITS +#define _FILE_OFFSET_BITS 64 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __APPLE__ +#include +#ifndef BYTE_ORDER +#define LITTLE_ENDIAN __DARWIN_LITTLE_ENDIAN +#define BIG_ENDIAN __DARWIN_BIG_ENDIAN +#define PDP_ENDIAN __DARWIN_PDP_ENDIAN +#define BYTE_ORDER __DARWIN_BYTE_ORDER +#endif +#endif + +/* + * osx correctly avoids defining strtoll when compiling in strict ansi mode. + * c++ 11 standard defines strtoll as well. + * We require strtoll, and if your embedded pre-c99 compiler lacks one, please + * implement a shim. + */ +#if !(defined(__cplusplus) && __cplusplus >= 201103L) && \ + !(defined(__DARWIN_C_LEVEL) && __DARWIN_C_LEVEL >= 200809L) +long long strtoll(const char *, char **, int); +#endif + +typedef int sock_t; +#define INVALID_SOCKET (-1) +#define SIZE_T_FMT "zu" +typedef struct stat cs_stat_t; +#define DIRSEP '/' +#define to64(x) strtoll(x, NULL, 10) +#define INT64_FMT PRId64 +#define INT64_X_FMT PRIx64 + +#ifndef __cdecl +#define __cdecl +#endif + +#ifndef va_copy +#ifdef __va_copy +#define va_copy __va_copy +#else +#define va_copy(x, y) (x) = (y) +#endif +#endif + +#define closesocket(x) close(x) + +#ifndef MG_MAX_HTTP_REQUEST_SIZE +#define MG_MAX_HTTP_REQUEST_SIZE 8192 +#endif + +#ifndef MG_MAX_HTTP_SEND_MBUF +#define MG_MAX_HTTP_SEND_MBUF 4096 +#endif + +#ifndef MG_MAX_HTTP_HEADERS +#define MG_MAX_HTTP_HEADERS 40 +#endif + +#ifndef CS_ENABLE_STDIO +#define CS_ENABLE_STDIO 1 +#endif + +#ifndef MG_ENABLE_BROADCAST +#define MG_ENABLE_BROADCAST 1 +#endif + +#ifndef MG_ENABLE_DIRECTORY_LISTING +#define MG_ENABLE_DIRECTORY_LISTING 1 +#endif + +#ifndef MG_ENABLE_FILESYSTEM +#define MG_ENABLE_FILESYSTEM 1 +#endif + +#ifndef MG_ENABLE_HTTP_CGI +#define MG_ENABLE_HTTP_CGI MG_ENABLE_FILESYSTEM +#endif + +#ifndef MG_NET_IF +#define MG_NET_IF MG_NET_IF_SOCKET +#endif + +#ifndef MG_HOSTS_FILE_NAME +#define MG_HOSTS_FILE_NAME "/etc/hosts" +#endif + +#ifndef MG_RESOLV_CONF_FILE_NAME +#define MG_RESOLV_CONF_FILE_NAME "/etc/resolv.conf" +#endif + +#endif /* CS_PLATFORM == CS_P_UNIX */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_UNIX_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/platform_esp32.h" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PLATFORM_ESP32_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_ESP32_H_ +#if CS_PLATFORM == CS_P_ESP32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SIZE_T_FMT "u" +typedef struct stat cs_stat_t; +#define DIRSEP '/' +#define to64(x) strtoll(x, NULL, 10) +#define INT64_FMT PRId64 +#define INT64_X_FMT PRIx64 +#define __cdecl +#define _FILE_OFFSET_BITS 32 + +#define MG_LWIP 1 + +#ifndef MG_NET_IF +#define MG_NET_IF MG_NET_IF_SOCKET +#endif + +#ifndef CS_ENABLE_STDIO +#define CS_ENABLE_STDIO 1 +#endif + +#endif /* CS_PLATFORM == CS_P_ESP32 */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_ESP32_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/platform_esp8266.h" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PLATFORM_ESP8266_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_ESP8266_H_ +#if CS_PLATFORM == CS_P_ESP8266 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SIZE_T_FMT "u" +typedef struct stat cs_stat_t; +#define DIRSEP '/' +#if !defined(MGOS_VFS_DEFINE_DIRENT) +#define CS_DEFINE_DIRENT +#endif + +#define to64(x) strtoll(x, NULL, 10) +#define INT64_FMT PRId64 +#define INT64_X_FMT PRIx64 +#define __cdecl +#define _FILE_OFFSET_BITS 32 + +#if !defined(RTOS_SDK) && !defined(__cplusplus) +#define fileno(x) -1 +#endif + +#define MG_LWIP 1 + +/* struct timeval is defined in sys/time.h. */ +#define LWIP_TIMEVAL_PRIVATE 0 + +#ifndef MG_NET_IF +#include +#if LWIP_SOCKET /* RTOS SDK has LWIP sockets */ +#define MG_NET_IF MG_NET_IF_SOCKET +#else +#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL +#endif +#endif + +#ifndef CS_ENABLE_STDIO +#define CS_ENABLE_STDIO 1 +#endif + +#define inet_ntop(af, src, dst, size) \ + (((af) == AF_INET) ? ipaddr_ntoa_r((const ip_addr_t *) (src), (dst), (size)) \ + : NULL) +#define inet_pton(af, src, dst) \ + (((af) == AF_INET) ? ipaddr_aton((src), (ip_addr_t *) (dst)) : 0) + +#endif /* CS_PLATFORM == CS_P_ESP8266 */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_ESP8266_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/platform_cc3100.h" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PLATFORM_CC3100_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_CC3100_H_ +#if CS_PLATFORM == CS_P_CC3100 + +#include +#include +#include +#include +#include +#include +#include + +#define MG_NET_IF MG_NET_IF_SIMPLELINK +#define MG_SSL_IF MG_SSL_IF_SIMPLELINK + +/* + * CC3100 SDK and STM32 SDK include headers w/out path, just like + * #include "simplelink.h". As result, we have to add all required directories + * into Makefile IPATH and do the same thing (include w/out path) + */ + +#include +#include +#undef timeval + +typedef int sock_t; +#define INVALID_SOCKET (-1) + +#define to64(x) strtoll(x, NULL, 10) +#define INT64_FMT PRId64 +#define INT64_X_FMT PRIx64 +#define SIZE_T_FMT "u" + +#define SOMAXCONN 8 + +const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); +char *inet_ntoa(struct in_addr in); +int inet_pton(int af, const char *src, void *dst); + +#endif /* CS_PLATFORM == CS_P_CC3100 */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_CC3100_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/platform_cc3200.h" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PLATFORM_CC3200_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_CC3200_H_ +#if CS_PLATFORM == CS_P_CC3200 + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef __TI_COMPILER_VERSION__ +#include +#include +#endif + +#define MG_NET_IF MG_NET_IF_SIMPLELINK +#define MG_SSL_IF MG_SSL_IF_SIMPLELINK + +/* Only SPIFFS supports directories, SLFS does not. */ +#if defined(CC3200_FS_SPIFFS) && !defined(MG_ENABLE_DIRECTORY_LISTING) +#define MG_ENABLE_DIRECTORY_LISTING 1 +#endif + +/* Amalgamated: #include "common/platforms/simplelink/cs_simplelink.h" */ + +typedef int sock_t; +#define INVALID_SOCKET (-1) +#define SIZE_T_FMT "u" +typedef struct stat cs_stat_t; +#define DIRSEP '/' +#define to64(x) strtoll(x, NULL, 10) +#define INT64_FMT PRId64 +#define INT64_X_FMT PRIx64 +#define __cdecl + +#define fileno(x) -1 + +/* Some functions we implement for Mongoose. */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __TI_COMPILER_VERSION__ +struct SlTimeval_t; +#define timeval SlTimeval_t +int gettimeofday(struct timeval *t, void *tz); +int settimeofday(const struct timeval *tv, const void *tz); + +int asprintf(char **strp, const char *fmt, ...); + +#endif + +/* TI's libc does not have stat & friends, add them. */ +#ifdef __TI_COMPILER_VERSION__ + +#include + +typedef unsigned int mode_t; +typedef size_t _off_t; +typedef long ssize_t; + +struct stat { + int st_ino; + mode_t st_mode; + int st_nlink; + time_t st_mtime; + off_t st_size; +}; + +int _stat(const char *pathname, struct stat *st); +int stat(const char *pathname, struct stat *st); + +#define __S_IFMT 0170000 + +#define __S_IFDIR 0040000 +#define __S_IFCHR 0020000 +#define __S_IFREG 0100000 + +#define __S_ISTYPE(mode, mask) (((mode) &__S_IFMT) == (mask)) + +#define S_IFDIR __S_IFDIR +#define S_IFCHR __S_IFCHR +#define S_IFREG __S_IFREG +#define S_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR) +#define S_ISREG(mode) __S_ISTYPE((mode), __S_IFREG) + +/* 5.x series compilers don't have va_copy, 16.x do. */ +#if __TI_COMPILER_VERSION__ < 16000000 +#define va_copy(apc, ap) ((apc) = (ap)) +#endif + +#endif /* __TI_COMPILER_VERSION__ */ + +#ifdef CC3200_FS_SLFS +#define MG_FS_SLFS +#endif + +#if (defined(CC3200_FS_SPIFFS) || defined(CC3200_FS_SLFS)) && \ + !defined(MG_ENABLE_FILESYSTEM) +#define MG_ENABLE_FILESYSTEM 1 +#define CS_DEFINE_DIRENT +#endif + +#ifndef CS_ENABLE_STDIO +#define CS_ENABLE_STDIO 1 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* CS_PLATFORM == CS_P_CC3200 */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_CC3200_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/platform_msp432.h" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PLATFORM_MSP432_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_MSP432_H_ +#if CS_PLATFORM == CS_P_MSP432 + +#include +#include +#include +#include +#include +#include +#include + +#ifndef __TI_COMPILER_VERSION__ +#include +#include +#endif + +#define MG_NET_IF MG_NET_IF_SIMPLELINK +#define MG_SSL_IF MG_SSL_IF_SIMPLELINK + +/* Amalgamated: #include "common/platforms/simplelink/cs_simplelink.h" */ + +typedef int sock_t; +#define INVALID_SOCKET (-1) +#define SIZE_T_FMT "u" +typedef struct stat cs_stat_t; +#define DIRSEP '/' +#define to64(x) strtoll(x, NULL, 10) +#define INT64_FMT PRId64 +#define INT64_X_FMT PRIx64 +#define __cdecl + +#define fileno(x) -1 + +/* Some functions we implement for Mongoose. */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __TI_COMPILER_VERSION__ +struct SlTimeval_t; +#define timeval SlTimeval_t +int gettimeofday(struct timeval *t, void *tz); +#endif + +/* TI's libc does not have stat & friends, add them. */ +#ifdef __TI_COMPILER_VERSION__ + +#include + +typedef unsigned int mode_t; +typedef size_t _off_t; +typedef long ssize_t; + +struct stat { + int st_ino; + mode_t st_mode; + int st_nlink; + time_t st_mtime; + off_t st_size; +}; + +int _stat(const char *pathname, struct stat *st); +#define stat(a, b) _stat(a, b) + +#define __S_IFMT 0170000 + +#define __S_IFDIR 0040000 +#define __S_IFCHR 0020000 +#define __S_IFREG 0100000 + +#define __S_ISTYPE(mode, mask) (((mode) &__S_IFMT) == (mask)) + +#define S_IFDIR __S_IFDIR +#define S_IFCHR __S_IFCHR +#define S_IFREG __S_IFREG +#define S_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR) +#define S_ISREG(mode) __S_ISTYPE((mode), __S_IFREG) + +/* As of 5.2.7, TI compiler does not support va_copy() yet. */ +#define va_copy(apc, ap) ((apc) = (ap)) + +#endif /* __TI_COMPILER_VERSION__ */ + +#ifndef CS_ENABLE_STDIO +#define CS_ENABLE_STDIO 1 +#endif + +#if (defined(CC3200_FS_SPIFFS) || defined(CC3200_FS_SLFS)) && \ + !defined(MG_ENABLE_FILESYSTEM) +#define MG_ENABLE_FILESYSTEM 1 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* CS_PLATFORM == CS_P_MSP432 */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_MSP432_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/platform_tm4c129.h" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PLATFORM_TM4C129_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_TM4C129_H_ +#if CS_PLATFORM == CS_P_TM4C129 + +#include +#include +#include +#include +#include +#include +#include + +#ifndef __TI_COMPILER_VERSION__ +#include +#include +#endif + +#define SIZE_T_FMT "u" +typedef struct stat cs_stat_t; +#define DIRSEP '/' +#define to64(x) strtoll(x, NULL, 10) +#define INT64_FMT PRId64 +#define INT64_X_FMT PRIx64 +#define __cdecl + +#ifndef MG_NET_IF +#include +#if LWIP_SOCKET +#define MG_NET_IF MG_NET_IF_SOCKET +#else +#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL +#endif +#define MG_LWIP 1 +#elif MG_NET_IF == MG_NET_IF_SIMPLELINK +/* Amalgamated: #include "common/platforms/simplelink/cs_simplelink.h" */ +#endif + +#ifndef CS_ENABLE_STDIO +#define CS_ENABLE_STDIO 1 +#endif + +#ifdef __TI_COMPILER_VERSION__ +/* As of 5.2.8, TI compiler does not support va_copy() yet. */ +#define va_copy(apc, ap) ((apc) = (ap)) +#endif /* __TI_COMPILER_VERSION__ */ + +#ifdef __cplusplus +} +#endif + +#endif /* CS_PLATFORM == CS_P_TM4C129 */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_TM4C129_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/platform_mbed.h" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PLATFORM_MBED_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_MBED_H_ +#if CS_PLATFORM == CS_P_MBED + +/* + * mbed.h contains C++ code (e.g. templates), thus, it should be processed + * only if included directly to startup file (ex: main.cpp) + */ +#ifdef __cplusplus +/* Amalgamated: #include "mbed.h" */ +#endif /* __cplusplus */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct stat cs_stat_t; +#define DIRSEP '/' + +#ifndef CS_ENABLE_STDIO +#define CS_ENABLE_STDIO 1 +#endif + +/* + * mbed can be compiled with the ARM compiler which + * just doesn't come with a gettimeofday shim + * because it's a BSD API and ARM targets embedded + * non-unix platforms. + */ +#if defined(__ARMCC_VERSION) || defined(__ICCARM__) +#define _TIMEVAL_DEFINED +#define gettimeofday _gettimeofday + +/* copied from GCC on ARM; for some reason useconds are signed */ +typedef long suseconds_t; /* microseconds (signed) */ +struct timeval { + time_t tv_sec; /* seconds */ + suseconds_t tv_usec; /* and microseconds */ +}; + +#endif + +#if MG_NET_IF == MG_NET_IF_SIMPLELINK + +#define MG_SIMPLELINK_NO_OSI 1 + +#include + +typedef int sock_t; +#define INVALID_SOCKET (-1) + +#define to64(x) strtoll(x, NULL, 10) +#define INT64_FMT PRId64 +#define INT64_X_FMT PRIx64 +#define SIZE_T_FMT "u" + +#define SOMAXCONN 8 + +const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); +char *inet_ntoa(struct in_addr in); +int inet_pton(int af, const char *src, void *dst); +int inet_aton(const char *cp, struct in_addr *inp); +in_addr_t inet_addr(const char *cp); + +#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK */ + +#endif /* CS_PLATFORM == CS_P_MBED */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_MBED_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/platform_nrf51.h" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ +#ifndef CS_COMMON_PLATFORMS_PLATFORM_NRF51_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_NRF51_H_ +#if CS_PLATFORM == CS_P_NRF51 + +#include +#include +#include +#include +#include +#include + +#define to64(x) strtoll(x, NULL, 10) + +#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL +#define MG_LWIP 1 +#define MG_ENABLE_IPV6 1 + +/* + * For ARM C Compiler, make lwip to export `struct timeval`; for other + * compilers, suppress it. + */ +#if !defined(__ARMCC_VERSION) +#define LWIP_TIMEVAL_PRIVATE 0 +#else +struct timeval; +int gettimeofday(struct timeval *tp, void *tzp); +#endif + +#define INT64_FMT PRId64 +#define SIZE_T_FMT "u" + +/* + * ARM C Compiler doesn't have strdup, so we provide it + */ +#define CS_ENABLE_STRDUP defined(__ARMCC_VERSION) + +#endif /* CS_PLATFORM == CS_P_NRF51 */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_NRF51_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/platform_nrf52.h" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ +#ifndef CS_COMMON_PLATFORMS_PLATFORM_NRF52_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_NRF52_H_ +#if CS_PLATFORM == CS_P_NRF52 + +#include +#include +#include +#include +#include +#include +#include +#include + +#define to64(x) strtoll(x, NULL, 10) + +#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL +#define MG_LWIP 1 +#define MG_ENABLE_IPV6 1 + +#if !defined(ENOSPC) +#define ENOSPC 28 /* No space left on device */ +#endif + +/* + * For ARM C Compiler, make lwip to export `struct timeval`; for other + * compilers, suppress it. + */ +#if !defined(__ARMCC_VERSION) +#define LWIP_TIMEVAL_PRIVATE 0 +#endif + +#define INT64_FMT PRId64 +#define SIZE_T_FMT "u" + +/* + * ARM C Compiler doesn't have strdup, so we provide it + */ +#define CS_ENABLE_STRDUP defined(__ARMCC_VERSION) + +#endif /* CS_PLATFORM == CS_P_NRF52 */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_NRF52_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/simplelink/cs_simplelink.h" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_SIMPLELINK_CS_SIMPLELINK_H_ +#define CS_COMMON_PLATFORMS_SIMPLELINK_CS_SIMPLELINK_H_ + +#if defined(MG_NET_IF) && MG_NET_IF == MG_NET_IF_SIMPLELINK + +/* If simplelink.h is already included, all bets are off. */ +#if !defined(__SIMPLELINK_H__) + +#include + +#ifndef __TI_COMPILER_VERSION__ +#undef __CONCAT +#undef FD_CLR +#undef FD_ISSET +#undef FD_SET +#undef FD_SETSIZE +#undef FD_ZERO +#undef fd_set +#endif + +#if CS_PLATFORM == CS_P_CC3220 +#include +#include +#include +#include +#else +/* We want to disable SL_INC_STD_BSD_API_NAMING, so we include user.h ourselves + * and undef it. */ +#define PROVISIONING_API_H_ +#include +#undef PROVISIONING_API_H_ +#undef SL_INC_STD_BSD_API_NAMING + +#include +#include +#endif /* CS_PLATFORM == CS_P_CC3220 */ + +/* Now define only the subset of the BSD API that we use. + * Notably, close(), read() and write() are not defined. */ +#define AF_INET SL_AF_INET + +#define socklen_t SlSocklen_t +#define sockaddr SlSockAddr_t +#define sockaddr_in SlSockAddrIn_t +#define in_addr SlInAddr_t + +#define SOCK_STREAM SL_SOCK_STREAM +#define SOCK_DGRAM SL_SOCK_DGRAM + +#define htonl sl_Htonl +#define ntohl sl_Ntohl +#define htons sl_Htons +#define ntohs sl_Ntohs + +#ifndef EACCES +#define EACCES SL_EACCES +#endif +#ifndef EAFNOSUPPORT +#define EAFNOSUPPORT SL_EAFNOSUPPORT +#endif +#ifndef EAGAIN +#define EAGAIN SL_EAGAIN +#endif +#ifndef EBADF +#define EBADF SL_EBADF +#endif +#ifndef EINVAL +#define EINVAL SL_EINVAL +#endif +#ifndef ENOMEM +#define ENOMEM SL_ENOMEM +#endif +#ifndef EWOULDBLOCK +#define EWOULDBLOCK SL_EWOULDBLOCK +#endif + +#define SOMAXCONN 8 + +#ifdef __cplusplus +extern "C" { +#endif + +const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); +char *inet_ntoa(struct in_addr in); +int inet_pton(int af, const char *src, void *dst); + +struct mg_mgr; +struct mg_connection; + +typedef void (*mg_init_cb)(struct mg_mgr *mgr); +bool mg_start_task(int priority, int stack_size, mg_init_cb mg_init); + +void mg_run_in_task(void (*cb)(struct mg_mgr *mgr, void *arg), void *cb_arg); + +int sl_fs_init(void); + +void sl_restart_cb(struct mg_mgr *mgr); + +int sl_set_ssl_opts(int sock, struct mg_connection *nc); + +#ifdef __cplusplus +} +#endif + +#endif /* !defined(__SIMPLELINK_H__) */ + +/* Compatibility with older versions of SimpleLink */ +#if SL_MAJOR_VERSION_NUM < 2 + +#define SL_ERROR_BSD_EAGAIN SL_EAGAIN +#define SL_ERROR_BSD_EALREADY SL_EALREADY +#define SL_ERROR_BSD_ENOPROTOOPT SL_ENOPROTOOPT +#define SL_ERROR_BSD_ESECDATEERROR SL_ESECDATEERROR +#define SL_ERROR_BSD_ESECSNOVERIFY SL_ESECSNOVERIFY +#define SL_ERROR_FS_FAILED_TO_ALLOCATE_MEM SL_FS_ERR_FAILED_TO_ALLOCATE_MEM +#define SL_ERROR_FS_FILE_HAS_NOT_BEEN_CLOSE_CORRECTLY \ + SL_FS_FILE_HAS_NOT_BEEN_CLOSE_CORRECTLY +#define SL_ERROR_FS_FILE_NAME_EXIST SL_FS_FILE_NAME_EXIST +#define SL_ERROR_FS_FILE_NOT_EXISTS SL_FS_ERR_FILE_NOT_EXISTS +#define SL_ERROR_FS_NO_AVAILABLE_NV_INDEX SL_FS_ERR_NO_AVAILABLE_NV_INDEX +#define SL_ERROR_FS_NOT_ENOUGH_STORAGE_SPACE SL_FS_ERR_NO_AVAILABLE_BLOCKS +#define SL_ERROR_FS_NOT_SUPPORTED SL_FS_ERR_NOT_SUPPORTED +#define SL_ERROR_FS_WRONG_FILE_NAME SL_FS_WRONG_FILE_NAME +#define SL_ERROR_FS_INVALID_HANDLE SL_FS_ERR_INVALID_HANDLE +#define SL_NETCFG_MAC_ADDRESS_GET SL_MAC_ADDRESS_GET +#define SL_SOCKET_FD_ZERO SL_FD_ZERO +#define SL_SOCKET_FD_SET SL_FD_SET +#define SL_SOCKET_FD_ISSET SL_FD_ISSET +#define SL_SO_SECURE_DOMAIN_NAME_VERIFICATION SO_SECURE_DOMAIN_NAME_VERIFICATION + +#define SL_FS_READ FS_MODE_OPEN_READ +#define SL_FS_WRITE FS_MODE_OPEN_WRITE + +#define SL_FI_FILE_SIZE(fi) ((fi).FileLen) +#define SL_FI_FILE_MAX_SIZE(fi) ((fi).AllocatedLen) + +#define SlDeviceVersion_t SlVersionFull +#define sl_DeviceGet sl_DevGet +#define SL_DEVICE_GENERAL SL_DEVICE_GENERAL_CONFIGURATION +#define SL_LEN_TYPE _u8 +#define SL_OPT_TYPE _u8 + +#else /* SL_MAJOR_VERSION_NUM >= 2 */ + +#define FS_MODE_OPEN_CREATE(max_size, flag) \ + (SL_FS_CREATE | SL_FS_CREATE_MAX_SIZE(max_size)) +#define SL_FI_FILE_SIZE(fi) ((fi).Len) +#define SL_FI_FILE_MAX_SIZE(fi) ((fi).MaxSize) + +#define SL_LEN_TYPE _u16 +#define SL_OPT_TYPE _u16 + +#endif /* SL_MAJOR_VERSION_NUM < 2 */ + +int slfs_open(const unsigned char *fname, uint32_t flags); + +#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK */ + +#endif /* CS_COMMON_PLATFORMS_SIMPLELINK_CS_SIMPLELINK_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/platform_wince.h" +#endif +#ifndef CS_COMMON_PLATFORMS_PLATFORM_WINCE_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_WINCE_H_ + +#if CS_PLATFORM == CS_P_WINCE + +/* + * MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015) + * MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013) + * MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012) + * MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010) + * MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008) + * MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005) + * MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio 2003) + * MSVC++ 7.0 _MSC_VER == 1300 + * MSVC++ 6.0 _MSC_VER == 1200 + * MSVC++ 5.0 _MSC_VER == 1100 + */ +#pragma warning(disable : 4127) /* FD_SET() emits warning, disable it */ +#pragma warning(disable : 4204) /* missing c99 support */ + +#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS +#define _WINSOCK_DEPRECATED_NO_WARNINGS 1 +#endif + +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include +#include +#include +#include + +#pragma comment(lib, "ws2.lib") /* Linking with WinCE winsock library */ + +#include +#include +#include + +#define strdup _strdup + +#ifndef EINPROGRESS +#define EINPROGRESS WSAEINPROGRESS +#endif + +#ifndef EWOULDBLOCK +#define EWOULDBLOCK WSAEWOULDBLOCK +#endif + +#ifndef EAGAIN +#define EAGAIN EWOULDBLOCK +#endif + +#ifndef __func__ +#define STRX(x) #x +#define STR(x) STRX(x) +#define __func__ __FILE__ ":" STR(__LINE__) +#endif + +#define snprintf _snprintf +#define fileno _fileno +#define vsnprintf _vsnprintf +#define sleep(x) Sleep((x) *1000) +#define to64(x) _atoi64(x) +#define rmdir _rmdir + +#if defined(_MSC_VER) && _MSC_VER >= 1400 +#define fseeko(x, y, z) _fseeki64((x), (y), (z)) +#else +#define fseeko(x, y, z) fseek((x), (y), (z)) +#endif + +typedef int socklen_t; + +#if _MSC_VER >= 1700 +#include +#else +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef int int32_t; +typedef unsigned int uint32_t; +typedef short int16_t; +typedef unsigned short uint16_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#endif + +typedef SOCKET sock_t; +typedef uint32_t in_addr_t; + +#ifndef UINT16_MAX +#define UINT16_MAX 65535 +#endif + +#ifndef UINT32_MAX +#define UINT32_MAX 4294967295 +#endif + +#ifndef pid_t +#define pid_t HANDLE +#endif + +#define INT64_FMT "I64d" +#define INT64_X_FMT "I64x" +/* TODO(alashkin): check if this is correct */ +#define SIZE_T_FMT "u" + +#define DIRSEP '\\' +#define CS_DEFINE_DIRENT + +#ifndef va_copy +#ifdef __va_copy +#define va_copy __va_copy +#else +#define va_copy(x, y) (x) = (y) +#endif +#endif + +#ifndef MG_MAX_HTTP_REQUEST_SIZE +#define MG_MAX_HTTP_REQUEST_SIZE 8192 +#endif + +#ifndef MG_MAX_HTTP_SEND_MBUF +#define MG_MAX_HTTP_SEND_MBUF 4096 +#endif + +#ifndef MG_MAX_HTTP_HEADERS +#define MG_MAX_HTTP_HEADERS 40 +#endif + +#ifndef CS_ENABLE_STDIO +#define CS_ENABLE_STDIO 1 +#endif + +#define abort() DebugBreak(); + +#ifndef BUFSIZ +#define BUFSIZ 4096 +#endif +/* + * Explicitly disabling MG_ENABLE_THREADS for WinCE + * because they are enabled for _WIN32 by default + */ +#ifndef MG_ENABLE_THREADS +#define MG_ENABLE_THREADS 0 +#endif + +#ifndef MG_ENABLE_FILESYSTEM +#define MG_ENABLE_FILESYSTEM 1 +#endif + +#ifndef MG_NET_IF +#define MG_NET_IF MG_NET_IF_SOCKET +#endif + +typedef struct _stati64 { + uint32_t st_mtime; + uint32_t st_size; + uint32_t st_mode; +} cs_stat_t; + +/* + * WinCE 6.0 has a lot of useful definitions in ATL (not windows.h) headers + * use #ifdefs to avoid conflicts + */ + +#ifndef ENOENT +#define ENOENT ERROR_PATH_NOT_FOUND +#endif + +#ifndef EACCES +#define EACCES ERROR_ACCESS_DENIED +#endif + +#ifndef ENOMEM +#define ENOMEM ERROR_NOT_ENOUGH_MEMORY +#endif + +#ifndef _UINTPTR_T_DEFINED +typedef unsigned int *uintptr_t; +#endif + +#define _S_IFREG 2 +#define _S_IFDIR 4 + +#ifndef S_ISDIR +#define S_ISDIR(x) (((x) &_S_IFDIR) != 0) +#endif + +#ifndef S_ISREG +#define S_ISREG(x) (((x) &_S_IFREG) != 0) +#endif + +int open(const char *filename, int oflag, int pmode); +int _wstati64(const wchar_t *path, cs_stat_t *st); +const char *strerror(); + +#endif /* CS_PLATFORM == CS_P_WINCE */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_WINCE_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/platform_nxp_lpc.h" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PLATFORM_NXP_LPC_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_NXP_LPC_H_ + +#if CS_PLATFORM == CS_P_NXP_LPC + +#include +#include +#include + +#define SIZE_T_FMT "u" +typedef struct stat cs_stat_t; +#define INT64_FMT "lld" +#define INT64_X_FMT "llx" +#define __cdecl + +#define MG_LWIP 1 + +#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL + +/* + * LPCXpress comes with 3 C library implementations: Newlib, NewlibNano and + *Redlib. + * See https://community.nxp.com/message/630860 for more details. + * + * Redlib is the default and lacks certain things, so we provide them. + */ +#ifdef __REDLIB_INTERFACE_VERSION__ + +/* Let LWIP define timeval for us. */ +#define LWIP_TIMEVAL_PRIVATE 1 + +#define va_copy(d, s) __builtin_va_copy(d, s) + +#define CS_ENABLE_TO64 1 +#define to64(x) cs_to64(x) + +#define CS_ENABLE_STRDUP 1 + +#else + +#include +#define LWIP_TIMEVAL_PRIVATE 0 +#define to64(x) strtoll(x, NULL, 10) + +#endif + +#endif /* CS_PLATFORM == CS_P_NXP_LPC */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_NXP_LPC_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/platform_nxp_kinetis.h" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PLATFORM_NXP_KINETIS_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_NXP_KINETIS_H_ + +#if CS_PLATFORM == CS_P_NXP_KINETIS + +#include +#include +#include +#include + +#define SIZE_T_FMT "u" +typedef struct stat cs_stat_t; +#define to64(x) strtoll(x, NULL, 10) +#define INT64_FMT "lld" +#define INT64_X_FMT "llx" +#define __cdecl + +#define MG_LWIP 1 + +#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL + +/* struct timeval is defined in sys/time.h. */ +#define LWIP_TIMEVAL_PRIVATE 0 + +#endif /* CS_PLATFORM == CS_P_NXP_KINETIS */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_NXP_KINETIS_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/platform_pic32.h" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PLATFORM_PIC32_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_PIC32_H_ + +#if CS_PLATFORM == CS_P_PIC32 + +#define MG_NET_IF MG_NET_IF_PIC32 + +#include +#include +#include +#include + +#include +#include + +#include + +typedef TCP_SOCKET sock_t; +#define to64(x) strtoll(x, NULL, 10) + +#define SIZE_T_FMT "lu" +#define INT64_FMT "lld" + +#ifndef CS_ENABLE_STDIO +#define CS_ENABLE_STDIO 1 +#endif + +char *inet_ntoa(struct in_addr in); + +#endif /* CS_PLATFORM == CS_P_PIC32 */ + +#endif /* CS_COMMON_PLATFORMS_PLATFORM_PIC32_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/platform_stm32.h" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PLATFORM_STM32_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_STM32_H_ +#if CS_PLATFORM == CS_P_STM32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define to64(x) strtoll(x, NULL, 10) +#define INT64_FMT PRId64 +#define SIZE_T_FMT "u" +typedef struct stat cs_stat_t; +#define DIRSEP '/' + +#ifndef CS_ENABLE_STDIO +#define CS_ENABLE_STDIO 1 +#endif + +#ifndef MG_ENABLE_FILESYSTEM +#define MG_ENABLE_FILESYSTEM 1 +#endif + +#endif /* CS_PLATFORM == CS_P_STM32 */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_STM32_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/platforms/lwip/mg_lwip.h" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_LWIP_MG_LWIP_H_ +#define CS_COMMON_PLATFORMS_LWIP_MG_LWIP_H_ + +#ifndef MG_LWIP +#define MG_LWIP 0 +#endif + +#if MG_LWIP + +/* + * When compiling for nRF5x chips with arm-none-eabi-gcc, it has BYTE_ORDER + * already defined, so in order to avoid warnings in lwip, we have to undefine + * it. + * + * TODO: Check if in the future versions of nRF5 SDK that changes. + * Current version of nRF51 SDK: 0.8.0 + * nRF5 SDK: 0.9.0 + */ +#if CS_PLATFORM == CS_P_NRF51 || CS_PLATFORM == CS_P_NRF52 +#undef BYTE_ORDER +#endif + +#include +#include +#include +#include +#include +#include + +#ifndef LWIP_PROVIDE_ERRNO +#include +#endif + +#if LWIP_SOCKET +#include +#else +/* We really need the definitions from sockets.h. */ +#undef LWIP_SOCKET +#define LWIP_SOCKET 1 +#include +#undef LWIP_SOCKET +#define LWIP_SOCKET 0 +#endif + +#define INVALID_SOCKET (-1) +#define SOMAXCONN 10 +typedef int sock_t; + +#if MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL +struct mg_mgr; +struct mg_connection; +uint32_t mg_lwip_get_poll_delay_ms(struct mg_mgr *mgr); +void mg_lwip_set_keepalive_params(struct mg_connection *nc, int idle, + int interval, int count); +#endif + +/* For older version of LWIP */ +#ifndef ipX_2_ip +#define ipX_2_ip(x) (x) +#endif + +#endif /* MG_LWIP */ + +#endif /* CS_COMMON_PLATFORMS_LWIP_MG_LWIP_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/cs_md5.h" +#endif +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_MD5_H_ +#define CS_COMMON_MD5_H_ + +/* Amalgamated: #include "common/platform.h" */ + +#ifndef CS_DISABLE_MD5 +#define CS_DISABLE_MD5 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef struct { + uint32_t buf[4]; + uint32_t bits[2]; + unsigned char in[64]; +} cs_md5_ctx; + +void cs_md5_init(cs_md5_ctx *c); +void cs_md5_update(cs_md5_ctx *c, const unsigned char *data, size_t len); +void cs_md5_final(unsigned char *md, cs_md5_ctx *c); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CS_COMMON_MD5_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/cs_sha1.h" +#endif +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_SHA1_H_ +#define CS_COMMON_SHA1_H_ + +#ifndef CS_DISABLE_SHA1 +#define CS_DISABLE_SHA1 0 +#endif + +#if !CS_DISABLE_SHA1 + +/* Amalgamated: #include "common/platform.h" */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef struct { + uint32_t state[5]; + uint32_t count[2]; + unsigned char buffer[64]; +} cs_sha1_ctx; + +void cs_sha1_init(cs_sha1_ctx *); +void cs_sha1_update(cs_sha1_ctx *, const unsigned char *data, uint32_t len); +void cs_sha1_final(unsigned char digest[20], cs_sha1_ctx *); +void cs_hmac_sha1(const unsigned char *key, size_t key_len, + const unsigned char *text, size_t text_len, + unsigned char out[20]); +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CS_DISABLE_SHA1 */ + +#endif /* CS_COMMON_SHA1_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/cs_time.h" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_CS_TIME_H_ +#define CS_COMMON_CS_TIME_H_ + +#include + +/* Amalgamated: #include "common/platform.h" */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Sub-second granularity time(). */ +double cs_time(void); + +/* + * Similar to (non-standard) timegm, converts broken-down time into the number + * of seconds since Unix Epoch. + */ +double cs_timegm(const struct tm *tm); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CS_COMMON_CS_TIME_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/mg_str.h" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_MG_STR_H_ +#define CS_COMMON_MG_STR_H_ + +#include + +/* Amalgamated: #include "common/platform.h" */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Describes chunk of memory */ +struct mg_str { + const char *p; /* Memory chunk pointer */ + size_t len; /* Memory chunk length */ +}; + +/* + * Helper function for creating mg_str struct from plain C string. + * `NULL` is allowed and becomes `{NULL, 0}`. + */ +struct mg_str mg_mk_str(const char *s); + +/* + * Like `mg_mk_str`, but takes string length explicitly. + */ +struct mg_str mg_mk_str_n(const char *s, size_t len); + +/* Macro for initializing mg_str. */ +#define MG_MK_STR(str_literal) \ + { str_literal, sizeof(str_literal) - 1 } +#define MG_NULL_STR \ + { NULL, 0 } + +/* + * Cross-platform version of `strcmp()` where where first string is + * specified by `struct mg_str`. + */ +int mg_vcmp(const struct mg_str *str2, const char *str1); + +/* + * Cross-platform version of `strncasecmp()` where first string is + * specified by `struct mg_str`. + */ +int mg_vcasecmp(const struct mg_str *str2, const char *str1); + +/* Creates a copy of s (heap-allocated). */ +struct mg_str mg_strdup(const struct mg_str s); + +/* + * Creates a copy of s (heap-allocated). + * Resulting string is NUL-terminated (but NUL is not included in len). + */ +struct mg_str mg_strdup_nul(const struct mg_str s); + +/* + * Locates character in a string. + */ +const char *mg_strchr(const struct mg_str s, int c); + +/* + * Compare two `mg_str`s; return value is the same as `strcmp`. + */ +int mg_strcmp(const struct mg_str str1, const struct mg_str str2); + +/* + * Like `mg_strcmp`, but compares at most `n` characters. + */ +int mg_strncmp(const struct mg_str str1, const struct mg_str str2, size_t n); + +/* + * Finds the first occurrence of a substring `needle` in the `haystack`. + */ +const char *mg_strstr(const struct mg_str haystack, const struct mg_str needle); + +#ifdef __cplusplus +} +#endif + +#endif /* CS_COMMON_MG_STR_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/mbuf.h" +#endif +/* + * Copyright (c) 2015 Cesanta Software Limited + * All rights reserved + */ + +/* + * === Memory Buffers + * + * Mbufs are mutable/growing memory buffers, like C++ strings. + * Mbuf can append data to the end of a buffer or insert data into arbitrary + * position in the middle of a buffer. The buffer grows automatically when + * needed. + */ + +#ifndef CS_COMMON_MBUF_H_ +#define CS_COMMON_MBUF_H_ + +#include +/* Amalgamated: #include "common/platform.h" */ + +#if defined(__cplusplus) +extern "C" { +#endif + +#ifndef MBUF_SIZE_MULTIPLIER +#define MBUF_SIZE_MULTIPLIER 1.5 +#endif + +/* Memory buffer descriptor */ +struct mbuf { + char *buf; /* Buffer pointer */ + size_t len; /* Data length. Data is located between offset 0 and len. */ + size_t size; /* Buffer size allocated by realloc(1). Must be >= len */ +}; + +/* + * Initialises an Mbuf. + * `initial_capacity` specifies the initial capacity of the mbuf. + */ +void mbuf_init(struct mbuf *, size_t initial_capacity); + +/* Frees the space allocated for the mbuffer and resets the mbuf structure. */ +void mbuf_free(struct mbuf *); + +/* + * Appends data to the Mbuf. + * + * Returns the number of bytes appended or 0 if out of memory. + */ +size_t mbuf_append(struct mbuf *, const void *data, size_t data_size); + +/* + * Inserts data at a specified offset in the Mbuf. + * + * Existing data will be shifted forwards and the buffer will + * be grown if necessary. + * Returns the number of bytes inserted. + */ +size_t mbuf_insert(struct mbuf *, size_t, const void *, size_t); + +/* Removes `data_size` bytes from the beginning of the buffer. */ +void mbuf_remove(struct mbuf *, size_t data_size); + +/* + * Resizes an Mbuf. + * + * If `new_size` is smaller than buffer's `len`, the + * resize is not performed. + */ +void mbuf_resize(struct mbuf *, size_t new_size); + +/* Shrinks an Mbuf by resizing its `size` to `len`. */ +void mbuf_trim(struct mbuf *); + +#if defined(__cplusplus) +} +#endif /* __cplusplus */ + +#endif /* CS_COMMON_MBUF_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/cs_base64.h" +#endif +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_CS_BASE64_H_ +#define CS_COMMON_CS_BASE64_H_ + +#ifndef DISABLE_BASE64 +#define DISABLE_BASE64 0 +#endif + +#if !DISABLE_BASE64 + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*cs_base64_putc_t)(char, void *); + +struct cs_base64_ctx { + /* cannot call it putc because it's a macro on some environments */ + cs_base64_putc_t b64_putc; + unsigned char chunk[3]; + int chunk_size; + void *user_data; +}; + +void cs_base64_init(struct cs_base64_ctx *ctx, cs_base64_putc_t putc, + void *user_data); +void cs_base64_update(struct cs_base64_ctx *ctx, const char *str, size_t len); +void cs_base64_finish(struct cs_base64_ctx *ctx); + +void cs_base64_encode(const unsigned char *src, int src_len, char *dst); +void cs_fprint_base64(FILE *f, const unsigned char *src, int src_len); +int cs_base64_decode(const unsigned char *s, int len, char *dst, int *dec_len); + +#ifdef __cplusplus +} +#endif + +#endif /* DISABLE_BASE64 */ + +#endif /* CS_COMMON_CS_BASE64_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/str_util.h" +#endif +/* + * Copyright (c) 2015 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_STR_UTIL_H_ +#define CS_COMMON_STR_UTIL_H_ + +#include +#include + +/* Amalgamated: #include "common/mg_str.h" */ +/* Amalgamated: #include "common/platform.h" */ + +#ifndef CS_ENABLE_STRDUP +#define CS_ENABLE_STRDUP 0 +#endif + +#ifndef CS_ENABLE_TO64 +#define CS_ENABLE_TO64 0 +#endif + +/* + * Expands to a string representation of its argument: e.g. + * `CS_STRINGIFY_LIT(5) expands to "5"` + */ +#define CS_STRINGIFY_LIT(x) #x + +/* + * Expands to a string representation of its argument, which is allowed + * to be a macro: e.g. + * + * #define FOO 123 + * CS_STRINGIFY_MACRO(FOO) + * + * expands to 123. + */ +#define CS_STRINGIFY_MACRO(x) CS_STRINGIFY_LIT(x) + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Equivalent of standard `strnlen()`. + */ +size_t c_strnlen(const char *s, size_t maxlen); + +/* + * Equivalent of standard `snprintf()`. + */ +int c_snprintf(char *buf, size_t buf_size, const char *format, ...); + +/* + * Equivalent of standard `vsnprintf()`. + */ +int c_vsnprintf(char *buf, size_t buf_size, const char *format, va_list ap); + +/* + * Find the first occurrence of find in s, where the search is limited to the + * first slen characters of s. + */ +const char *c_strnstr(const char *s, const char *find, size_t slen); + +/* + * Stringify binary data. Output buffer size must be 2 * size_of_input + 1 + * because each byte of input takes 2 bytes in string representation + * plus 1 byte for the terminating \0 character. + */ +void cs_to_hex(char *to, const unsigned char *p, size_t len); + +/* + * Convert stringified binary data back to binary. + * Does the reverse of `cs_to_hex()`. + */ +void cs_from_hex(char *to, const char *p, size_t len); + +#if CS_ENABLE_STRDUP +/* + * Equivalent of standard `strdup()`, defined if only `CS_ENABLE_STRDUP` is 1. + */ +char *strdup(const char *src); +#endif + +#if CS_ENABLE_TO64 +#include +/* + * Simple string -> int64 conversion routine. + */ +int64_t cs_to64(const char *s); +#endif + +/* + * Cross-platform version of `strncasecmp()`. + */ +int mg_ncasecmp(const char *s1, const char *s2, size_t len); + +/* + * Cross-platform version of `strcasecmp()`. + */ +int mg_casecmp(const char *s1, const char *s2); + +/* + * Prints message to the buffer. If the buffer is large enough to hold the + * message, it returns buffer. If buffer is to small, it allocates a large + * enough buffer on heap and returns allocated buffer. + * This is a supposed use case: + * + * ```c + * char buf[5], *p = buf; + * mg_avprintf(&p, sizeof(buf), "%s", "hi there"); + * use_p_somehow(p); + * if (p != buf) { + * free(p); + * } + * ``` + * + * The purpose of this is to avoid malloc-ing if generated strings are small. + */ +int mg_asprintf(char **buf, size_t size, const char *fmt, ...); + +/* Same as mg_asprintf, but takes varargs list. */ +int mg_avprintf(char **buf, size_t size, const char *fmt, va_list ap); + +/* + * A helper function for traversing a comma separated list of values. + * It returns a list pointer shifted to the next value or NULL if the end + * of the list found. + * The value is stored in a val vector. If the value has a form "x=y", then + * eq_val vector is initialised to point to the "y" part, and val vector length + * is adjusted to point only to "x". + * If the list is just a comma separated list of entries, like "aa,bb,cc" then + * `eq_val` will contain zero-length string. + * + * The purpose of this function is to parse comma separated string without + * any copying/memory allocation. + */ +const char *mg_next_comma_list_entry(const char *list, struct mg_str *val, + struct mg_str *eq_val); + +/* + * Like `mg_next_comma_list_entry()`, but takes `list` as `struct mg_str`. + */ +struct mg_str mg_next_comma_list_entry_n(struct mg_str list, struct mg_str *val, + struct mg_str *eq_val); + +/* + * Matches 0-terminated string (mg_match_prefix) or string with given length + * mg_match_prefix_n against a glob pattern. Glob syntax: + * ``` + * - * matches zero or more characters until a slash character / + * - ** matches zero or more characters + * - ? Matches exactly one character which is not a slash / + * - | or , divides alternative patterns + * - any other character matches itself + * ``` + * Match is case-insensitive. Return number of bytes matched. + * Examples: + * ``` + * mg_match_prefix("a*f", len, "abcdefgh") == 6 + * mg_match_prefix("a*f", len, "abcdexgh") == 0 + * mg_match_prefix("a*f|de*,xy", len, "defgh") == 5 + * mg_match_prefix("?*", len, "abc") == 3 + * mg_match_prefix("?*", len, "") == 0 + * ``` + */ +size_t mg_match_prefix(const char *pattern, int pattern_len, const char *str); + +/* + * Like `mg_match_prefix()`, but takes `pattern` and `str` as `struct mg_str`. + */ +size_t mg_match_prefix_n(const struct mg_str pattern, const struct mg_str str); + +#ifdef __cplusplus +} +#endif + +#endif /* CS_COMMON_STR_UTIL_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "common/queue.h" +#endif +/* clang-format off */ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. 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. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + * $FreeBSD$ + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +/* + * This file defines four types of data structures: singly-linked lists, + * singly-linked tail queues, lists and tail queues. + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction. Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A singly-linked tail queue is headed by a pair of pointers, one to the + * head of the list and the other to the tail of the list. The elements are + * singly linked for minimum space and pointer manipulation overhead at the + * expense of O(n) removal for arbitrary elements. New elements can be added + * to the list after an existing element, at the head of the list, or at the + * end of the list. Elements being removed from the head of the tail queue + * should use the explicit macro for this purpose for optimum efficiency. + * A singly-linked tail queue may only be traversed in the forward direction. + * Singly-linked tail queues are ideal for applications with large datasets + * and few or no removals or for implementing a FIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may be traversed in either direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * For details on the use of these macros, see the queue(3) manual page. + * + * + * SLIST LIST STAILQ TAILQ + * _HEAD + + + + + * _CLASS_HEAD + + + + + * _HEAD_INITIALIZER + + + + + * _ENTRY + + + + + * _CLASS_ENTRY + + + + + * _INIT + + + + + * _EMPTY + + + + + * _FIRST + + + + + * _NEXT + + + + + * _PREV - + - + + * _LAST - - + + + * _FOREACH + + + + + * _FOREACH_FROM + + + + + * _FOREACH_SAFE + + + + + * _FOREACH_FROM_SAFE + + + + + * _FOREACH_REVERSE - - - + + * _FOREACH_REVERSE_FROM - - - + + * _FOREACH_REVERSE_SAFE - - - + + * _FOREACH_REVERSE_FROM_SAFE - - - + + * _INSERT_HEAD + + + + + * _INSERT_BEFORE - + - + + * _INSERT_AFTER + + + + + * _INSERT_TAIL - - + + + * _CONCAT - - + + + * _REMOVE_AFTER + - + - + * _REMOVE_HEAD + - + - + * _REMOVE + + + + + * _SWAP + + + + + * + */ +#ifdef QUEUE_MACRO_DEBUG +/* Store the last 2 places the queue element or head was altered */ +struct qm_trace { + unsigned long lastline; + unsigned long prevline; + const char *lastfile; + const char *prevfile; +}; + +#define TRACEBUF struct qm_trace trace; +#define TRACEBUF_INITIALIZER { __LINE__, 0, __FILE__, NULL } , +#define TRASHIT(x) do {(x) = (void *)-1;} while (0) +#define QMD_SAVELINK(name, link) void **name = (void *)&(link) + +#define QMD_TRACE_HEAD(head) do { \ + (head)->trace.prevline = (head)->trace.lastline; \ + (head)->trace.prevfile = (head)->trace.lastfile; \ + (head)->trace.lastline = __LINE__; \ + (head)->trace.lastfile = __FILE__; \ +} while (0) + +#define QMD_TRACE_ELEM(elem) do { \ + (elem)->trace.prevline = (elem)->trace.lastline; \ + (elem)->trace.prevfile = (elem)->trace.lastfile; \ + (elem)->trace.lastline = __LINE__; \ + (elem)->trace.lastfile = __FILE__; \ +} while (0) + +#else +#define QMD_TRACE_ELEM(elem) +#define QMD_TRACE_HEAD(head) +#define QMD_SAVELINK(name, link) +#define TRACEBUF +#define TRACEBUF_INITIALIZER +#define TRASHIT(x) +#endif /* QUEUE_MACRO_DEBUG */ + +#ifdef __cplusplus +/* + * In C++ there can be structure lists and class lists: + */ +#define QUEUE_TYPEOF(type) type +#else +#define QUEUE_TYPEOF(type) struct type +#endif + +/* + * Singly-linked List declarations. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_CLASS_HEAD(name, type) \ +struct name { \ + class type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +#define SLIST_CLASS_ENTRY(type) \ +struct { \ + class type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List functions. + */ +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) + +#define SLIST_FIRST(head) ((head)->slh_first) + +#define SLIST_FOREACH(var, head, field) \ + for ((var) = SLIST_FIRST((head)); \ + (var); \ + (var) = SLIST_NEXT((var), field)) + +#define SLIST_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \ + (var); \ + (var) = SLIST_NEXT((var), field)) + +#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SLIST_FIRST((head)); \ + (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define SLIST_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \ + (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \ + for ((varp) = &SLIST_FIRST((head)); \ + ((var) = *(varp)) != NULL; \ + (varp) = &SLIST_NEXT((var), field)) + +#define SLIST_INIT(head) do { \ + SLIST_FIRST((head)) = NULL; \ +} while (0) + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \ + SLIST_NEXT((slistelm), field) = (elm); \ +} while (0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \ + SLIST_FIRST((head)) = (elm); \ +} while (0) + +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.sle_next); \ + if (SLIST_FIRST((head)) == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + QUEUE_TYPEOF(type) *curelm = SLIST_FIRST(head); \ + while (SLIST_NEXT(curelm, field) != (elm)) \ + curelm = SLIST_NEXT(curelm, field); \ + SLIST_REMOVE_AFTER(curelm, field); \ + } \ + TRASHIT(*oldnext); \ +} while (0) + +#define SLIST_REMOVE_AFTER(elm, field) do { \ + SLIST_NEXT(elm, field) = \ + SLIST_NEXT(SLIST_NEXT(elm, field), field); \ +} while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \ +} while (0) + +#define SLIST_SWAP(head1, head2, type) do { \ + QUEUE_TYPEOF(type) *swap_first = SLIST_FIRST(head1); \ + SLIST_FIRST(head1) = SLIST_FIRST(head2); \ + SLIST_FIRST(head2) = swap_first; \ +} while (0) + +/* + * Singly-linked Tail queue declarations. + */ +#define STAILQ_HEAD(name, type) \ +struct name { \ + struct type *stqh_first;/* first element */ \ + struct type **stqh_last;/* addr of last next element */ \ +} + +#define STAILQ_CLASS_HEAD(name, type) \ +struct name { \ + class type *stqh_first; /* first element */ \ + class type **stqh_last; /* addr of last next element */ \ +} + +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#define STAILQ_ENTRY(type) \ +struct { \ + struct type *stqe_next; /* next element */ \ +} + +#define STAILQ_CLASS_ENTRY(type) \ +struct { \ + class type *stqe_next; /* next element */ \ +} + +/* + * Singly-linked Tail queue functions. + */ +#define STAILQ_CONCAT(head1, head2) do { \ + if (!STAILQ_EMPTY((head2))) { \ + *(head1)->stqh_last = (head2)->stqh_first; \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_INIT((head2)); \ + } \ +} while (0) + +#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) + +#define STAILQ_FIRST(head) ((head)->stqh_first) + +#define STAILQ_FOREACH(var, head, field) \ + for((var) = STAILQ_FIRST((head)); \ + (var); \ + (var) = STAILQ_NEXT((var), field)) + +#define STAILQ_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \ + (var); \ + (var) = STAILQ_NEXT((var), field)) + +#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = STAILQ_FIRST((head)); \ + (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define STAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \ + (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define STAILQ_INIT(head) do { \ + STAILQ_FIRST((head)) = NULL; \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_NEXT((tqelm), field) = (elm); \ +} while (0) + +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_FIRST((head)) = (elm); \ +} while (0) + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + STAILQ_NEXT((elm), field) = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ +} while (0) + +#define STAILQ_LAST(head, type, field) \ + (STAILQ_EMPTY((head)) ? NULL : \ + __containerof((head)->stqh_last, \ + QUEUE_TYPEOF(type), field.stqe_next)) + +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) + +#define STAILQ_REMOVE(head, elm, type, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.stqe_next); \ + if (STAILQ_FIRST((head)) == (elm)) { \ + STAILQ_REMOVE_HEAD((head), field); \ + } \ + else { \ + QUEUE_TYPEOF(type) *curelm = STAILQ_FIRST(head); \ + while (STAILQ_NEXT(curelm, field) != (elm)) \ + curelm = STAILQ_NEXT(curelm, field); \ + STAILQ_REMOVE_AFTER(head, curelm, field); \ + } \ + TRASHIT(*oldnext); \ +} while (0) + +#define STAILQ_REMOVE_AFTER(head, elm, field) do { \ + if ((STAILQ_NEXT(elm, field) = \ + STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ +} while (0) + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if ((STAILQ_FIRST((head)) = \ + STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_SWAP(head1, head2, type) do { \ + QUEUE_TYPEOF(type) *swap_first = STAILQ_FIRST(head1); \ + QUEUE_TYPEOF(type) **swap_last = (head1)->stqh_last; \ + STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_FIRST(head2) = swap_first; \ + (head2)->stqh_last = swap_last; \ + if (STAILQ_EMPTY(head1)) \ + (head1)->stqh_last = &STAILQ_FIRST(head1); \ + if (STAILQ_EMPTY(head2)) \ + (head2)->stqh_last = &STAILQ_FIRST(head2); \ +} while (0) + + +/* + * List declarations. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_CLASS_HEAD(name, type) \ +struct name { \ + class type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +#define LIST_CLASS_ENTRY(type) \ +struct { \ + class type *le_next; /* next element */ \ + class type **le_prev; /* address of previous next element */ \ +} + +/* + * List functions. + */ + +#if (defined(_KERNEL) && defined(INVARIANTS)) +#define QMD_LIST_CHECK_HEAD(head, field) do { \ + if (LIST_FIRST((head)) != NULL && \ + LIST_FIRST((head))->field.le_prev != \ + &LIST_FIRST((head))) \ + panic("Bad list head %p first->prev != head", (head)); \ +} while (0) + +#define QMD_LIST_CHECK_NEXT(elm, field) do { \ + if (LIST_NEXT((elm), field) != NULL && \ + LIST_NEXT((elm), field)->field.le_prev != \ + &((elm)->field.le_next)) \ + panic("Bad link elm %p next->prev != elm", (elm)); \ +} while (0) + +#define QMD_LIST_CHECK_PREV(elm, field) do { \ + if (*(elm)->field.le_prev != (elm)) \ + panic("Bad link elm %p prev->next != elm", (elm)); \ +} while (0) +#else +#define QMD_LIST_CHECK_HEAD(head, field) +#define QMD_LIST_CHECK_NEXT(elm, field) +#define QMD_LIST_CHECK_PREV(elm, field) +#endif /* (_KERNEL && INVARIANTS) */ + +#define LIST_EMPTY(head) ((head)->lh_first == NULL) + +#define LIST_FIRST(head) ((head)->lh_first) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = LIST_FIRST((head)); \ + (var); \ + (var) = LIST_NEXT((var), field)) + +#define LIST_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : LIST_FIRST((head))); \ + (var); \ + (var) = LIST_NEXT((var), field)) + +#define LIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = LIST_FIRST((head)); \ + (var) && ((tvar) = LIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define LIST_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : LIST_FIRST((head))); \ + (var) && ((tvar) = LIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define LIST_INIT(head) do { \ + LIST_FIRST((head)) = NULL; \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + QMD_LIST_CHECK_NEXT(listelm, field); \ + if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\ + LIST_NEXT((listelm), field)->field.le_prev = \ + &LIST_NEXT((elm), field); \ + LIST_NEXT((listelm), field) = (elm); \ + (elm)->field.le_prev = &LIST_NEXT((listelm), field); \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + QMD_LIST_CHECK_PREV(listelm, field); \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + LIST_NEXT((elm), field) = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &LIST_NEXT((elm), field); \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + QMD_LIST_CHECK_HEAD((head), field); \ + if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ + LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\ + LIST_FIRST((head)) = (elm); \ + (elm)->field.le_prev = &LIST_FIRST((head)); \ +} while (0) + +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_PREV(elm, head, type, field) \ + ((elm)->field.le_prev == &LIST_FIRST((head)) ? NULL : \ + __containerof((elm)->field.le_prev, \ + QUEUE_TYPEOF(type), field.le_next)) + +#define LIST_REMOVE(elm, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.le_next); \ + QMD_SAVELINK(oldprev, (elm)->field.le_prev); \ + QMD_LIST_CHECK_NEXT(elm, field); \ + QMD_LIST_CHECK_PREV(elm, field); \ + if (LIST_NEXT((elm), field) != NULL) \ + LIST_NEXT((elm), field)->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = LIST_NEXT((elm), field); \ + TRASHIT(*oldnext); \ + TRASHIT(*oldprev); \ +} while (0) + +#define LIST_SWAP(head1, head2, type, field) do { \ + QUEUE_TYPEOF(type) *swap_tmp = LIST_FIRST(head1); \ + LIST_FIRST((head1)) = LIST_FIRST((head2)); \ + LIST_FIRST((head2)) = swap_tmp; \ + if ((swap_tmp = LIST_FIRST((head1))) != NULL) \ + swap_tmp->field.le_prev = &LIST_FIRST((head1)); \ + if ((swap_tmp = LIST_FIRST((head2))) != NULL) \ + swap_tmp->field.le_prev = &LIST_FIRST((head2)); \ +} while (0) + +/* + * Tail queue declarations. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ + TRACEBUF \ +} + +#define TAILQ_CLASS_HEAD(name, type) \ +struct name { \ + class type *tqh_first; /* first element */ \ + class type **tqh_last; /* addr of last next element */ \ + TRACEBUF \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first, TRACEBUF_INITIALIZER } + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ + TRACEBUF \ +} + +#define TAILQ_CLASS_ENTRY(type) \ +struct { \ + class type *tqe_next; /* next element */ \ + class type **tqe_prev; /* address of previous next element */ \ + TRACEBUF \ +} + +/* + * Tail queue functions. + */ +#if (defined(_KERNEL) && defined(INVARIANTS)) +#define QMD_TAILQ_CHECK_HEAD(head, field) do { \ + if (!TAILQ_EMPTY(head) && \ + TAILQ_FIRST((head))->field.tqe_prev != \ + &TAILQ_FIRST((head))) \ + panic("Bad tailq head %p first->prev != head", (head)); \ +} while (0) + +#define QMD_TAILQ_CHECK_TAIL(head, field) do { \ + if (*(head)->tqh_last != NULL) \ + panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); \ +} while (0) + +#define QMD_TAILQ_CHECK_NEXT(elm, field) do { \ + if (TAILQ_NEXT((elm), field) != NULL && \ + TAILQ_NEXT((elm), field)->field.tqe_prev != \ + &((elm)->field.tqe_next)) \ + panic("Bad link elm %p next->prev != elm", (elm)); \ +} while (0) + +#define QMD_TAILQ_CHECK_PREV(elm, field) do { \ + if (*(elm)->field.tqe_prev != (elm)) \ + panic("Bad link elm %p prev->next != elm", (elm)); \ +} while (0) +#else +#define QMD_TAILQ_CHECK_HEAD(head, field) +#define QMD_TAILQ_CHECK_TAIL(head, headname) +#define QMD_TAILQ_CHECK_NEXT(elm, field) +#define QMD_TAILQ_CHECK_PREV(elm, field) +#endif /* (_KERNEL && INVARIANTS) */ + +#define TAILQ_CONCAT(head1, head2, field) do { \ + if (!TAILQ_EMPTY(head2)) { \ + *(head1)->tqh_last = (head2)->tqh_first; \ + (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ + (head1)->tqh_last = (head2)->tqh_last; \ + TAILQ_INIT((head2)); \ + QMD_TRACE_HEAD(head1); \ + QMD_TRACE_HEAD(head2); \ + } \ +} while (0) + +#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) + +#define TAILQ_FIRST(head) ((head)->tqh_first) + +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = TAILQ_FIRST((head)); \ + (var); \ + (var) = TAILQ_NEXT((var), field)) + +#define TAILQ_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \ + (var); \ + (var) = TAILQ_NEXT((var), field)) + +#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = TAILQ_FIRST((head)); \ + (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \ + (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var); \ + (var) = TAILQ_PREV((var), headname, field)) + +#define TAILQ_FOREACH_REVERSE_FROM(var, head, headname, field) \ + for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \ + (var); \ + (var) = TAILQ_PREV((var), headname, field)) + +#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ + (var) = (tvar)) + +#define TAILQ_FOREACH_REVERSE_FROM_SAFE(var, head, headname, field, tvar) \ + for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \ + (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ + (var) = (tvar)) + +#define TAILQ_INIT(head) do { \ + TAILQ_FIRST((head)) = NULL; \ + (head)->tqh_last = &TAILQ_FIRST((head)); \ + QMD_TRACE_HEAD(head); \ +} while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + QMD_TAILQ_CHECK_NEXT(listelm, field); \ + if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + &TAILQ_NEXT((elm), field); \ + else { \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_HEAD(head); \ + } \ + TAILQ_NEXT((listelm), field) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \ + QMD_TRACE_ELEM(&(elm)->field); \ + QMD_TRACE_ELEM(&(listelm)->field); \ +} while (0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + QMD_TAILQ_CHECK_PREV(listelm, field); \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + TAILQ_NEXT((elm), field) = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_ELEM(&(elm)->field); \ + QMD_TRACE_ELEM(&(listelm)->field); \ +} while (0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + QMD_TAILQ_CHECK_HEAD(head, field); \ + if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \ + TAILQ_FIRST((head))->field.tqe_prev = \ + &TAILQ_NEXT((elm), field); \ + else \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + TAILQ_FIRST((head)) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \ + QMD_TRACE_HEAD(head); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + QMD_TAILQ_CHECK_TAIL(head, field); \ + TAILQ_NEXT((elm), field) = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_HEAD(head); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) + +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) + +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) + +#define TAILQ_REMOVE(head, elm, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.tqe_next); \ + QMD_SAVELINK(oldprev, (elm)->field.tqe_prev); \ + QMD_TAILQ_CHECK_NEXT(elm, field); \ + QMD_TAILQ_CHECK_PREV(elm, field); \ + if ((TAILQ_NEXT((elm), field)) != NULL) \ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else { \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + QMD_TRACE_HEAD(head); \ + } \ + *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \ + TRASHIT(*oldnext); \ + TRASHIT(*oldprev); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + +#define TAILQ_SWAP(head1, head2, type, field) do { \ + QUEUE_TYPEOF(type) *swap_first = (head1)->tqh_first; \ + QUEUE_TYPEOF(type) **swap_last = (head1)->tqh_last; \ + (head1)->tqh_first = (head2)->tqh_first; \ + (head1)->tqh_last = (head2)->tqh_last; \ + (head2)->tqh_first = swap_first; \ + (head2)->tqh_last = swap_last; \ + if ((swap_first = (head1)->tqh_first) != NULL) \ + swap_first->field.tqe_prev = &(head1)->tqh_first; \ + else \ + (head1)->tqh_last = &(head1)->tqh_first; \ + if ((swap_first = (head2)->tqh_first) != NULL) \ + swap_first->field.tqe_prev = &(head2)->tqh_first; \ + else \ + (head2)->tqh_last = &(head2)->tqh_first; \ +} while (0) + +#endif /* !_SYS_QUEUE_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_features.h" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_MONGOOSE_SRC_FEATURES_H_ +#define CS_MONGOOSE_SRC_FEATURES_H_ + +#ifndef MG_DISABLE_HTTP_DIGEST_AUTH +#define MG_DISABLE_HTTP_DIGEST_AUTH 0 +#endif + +#ifndef MG_DISABLE_HTTP_KEEP_ALIVE +#define MG_DISABLE_HTTP_KEEP_ALIVE 0 +#endif + +#ifndef MG_DISABLE_PFS +#define MG_DISABLE_PFS 0 +#endif + +#ifndef MG_DISABLE_WS_RANDOM_MASK +#define MG_DISABLE_WS_RANDOM_MASK 0 +#endif + +#ifndef MG_ENABLE_ASYNC_RESOLVER +#define MG_ENABLE_ASYNC_RESOLVER 1 +#endif + +#ifndef MG_ENABLE_BROADCAST +#define MG_ENABLE_BROADCAST 0 +#endif + +#ifndef MG_ENABLE_COAP +#define MG_ENABLE_COAP 0 +#endif + +#ifndef MG_ENABLE_DEBUG +#define MG_ENABLE_DEBUG 0 +#endif + +#ifndef MG_ENABLE_DIRECTORY_LISTING +#define MG_ENABLE_DIRECTORY_LISTING 0 +#endif + +#ifndef MG_ENABLE_DNS +#define MG_ENABLE_DNS 1 +#endif + +#ifndef MG_ENABLE_DNS_SERVER +#define MG_ENABLE_DNS_SERVER 0 +#endif + +#ifndef MG_ENABLE_FAKE_DAVLOCK +#define MG_ENABLE_FAKE_DAVLOCK 0 +#endif + +#ifndef MG_ENABLE_FILESYSTEM +#define MG_ENABLE_FILESYSTEM 0 +#endif + +#ifndef MG_ENABLE_GETADDRINFO +#define MG_ENABLE_GETADDRINFO 0 +#endif + +#ifndef MG_ENABLE_HEXDUMP +#define MG_ENABLE_HEXDUMP CS_ENABLE_STDIO +#endif + +#ifndef MG_ENABLE_HTTP +#define MG_ENABLE_HTTP 1 +#endif + +#ifndef MG_ENABLE_HTTP_CGI +#define MG_ENABLE_HTTP_CGI 0 +#endif + +#ifndef MG_ENABLE_HTTP_SSI +#define MG_ENABLE_HTTP_SSI MG_ENABLE_FILESYSTEM +#endif + +#ifndef MG_ENABLE_HTTP_SSI_EXEC +#define MG_ENABLE_HTTP_SSI_EXEC 0 +#endif + +#ifndef MG_ENABLE_HTTP_STREAMING_MULTIPART +#define MG_ENABLE_HTTP_STREAMING_MULTIPART 0 +#endif + +#ifndef MG_ENABLE_HTTP_WEBDAV +#define MG_ENABLE_HTTP_WEBDAV 0 +#endif + +#ifndef MG_ENABLE_HTTP_WEBSOCKET +#define MG_ENABLE_HTTP_WEBSOCKET MG_ENABLE_HTTP +#endif + +#ifndef MG_ENABLE_IPV6 +#define MG_ENABLE_IPV6 0 +#endif + +#ifndef MG_ENABLE_MQTT +#define MG_ENABLE_MQTT 1 +#endif + +#ifndef MG_ENABLE_SOCKS +#define MG_ENABLE_SOCKS 0 +#endif + +#ifndef MG_ENABLE_MQTT_BROKER +#define MG_ENABLE_MQTT_BROKER 0 +#endif + +#ifndef MG_ENABLE_SSL +#define MG_ENABLE_SSL 0 +#endif + +#ifndef MG_ENABLE_SYNC_RESOLVER +#define MG_ENABLE_SYNC_RESOLVER 0 +#endif + +#ifndef MG_ENABLE_STDIO +#define MG_ENABLE_STDIO CS_ENABLE_STDIO +#endif + +#ifndef MG_NET_IF +#define MG_NET_IF MG_NET_IF_SOCKET +#endif + +#ifndef MG_SSL_IF +#define MG_SSL_IF MG_SSL_IF_OPENSSL +#endif + +#ifndef MG_ENABLE_THREADS /* ifdef-ok */ +#ifdef _WIN32 +#define MG_ENABLE_THREADS 1 +#else +#define MG_ENABLE_THREADS 0 +#endif +#endif + +#if MG_ENABLE_DEBUG && !defined(CS_ENABLE_DEBUG) +#define CS_ENABLE_DEBUG 1 +#endif + +/* MQTT broker requires MQTT */ +#if MG_ENABLE_MQTT_BROKER && !MG_ENABLE_MQTT +#undef MG_ENABLE_MQTT +#define MG_ENABLE_MQTT 1 +#endif + +#ifndef MG_ENABLE_HTTP_URL_REWRITES +#define MG_ENABLE_HTTP_URL_REWRITES \ + (CS_PLATFORM == CS_P_WINDOWS || CS_PLATFORM == CS_P_UNIX) +#endif + +#ifndef MG_ENABLE_SNTP +#define MG_ENABLE_SNTP 0 +#endif + +#ifndef MG_ENABLE_EXTRA_ERRORS_DESC +#define MG_ENABLE_EXTRA_ERRORS_DESC 0 +#endif + +#ifndef MG_ENABLE_CALLBACK_USERDATA +#define MG_ENABLE_CALLBACK_USERDATA 0 +#endif + +#if MG_ENABLE_CALLBACK_USERDATA +#define MG_UD_ARG(ud) , ud +#define MG_CB(cb, ud) cb, ud +#else +#define MG_UD_ARG(ud) +#define MG_CB(cb, ud) cb +#endif + +#endif /* CS_MONGOOSE_SRC_FEATURES_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_net_if.h" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_MONGOOSE_SRC_NET_IF_H_ +#define CS_MONGOOSE_SRC_NET_IF_H_ + +/* Amalgamated: #include "common/platform.h" */ + +/* + * Internal async networking core interface. + * Consists of calls made by the core, which should not block, + * and callbacks back into the core ("..._cb"). + * Callbacks may (will) cause methods to be invoked from within, + * but methods are not allowed to invoke callbacks inline. + * + * Implementation must ensure that only one callback is invoked at any time. + */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define MG_MAIN_IFACE 0 + +struct mg_mgr; +struct mg_connection; +union socket_address; + +struct mg_iface_vtable; + +struct mg_iface { + struct mg_mgr *mgr; + void *data; /* Implementation-specific data */ + const struct mg_iface_vtable *vtable; +}; + +struct mg_iface_vtable { + void (*init)(struct mg_iface *iface); + void (*free)(struct mg_iface *iface); + void (*add_conn)(struct mg_connection *nc); + void (*remove_conn)(struct mg_connection *nc); + time_t (*poll)(struct mg_iface *iface, int timeout_ms); + + /* Set up a listening TCP socket on a given address. rv = 0 -> ok. */ + int (*listen_tcp)(struct mg_connection *nc, union socket_address *sa); + /* Request that a "listening" UDP socket be created. */ + int (*listen_udp)(struct mg_connection *nc, union socket_address *sa); + + /* Request that a TCP connection is made to the specified address. */ + void (*connect_tcp)(struct mg_connection *nc, const union socket_address *sa); + /* Open a UDP socket. Doesn't actually connect anything. */ + void (*connect_udp)(struct mg_connection *nc); + + /* Send functions for TCP and UDP. Sent data is copied before return. */ + void (*tcp_send)(struct mg_connection *nc, const void *buf, size_t len); + void (*udp_send)(struct mg_connection *nc, const void *buf, size_t len); + + void (*recved)(struct mg_connection *nc, size_t len); + + /* Perform interface-related connection initialization. Return 1 on ok. */ + int (*create_conn)(struct mg_connection *nc); + /* Perform interface-related cleanup on connection before destruction. */ + void (*destroy_conn)(struct mg_connection *nc); + + /* Associate a socket to a connection. */ + void (*sock_set)(struct mg_connection *nc, sock_t sock); + + /* Put connection's address into *sa, local (remote = 0) or remote. */ + void (*get_conn_addr)(struct mg_connection *nc, int remote, + union socket_address *sa); +}; + +extern const struct mg_iface_vtable *mg_ifaces[]; +extern int mg_num_ifaces; + +/* Creates a new interface instance. */ +struct mg_iface *mg_if_create_iface(const struct mg_iface_vtable *vtable, + struct mg_mgr *mgr); + +/* + * Find an interface with a given implementation. The search is started from + * interface `from`, exclusive. Returns NULL if none is found. + */ +struct mg_iface *mg_find_iface(struct mg_mgr *mgr, + const struct mg_iface_vtable *vtable, + struct mg_iface *from); +/* + * Deliver a new TCP connection. Returns NULL in case on error (unable to + * create connection, in which case interface state should be discarded. + * This is phase 1 of the two-phase process - MG_EV_ACCEPT will be delivered + * when mg_if_accept_tcp_cb is invoked. + */ +struct mg_connection *mg_if_accept_new_conn(struct mg_connection *lc); +void mg_if_accept_tcp_cb(struct mg_connection *nc, union socket_address *sa, + size_t sa_len); + +/* Callback invoked by connect methods. err = 0 -> ok, != 0 -> error. */ +void mg_if_connect_cb(struct mg_connection *nc, int err); +/* Callback that reports that data has been put on the wire. */ +void mg_if_sent_cb(struct mg_connection *nc, int num_sent); +/* + * Receive callback. + * if `own` is true, buf must be heap-allocated and ownership is transferred + * to the core. + * Core will acknowledge consumption by calling iface::recved. + */ +void mg_if_recv_tcp_cb(struct mg_connection *nc, void *buf, int len, int own); +/* + * Receive callback. + * buf must be heap-allocated and ownership is transferred to the core. + * Core will acknowledge consumption by calling iface::recved. + */ +void mg_if_recv_udp_cb(struct mg_connection *nc, void *buf, int len, + union socket_address *sa, size_t sa_len); + +/* void mg_if_close_conn(struct mg_connection *nc); */ + +/* Deliver a POLL event to the connection. */ +void mg_if_poll(struct mg_connection *nc, time_t now); + +/* Deliver a TIMER event to the connection. */ +void mg_if_timer(struct mg_connection *c, double now); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CS_MONGOOSE_SRC_NET_IF_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_ssl_if.h" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_MONGOOSE_SRC_SSL_IF_H_ +#define CS_MONGOOSE_SRC_SSL_IF_H_ + +#if MG_ENABLE_SSL + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +struct mg_ssl_if_ctx; +struct mg_connection; + +void mg_ssl_if_init(); + +enum mg_ssl_if_result { + MG_SSL_OK = 0, + MG_SSL_WANT_READ = -1, + MG_SSL_WANT_WRITE = -2, + MG_SSL_ERROR = -3, +}; + +struct mg_ssl_if_conn_params { + const char *cert; + const char *key; + const char *ca_cert; + const char *server_name; + const char *cipher_suites; + const char *psk_identity; + const char *psk_key; +}; + +enum mg_ssl_if_result mg_ssl_if_conn_init( + struct mg_connection *nc, const struct mg_ssl_if_conn_params *params, + const char **err_msg); +enum mg_ssl_if_result mg_ssl_if_conn_accept(struct mg_connection *nc, + struct mg_connection *lc); +void mg_ssl_if_conn_close_notify(struct mg_connection *nc); +void mg_ssl_if_conn_free(struct mg_connection *nc); + +enum mg_ssl_if_result mg_ssl_if_handshake(struct mg_connection *nc); +int mg_ssl_if_read(struct mg_connection *nc, void *buf, size_t buf_size); +int mg_ssl_if_write(struct mg_connection *nc, const void *data, size_t len); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* MG_ENABLE_SSL */ + +#endif /* CS_MONGOOSE_SRC_SSL_IF_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_net.h" +#endif +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + * This software is dual-licensed: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. For the terms of this + * license, see . + * + * You are free to use this software under the terms of the GNU General + * Public License, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * Alternatively, you can license this software under a commercial + * license, as set out in . + */ + +/* + * === Core API: TCP/UDP/SSL + * + * NOTE: Mongoose manager is single threaded. It does not protect + * its data structures by mutexes, therefore all functions that are dealing + * with a particular event manager should be called from the same thread, + * with exception of the `mg_broadcast()` function. It is fine to have different + * event managers handled by different threads. + */ + +#ifndef CS_MONGOOSE_SRC_NET_H_ +#define CS_MONGOOSE_SRC_NET_H_ + +/* Amalgamated: #include "mg_common.h" */ +/* Amalgamated: #include "mg_net_if.h" */ +/* Amalgamated: #include "common/mbuf.h" */ + +#ifndef MG_VPRINTF_BUFFER_SIZE +#define MG_VPRINTF_BUFFER_SIZE 100 +#endif + +#ifdef MG_USE_READ_WRITE +#define MG_RECV_FUNC(s, b, l, f) read(s, b, l) +#define MG_SEND_FUNC(s, b, l, f) write(s, b, l) +#else +#define MG_RECV_FUNC(s, b, l, f) recv(s, b, l, f) +#define MG_SEND_FUNC(s, b, l, f) send(s, b, l, f) +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +union socket_address { + struct sockaddr sa; + struct sockaddr_in sin; +#if MG_ENABLE_IPV6 + struct sockaddr_in6 sin6; +#else + struct sockaddr sin6; +#endif +}; + +struct mg_connection; + +/* + * Callback function (event handler) prototype. Must be defined by the user. + * Mongoose calls the event handler, passing the events defined below. + */ +typedef void (*mg_event_handler_t)(struct mg_connection *nc, int ev, + void *ev_data MG_UD_ARG(void *user_data)); + +/* Events. Meaning of event parameter (evp) is given in the comment. */ +#define MG_EV_POLL 0 /* Sent to each connection on each mg_mgr_poll() call */ +#define MG_EV_ACCEPT 1 /* New connection accepted. union socket_address * */ +#define MG_EV_CONNECT 2 /* connect() succeeded or failed. int * */ +#define MG_EV_RECV 3 /* Data has been received. int *num_bytes */ +#define MG_EV_SEND 4 /* Data has been written to a socket. int *num_bytes */ +#define MG_EV_CLOSE 5 /* Connection is closed. NULL */ +#define MG_EV_TIMER 6 /* now >= conn->ev_timer_time. double * */ + +/* + * Mongoose event manager. + */ +struct mg_mgr { + struct mg_connection *active_connections; +#if MG_ENABLE_HEXDUMP + const char *hexdump_file; /* Debug hexdump file path */ +#endif +#if MG_ENABLE_BROADCAST + sock_t ctl[2]; /* Socketpair for mg_broadcast() */ +#endif + void *user_data; /* User data */ + int num_ifaces; + struct mg_iface **ifaces; /* network interfaces */ + const char *nameserver; /* DNS server to use */ +}; + +/* + * Mongoose connection. + */ +struct mg_connection { + struct mg_connection *next, *prev; /* mg_mgr::active_connections linkage */ + struct mg_connection *listener; /* Set only for accept()-ed connections */ + struct mg_mgr *mgr; /* Pointer to containing manager */ + + sock_t sock; /* Socket to the remote peer */ + int err; + union socket_address sa; /* Remote peer address */ + size_t recv_mbuf_limit; /* Max size of recv buffer */ + struct mbuf recv_mbuf; /* Received data */ + struct mbuf send_mbuf; /* Data scheduled for sending */ + time_t last_io_time; /* Timestamp of the last socket IO */ + double ev_timer_time; /* Timestamp of the future MG_EV_TIMER */ +#if MG_ENABLE_SSL + void *ssl_if_data; /* SSL library data. */ +#endif + mg_event_handler_t proto_handler; /* Protocol-specific event handler */ + void *proto_data; /* Protocol-specific data */ + void (*proto_data_destructor)(void *proto_data); + mg_event_handler_t handler; /* Event handler function */ + void *user_data; /* User-specific data */ + union { + void *v; + /* + * the C standard is fussy about fitting function pointers into + * void pointers, since some archs might have fat pointers for functions. + */ + mg_event_handler_t f; + } priv_1; + void *priv_2; + void *mgr_data; /* Implementation-specific event manager's data. */ + struct mg_iface *iface; + unsigned long flags; +/* Flags set by Mongoose */ +#define MG_F_LISTENING (1 << 0) /* This connection is listening */ +#define MG_F_UDP (1 << 1) /* This connection is UDP */ +#define MG_F_RESOLVING (1 << 2) /* Waiting for async resolver */ +#define MG_F_CONNECTING (1 << 3) /* connect() call in progress */ +#define MG_F_SSL (1 << 4) /* SSL is enabled on the connection */ +#define MG_F_SSL_HANDSHAKE_DONE (1 << 5) /* SSL hanshake has completed */ +#define MG_F_WANT_READ (1 << 6) /* SSL specific */ +#define MG_F_WANT_WRITE (1 << 7) /* SSL specific */ +#define MG_F_IS_WEBSOCKET (1 << 8) /* Websocket specific */ + +/* Flags that are settable by user */ +#define MG_F_SEND_AND_CLOSE (1 << 10) /* Push remaining data and close */ +#define MG_F_CLOSE_IMMEDIATELY (1 << 11) /* Disconnect */ +#define MG_F_WEBSOCKET_NO_DEFRAG (1 << 12) /* Websocket specific */ +#define MG_F_DELETE_CHUNK (1 << 13) /* HTTP specific */ +#define MG_F_ENABLE_BROADCAST (1 << 14) /* Allow broadcast address usage */ + +#define MG_F_USER_1 (1 << 20) /* Flags left for application */ +#define MG_F_USER_2 (1 << 21) +#define MG_F_USER_3 (1 << 22) +#define MG_F_USER_4 (1 << 23) +#define MG_F_USER_5 (1 << 24) +#define MG_F_USER_6 (1 << 25) +}; + +/* + * Initialise Mongoose manager. Side effect: ignores SIGPIPE signal. + * `mgr->user_data` field will be initialised with a `user_data` parameter. + * That is an arbitrary pointer, where the user code can associate some data + * with the particular Mongoose manager. For example, a C++ wrapper class + * could be written in which case `user_data` can hold a pointer to the + * class instance. + */ +void mg_mgr_init(struct mg_mgr *mgr, void *user_data); + +/* + * Optional parameters to `mg_mgr_init_opt()`. + * + * If `main_iface` is not NULL, it will be used as the main interface in the + * default interface set. The pointer will be free'd by `mg_mgr_free`. + * Otherwise, the main interface will be autodetected based on the current + * platform. + * + * If `num_ifaces` is 0 and `ifaces` is NULL, the default interface set will be + * used. + * This is an advanced option, as it requires you to construct a full interface + * set, including special networking interfaces required by some optional + * features such as TCP tunneling. Memory backing `ifaces` and each of the + * `num_ifaces` pointers it contains will be reclaimed by `mg_mgr_free`. + */ +struct mg_mgr_init_opts { + const struct mg_iface_vtable *main_iface; + int num_ifaces; + const struct mg_iface_vtable **ifaces; + const char *nameserver; +}; + +/* + * Like `mg_mgr_init` but with more options. + * + * Notably, this allows you to create a manger and choose + * dynamically which networking interface implementation to use. + */ +void mg_mgr_init_opt(struct mg_mgr *mgr, void *user_data, + struct mg_mgr_init_opts opts); + +/* + * De-initialises Mongoose manager. + * + * Closes and deallocates all active connections. + */ +void mg_mgr_free(struct mg_mgr *); + +/* + * This function performs the actual IO and must be called in a loop + * (an event loop). It returns the current timestamp. + * `milli` is the maximum number of milliseconds to sleep. + * `mg_mgr_poll()` checks all connections for IO readiness. If at least one + * of the connections is IO-ready, `mg_mgr_poll()` triggers the respective + * event handlers and returns. + */ +time_t mg_mgr_poll(struct mg_mgr *, int milli); + +#if MG_ENABLE_BROADCAST +/* + * Passes a message of a given length to all connections. + * + * Must be called from a thread that does NOT call `mg_mgr_poll()`. + * Note that `mg_broadcast()` is the only function + * that can be, and must be, called from a different (non-IO) thread. + * + * `func` callback function will be called by the IO thread for each + * connection. When called, the event will be `MG_EV_POLL`, and a message will + * be passed as the `ev_data` pointer. Maximum message size is capped + * by `MG_CTL_MSG_MESSAGE_SIZE` which is set to 8192 bytes. + */ +void mg_broadcast(struct mg_mgr *mgr, mg_event_handler_t cb, void *data, + size_t len); +#endif + +/* + * Iterates over all active connections. + * + * Returns the next connection from the list + * of active connections or `NULL` if there are no more connections. Below + * is the iteration idiom: + * + * ```c + * for (c = mg_next(srv, NULL); c != NULL; c = mg_next(srv, c)) { + * // Do something with connection `c` + * } + * ``` + */ +struct mg_connection *mg_next(struct mg_mgr *mgr, struct mg_connection *c); + +/* + * Optional parameters to `mg_add_sock_opt()`. + * + * `flags` is an initial `struct mg_connection::flags` bitmask to set, + * see `MG_F_*` flags definitions. + */ +struct mg_add_sock_opts { + void *user_data; /* Initial value for connection's user_data */ + unsigned int flags; /* Initial connection flags */ + const char **error_string; /* Placeholder for the error string */ + struct mg_iface *iface; /* Interface instance */ +}; + +/* + * Creates a connection, associates it with the given socket and event handler + * and adds it to the manager. + * + * For more options see the `mg_add_sock_opt` variant. + */ +struct mg_connection *mg_add_sock(struct mg_mgr *mgr, sock_t sock, + MG_CB(mg_event_handler_t handler, + void *user_data)); + +/* + * Creates a connection, associates it with the given socket and event handler + * and adds to the manager. + * + * See the `mg_add_sock_opts` structure for a description of the options. + */ +struct mg_connection *mg_add_sock_opt(struct mg_mgr *mgr, sock_t sock, + MG_CB(mg_event_handler_t handler, + void *user_data), + struct mg_add_sock_opts opts); + +/* + * Optional parameters to `mg_bind_opt()`. + * + * `flags` is an initial `struct mg_connection::flags` bitmask to set, + * see `MG_F_*` flags definitions. + */ +struct mg_bind_opts { + void *user_data; /* Initial value for connection's user_data */ + unsigned int flags; /* Extra connection flags */ + const char **error_string; /* Placeholder for the error string */ + struct mg_iface *iface; /* Interface instance */ +#if MG_ENABLE_SSL + /* + * SSL settings. + * + * Server certificate to present to clients or client certificate to + * present to tunnel dispatcher (for tunneled connections). + */ + const char *ssl_cert; + /* Private key corresponding to the certificate. If ssl_cert is set but + * ssl_key is not, ssl_cert is used. */ + const char *ssl_key; + /* CA bundle used to verify client certificates or tunnel dispatchers. */ + const char *ssl_ca_cert; + /* Colon-delimited list of acceptable cipher suites. + * Names depend on the library used, for example: + * + * ECDH-ECDSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256 (OpenSSL) + * TLS-ECDH-ECDSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256 + * (mbedTLS) + * + * For OpenSSL the list can be obtained by running "openssl ciphers". + * For mbedTLS, names can be found in library/ssl_ciphersuites.c + * If NULL, a reasonable default is used. + */ + const char *ssl_cipher_suites; +#endif +}; + +/* + * Creates a listening connection. + * + * See `mg_bind_opt` for full documentation. + */ +struct mg_connection *mg_bind(struct mg_mgr *mgr, const char *address, + MG_CB(mg_event_handler_t handler, + void *user_data)); +/* + * Creates a listening connection. + * + * The `address` parameter specifies which address to bind to. It's format is + * the same as for the `mg_connect()` call, where `HOST` part is optional. + * `address` can be just a port number, e.g. `:8000`. To bind to a specific + * interface, an IP address can be specified, e.g. `1.2.3.4:8000`. By default, + * a TCP connection is created. To create UDP connection, prepend `udp://` + * prefix, e.g. `udp://:8000`. To summarize, `address` parameter has following + * format: `[PROTO://][IP_ADDRESS]:PORT`, where `PROTO` could be `tcp` or + * `udp`. + * + * See the `mg_bind_opts` structure for a description of the optional + * parameters. + * + * Returns a new listening connection or `NULL` on error. + * NOTE: The connection remains owned by the manager, do not free(). + */ +struct mg_connection *mg_bind_opt(struct mg_mgr *mgr, const char *address, + MG_CB(mg_event_handler_t handler, + void *user_data), + struct mg_bind_opts opts); + +/* Optional parameters to `mg_connect_opt()` */ +struct mg_connect_opts { + void *user_data; /* Initial value for connection's user_data */ + unsigned int flags; /* Extra connection flags */ + const char **error_string; /* Placeholder for the error string */ + struct mg_iface *iface; /* Interface instance */ + const char *nameserver; /* DNS server to use, NULL for default */ +#if MG_ENABLE_SSL + /* + * SSL settings. + * Client certificate to present to the server. + */ + const char *ssl_cert; + /* + * Private key corresponding to the certificate. + * If ssl_cert is set but ssl_key is not, ssl_cert is used. + */ + const char *ssl_key; + /* + * Verify server certificate using this CA bundle. If set to "*", then SSL + * is enabled but no cert verification is performed. + */ + const char *ssl_ca_cert; + /* Colon-delimited list of acceptable cipher suites. + * Names depend on the library used, for example: + * + * ECDH-ECDSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256 (OpenSSL) + * TLS-ECDH-ECDSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256 + * (mbedTLS) + * + * For OpenSSL the list can be obtained by running "openssl ciphers". + * For mbedTLS, names can be found in library/ssl_ciphersuites.c + * If NULL, a reasonable default is used. + */ + const char *ssl_cipher_suites; + /* + * Server name verification. If ssl_ca_cert is set and the certificate has + * passed verification, its subject will be verified against this string. + * By default (if ssl_server_name is NULL) hostname part of the address will + * be used. Wildcard matching is supported. A special value of "*" disables + * name verification. + */ + const char *ssl_server_name; + /* + * PSK identity and key. Identity is a NUL-terminated string and key is a hex + * string. Key must be either 16 or 32 bytes (32 or 64 hex digits) for AES-128 + * or AES-256 respectively. + * Note: Default list of cipher suites does not include PSK suites, if you + * want to use PSK you will need to set ssl_cipher_suites as well. + */ + const char *ssl_psk_identity; + const char *ssl_psk_key; +#endif +}; + +/* + * Connects to a remote host. + * + * See `mg_connect_opt()` for full documentation. + */ +struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *address, + MG_CB(mg_event_handler_t handler, + void *user_data)); + +/* + * Connects to a remote host. + * + * The `address` format is `[PROTO://]HOST:PORT`. `PROTO` could be `tcp` or + * `udp`. `HOST` could be an IP address, + * IPv6 address (if Mongoose is compiled with `-DMG_ENABLE_IPV6`) or a host + * name. If `HOST` is a name, Mongoose will resolve it asynchronously. Examples + * of valid addresses: `google.com:80`, `udp://1.2.3.4:53`, `10.0.0.1:443`, + * `[::1]:80` + * + * See the `mg_connect_opts` structure for a description of the optional + * parameters. + * + * Returns a new outbound connection or `NULL` on error. + * + * NOTE: The connection remains owned by the manager, do not free(). + * + * NOTE: To enable IPv6 addresses `-DMG_ENABLE_IPV6` should be specified + * in the compilation flags. + * + * NOTE: The new connection will receive `MG_EV_CONNECT` as its first event + * which will report the connect success status. + * If the asynchronous resolution fails or the `connect()` syscall fails for + * whatever reason (e.g. with `ECONNREFUSED` or `ENETUNREACH`), then + * `MG_EV_CONNECT` event will report failure. Code example below: + * + * ```c + * static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { + * int connect_status; + * + * switch (ev) { + * case MG_EV_CONNECT: + * connect_status = * (int *) ev_data; + * if (connect_status == 0) { + * // Success + * } else { + * // Error + * printf("connect() error: %s\n", strerror(connect_status)); + * } + * break; + * ... + * } + * } + * + * ... + * mg_connect(mgr, "my_site.com:80", ev_handler); + * ``` + */ +struct mg_connection *mg_connect_opt(struct mg_mgr *mgr, const char *address, + MG_CB(mg_event_handler_t handler, + void *user_data), + struct mg_connect_opts opts); + +#if MG_ENABLE_SSL && MG_NET_IF != MG_NET_IF_SIMPLELINK +/* + * Note: This function is deprecated. Please, use SSL options in + * mg_connect_opt. + * + * Enables SSL for a given connection. + * `cert` is a server certificate file name for a listening connection + * or a client certificate file name for an outgoing connection. + * The certificate files must be in PEM format. The server certificate file + * must contain a certificate, concatenated with a private key, optionally + * concatenated with DH parameters. + * `ca_cert` is a CA certificate or NULL if peer verification is not + * required. + * Return: NULL on success or error message on error. + */ +const char *mg_set_ssl(struct mg_connection *nc, const char *cert, + const char *ca_cert); +#endif + +/* + * Sends data to the connection. + * + * Note that sending functions do not actually push data to the socket. + * They just append data to the output buffer. MG_EV_SEND will be delivered when + * the data has actually been pushed out. + */ +void mg_send(struct mg_connection *, const void *buf, int len); + +/* Enables format string warnings for mg_printf */ +#if defined(__GNUC__) +__attribute__((format(printf, 2, 3))) +#endif +/* don't separate from mg_printf declaration */ + +/* + * Sends `printf`-style formatted data to the connection. + * + * See `mg_send` for more details on send semantics. + */ +int mg_printf(struct mg_connection *, const char *fmt, ...); + +/* Same as `mg_printf()`, but takes `va_list ap` as an argument. */ +int mg_vprintf(struct mg_connection *, const char *fmt, va_list ap); + +/* + * Creates a socket pair. + * `sock_type` can be either `SOCK_STREAM` or `SOCK_DGRAM`. + * Returns 0 on failure and 1 on success. + */ +int mg_socketpair(sock_t[2], int sock_type); + +#if MG_ENABLE_SYNC_RESOLVER +/* + * Convert domain name into IP address. + * + * This is a utility function. If compilation flags have + * `-DMG_ENABLE_GETADDRINFO`, then `getaddrinfo()` call is used for name + * resolution. Otherwise, `gethostbyname()` is used. + * + * CAUTION: this function can block. + * Return 1 on success, 0 on failure. + */ +int mg_resolve(const char *domain_name, char *ip_addr_buf, size_t buf_len); +#endif + +/* + * Verify given IP address against the ACL. + * + * `remote_ip` - an IPv4 address to check, in host byte order + * `acl` - a comma separated list of IP subnets: `x.x.x.x/x` or `x.x.x.x`. + * Each subnet is + * prepended by either a - or a + sign. A plus sign means allow, where a + * minus sign means deny. If a subnet mask is omitted, such as `-1.2.3.4`, + * it means that only that single IP address is denied. + * Subnet masks may vary from 0 to 32, inclusive. The default setting + * is to allow all access. On each request the full list is traversed, + * and the last match wins. Example: + * + * `-0.0.0.0/0,+192.168/16` - deny all accesses, only allow 192.168/16 subnet + * + * To learn more about subnet masks, see this + * link:https://en.wikipedia.org/wiki/Subnetwork[Wikipedia page on Subnetwork]. + * + * Returns -1 if ACL is malformed, 0 if address is disallowed, 1 if allowed. + */ +int mg_check_ip_acl(const char *acl, uint32_t remote_ip); + +/* + * Schedules an MG_EV_TIMER event to be delivered at `timestamp` time. + * `timestamp` is UNIX time (the number of seconds since Epoch). It is + * `double` instead of `time_t` to allow for sub-second precision. + * Returns the old timer value. + * + * Example: set the connect timeout to 1.5 seconds: + * + * ``` + * c = mg_connect(&mgr, "cesanta.com", ev_handler); + * mg_set_timer(c, mg_time() + 1.5); + * ... + * + * void ev_handler(struct mg_connection *c, int ev, void *ev_data) { + * switch (ev) { + * case MG_EV_CONNECT: + * mg_set_timer(c, 0); // Clear connect timer + * break; + * case MG_EV_TIMER: + * log("Connect timeout"); + * c->flags |= MG_F_CLOSE_IMMEDIATELY; + * break; + * ``` + */ +double mg_set_timer(struct mg_connection *c, double timestamp); + +/* + * A sub-second precision version of time(). + */ +double mg_time(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CS_MONGOOSE_SRC_NET_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_uri.h" +#endif +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +/* + * === URI + */ + +#ifndef CS_MONGOOSE_SRC_URI_H_ +#define CS_MONGOOSE_SRC_URI_H_ + +/* Amalgamated: #include "mg_net.h" */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * Parses an URI and fills string chunks with locations of the respective + * uri components within the input uri string. NULL pointers will be + * ignored. + * + * General syntax: + * + * [scheme://[user_info@]]host[:port][/path][?query][#fragment] + * + * Example: + * + * foo.com:80 + * tcp://foo.com:1234 + * http://foo.com:80/bar?baz=1 + * https://user:pw@foo.com:443/blah + * + * `path` will include the leading slash. `query` won't include the leading `?`. + * `host` can contain embedded colons if surrounded by square brackets in order + * to support IPv6 literal addresses. + * + * + * Returns 0 on success, -1 on error. + */ +int mg_parse_uri(const struct mg_str uri, struct mg_str *scheme, + struct mg_str *user_info, struct mg_str *host, + unsigned int *port, struct mg_str *path, struct mg_str *query, + struct mg_str *fragment); + +/* + * Assemble URI from parts. Any of the inputs can be NULL or zero-length mg_str. + * + * If normalize_path is true, path is normalized by resolving relative refs. + * + * Result is a heap-allocated string (uri->p must be free()d after use). + * + * Returns 0 on success, -1 on error. + */ +int mg_assemble_uri(const struct mg_str *scheme, const struct mg_str *user_info, + const struct mg_str *host, unsigned int port, + const struct mg_str *path, const struct mg_str *query, + const struct mg_str *fragment, int normalize_path, + struct mg_str *uri); + +int mg_normalize_uri_path(const struct mg_str *in, struct mg_str *out); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* CS_MONGOOSE_SRC_URI_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_util.h" +#endif +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +/* + * === Utility API + */ + +#ifndef CS_MONGOOSE_SRC_UTIL_H_ +#define CS_MONGOOSE_SRC_UTIL_H_ + +#include + +/* Amalgamated: #include "mg_common.h" */ +/* Amalgamated: #include "mg_net_if.h" */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef MG_MAX_PATH +#ifdef PATH_MAX +#define MG_MAX_PATH PATH_MAX +#else +#define MG_MAX_PATH 256 +#endif +#endif + +/* + * Fetches substring from input string `s`, `end` into `v`. + * Skips initial delimiter characters. Records first non-delimiter character + * at the beginning of substring `v`. Then scans the rest of the string + * until a delimiter character or end-of-string is found. + * `delimiters` is a 0-terminated string containing delimiter characters. + * Either one of `delimiters` or `end_string` terminates the search. + * Returns an `s` pointer, advanced forward where parsing has stopped. + */ +const char *mg_skip(const char *s, const char *end_string, + const char *delimiters, struct mg_str *v); + +/* + * Decodes base64-encoded string `s`, `len` into the destination `dst`. + * The destination has to have enough space to hold the decoded buffer. + * Decoding stops either when all strings have been decoded or invalid an + * character appeared. + * Destination is '\0'-terminated. + * Returns the number of decoded characters. On success, that should be equal + * to `len`. On error (invalid character) the return value is smaller then + * `len`. + */ +int mg_base64_decode(const unsigned char *s, int len, char *dst); + +/* + * Base64-encode chunk of memory `src`, `src_len` into the destination `dst`. + * Destination has to have enough space to hold encoded buffer. + * Destination is '\0'-terminated. + */ +void mg_base64_encode(const unsigned char *src, int src_len, char *dst); + +#if MG_ENABLE_FILESYSTEM +/* + * Performs a 64-bit `stat()` call against a given file. + * + * `path` should be UTF8 encoded. + * + * Return value is the same as for `stat()` syscall. + */ +int mg_stat(const char *path, cs_stat_t *st); + +/* + * Opens the given file and returns a file stream. + * + * `path` and `mode` should be UTF8 encoded. + * + * Return value is the same as for the `fopen()` call. + */ +FILE *mg_fopen(const char *path, const char *mode); + +/* + * Opens the given file and returns a file stream. + * + * `path` should be UTF8 encoded. + * + * Return value is the same as for the `open()` syscall. + */ +int mg_open(const char *path, int flag, int mode); + +/* + * Reads data from the given file stream. + * + * Return value is a number of bytes readen. + */ +size_t mg_fread(void *ptr, size_t size, size_t count, FILE *f); + +/* + * Writes data to the given file stream. + * + * Return value is a number of bytes wtitten. + */ +size_t mg_fwrite(const void *ptr, size_t size, size_t count, FILE *f); + +#endif /* MG_ENABLE_FILESYSTEM */ + +#if MG_ENABLE_THREADS +/* + * Starts a new detached thread. + * Arguments and semantics are the same as pthead's `pthread_create()`. + * `thread_func` is a thread function, `thread_func_param` is a parameter + * that is passed to the thread function. + */ +void *mg_start_thread(void *(*thread_func)(void *), void *thread_func_param); +#endif + +void mg_set_close_on_exec(sock_t); + +#define MG_SOCK_STRINGIFY_IP 1 +#define MG_SOCK_STRINGIFY_PORT 2 +#define MG_SOCK_STRINGIFY_REMOTE 4 +/* + * Converts a connection's local or remote address into string. + * + * The `flags` parameter is a bit mask that controls the behaviour, + * see `MG_SOCK_STRINGIFY_*` definitions. + * + * - MG_SOCK_STRINGIFY_IP - print IP address + * - MG_SOCK_STRINGIFY_PORT - print port number + * - MG_SOCK_STRINGIFY_REMOTE - print remote peer's IP/port, not local address + * + * If both port number and IP address are printed, they are separated by `:`. + * If compiled with `-DMG_ENABLE_IPV6`, IPv6 addresses are supported. + * Return length of the stringified address. + */ +int mg_conn_addr_to_str(struct mg_connection *c, char *buf, size_t len, + int flags); +#if MG_NET_IF == MG_NET_IF_SOCKET +/* Legacy interface. */ +void mg_sock_to_str(sock_t sock, char *buf, size_t len, int flags); +#endif + +/* + * Convert the socket's address into string. + * + * `flags` is MG_SOCK_STRINGIFY_IP and/or MG_SOCK_STRINGIFY_PORT. + */ +int mg_sock_addr_to_str(const union socket_address *sa, char *buf, size_t len, + int flags); + +#if MG_ENABLE_HEXDUMP +/* + * Generates a human-readable hexdump of memory chunk. + * + * Takes a memory buffer `buf` of length `len` and creates a hex dump of that + * buffer in `dst`. The generated output is a-la hexdump(1). + * Returns the length of generated string, excluding terminating `\0`. If + * returned length is bigger than `dst_len`, the overflow bytes are discarded. + */ +int mg_hexdump(const void *buf, int len, char *dst, int dst_len); + +/* Same as mg_hexdump, but with output going to file instead of a buffer. */ +void mg_hexdumpf(FILE *fp, const void *buf, int len); + +/* + * Generates human-readable hexdump of the data sent or received by the + * connection. `path` is a file name where hexdump should be written. + * `num_bytes` is a number of bytes sent/received. `ev` is one of the `MG_*` + * events sent to an event handler. This function is supposed to be called from + * the event handler. + */ +void mg_hexdump_connection(struct mg_connection *nc, const char *path, + const void *buf, int num_bytes, int ev); +#endif + +/* + * Returns true if target platform is big endian. + */ +int mg_is_big_endian(void); + +/* + * Use with cs_base64_init/update/finish in order to write out base64 in chunks. + */ +void mg_mbuf_append_base64_putc(char ch, void *user_data); + +/* + * Encode `len` bytes starting at `data` as base64 and append them to an mbuf. + */ +void mg_mbuf_append_base64(struct mbuf *mbuf, const void *data, size_t len); + +/* + * Generate a Basic Auth header and appends it to buf. + * If pass is NULL, then user is expected to contain the credentials pair + * already encoded as `user:pass`. + */ +void mg_basic_auth_header(const struct mg_str user, const struct mg_str pass, + struct mbuf *buf); + +/* + * URL-escape the specified string. + * All non-printable characters are escaped, plus `._-$,;~()/`. + * Input need not be NUL-terminated, but the returned string is. + * Returned string is heap-allocated and must be free()'d. + */ +struct mg_str mg_url_encode(const struct mg_str src); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* CS_MONGOOSE_SRC_UTIL_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_http.h" +#endif +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +/* + * === Common API reference + */ + +#ifndef CS_MONGOOSE_SRC_HTTP_H_ +#define CS_MONGOOSE_SRC_HTTP_H_ + +#if MG_ENABLE_HTTP + +/* Amalgamated: #include "mg_net.h" */ +/* Amalgamated: #include "common/mg_str.h" */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef MG_MAX_HTTP_HEADERS +#define MG_MAX_HTTP_HEADERS 20 +#endif + +#ifndef MG_MAX_HTTP_REQUEST_SIZE +#define MG_MAX_HTTP_REQUEST_SIZE 1024 +#endif + +#ifndef MG_MAX_HTTP_SEND_MBUF +#define MG_MAX_HTTP_SEND_MBUF 1024 +#endif + +#ifndef MG_CGI_ENVIRONMENT_SIZE +#define MG_CGI_ENVIRONMENT_SIZE 8192 +#endif + +/* HTTP message */ +struct http_message { + struct mg_str message; /* Whole message: request line + headers + body */ + struct mg_str body; /* Message body. 0-length for requests with no body */ + + /* HTTP Request line (or HTTP response line) */ + struct mg_str method; /* "GET" */ + struct mg_str uri; /* "/my_file.html" */ + struct mg_str proto; /* "HTTP/1.1" -- for both request and response */ + + /* For responses, code and response status message are set */ + int resp_code; + struct mg_str resp_status_msg; + + /* + * Query-string part of the URI. For example, for HTTP request + * GET /foo/bar?param1=val1¶m2=val2 + * | uri | query_string | + * + * Note that question mark character doesn't belong neither to the uri, + * nor to the query_string + */ + struct mg_str query_string; + + /* Headers */ + struct mg_str header_names[MG_MAX_HTTP_HEADERS]; + struct mg_str header_values[MG_MAX_HTTP_HEADERS]; +}; + +#if MG_ENABLE_HTTP_WEBSOCKET +/* WebSocket message */ +struct websocket_message { + unsigned char *data; + size_t size; + unsigned char flags; +}; +#endif + +/* HTTP multipart part */ +struct mg_http_multipart_part { + const char *file_name; + const char *var_name; + struct mg_str data; + int status; /* <0 on error */ + void *user_data; +}; + +/* SSI call context */ +struct mg_ssi_call_ctx { + struct http_message *req; /* The request being processed. */ + struct mg_str file; /* Filesystem path of the file being processed. */ + struct mg_str arg; /* The argument passed to the tag: . */ +}; + +/* HTTP and websocket events. void *ev_data is described in a comment. */ +#define MG_EV_HTTP_REQUEST 100 /* struct http_message * */ +#define MG_EV_HTTP_REPLY 101 /* struct http_message * */ +#define MG_EV_HTTP_CHUNK 102 /* struct http_message * */ +#define MG_EV_SSI_CALL 105 /* char * */ +#define MG_EV_SSI_CALL_CTX 106 /* struct mg_ssi_call_ctx * */ + +#if MG_ENABLE_HTTP_WEBSOCKET +#define MG_EV_WEBSOCKET_HANDSHAKE_REQUEST 111 /* struct http_message * */ +#define MG_EV_WEBSOCKET_HANDSHAKE_DONE 112 /* NULL */ +#define MG_EV_WEBSOCKET_FRAME 113 /* struct websocket_message * */ +#define MG_EV_WEBSOCKET_CONTROL_FRAME 114 /* struct websocket_message * */ +#endif + +#if MG_ENABLE_HTTP_STREAMING_MULTIPART +#define MG_EV_HTTP_MULTIPART_REQUEST 121 /* struct http_message */ +#define MG_EV_HTTP_PART_BEGIN 122 /* struct mg_http_multipart_part */ +#define MG_EV_HTTP_PART_DATA 123 /* struct mg_http_multipart_part */ +#define MG_EV_HTTP_PART_END 124 /* struct mg_http_multipart_part */ +/* struct mg_http_multipart_part */ +#define MG_EV_HTTP_MULTIPART_REQUEST_END 125 +#endif + +/* + * Attaches a built-in HTTP event handler to the given connection. + * The user-defined event handler will receive following extra events: + * + * - MG_EV_HTTP_REQUEST: HTTP request has arrived. Parsed HTTP request + * is passed as + * `struct http_message` through the handler's `void *ev_data` pointer. + * - MG_EV_HTTP_REPLY: The HTTP reply has arrived. The parsed HTTP reply is + * passed as `struct http_message` through the handler's `void *ev_data` + * pointer. + * - MG_EV_HTTP_CHUNK: The HTTP chunked-encoding chunk has arrived. + * The parsed HTTP reply is passed as `struct http_message` through the + * handler's `void *ev_data` pointer. `http_message::body` would contain + * incomplete, reassembled HTTP body. + * It will grow with every new chunk that arrives, and it can + * potentially consume a lot of memory. An event handler may process + * the body as chunks are coming, and signal Mongoose to delete processed + * body by setting `MG_F_DELETE_CHUNK` in `mg_connection::flags`. When + * the last zero chunk is received, + * Mongoose sends `MG_EV_HTTP_REPLY` event with + * full reassembled body (if handler did not signal to delete chunks) or + * with empty body (if handler did signal to delete chunks). + * - MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: server has received the WebSocket + * handshake request. `ev_data` contains parsed HTTP request. + * - MG_EV_WEBSOCKET_HANDSHAKE_DONE: server has completed the WebSocket + * handshake. `ev_data` is `NULL`. + * - MG_EV_WEBSOCKET_FRAME: new WebSocket frame has arrived. `ev_data` is + * `struct websocket_message *` + * + * When compiled with MG_ENABLE_HTTP_STREAMING_MULTIPART, Mongoose parses + * multipart requests and splits them into separate events: + * - MG_EV_HTTP_MULTIPART_REQUEST: Start of the request. + * This event is sent before body is parsed. After this, the user + * should expect a sequence of PART_BEGIN/DATA/END requests. + * This is also the last time when headers and other request fields are + * accessible. + * - MG_EV_HTTP_PART_BEGIN: Start of a part of a multipart message. + * Argument: mg_http_multipart_part with var_name and file_name set + * (if present). No data is passed in this message. + * - MG_EV_HTTP_PART_DATA: new portion of data from the multipart message. + * Argument: mg_http_multipart_part. var_name and file_name are preserved, + * data is available in mg_http_multipart_part.data. + * - MG_EV_HTTP_PART_END: End of the current part. var_name, file_name are + * the same, no data in the message. If status is 0, then the part is + * properly terminated with a boundary, status < 0 means that connection + * was terminated. + * - MG_EV_HTTP_MULTIPART_REQUEST_END: End of the multipart request. + * Argument: mg_http_multipart_part, var_name and file_name are NULL, + * status = 0 means request was properly closed, < 0 means connection + * was terminated (note: in this case both PART_END and REQUEST_END are + * delivered). + */ +void mg_set_protocol_http_websocket(struct mg_connection *nc); + +#if MG_ENABLE_HTTP_WEBSOCKET +/* + * Send websocket handshake to the server. + * + * `nc` must be a valid connection, connected to a server. `uri` is an URI + * to fetch, extra_headers` is extra HTTP headers to send or `NULL`. + * + * This function is intended to be used by websocket client. + * + * Note that the Host header is mandatory in HTTP/1.1 and must be + * included in `extra_headers`. `mg_send_websocket_handshake2` offers + * a better API for that. + * + * Deprecated in favour of `mg_send_websocket_handshake2` + */ +void mg_send_websocket_handshake(struct mg_connection *nc, const char *uri, + const char *extra_headers); + +/* + * Send websocket handshake to the server. + * + * `nc` must be a valid connection, connected to a server. `uri` is an URI + * to fetch, `host` goes into the `Host` header, `protocol` goes into the + * `Sec-WebSocket-Proto` header (NULL to omit), extra_headers` is extra HTTP + * headers to send or `NULL`. + * + * This function is intended to be used by websocket client. + */ +void mg_send_websocket_handshake2(struct mg_connection *nc, const char *path, + const char *host, const char *protocol, + const char *extra_headers); + +/* Like mg_send_websocket_handshake2 but also passes basic auth header */ +void mg_send_websocket_handshake3(struct mg_connection *nc, const char *path, + const char *host, const char *protocol, + const char *extra_headers, const char *user, + const char *pass); + +/* Same as mg_send_websocket_handshake3 but with strings not necessarily + * NUL-temrinated */ +void mg_send_websocket_handshake3v(struct mg_connection *nc, + const struct mg_str path, + const struct mg_str host, + const struct mg_str protocol, + const struct mg_str extra_headers, + const struct mg_str user, + const struct mg_str pass); + +/* + * Helper function that creates an outbound WebSocket connection. + * + * `url` is a URL to connect to. It must be properly URL-encoded, e.g. have + * no spaces, etc. By default, `mg_connect_ws()` sends Connection and + * Host headers. `extra_headers` is an extra HTTP header to send, e.g. + * `"User-Agent: my-app\r\n"`. + * If `protocol` is not NULL, then a `Sec-WebSocket-Protocol` header is sent. + * + * Examples: + * + * ```c + * nc1 = mg_connect_ws(mgr, ev_handler_1, "ws://echo.websocket.org", NULL, + * NULL); + * nc2 = mg_connect_ws(mgr, ev_handler_1, "wss://echo.websocket.org", NULL, + * NULL); + * nc3 = mg_connect_ws(mgr, ev_handler_1, "ws://api.cesanta.com", + * "clubby.cesanta.com", NULL); + * ``` + */ +struct mg_connection *mg_connect_ws(struct mg_mgr *mgr, + MG_CB(mg_event_handler_t event_handler, + void *user_data), + const char *url, const char *protocol, + const char *extra_headers); + +/* + * Helper function that creates an outbound WebSocket connection + * + * Mostly identical to `mg_connect_ws`, but allows to provide extra parameters + * (for example, SSL parameters) + */ +struct mg_connection *mg_connect_ws_opt( + struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data), + struct mg_connect_opts opts, const char *url, const char *protocol, + const char *extra_headers); + +/* + * Send WebSocket frame to the remote end. + * + * `op_and_flags` specifies the frame's type. It's one of: + * + * - WEBSOCKET_OP_CONTINUE + * - WEBSOCKET_OP_TEXT + * - WEBSOCKET_OP_BINARY + * - WEBSOCKET_OP_CLOSE + * - WEBSOCKET_OP_PING + * - WEBSOCKET_OP_PONG + * + * Orred with one of the flags: + * + * - WEBSOCKET_DONT_FIN: Don't set the FIN flag on the frame to be sent. + * + * `data` and `data_len` contain frame data. + */ +void mg_send_websocket_frame(struct mg_connection *nc, int op_and_flags, + const void *data, size_t data_len); + +/* + * Like `mg_send_websocket_frame()`, but composes a single frame from multiple + * buffers. + */ +void mg_send_websocket_framev(struct mg_connection *nc, int op_and_flags, + const struct mg_str *strings, int num_strings); + +/* + * Sends WebSocket frame to the remote end. + * + * Like `mg_send_websocket_frame()`, but allows to create formatted messages + * with `printf()`-like semantics. + */ +void mg_printf_websocket_frame(struct mg_connection *nc, int op_and_flags, + const char *fmt, ...); + +/* Websocket opcodes, from http://tools.ietf.org/html/rfc6455 */ +#define WEBSOCKET_OP_CONTINUE 0 +#define WEBSOCKET_OP_TEXT 1 +#define WEBSOCKET_OP_BINARY 2 +#define WEBSOCKET_OP_CLOSE 8 +#define WEBSOCKET_OP_PING 9 +#define WEBSOCKET_OP_PONG 10 + +/* + * If set causes the FIN flag to not be set on outbound + * frames. This enables sending multiple fragments of a single + * logical message. + * + * The WebSocket protocol mandates that if the FIN flag of a data + * frame is not set, the next frame must be a WEBSOCKET_OP_CONTINUE. + * The last frame must have the FIN bit set. + * + * Note that mongoose will automatically defragment incoming messages, + * so this flag is used only on outbound messages. + */ +#define WEBSOCKET_DONT_FIN 0x100 + +#endif /* MG_ENABLE_HTTP_WEBSOCKET */ + +/* + * Decodes a URL-encoded string. + * + * Source string is specified by (`src`, `src_len`), and destination is + * (`dst`, `dst_len`). If `is_form_url_encoded` is non-zero, then + * `+` character is decoded as a blank space character. This function + * guarantees to NUL-terminate the destination. If destination is too small, + * then the source string is partially decoded and `-1` is returned. + *Otherwise, + * a length of the decoded string is returned, not counting final NUL. + */ +int mg_url_decode(const char *src, int src_len, char *dst, int dst_len, + int is_form_url_encoded); + +extern void mg_hash_md5_v(size_t num_msgs, const uint8_t *msgs[], + const size_t *msg_lens, uint8_t *digest); +extern void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[], + const size_t *msg_lens, uint8_t *digest); + +/* + * Flags for `mg_http_is_authorized()`. + */ +#define MG_AUTH_FLAG_IS_DIRECTORY (1 << 0) +#define MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE (1 << 1) +#define MG_AUTH_FLAG_ALLOW_MISSING_FILE (1 << 2) + +/* + * Checks whether an http request is authorized. `domain` is the authentication + * realm, `passwords_file` is a htdigest file (can be created e.g. with + * `htdigest` utility). If either `domain` or `passwords_file` is NULL, this + * function always returns 1; otherwise checks the authentication in the + * http request and returns 1 only if there is a match; 0 otherwise. + */ +int mg_http_is_authorized(struct http_message *hm, struct mg_str path, + const char *domain, const char *passwords_file, + int flags); + +/* + * Sends 401 Unauthorized response. + */ +void mg_http_send_digest_auth_request(struct mg_connection *c, + const char *domain); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* MG_ENABLE_HTTP */ + +#endif /* CS_MONGOOSE_SRC_HTTP_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_http_server.h" +#endif +/* + * === Server API reference + */ + +#ifndef CS_MONGOOSE_SRC_HTTP_SERVER_H_ +#define CS_MONGOOSE_SRC_HTTP_SERVER_H_ + +#if MG_ENABLE_HTTP + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * Parses a HTTP message. + * + * `is_req` should be set to 1 if parsing a request, 0 if reply. + * + * Returns the number of bytes parsed. If HTTP message is + * incomplete `0` is returned. On parse error, a negative number is returned. + */ +int mg_parse_http(const char *s, int n, struct http_message *hm, int is_req); + +/* + * Searches and returns the header `name` in parsed HTTP message `hm`. + * If header is not found, NULL is returned. Example: + * + * struct mg_str *host_hdr = mg_get_http_header(hm, "Host"); + */ +struct mg_str *mg_get_http_header(struct http_message *hm, const char *name); + +/* + * Parses the HTTP header `hdr`. Finds variable `var_name` and stores its value + * in the buffer `*buf`, `buf_size`. If the buffer size is not enough, + * allocates a buffer of required size and writes it to `*buf`, similar to + * asprintf(). The caller should always check whether the buffer was updated, + * and free it if so. + * + * This function is supposed to parse cookies, authentication headers, etc. + * Example (error handling omitted): + * + * char user_buf[20]; + * char *user = user_buf; + * struct mg_str *hdr = mg_get_http_header(hm, "Authorization"); + * mg_http_parse_header2(hdr, "username", &user, sizeof(user_buf)); + * // ... do something useful with user + * if (user != user_buf) { + * free(user); + * } + * + * Returns the length of the variable's value. If variable is not found, 0 is + * returned. + */ +int mg_http_parse_header2(struct mg_str *hdr, const char *var_name, char **buf, + size_t buf_size); + +/* + * DEPRECATED: use mg_http_parse_header2() instead. + * + * Same as mg_http_parse_header2(), but takes buffer as a `char *` (instead of + * `char **`), and thus it cannot allocate a new buffer if the provided one + * is not enough, and just returns 0 in that case. + */ +int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf, + size_t buf_size) +#ifdef __GNUC__ + __attribute__((deprecated)); +#endif +; + +/* + * Gets and parses the Authorization: Basic header + * Returns -1 if no Authorization header is found, or if + * mg_parse_http_basic_auth + * fails parsing the resulting header. + */ +int mg_get_http_basic_auth(struct http_message *hm, char *user, size_t user_len, + char *pass, size_t pass_len); + +/* + * Parses the Authorization: Basic header + * Returns -1 iif the authorization type is not "Basic" or any other error such + * as incorrectly encoded base64 user password pair. + */ +int mg_parse_http_basic_auth(struct mg_str *hdr, char *user, size_t user_len, + char *pass, size_t pass_len); + +/* + * Parses the buffer `buf`, `buf_len` that contains multipart form data chunks. + * Stores the chunk name in a `var_name`, `var_name_len` buffer. + * If a chunk is an uploaded file, then `file_name`, `file_name_len` is + * filled with an uploaded file name. `chunk`, `chunk_len` + * points to the chunk data. + * + * Return: number of bytes to skip to the next chunk or 0 if there are + * no more chunks. + * + * Usage example: + * + * ```c + * static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { + * switch(ev) { + * case MG_EV_HTTP_REQUEST: { + * struct http_message *hm = (struct http_message *) ev_data; + * char var_name[100], file_name[100]; + * const char *chunk; + * size_t chunk_len, n1, n2; + * + * n1 = n2 = 0; + * while ((n2 = mg_parse_multipart(hm->body.p + n1, + * hm->body.len - n1, + * var_name, sizeof(var_name), + * file_name, sizeof(file_name), + * &chunk, &chunk_len)) > 0) { + * printf("var: %s, file_name: %s, size: %d, chunk: [%.*s]\n", + * var_name, file_name, (int) chunk_len, + * (int) chunk_len, chunk); + * n1 += n2; + * } + * } + * break; + * ``` + */ +size_t mg_parse_multipart(const char *buf, size_t buf_len, char *var_name, + size_t var_name_len, char *file_name, + size_t file_name_len, const char **chunk, + size_t *chunk_len); + +/* + * Fetches a HTTP form variable. + * + * Fetches a variable `name` from a `buf` into a buffer specified by `dst`, + * `dst_len`. The destination is always zero-terminated. Returns the length of + * a fetched variable. If not found, 0 is returned. `buf` must be valid + * url-encoded buffer. If destination is too small or an error occured, + * negative number is returned. + */ +int mg_get_http_var(const struct mg_str *buf, const char *name, char *dst, + size_t dst_len); + +#if MG_ENABLE_FILESYSTEM +/* + * This structure defines how `mg_serve_http()` works. + * Best practice is to set only required settings, and leave the rest as NULL. + */ +struct mg_serve_http_opts { + /* Path to web root directory */ + const char *document_root; + + /* List of index files. Default is "" */ + const char *index_files; + + /* + * Leave as NULL to disable authentication. + * To enable directory protection with authentication, set this to ".htpasswd" + * Then, creating ".htpasswd" file in any directory automatically protects + * it with digest authentication. + * Use `mongoose` web server binary, or `htdigest` Apache utility to + * create/manipulate passwords file. + * Make sure `auth_domain` is set to a valid domain name. + */ + const char *per_directory_auth_file; + + /* Authorization domain (domain name of this web server) */ + const char *auth_domain; + + /* + * Leave as NULL to disable authentication. + * Normally, only selected directories in the document root are protected. + * If absolutely every access to the web server needs to be authenticated, + * regardless of the URI, set this option to the path to the passwords file. + * Format of that file is the same as ".htpasswd" file. Make sure that file + * is located outside document root to prevent people fetching it. + */ + const char *global_auth_file; + + /* Set to "no" to disable directory listing. Enabled by default. */ + const char *enable_directory_listing; + + /* + * SSI files pattern. If not set, "**.shtml$|**.shtm$" is used. + * + * All files that match ssi_pattern are treated as SSI. + * + * Server Side Includes (SSI) is a simple interpreted server-side scripting + * language which is most commonly used to include the contents of a file + * into a web page. It can be useful when it is desirable to include a common + * piece of code throughout a website, for example, headers and footers. + * + * In order for a webpage to recognize an SSI-enabled HTML file, the + * filename should end with a special extension, by default the extension + * should be either .shtml or .shtm + * + * Unknown SSI directives are silently ignored by Mongoose. Currently, + * the following SSI directives are supported: + * <!--#include FILE_TO_INCLUDE --> + * <!--#exec "COMMAND_TO_EXECUTE" --> + * <!--#call COMMAND --> + * + * Note that <!--#include ...> directive supports three path + *specifications: + * + * <!--#include virtual="path" --> Path is relative to web server root + * <!--#include abspath="path" --> Path is absolute or relative to the + * web server working dir + * <!--#include file="path" -->, Path is relative to current document + * <!--#include "path" --> + * + * The include directive may be used to include the contents of a file or + * the result of running a CGI script. + * + * The exec directive is used to execute + * a command on a server, and show command's output. Example: + * + * <!--#exec "ls -l" --> + * + * The call directive is a way to invoke a C handler from the HTML page. + * On each occurence of <!--#call COMMAND OPTIONAL_PARAMS> directive, + * Mongoose calls a registered event handler with MG_EV_SSI_CALL event, + * and event parameter will point to the COMMAND OPTIONAL_PARAMS string. + * An event handler can output any text, for example by calling + * `mg_printf()`. This is a flexible way of generating a web page on + * server side by calling a C event handler. Example: + * + * <!--#call foo --> ... <!--#call bar --> + * + * In the event handler: + * case MG_EV_SSI_CALL: { + * const char *param = (const char *) ev_data; + * if (strcmp(param, "foo") == 0) { + * mg_printf(c, "hello from foo"); + * } else if (strcmp(param, "bar") == 0) { + * mg_printf(c, "hello from bar"); + * } + * break; + * } + */ + const char *ssi_pattern; + + /* IP ACL. By default, NULL, meaning all IPs are allowed to connect */ + const char *ip_acl; + +#if MG_ENABLE_HTTP_URL_REWRITES + /* URL rewrites. + * + * Comma-separated list of `uri_pattern=url_file_or_directory_path` rewrites. + * When HTTP request is received, Mongoose constructs a file name from the + * requested URI by combining `document_root` and the URI. However, if the + * rewrite option is used and `uri_pattern` matches requested URI, then + * `document_root` is ignored. Instead, `url_file_or_directory_path` is used, + * which should be a full path name or a path relative to the web server's + * current working directory. It can also be an URI (http:// or https://) + * in which case mongoose will behave as a reverse proxy for that destination. + * + * Note that `uri_pattern`, as all Mongoose patterns, is a prefix pattern. + * + * If uri_pattern starts with `@` symbol, then Mongoose compares it with the + * HOST header of the request. If they are equal, Mongoose sets document root + * to `file_or_directory_path`, implementing virtual hosts support. + * Example: `@foo.com=/document/root/for/foo.com` + * + * If `uri_pattern` starts with `%` symbol, then Mongoose compares it with + * the listening port. If they match, then Mongoose issues a 301 redirect. + * For example, to redirect all HTTP requests to the + * HTTPS port, do `%80=https://my.site.com`. Note that the request URI is + * automatically appended to the redirect location. + */ + const char *url_rewrites; +#endif + + /* DAV document root. If NULL, DAV requests are going to fail. */ + const char *dav_document_root; + + /* + * DAV passwords file. If NULL, DAV requests are going to fail. + * If passwords file is set to "-", then DAV auth is disabled. + */ + const char *dav_auth_file; + + /* Glob pattern for the files to hide. */ + const char *hidden_file_pattern; + + /* Set to non-NULL to enable CGI, e.g. **.cgi$|**.php$" */ + const char *cgi_file_pattern; + + /* If not NULL, ignore CGI script hashbang and use this interpreter */ + const char *cgi_interpreter; + + /* + * Comma-separated list of Content-Type overrides for path suffixes, e.g. + * ".txt=text/plain; charset=utf-8,.c=text/plain" + */ + const char *custom_mime_types; + + /* + * Extra HTTP headers to add to each server response. + * Example: to enable CORS, set this to "Access-Control-Allow-Origin: *". + */ + const char *extra_headers; +}; + +/* + * Serves given HTTP request according to the `options`. + * + * Example code snippet: + * + * ```c + * static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { + * struct http_message *hm = (struct http_message *) ev_data; + * struct mg_serve_http_opts opts = { .document_root = "/var/www" }; // C99 + * + * switch (ev) { + * case MG_EV_HTTP_REQUEST: + * mg_serve_http(nc, hm, opts); + * break; + * default: + * break; + * } + * } + * ``` + */ +void mg_serve_http(struct mg_connection *nc, struct http_message *hm, + struct mg_serve_http_opts opts); + +/* + * Serves a specific file with a given MIME type and optional extra headers. + * + * Example code snippet: + * + * ```c + * static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { + * switch (ev) { + * case MG_EV_HTTP_REQUEST: { + * struct http_message *hm = (struct http_message *) ev_data; + * mg_http_serve_file(nc, hm, "file.txt", + * mg_mk_str("text/plain"), mg_mk_str("")); + * break; + * } + * ... + * } + * } + * ``` + */ +void mg_http_serve_file(struct mg_connection *nc, struct http_message *hm, + const char *path, const struct mg_str mime_type, + const struct mg_str extra_headers); + +#if MG_ENABLE_HTTP_STREAMING_MULTIPART + +/* Callback prototype for `mg_file_upload_handler()`. */ +typedef struct mg_str (*mg_fu_fname_fn)(struct mg_connection *nc, + struct mg_str fname); + +/* + * File upload handler. + * This handler can be used to implement file uploads with minimum code. + * This handler will process MG_EV_HTTP_PART_* events and store file data into + * a local file. + * `local_name_fn` will be invoked with whatever name was provided by the client + * and will expect the name of the local file to open. A return value of NULL + * will abort file upload (client will get a "403 Forbidden" response). If + * non-null, the returned string must be heap-allocated and will be freed by + * the caller. + * Exception: it is ok to return the same string verbatim. + * + * Example: + * + * ```c + * struct mg_str upload_fname(struct mg_connection *nc, struct mg_str fname) { + * // Just return the same filename. Do not actually do this except in test! + * // fname is user-controlled and needs to be sanitized. + * return fname; + * } + * void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { + * switch (ev) { + * ... + * case MG_EV_HTTP_PART_BEGIN: + * case MG_EV_HTTP_PART_DATA: + * case MG_EV_HTTP_PART_END: + * mg_file_upload_handler(nc, ev, ev_data, upload_fname); + * break; + * } + * } + * ``` + */ +void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data, + mg_fu_fname_fn local_name_fn + MG_UD_ARG(void *user_data)); +#endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */ +#endif /* MG_ENABLE_FILESYSTEM */ + +/* + * Registers a callback for a specified http endpoint + * Note: if callback is registered it is called instead of the + * callback provided in mg_bind + * + * Example code snippet: + * + * ```c + * static void handle_hello1(struct mg_connection *nc, int ev, void *ev_data) { + * (void) ev; (void) ev_data; + * mg_printf(nc, "HTTP/1.0 200 OK\r\n\r\n[I am Hello1]"); + * nc->flags |= MG_F_SEND_AND_CLOSE; + * } + * + * static void handle_hello2(struct mg_connection *nc, int ev, void *ev_data) { + * (void) ev; (void) ev_data; + * mg_printf(nc, "HTTP/1.0 200 OK\r\n\r\n[I am Hello2]"); + * nc->flags |= MG_F_SEND_AND_CLOSE; + * } + * + * void init() { + * nc = mg_bind(&mgr, local_addr, cb1); + * mg_register_http_endpoint(nc, "/hello1", handle_hello1); + * mg_register_http_endpoint(nc, "/hello1/hello2", handle_hello2); + * } + * ``` + */ +void mg_register_http_endpoint(struct mg_connection *nc, const char *uri_path, + MG_CB(mg_event_handler_t handler, + void *user_data)); + +struct mg_http_endpoint_opts { + void *user_data; + /* Authorization domain (realm) */ + const char *auth_domain; + const char *auth_file; +}; + +void mg_register_http_endpoint_opt(struct mg_connection *nc, + const char *uri_path, + mg_event_handler_t handler, + struct mg_http_endpoint_opts opts); + +/* + * Authenticates a HTTP request against an opened password file. + * Returns 1 if authenticated, 0 otherwise. + */ +int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain, + FILE *fp); + +/* + * Authenticates given response params against an opened password file. + * Returns 1 if authenticated, 0 otherwise. + * + * It's used by mg_http_check_digest_auth(). + */ +int mg_check_digest_auth(struct mg_str method, struct mg_str uri, + struct mg_str username, struct mg_str cnonce, + struct mg_str response, struct mg_str qop, + struct mg_str nc, struct mg_str nonce, + struct mg_str auth_domain, FILE *fp); + +/* + * Sends buffer `buf` of size `len` to the client using chunked HTTP encoding. + * This function sends the buffer size as hex number + newline first, then + * the buffer itself, then the newline. For example, + * `mg_send_http_chunk(nc, "foo", 3)` will append the `3\r\nfoo\r\n` string + * to the `nc->send_mbuf` output IO buffer. + * + * NOTE: The HTTP header "Transfer-Encoding: chunked" should be sent prior to + * using this function. + * + * NOTE: do not forget to send an empty chunk at the end of the response, + * to tell the client that everything was sent. Example: + * + * ``` + * mg_printf_http_chunk(nc, "%s", "my response!"); + * mg_send_http_chunk(nc, "", 0); // Tell the client we're finished + * ``` + */ +void mg_send_http_chunk(struct mg_connection *nc, const char *buf, size_t len); + +/* + * Sends a printf-formatted HTTP chunk. + * Functionality is similar to `mg_send_http_chunk()`. + */ +void mg_printf_http_chunk(struct mg_connection *nc, const char *fmt, ...); + +/* + * Sends the response status line. + * If `extra_headers` is not NULL, then `extra_headers` are also sent + * after the response line. `extra_headers` must NOT end end with new line. + * Example: + * + * mg_send_response_line(nc, 200, "Access-Control-Allow-Origin: *"); + * + * Will result in: + * + * HTTP/1.1 200 OK\r\n + * Access-Control-Allow-Origin: *\r\n + */ +void mg_send_response_line(struct mg_connection *nc, int status_code, + const char *extra_headers); + +/* + * Sends an error response. If reason is NULL, the message will be inferred + * from the error code (if supported). + */ +void mg_http_send_error(struct mg_connection *nc, int code, const char *reason); + +/* + * Sends a redirect response. + * `status_code` should be either 301 or 302 and `location` point to the + * new location. + * If `extra_headers` is not empty, then `extra_headers` are also sent + * after the response line. `extra_headers` must NOT end end with new line. + * + * Example: + * + * mg_http_send_redirect(nc, 302, mg_mk_str("/login"), mg_mk_str(NULL)); + */ +void mg_http_send_redirect(struct mg_connection *nc, int status_code, + const struct mg_str location, + const struct mg_str extra_headers); + +/* + * Sends the response line and headers. + * This function sends the response line with the `status_code`, and + * automatically + * sends one header: either "Content-Length" or "Transfer-Encoding". + * If `content_length` is negative, then "Transfer-Encoding: chunked" header + * is sent, otherwise, "Content-Length" header is sent. + * + * NOTE: If `Transfer-Encoding` is `chunked`, then message body must be sent + * using `mg_send_http_chunk()` or `mg_printf_http_chunk()` functions. + * Otherwise, `mg_send()` or `mg_printf()` must be used. + * Extra headers could be set through `extra_headers`. Note `extra_headers` + * must NOT be terminated by a new line. + */ +void mg_send_head(struct mg_connection *n, int status_code, + int64_t content_length, const char *extra_headers); + +/* + * Sends a printf-formatted HTTP chunk, escaping HTML tags. + */ +void mg_printf_html_escape(struct mg_connection *nc, const char *fmt, ...); + +#if MG_ENABLE_HTTP_URL_REWRITES +/* + * Proxies a given request to a given upstream http server. The path prefix + * in `mount` will be stripped of the path requested to the upstream server, + * e.g. if mount is /api and upstream is http://localhost:8001/foo + * then an incoming request to /api/bar will cause a request to + * http://localhost:8001/foo/bar + * + * EXPERIMENTAL API. Please use http_serve_http + url_rewrites if a static + * mapping is good enough. + */ +void mg_http_reverse_proxy(struct mg_connection *nc, + const struct http_message *hm, struct mg_str mount, + struct mg_str upstream); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* MG_ENABLE_HTTP */ + +#endif /* CS_MONGOOSE_SRC_HTTP_SERVER_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_http_client.h" +#endif +/* + * === Client API reference + */ + +#ifndef CS_MONGOOSE_SRC_HTTP_CLIENT_H_ +#define CS_MONGOOSE_SRC_HTTP_CLIENT_H_ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * Helper function that creates an outbound HTTP connection. + * + * `url` is the URL to fetch. It must be properly URL-encoded, e.g. have + * no spaces, etc. By default, `mg_connect_http()` sends the Connection and + * Host headers. `extra_headers` is an extra HTTP header to send, e.g. + * `"User-Agent: my-app\r\n"`. + * If `post_data` is NULL, then a GET request is created. Otherwise, a POST + * request is created with the specified POST data. Note that if the data being + * posted is a form submission, the `Content-Type` header should be set + * accordingly (see example below). + * + * Examples: + * + * ```c + * nc1 = mg_connect_http(mgr, ev_handler_1, "http://www.google.com", NULL, + * NULL); + * nc2 = mg_connect_http(mgr, ev_handler_1, "https://github.com", NULL, NULL); + * nc3 = mg_connect_http( + * mgr, ev_handler_1, "my_server:8000/form_submit/", + * "Content-Type: application/x-www-form-urlencoded\r\n", + * "var_1=value_1&var_2=value_2"); + * ``` + */ +struct mg_connection *mg_connect_http( + struct mg_mgr *mgr, + MG_CB(mg_event_handler_t event_handler, void *user_data), const char *url, + const char *extra_headers, const char *post_data); + +/* + * Helper function that creates an outbound HTTP connection. + * + * Mostly identical to mg_connect_http, but allows you to provide extra + *parameters + * (for example, SSL parameters) + */ +struct mg_connection *mg_connect_http_opt( + struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data), + struct mg_connect_opts opts, const char *url, const char *extra_headers, + const char *post_data); + +/* Creates digest authentication header for a client request. */ +int mg_http_create_digest_auth_header(char *buf, size_t buf_len, + const char *method, const char *uri, + const char *auth_domain, const char *user, + const char *passwd, const char *nonce); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* CS_MONGOOSE_SRC_HTTP_CLIENT_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_mqtt.h" +#endif +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + * This software is dual-licensed: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. For the terms of this + * license, see . + * + * You are free to use this software under the terms of the GNU General + * Public License, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * Alternatively, you can license this software under a commercial + * license, as set out in . + */ + +/* + * === MQTT API reference + */ + +#ifndef CS_MONGOOSE_SRC_MQTT_H_ +#define CS_MONGOOSE_SRC_MQTT_H_ + +/* Amalgamated: #include "mg_net.h" */ + +struct mg_mqtt_message { + int cmd; + int qos; + int len; /* message length in the IO buffer */ + struct mg_str topic; + struct mg_str payload; + + uint8_t connack_ret_code; /* connack */ + uint16_t message_id; /* puback */ + + /* connect */ + uint8_t protocol_version; + uint8_t connect_flags; + uint16_t keep_alive_timer; + struct mg_str protocol_name; + struct mg_str client_id; + struct mg_str will_topic; + struct mg_str will_message; + struct mg_str user_name; + struct mg_str password; +}; + +struct mg_mqtt_topic_expression { + const char *topic; + uint8_t qos; +}; + +struct mg_send_mqtt_handshake_opts { + unsigned char flags; /* connection flags */ + uint16_t keep_alive; + const char *will_topic; + const char *will_message; + const char *user_name; + const char *password; +}; + +/* mg_mqtt_proto_data should be in header to allow external access to it */ +struct mg_mqtt_proto_data { + uint16_t keep_alive; + double last_control_time; +}; + +/* Message types */ +#define MG_MQTT_CMD_CONNECT 1 +#define MG_MQTT_CMD_CONNACK 2 +#define MG_MQTT_CMD_PUBLISH 3 +#define MG_MQTT_CMD_PUBACK 4 +#define MG_MQTT_CMD_PUBREC 5 +#define MG_MQTT_CMD_PUBREL 6 +#define MG_MQTT_CMD_PUBCOMP 7 +#define MG_MQTT_CMD_SUBSCRIBE 8 +#define MG_MQTT_CMD_SUBACK 9 +#define MG_MQTT_CMD_UNSUBSCRIBE 10 +#define MG_MQTT_CMD_UNSUBACK 11 +#define MG_MQTT_CMD_PINGREQ 12 +#define MG_MQTT_CMD_PINGRESP 13 +#define MG_MQTT_CMD_DISCONNECT 14 + +/* MQTT event types */ +#define MG_MQTT_EVENT_BASE 200 +#define MG_EV_MQTT_CONNECT (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_CONNECT) +#define MG_EV_MQTT_CONNACK (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_CONNACK) +#define MG_EV_MQTT_PUBLISH (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_PUBLISH) +#define MG_EV_MQTT_PUBACK (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_PUBACK) +#define MG_EV_MQTT_PUBREC (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_PUBREC) +#define MG_EV_MQTT_PUBREL (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_PUBREL) +#define MG_EV_MQTT_PUBCOMP (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_PUBCOMP) +#define MG_EV_MQTT_SUBSCRIBE (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_SUBSCRIBE) +#define MG_EV_MQTT_SUBACK (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_SUBACK) +#define MG_EV_MQTT_UNSUBSCRIBE (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_UNSUBSCRIBE) +#define MG_EV_MQTT_UNSUBACK (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_UNSUBACK) +#define MG_EV_MQTT_PINGREQ (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_PINGREQ) +#define MG_EV_MQTT_PINGRESP (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_PINGRESP) +#define MG_EV_MQTT_DISCONNECT (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_DISCONNECT) + +/* Message flags */ +#define MG_MQTT_RETAIN 0x1 +#define MG_MQTT_DUP 0x4 +#define MG_MQTT_QOS(qos) ((qos) << 1) +#define MG_MQTT_GET_QOS(flags) (((flags) &0x6) >> 1) +#define MG_MQTT_SET_QOS(flags, qos) (flags) = ((flags) & ~0x6) | ((qos) << 1) + +/* Connection flags */ +#define MG_MQTT_CLEAN_SESSION 0x02 +#define MG_MQTT_HAS_WILL 0x04 +#define MG_MQTT_WILL_RETAIN 0x20 +#define MG_MQTT_HAS_PASSWORD 0x40 +#define MG_MQTT_HAS_USER_NAME 0x80 +#define MG_MQTT_GET_WILL_QOS(flags) (((flags) &0x18) >> 3) +#define MG_MQTT_SET_WILL_QOS(flags, qos) \ + (flags) = ((flags) & ~0x18) | ((qos) << 3) + +/* CONNACK return codes */ +#define MG_EV_MQTT_CONNACK_ACCEPTED 0 +#define MG_EV_MQTT_CONNACK_UNACCEPTABLE_VERSION 1 +#define MG_EV_MQTT_CONNACK_IDENTIFIER_REJECTED 2 +#define MG_EV_MQTT_CONNACK_SERVER_UNAVAILABLE 3 +#define MG_EV_MQTT_CONNACK_BAD_AUTH 4 +#define MG_EV_MQTT_CONNACK_NOT_AUTHORIZED 5 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * Attaches a built-in MQTT event handler to the given connection. + * + * The user-defined event handler will receive following extra events: + * + * - MG_EV_MQTT_CONNACK + * - MG_EV_MQTT_PUBLISH + * - MG_EV_MQTT_PUBACK + * - MG_EV_MQTT_PUBREC + * - MG_EV_MQTT_PUBREL + * - MG_EV_MQTT_PUBCOMP + * - MG_EV_MQTT_SUBACK + */ +void mg_set_protocol_mqtt(struct mg_connection *nc); + +/* Sends an MQTT handshake. */ +void mg_send_mqtt_handshake(struct mg_connection *nc, const char *client_id); + +/* Sends an MQTT handshake with optional parameters. */ +void mg_send_mqtt_handshake_opt(struct mg_connection *nc, const char *client_id, + struct mg_send_mqtt_handshake_opts); + +/* Publishes a message to a given topic. */ +void mg_mqtt_publish(struct mg_connection *nc, const char *topic, + uint16_t message_id, int flags, const void *data, + size_t len); + +/* Subscribes to a bunch of topics. */ +void mg_mqtt_subscribe(struct mg_connection *nc, + const struct mg_mqtt_topic_expression *topics, + size_t topics_len, uint16_t message_id); + +/* Unsubscribes from a bunch of topics. */ +void mg_mqtt_unsubscribe(struct mg_connection *nc, char **topics, + size_t topics_len, uint16_t message_id); + +/* Sends a DISCONNECT command. */ +void mg_mqtt_disconnect(struct mg_connection *nc); + +/* Sends a CONNACK command with a given `return_code`. */ +void mg_mqtt_connack(struct mg_connection *nc, uint8_t return_code); + +/* Sends a PUBACK command with a given `message_id`. */ +void mg_mqtt_puback(struct mg_connection *nc, uint16_t message_id); + +/* Sends a PUBREC command with a given `message_id`. */ +void mg_mqtt_pubrec(struct mg_connection *nc, uint16_t message_id); + +/* Sends a PUBREL command with a given `message_id`. */ +void mg_mqtt_pubrel(struct mg_connection *nc, uint16_t message_id); + +/* Sends a PUBCOMP command with a given `message_id`. */ +void mg_mqtt_pubcomp(struct mg_connection *nc, uint16_t message_id); + +/* + * Sends a SUBACK command with a given `message_id` + * and a sequence of granted QoSs. + */ +void mg_mqtt_suback(struct mg_connection *nc, uint8_t *qoss, size_t qoss_len, + uint16_t message_id); + +/* Sends a UNSUBACK command with a given `message_id`. */ +void mg_mqtt_unsuback(struct mg_connection *nc, uint16_t message_id); + +/* Sends a PINGREQ command. */ +void mg_mqtt_ping(struct mg_connection *nc); + +/* Sends a PINGRESP command. */ +void mg_mqtt_pong(struct mg_connection *nc); + +/* + * Extracts the next topic expression from a SUBSCRIBE command payload. + * + * The topic expression name will point to a string in the payload buffer. + * Returns the pos of the next topic expression or -1 when the list + * of topics is exhausted. + */ +int mg_mqtt_next_subscribe_topic(struct mg_mqtt_message *msg, + struct mg_str *topic, uint8_t *qos, int pos); + +/* + * Matches a topic against a topic expression + * + * Returns 1 if it matches; 0 otherwise. + */ +int mg_mqtt_match_topic_expression(struct mg_str exp, struct mg_str topic); + +/* + * Same as `mg_mqtt_match_topic_expression()`, but takes `exp` as a + * NULL-terminated string. + */ +int mg_mqtt_vmatch_topic_expression(const char *exp, struct mg_str topic); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CS_MONGOOSE_SRC_MQTT_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_mqtt_server.h" +#endif +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + * This software is dual-licensed: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. For the terms of this + * license, see . + * + * You are free to use this software under the terms of the GNU General + * Public License, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * Alternatively, you can license this software under a commercial + * license, as set out in . + */ + +/* + * === MQTT Server API reference + */ + +#ifndef CS_MONGOOSE_SRC_MQTT_BROKER_H_ +#define CS_MONGOOSE_SRC_MQTT_BROKER_H_ + +#if MG_ENABLE_MQTT_BROKER + +/* Amalgamated: #include "common/queue.h" */ +/* Amalgamated: #include "mg_mqtt.h" */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef MG_MQTT_MAX_SESSION_SUBSCRIPTIONS +#define MG_MQTT_MAX_SESSION_SUBSCRIPTIONS 512 +#endif + +struct mg_mqtt_broker; + +/* MQTT session (Broker side). */ +struct mg_mqtt_session { + struct mg_mqtt_broker *brk; /* Broker */ + LIST_ENTRY(mg_mqtt_session) link; /* mg_mqtt_broker::sessions linkage */ + struct mg_connection *nc; /* Connection with the client */ + size_t num_subscriptions; /* Size of `subscriptions` array */ + void *user_data; /* User data */ + struct mg_mqtt_topic_expression *subscriptions; +}; + +/* MQTT broker. */ +struct mg_mqtt_broker { + LIST_HEAD(_mg_sesshead, mg_mqtt_session) sessions; /* Session list */ + void *user_data; /* User data */ +}; + +/* Initialises a MQTT broker. */ +void mg_mqtt_broker_init(struct mg_mqtt_broker *brk, void *user_data); + +/* + * Processes a MQTT broker message. + * + * The listening connection expects a pointer to an initialised + * `mg_mqtt_broker` structure in the `user_data` field. + * + * Basic usage: + * + * ```c + * mg_mqtt_broker_init(&brk, NULL); + * + * if ((nc = mg_bind(&mgr, address, mg_mqtt_broker)) == NULL) { + * // fail; + * } + * nc->user_data = &brk; + * ``` + * + * New incoming connections will receive a `mg_mqtt_session` structure + * in the connection `user_data`. The original `user_data` will be stored + * in the `user_data` field of the session structure. This allows the user + * handler to store user data before `mg_mqtt_broker` creates the session. + * + * Since only the MG_EV_ACCEPT message is processed by the listening socket, + * for most events the `user_data` will thus point to a `mg_mqtt_session`. + */ +void mg_mqtt_broker(struct mg_connection *brk, int ev, void *data); + +/* + * Iterates over all MQTT session connections. Example: + * + * ```c + * struct mg_mqtt_session *s; + * for (s = mg_mqtt_next(brk, NULL); s != NULL; s = mg_mqtt_next(brk, s)) { + * // Do something + * } + * ``` + */ +struct mg_mqtt_session *mg_mqtt_next(struct mg_mqtt_broker *brk, + struct mg_mqtt_session *s); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* MG_ENABLE_MQTT_BROKER */ +#endif /* CS_MONGOOSE_SRC_MQTT_BROKER_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_dns.h" +#endif +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +/* + * === DNS API reference + */ + +#ifndef CS_MONGOOSE_SRC_DNS_H_ +#define CS_MONGOOSE_SRC_DNS_H_ + +/* Amalgamated: #include "mg_net.h" */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define MG_DNS_A_RECORD 0x01 /* Lookup IP address */ +#define MG_DNS_CNAME_RECORD 0x05 /* Lookup CNAME */ +#define MG_DNS_PTR_RECORD 0x0c /* Lookup PTR */ +#define MG_DNS_TXT_RECORD 0x10 /* Lookup TXT */ +#define MG_DNS_AAAA_RECORD 0x1c /* Lookup IPv6 address */ +#define MG_DNS_SRV_RECORD 0x21 /* Lookup SRV */ +#define MG_DNS_MX_RECORD 0x0f /* Lookup mail server for domain */ +#define MG_DNS_ANY_RECORD 0xff +#define MG_DNS_NSEC_RECORD 0x2f + +#define MG_MAX_DNS_QUESTIONS 32 +#define MG_MAX_DNS_ANSWERS 32 + +#define MG_DNS_MESSAGE 100 /* High-level DNS message event */ + +enum mg_dns_resource_record_kind { + MG_DNS_INVALID_RECORD = 0, + MG_DNS_QUESTION, + MG_DNS_ANSWER +}; + +/* DNS resource record. */ +struct mg_dns_resource_record { + struct mg_str name; /* buffer with compressed name */ + int rtype; + int rclass; + int ttl; + enum mg_dns_resource_record_kind kind; + struct mg_str rdata; /* protocol data (can be a compressed name) */ +}; + +/* DNS message (request and response). */ +struct mg_dns_message { + struct mg_str pkt; /* packet body */ + uint16_t flags; + uint16_t transaction_id; + int num_questions; + int num_answers; + struct mg_dns_resource_record questions[MG_MAX_DNS_QUESTIONS]; + struct mg_dns_resource_record answers[MG_MAX_DNS_ANSWERS]; +}; + +struct mg_dns_resource_record *mg_dns_next_record( + struct mg_dns_message *msg, int query, struct mg_dns_resource_record *prev); + +/* + * Parses the record data from a DNS resource record. + * + * - A: struct in_addr *ina + * - AAAA: struct in6_addr *ina + * - CNAME: char buffer + * + * Returns -1 on error. + * + * TODO(mkm): MX + */ +int mg_dns_parse_record_data(struct mg_dns_message *msg, + struct mg_dns_resource_record *rr, void *data, + size_t data_len); + +/* + * Sends a DNS query to the remote end. + */ +void mg_send_dns_query(struct mg_connection *nc, const char *name, + int query_type); + +/* + * Inserts a DNS header to an IO buffer. + * + * Returns the number of bytes inserted. + */ +int mg_dns_insert_header(struct mbuf *io, size_t pos, + struct mg_dns_message *msg); + +/* + * Appends already encoded questions from an existing message. + * + * This is useful when generating a DNS reply message which includes + * all question records. + * + * Returns the number of appended bytes. + */ +int mg_dns_copy_questions(struct mbuf *io, struct mg_dns_message *msg); + +/* + * Encodes and appends a DNS resource record to an IO buffer. + * + * The record metadata is taken from the `rr` parameter, while the name and data + * are taken from the parameters, encoded in the appropriate format depending on + * record type and stored in the IO buffer. The encoded values might contain + * offsets within the IO buffer. It's thus important that the IO buffer doesn't + * get trimmed while a sequence of records are encoded while preparing a DNS + * reply. + * + * This function doesn't update the `name` and `rdata` pointers in the `rr` + * struct because they might be invalidated as soon as the IO buffer grows + * again. + * + * Returns the number of bytes appended or -1 in case of error. + */ +int mg_dns_encode_record(struct mbuf *io, struct mg_dns_resource_record *rr, + const char *name, size_t nlen, const void *rdata, + size_t rlen); + +/* + * Encodes a DNS name. + */ +int mg_dns_encode_name(struct mbuf *io, const char *name, size_t len); + +/* Low-level: parses a DNS response. */ +int mg_parse_dns(const char *buf, int len, struct mg_dns_message *msg); + +/* + * Uncompresses a DNS compressed name. + * + * The containing DNS message is required because of the compressed encoding + * and reference suffixes present elsewhere in the packet. + * + * If the name is less than `dst_len` characters long, the remainder + * of `dst` is terminated with `\0` characters. Otherwise, `dst` is not + * terminated. + * + * If `dst_len` is 0 `dst` can be NULL. + * Returns the uncompressed name length. + */ +size_t mg_dns_uncompress_name(struct mg_dns_message *msg, struct mg_str *name, + char *dst, int dst_len); + +/* + * Attaches a built-in DNS event handler to the given listening connection. + * + * The DNS event handler parses the incoming UDP packets, treating them as DNS + * requests. If an incoming packet gets successfully parsed by the DNS event + * handler, a user event handler will receive an `MG_DNS_REQUEST` event, with + * `ev_data` pointing to the parsed `struct mg_dns_message`. + * + * See + * [captive_dns_server](https://github.com/cesanta/mongoose/tree/master/examples/captive_dns_server) + * example on how to handle DNS request and send DNS reply. + */ +void mg_set_protocol_dns(struct mg_connection *nc); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* CS_MONGOOSE_SRC_DNS_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_dns_server.h" +#endif +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +/* + * === DNS server API reference + * + * Disabled by default; enable with `-DMG_ENABLE_DNS_SERVER`. + */ + +#ifndef CS_MONGOOSE_SRC_DNS_SERVER_H_ +#define CS_MONGOOSE_SRC_DNS_SERVER_H_ + +#if MG_ENABLE_DNS_SERVER + +/* Amalgamated: #include "mg_dns.h" */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define MG_DNS_SERVER_DEFAULT_TTL 3600 + +struct mg_dns_reply { + struct mg_dns_message *msg; + struct mbuf *io; + size_t start; +}; + +/* + * Creates a DNS reply. + * + * The reply will be based on an existing query message `msg`. + * The query body will be appended to the output buffer. + * "reply + recursion allowed" will be added to the message flags and the + * message's num_answers will be set to 0. + * + * Answer records can be appended with `mg_dns_send_reply` or by lower + * level function defined in the DNS API. + * + * In order to send a reply use `mg_dns_send_reply`. + * It's possible to use a connection's send buffer as reply buffer, + * and it will work for both UDP and TCP connections. + * + * Example: + * + * ```c + * reply = mg_dns_create_reply(&nc->send_mbuf, msg); + * for (i = 0; i < msg->num_questions; i++) { + * rr = &msg->questions[i]; + * if (rr->rtype == MG_DNS_A_RECORD) { + * mg_dns_reply_record(&reply, rr, 3600, &dummy_ip_addr, 4); + * } + * } + * mg_dns_send_reply(nc, &reply); + * ``` + */ +struct mg_dns_reply mg_dns_create_reply(struct mbuf *io, + struct mg_dns_message *msg); + +/* + * Appends a DNS reply record to the IO buffer and to the DNS message. + * + * The message's num_answers field will be incremented. It's the caller's duty + * to ensure num_answers is properly initialised. + * + * Returns -1 on error. + */ +int mg_dns_reply_record(struct mg_dns_reply *reply, + struct mg_dns_resource_record *question, + const char *name, int rtype, int ttl, const void *rdata, + size_t rdata_len); + +/* + * Sends a DNS reply through a connection. + * + * The DNS data is stored in an IO buffer pointed by reply structure in `r`. + * This function mutates the content of that buffer in order to ensure that + * the DNS header reflects the size and flags of the message, that might have + * been updated either with `mg_dns_reply_record` or by direct manipulation of + * `r->message`. + * + * Once sent, the IO buffer will be trimmed unless the reply IO buffer + * is the connection's send buffer and the connection is not in UDP mode. + */ +void mg_dns_send_reply(struct mg_connection *nc, struct mg_dns_reply *r); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* MG_ENABLE_DNS_SERVER */ +#endif /* CS_MONGOOSE_SRC_DNS_SERVER_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_resolv.h" +#endif +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +/* + * === API reference + */ + +#ifndef CS_MONGOOSE_SRC_RESOLV_H_ +#define CS_MONGOOSE_SRC_RESOLV_H_ + +/* Amalgamated: #include "mg_dns.h" */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +enum mg_resolve_err { + MG_RESOLVE_OK = 0, + MG_RESOLVE_NO_ANSWERS = 1, + MG_RESOLVE_EXCEEDED_RETRY_COUNT = 2, + MG_RESOLVE_TIMEOUT = 3 +}; + +typedef void (*mg_resolve_callback_t)(struct mg_dns_message *dns_message, + void *user_data, enum mg_resolve_err); + +/* Options for `mg_resolve_async_opt`. */ +struct mg_resolve_async_opts { + const char *nameserver; + int max_retries; /* defaults to 2 if zero */ + int timeout; /* in seconds; defaults to 5 if zero */ + int accept_literal; /* pseudo-resolve literal ipv4 and ipv6 addrs */ + int only_literal; /* only resolves literal addrs; sync cb invocation */ + struct mg_connection **dns_conn; /* return DNS connection */ +}; + +/* See `mg_resolve_async_opt()` */ +int mg_resolve_async(struct mg_mgr *mgr, const char *name, int query, + mg_resolve_callback_t cb, void *data); + +/* Set default DNS server */ +void mg_set_nameserver(struct mg_mgr *mgr, const char *nameserver); + +/* + * Resolved a DNS name asynchronously. + * + * Upon successful resolution, the user callback will be invoked + * with the full DNS response message and a pointer to the user's + * context `data`. + * + * In case of timeout while performing the resolution the callback + * will receive a NULL `msg`. + * + * The DNS answers can be extracted with `mg_next_record` and + * `mg_dns_parse_record_data`: + * + * [source,c] + * ---- + * struct in_addr ina; + * struct mg_dns_resource_record *rr = mg_next_record(msg, MG_DNS_A_RECORD, + * NULL); + * mg_dns_parse_record_data(msg, rr, &ina, sizeof(ina)); + * ---- + */ +int mg_resolve_async_opt(struct mg_mgr *mgr, const char *name, int query, + mg_resolve_callback_t cb, void *data, + struct mg_resolve_async_opts opts); + +/* + * Resolve a name from `/etc/hosts`. + * + * Returns 0 on success, -1 on failure. + */ +int mg_resolve_from_hosts_file(const char *host, union socket_address *usa); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* CS_MONGOOSE_SRC_RESOLV_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_coap.h" +#endif +/* + * Copyright (c) 2015 Cesanta Software Limited + * All rights reserved + * This software is dual-licensed: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. For the terms of this + * license, see . + * + * You are free to use this software under the terms of the GNU General + * Public License, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * Alternatively, you can license this software under a commercial + * license, as set out in . + */ + +/* + * === CoAP API reference + * + * CoAP message format: + * + * ``` + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + * |Ver| T | TKL | Code | Message ID | Token (if any, TKL bytes) ... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + * | Options (if any) ... |1 1 1 1 1 1 1 1| Payload (if any) ... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + * ``` + */ + +#ifndef CS_MONGOOSE_SRC_COAP_H_ +#define CS_MONGOOSE_SRC_COAP_H_ + +#if MG_ENABLE_COAP + +#define MG_COAP_MSG_TYPE_FIELD 0x2 +#define MG_COAP_CODE_CLASS_FIELD 0x4 +#define MG_COAP_CODE_DETAIL_FIELD 0x8 +#define MG_COAP_MSG_ID_FIELD 0x10 +#define MG_COAP_TOKEN_FIELD 0x20 +#define MG_COAP_OPTIOMG_FIELD 0x40 +#define MG_COAP_PAYLOAD_FIELD 0x80 + +#define MG_COAP_ERROR 0x10000 +#define MG_COAP_FORMAT_ERROR (MG_COAP_ERROR | 0x20000) +#define MG_COAP_IGNORE (MG_COAP_ERROR | 0x40000) +#define MG_COAP_NOT_ENOUGH_DATA (MG_COAP_ERROR | 0x80000) +#define MG_COAP_NETWORK_ERROR (MG_COAP_ERROR | 0x100000) + +#define MG_COAP_MSG_CON 0 +#define MG_COAP_MSG_NOC 1 +#define MG_COAP_MSG_ACK 2 +#define MG_COAP_MSG_RST 3 +#define MG_COAP_MSG_MAX 3 + +#define MG_COAP_CODECLASS_REQUEST 0 +#define MG_COAP_CODECLASS_RESP_OK 2 +#define MG_COAP_CODECLASS_CLIENT_ERR 4 +#define MG_COAP_CODECLASS_SRV_ERR 5 + +#define MG_COAP_EVENT_BASE 300 +#define MG_EV_COAP_CON (MG_COAP_EVENT_BASE + MG_COAP_MSG_CON) +#define MG_EV_COAP_NOC (MG_COAP_EVENT_BASE + MG_COAP_MSG_NOC) +#define MG_EV_COAP_ACK (MG_COAP_EVENT_BASE + MG_COAP_MSG_ACK) +#define MG_EV_COAP_RST (MG_COAP_EVENT_BASE + MG_COAP_MSG_RST) + +/* + * CoAP options. + * Use mg_coap_add_option and mg_coap_free_options + * for creation and destruction. + */ +struct mg_coap_option { + struct mg_coap_option *next; + uint32_t number; + struct mg_str value; +}; + +/* CoAP message. See RFC 7252 for details. */ +struct mg_coap_message { + uint32_t flags; + uint8_t msg_type; + uint8_t code_class; + uint8_t code_detail; + uint16_t msg_id; + struct mg_str token; + struct mg_coap_option *options; + struct mg_str payload; + struct mg_coap_option *optiomg_tail; +}; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Sets CoAP protocol handler - triggers CoAP specific events. */ +int mg_set_protocol_coap(struct mg_connection *nc); + +/* + * Adds a new option to mg_coap_message structure. + * Returns pointer to the newly created option. + * Note: options must be freed by using mg_coap_free_options + */ +struct mg_coap_option *mg_coap_add_option(struct mg_coap_message *cm, + uint32_t number, char *value, + size_t len); + +/* + * Frees the memory allocated for options. + * If the cm parameter doesn't contain any option it does nothing. + */ +void mg_coap_free_options(struct mg_coap_message *cm); + +/* + * Composes a CoAP message from `mg_coap_message` + * and sends it into `nc` connection. + * Returns 0 on success. On error, it is a bitmask: + * + * - `#define MG_COAP_ERROR 0x10000` + * - `#define MG_COAP_FORMAT_ERROR (MG_COAP_ERROR | 0x20000)` + * - `#define MG_COAP_IGNORE (MG_COAP_ERROR | 0x40000)` + * - `#define MG_COAP_NOT_ENOUGH_DATA (MG_COAP_ERROR | 0x80000)` + * - `#define MG_COAP_NETWORK_ERROR (MG_COAP_ERROR | 0x100000)` + */ +uint32_t mg_coap_send_message(struct mg_connection *nc, + struct mg_coap_message *cm); + +/* + * Composes CoAP acknowledgement from `mg_coap_message` + * and sends it into `nc` connection. + * Return value: see `mg_coap_send_message()` + */ +uint32_t mg_coap_send_ack(struct mg_connection *nc, uint16_t msg_id); + +/* + * Parses CoAP message and fills mg_coap_message and returns cm->flags. + * This is a helper function. + * + * NOTE: usually CoAP works over UDP, so lack of data means format error. + * But, in theory, it is possible to use CoAP over TCP (according to RFC) + * + * The caller has to check results and treat COAP_NOT_ENOUGH_DATA according to + * underlying protocol: + * + * - in case of UDP COAP_NOT_ENOUGH_DATA means COAP_FORMAT_ERROR, + * - in case of TCP client can try to receive more data + * + * Return value: see `mg_coap_send_message()` + */ +uint32_t mg_coap_parse(struct mbuf *io, struct mg_coap_message *cm); + +/* + * Composes CoAP message from mg_coap_message structure. + * This is a helper function. + * Return value: see `mg_coap_send_message()` + */ +uint32_t mg_coap_compose(struct mg_coap_message *cm, struct mbuf *io); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* MG_ENABLE_COAP */ + +#endif /* CS_MONGOOSE_SRC_COAP_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_sntp.h" +#endif +/* + * Copyright (c) 2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_MONGOOSE_SRC_SNTP_H_ +#define CS_MONGOOSE_SRC_SNTP_H_ + +#if MG_ENABLE_SNTP + +#define MG_SNTP_EVENT_BASE 500 + +/* + * Received reply from time server. Event handler parameter contains + * pointer to mg_sntp_message structure + */ +#define MG_SNTP_REPLY (MG_SNTP_EVENT_BASE + 1) + +/* Received malformed SNTP packet */ +#define MG_SNTP_MALFORMED_REPLY (MG_SNTP_EVENT_BASE + 2) + +/* Failed to get time from server (timeout etc) */ +#define MG_SNTP_FAILED (MG_SNTP_EVENT_BASE + 3) + +struct mg_sntp_message { + /* if server sends this flags, user should not send requests to it */ + int kiss_of_death; + /* usual mg_time */ + double time; +}; + +/* Establishes connection to given sntp server */ +struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, + MG_CB(mg_event_handler_t event_handler, + void *user_data), + const char *sntp_server_name); + +/* Sends time request to given connection */ +void mg_sntp_send_request(struct mg_connection *c); + +/* + * Helper function + * Establishes connection to time server, tries to send request + * repeats sending SNTP_ATTEMPTS times every SNTP_TIMEOUT sec + * (if needed) + * See sntp_client example + */ +struct mg_connection *mg_sntp_get_time(struct mg_mgr *mgr, + mg_event_handler_t event_handler, + const char *sntp_server_name); + +#endif + +#endif /* CS_MONGOOSE_SRC_SNTP_H_ */ +#ifdef MG_MODULE_LINES +#line 1 "mongoose/src/mg_socks.h" +#endif +/* + * Copyright (c) 2017 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_MONGOOSE_SRC_SOCKS_H_ +#define CS_MONGOOSE_SRC_SOCKS_H_ + +#if MG_ENABLE_SOCKS + +#define MG_SOCKS_VERSION 5 + +#define MG_SOCKS_HANDSHAKE_DONE MG_F_USER_1 +#define MG_SOCKS_CONNECT_DONE MG_F_USER_2 + +/* SOCKS5 handshake methods */ +enum mg_socks_handshake_method { + MG_SOCKS_HANDSHAKE_NOAUTH = 0, /* Handshake method - no authentication */ + MG_SOCKS_HANDSHAKE_GSSAPI = 1, /* Handshake method - GSSAPI auth */ + MG_SOCKS_HANDSHAKE_USERPASS = 2, /* Handshake method - user/password auth */ + MG_SOCKS_HANDSHAKE_FAILURE = 0xff, /* Handshake method - failure */ +}; + +/* SOCKS5 commands */ +enum mg_socks_command { + MG_SOCKS_CMD_CONNECT = 1, /* Command: CONNECT */ + MG_SOCKS_CMD_BIND = 2, /* Command: BIND */ + MG_SOCKS_CMD_UDP_ASSOCIATE = 3, /* Command: UDP ASSOCIATE */ +}; + +/* SOCKS5 address types */ +enum mg_socks_address_type { + MG_SOCKS_ADDR_IPV4 = 1, /* Address type: IPv4 */ + MG_SOCKS_ADDR_DOMAIN = 3, /* Address type: Domain name */ + MG_SOCKS_ADDR_IPV6 = 4, /* Address type: IPv6 */ +}; + +/* SOCKS5 response codes */ +enum mg_socks_response { + MG_SOCKS_SUCCESS = 0, /* Response: success */ + MG_SOCKS_FAILURE = 1, /* Response: failure */ + MG_SOCKS_NOT_ALLOWED = 2, /* Response: connection not allowed */ + MG_SOCKS_NET_UNREACHABLE = 3, /* Response: network unreachable */ + MG_SOCKS_HOST_UNREACHABLE = 4, /* Response: network unreachable */ + MG_SOCKS_CONN_REFUSED = 5, /* Response: network unreachable */ + MG_SOCKS_TTL_EXPIRED = 6, /* Response: network unreachable */ + MG_SOCKS_CMD_NOT_SUPPORTED = 7, /* Response: network unreachable */ + MG_SOCKS_ADDR_NOT_SUPPORTED = 8, /* Response: network unreachable */ +}; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Turn the connection into the SOCKS server */ +void mg_set_protocol_socks(struct mg_connection *c); + +/* Create socks tunnel for the client connection */ +struct mg_iface *mg_socks_mk_iface(struct mg_mgr *, const char *proxy_addr); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif +#endif diff --git a/src/mongoose-6.11/src/CPPLINT.cfg b/src/mongoose-6.11/src/CPPLINT.cfg new file mode 100644 index 0000000..6809e55 --- /dev/null +++ b/src/mongoose-6.11/src/CPPLINT.cfg @@ -0,0 +1 @@ +exclude_files=sha1\.c diff --git a/src/mongoose-6.11/src/common/cs_dbg.c b/src/mongoose-6.11/src/common/cs_dbg.c new file mode 100644 index 0000000..da1bc2e --- /dev/null +++ b/src/mongoose-6.11/src/common/cs_dbg.c @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#include "common/cs_dbg.h" + +#include +#include +#include + +#include "common/cs_time.h" +#include "common/str_util.h" + +enum cs_log_level cs_log_threshold WEAK = +#if CS_ENABLE_DEBUG + LL_VERBOSE_DEBUG; +#else + LL_ERROR; +#endif + +static char *s_filter_pattern = NULL; +static size_t s_filter_pattern_len; + +void cs_log_set_filter(const char *pattern) WEAK; + +#if CS_ENABLE_STDIO + +FILE *cs_log_file WEAK = NULL; + +#if CS_LOG_ENABLE_TS_DIFF +double cs_log_ts WEAK; +#endif + +enum cs_log_level cs_log_cur_msg_level WEAK = LL_NONE; + +void cs_log_set_filter(const char *pattern) { + free(s_filter_pattern); + if (pattern != NULL) { + s_filter_pattern = strdup(pattern); + s_filter_pattern_len = strlen(pattern); + } else { + s_filter_pattern = NULL; + s_filter_pattern_len = 0; + } +} + +int cs_log_print_prefix(enum cs_log_level, const char *, const char *) WEAK; +int cs_log_print_prefix(enum cs_log_level level, const char *func, + const char *filename) { + char prefix[21]; + + if (level > cs_log_threshold) return 0; + if (s_filter_pattern != NULL && + mg_match_prefix(s_filter_pattern, s_filter_pattern_len, func) == 0 && + mg_match_prefix(s_filter_pattern, s_filter_pattern_len, filename) == 0) { + return 0; + } + + strncpy(prefix, func, 20); + prefix[20] = '\0'; + if (cs_log_file == NULL) cs_log_file = stderr; + cs_log_cur_msg_level = level; + fprintf(cs_log_file, "%-20s ", prefix); +#if CS_LOG_ENABLE_TS_DIFF + { + double now = cs_time(); + fprintf(cs_log_file, "%7u ", (unsigned int) ((now - cs_log_ts) * 1000000)); + cs_log_ts = now; + } +#endif + return 1; +} + +void cs_log_printf(const char *fmt, ...) WEAK; +void cs_log_printf(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vfprintf(cs_log_file, fmt, ap); + va_end(ap); + fputc('\n', cs_log_file); + fflush(cs_log_file); + cs_log_cur_msg_level = LL_NONE; +} + +void cs_log_set_file(FILE *file) WEAK; +void cs_log_set_file(FILE *file) { + cs_log_file = file; +} + +#else + +void cs_log_set_filter(const char *pattern) { + (void) pattern; +} + +#endif /* CS_ENABLE_STDIO */ + +void cs_log_set_level(enum cs_log_level level) WEAK; +void cs_log_set_level(enum cs_log_level level) { + cs_log_threshold = level; +#if CS_LOG_ENABLE_TS_DIFF && CS_ENABLE_STDIO + cs_log_ts = cs_time(); +#endif +} diff --git a/src/mongoose-6.11/src/common/cs_dbg.h b/src/mongoose-6.11/src/common/cs_dbg.h new file mode 100644 index 0000000..24a7022 --- /dev/null +++ b/src/mongoose-6.11/src/common/cs_dbg.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_CS_DBG_H_ +#define CS_COMMON_CS_DBG_H_ + +#include "common/platform.h" + +#if CS_ENABLE_STDIO +#include +#endif + +#ifndef CS_ENABLE_DEBUG +#define CS_ENABLE_DEBUG 0 +#endif + +#ifndef CS_LOG_ENABLE_TS_DIFF +#define CS_LOG_ENABLE_TS_DIFF 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * Log level; `LL_INFO` is the default. Use `cs_log_set_level()` to change it. + */ +enum cs_log_level { + LL_NONE = -1, + LL_ERROR = 0, + LL_WARN = 1, + LL_INFO = 2, + LL_DEBUG = 3, + LL_VERBOSE_DEBUG = 4, + + _LL_MIN = -2, + _LL_MAX = 5, +}; + +/* + * Set max log level to print; messages with the level above the given one will + * not be printed. + */ +void cs_log_set_level(enum cs_log_level level); + +/* + * Set log filter. NULL (a default) logs everything. + * Otherwise, function name and file name will be tested against the given + * pattern, and only matching messages will be printed. + * + * For the pattern syntax, refer to `mg_match_prefix()` in `str_util.h`. + * + * Example: + * ```c + * void foo(void) { + * LOG(LL_INFO, ("hello from foo")); + * } + * + * void bar(void) { + * LOG(LL_INFO, ("hello from bar")); + * } + * + * void test(void) { + * cs_log_set_filter(NULL); + * foo(); + * bar(); + * + * cs_log_set_filter("f*"); + * foo(); + * bar(); // Will NOT print anything + * + * cs_log_set_filter("bar"); + * foo(); // Will NOT print anything + * bar(); + * } + * ``` + */ +void cs_log_set_filter(const char *pattern); + +/* + * Helper function which prints message prefix with the given `level`, function + * name `func` and `filename`. If message should be printed (accordingly to the + * current log level and filter), prints the prefix and returns 1, otherwise + * returns 0. + * + * Clients should typically just use `LOG()` macro. + */ +int cs_log_print_prefix(enum cs_log_level level, const char *func, + const char *filename); + +extern enum cs_log_level cs_log_threshold; + +#if CS_ENABLE_STDIO + +/* + * Set file to write logs into. If `NULL`, logs go to `stderr`. + */ +void cs_log_set_file(FILE *file); + +/* + * Prints log to the current log file, appends "\n" in the end and flushes the + * stream. + */ +void cs_log_printf(const char *fmt, ...) +#ifdef __GNUC__ + __attribute__((format(printf, 1, 2))) +#endif + ; + +/* + * Format and print message `x` with the given level `l`. Example: + * + * ```c + * LOG(LL_INFO, ("my info message: %d", 123)); + * LOG(LL_DEBUG, ("my debug message: %d", 123)); + * ``` + */ +#define LOG(l, x) \ + do { \ + if (cs_log_print_prefix(l, __func__, __FILE__)) cs_log_printf x; \ + } while (0) + +#ifndef CS_NDEBUG + +/* + * Shortcut for `LOG(LL_VERBOSE_DEBUG, (...))` + */ +#define DBG(x) LOG(LL_VERBOSE_DEBUG, x) + +#else /* NDEBUG */ + +#define DBG(x) + +#endif + +#else /* CS_ENABLE_STDIO */ + +#define LOG(l, x) +#define DBG(x) + +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CS_COMMON_CS_DBG_H_ */ diff --git a/src/mongoose-6.11/src/common/cs_md5.c b/src/mongoose-6.11/src/common/cs_md5.c new file mode 100644 index 0000000..f8c8b68 --- /dev/null +++ b/src/mongoose-6.11/src/common/cs_md5.c @@ -0,0 +1,211 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +#include "common/cs_md5.h" +#include "common/str_util.h" + +#if !defined(EXCLUDE_COMMON) +#if !CS_DISABLE_MD5 + +#include "common/cs_endian.h" + +static void byteReverse(unsigned char *buf, unsigned longs) { +/* Forrest: MD5 expect LITTLE_ENDIAN, swap if BIG_ENDIAN */ +#if BYTE_ORDER == BIG_ENDIAN + do { + uint32_t t = (uint32_t)((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + *(uint32_t *) buf = t; + buf += 4; + } while (--longs); +#else + (void) buf; + (void) longs; +#endif +} + +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +#define MD5STEP(f, w, x, y, z, data, s) \ + (w += f(x, y, z) + data, w = w << s | w >> (32 - s), w += x) + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void cs_md5_init(cs_md5_ctx *ctx) { + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +static void cs_md5_transform(uint32_t buf[4], uint32_t const in[16]) { + register uint32_t a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +void cs_md5_update(cs_md5_ctx *ctx, const unsigned char *buf, size_t len) { + uint32_t t; + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t) ctx->bits[1]++; + ctx->bits[1] += (uint32_t) len >> 29; + + t = (t >> 3) & 0x3f; + + if (t) { + unsigned char *p = (unsigned char *) ctx->in + t; + + t = 64 - t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse(ctx->in, 16); + cs_md5_transform(ctx->buf, (uint32_t *) ctx->in); + buf += t; + len -= t; + } + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + cs_md5_transform(ctx->buf, (uint32_t *) ctx->in); + buf += 64; + len -= 64; + } + + memcpy(ctx->in, buf, len); +} + +void cs_md5_final(unsigned char digest[16], cs_md5_ctx *ctx) { + unsigned count; + unsigned char *p; + uint32_t *a; + + count = (ctx->bits[0] >> 3) & 0x3F; + + p = ctx->in + count; + *p++ = 0x80; + count = 64 - 1 - count; + if (count < 8) { + memset(p, 0, count); + byteReverse(ctx->in, 16); + cs_md5_transform(ctx->buf, (uint32_t *) ctx->in); + memset(ctx->in, 0, 56); + } else { + memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); + + a = (uint32_t *) ctx->in; + a[14] = ctx->bits[0]; + a[15] = ctx->bits[1]; + + cs_md5_transform(ctx->buf, (uint32_t *) ctx->in); + byteReverse((unsigned char *) ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset((char *) ctx, 0, sizeof(*ctx)); +} + +#endif /* CS_DISABLE_MD5 */ +#endif /* EXCLUDE_COMMON */ diff --git a/src/mongoose-6.11/src/common/cs_md5.h b/src/mongoose-6.11/src/common/cs_md5.h new file mode 100644 index 0000000..1450fd2 --- /dev/null +++ b/src/mongoose-6.11/src/common/cs_md5.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_MD5_H_ +#define CS_COMMON_MD5_H_ + +#include "common/platform.h" + +#ifndef CS_DISABLE_MD5 +#define CS_DISABLE_MD5 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef struct { + uint32_t buf[4]; + uint32_t bits[2]; + unsigned char in[64]; +} cs_md5_ctx; + +void cs_md5_init(cs_md5_ctx *c); +void cs_md5_update(cs_md5_ctx *c, const unsigned char *data, size_t len); +void cs_md5_final(unsigned char *md, cs_md5_ctx *c); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CS_COMMON_MD5_H_ */ diff --git a/src/mongoose-6.11/src/common/cs_time.c b/src/mongoose-6.11/src/common/cs_time.c new file mode 100644 index 0000000..f21845c --- /dev/null +++ b/src/mongoose-6.11/src/common/cs_time.c @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#include "common/cs_time.h" + +#ifndef _WIN32 +#include +/* + * There is no sys/time.h on ARMCC. + */ +#if !(defined(__ARMCC_VERSION) || defined(__ICCARM__)) && \ + !defined(__TI_COMPILER_VERSION__) && \ + (!defined(CS_PLATFORM) || CS_PLATFORM != CS_P_NXP_LPC) +#include +#endif +#else +#include +#endif + +double cs_time(void) WEAK; +double cs_time(void) { + double now; +#ifndef _WIN32 + struct timeval tv; + if (gettimeofday(&tv, NULL /* tz */) != 0) return 0; + now = (double) tv.tv_sec + (((double) tv.tv_usec) / 1000000.0); +#else + SYSTEMTIME sysnow; + FILETIME ftime; + GetLocalTime(&sysnow); + SystemTimeToFileTime(&sysnow, &ftime); + /* + * 1. VC 6.0 doesn't support conversion uint64 -> double, so, using int64 + * This should not cause a problems in this (21th) century + * 2. Windows FILETIME is a number of 100-nanosecond intervals since January + * 1, 1601 while time_t is a number of _seconds_ since January 1, 1970 UTC, + * thus, we need to convert to seconds and adjust amount (subtract 11644473600 + * seconds) + */ + now = (double) (((int64_t) ftime.dwLowDateTime + + ((int64_t) ftime.dwHighDateTime << 32)) / + 10000000.0) - + 11644473600; +#endif /* _WIN32 */ + return now; +} + +double cs_timegm(const struct tm *tm) { + /* Month-to-day offset for non-leap-years. */ + static const int month_day[12] = {0, 31, 59, 90, 120, 151, + 181, 212, 243, 273, 304, 334}; + + /* Most of the calculation is easy; leap years are the main difficulty. */ + int month = tm->tm_mon % 12; + int year = tm->tm_year + tm->tm_mon / 12; + int year_for_leap; + int64_t rt; + + if (month < 0) { /* Negative values % 12 are still negative. */ + month += 12; + --year; + } + + /* This is the number of Februaries since 1900. */ + year_for_leap = (month > 1) ? year + 1 : year; + + rt = + tm->tm_sec /* Seconds */ + + + 60 * + (tm->tm_min /* Minute = 60 seconds */ + + + 60 * (tm->tm_hour /* Hour = 60 minutes */ + + + 24 * (month_day[month] + tm->tm_mday - 1 /* Day = 24 hours */ + + 365 * (year - 70) /* Year = 365 days */ + + (year_for_leap - 69) / 4 /* Every 4 years is leap... */ + - (year_for_leap - 1) / 100 /* Except centuries... */ + + (year_for_leap + 299) / 400))); /* Except 400s. */ + return rt < 0 ? -1 : (double) rt; +} diff --git a/src/mongoose-6.11/src/common/cs_time.h b/src/mongoose-6.11/src/common/cs_time.h new file mode 100644 index 0000000..3b59073 --- /dev/null +++ b/src/mongoose-6.11/src/common/cs_time.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_CS_TIME_H_ +#define CS_COMMON_CS_TIME_H_ + +#include + +#include "common/platform.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Sub-second granularity time(). */ +double cs_time(void); + +/* + * Similar to (non-standard) timegm, converts broken-down time into the number + * of seconds since Unix Epoch. + */ +double cs_timegm(const struct tm *tm); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CS_COMMON_CS_TIME_H_ */ diff --git a/src/mongoose-6.11/src/common/mg_mem.h b/src/mongoose-6.11/src/common/mg_mem.h new file mode 100644 index 0000000..65d71c7 --- /dev/null +++ b/src/mongoose-6.11/src/common/mg_mem.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_MG_MEM_H_ +#define CS_COMMON_MG_MEM_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef MG_MALLOC +#define MG_MALLOC malloc +#endif + +#ifndef MG_CALLOC +#define MG_CALLOC calloc +#endif + +#ifndef MG_REALLOC +#define MG_REALLOC realloc +#endif + +#ifndef MG_FREE +#define MG_FREE free +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* CS_COMMON_MG_MEM_H_ */ diff --git a/src/mongoose-6.11/src/common/platform.h b/src/mongoose-6.11/src/common/platform.h new file mode 100644 index 0000000..b517cab --- /dev/null +++ b/src/mongoose-6.11/src/common/platform.h @@ -0,0 +1,121 @@ +#ifndef CS_COMMON_PLATFORM_H_ +#define CS_COMMON_PLATFORM_H_ + +/* + * For the "custom" platform, includes and dependencies can be + * provided through mg_locals.h. + */ +#define CS_P_CUSTOM 0 +#define CS_P_UNIX 1 +#define CS_P_WINDOWS 2 +#define CS_P_ESP32 15 +#define CS_P_ESP8266 3 +#define CS_P_CC3100 6 +#define CS_P_CC3200 4 +#define CS_P_CC3220 17 +#define CS_P_MSP432 5 +#define CS_P_TM4C129 14 +#define CS_P_MBED 7 +#define CS_P_WINCE 8 +#define CS_P_NXP_LPC 13 +#define CS_P_NXP_KINETIS 9 +#define CS_P_NRF51 12 +#define CS_P_NRF52 10 +#define CS_P_PIC32 11 +#define CS_P_STM32 16 +/* Next id: 18 */ + +/* If not specified explicitly, we guess platform by defines. */ +#ifndef CS_PLATFORM + +#if defined(TARGET_IS_MSP432P4XX) || defined(__MSP432P401R__) +#define CS_PLATFORM CS_P_MSP432 +#elif defined(cc3200) || defined(TARGET_IS_CC3200) +#define CS_PLATFORM CS_P_CC3200 +#elif defined(cc3220) || defined(TARGET_IS_CC3220) +#define CS_PLATFORM CS_P_CC3220 +#elif defined(__unix__) || defined(__APPLE__) +#define CS_PLATFORM CS_P_UNIX +#elif defined(WINCE) +#define CS_PLATFORM CS_P_WINCE +#elif defined(_WIN32) +#define CS_PLATFORM CS_P_WINDOWS +#elif defined(__MBED__) +#define CS_PLATFORM CS_P_MBED +#elif defined(__USE_LPCOPEN) +#define CS_PLATFORM CS_P_NXP_LPC +#elif defined(FRDM_K64F) || defined(FREEDOM) +#define CS_PLATFORM CS_P_NXP_KINETIS +#elif defined(PIC32) +#define CS_PLATFORM CS_P_PIC32 +#elif defined(ESP_PLATFORM) +#define CS_PLATFORM CS_P_ESP32 +#elif defined(ICACHE_FLASH) +#define CS_PLATFORM CS_P_ESP8266 +#elif defined(TARGET_IS_TM4C129_RA0) || defined(TARGET_IS_TM4C129_RA1) || \ + defined(TARGET_IS_TM4C129_RA2) +#define CS_PLATFORM CS_P_TM4C129 +#elif defined(STM32) +#define CS_PLATFORM CS_P_STM32 +#endif + +#ifndef CS_PLATFORM +#error "CS_PLATFORM is not specified and we couldn't guess it." +#endif + +#endif /* !defined(CS_PLATFORM) */ + +#define MG_NET_IF_SOCKET 1 +#define MG_NET_IF_SIMPLELINK 2 +#define MG_NET_IF_LWIP_LOW_LEVEL 3 +#define MG_NET_IF_PIC32 4 + +#define MG_SSL_IF_OPENSSL 1 +#define MG_SSL_IF_MBEDTLS 2 +#define MG_SSL_IF_SIMPLELINK 3 + +#include "common/platforms/platform_unix.h" +#include "common/platforms/platform_windows.h" +#include "common/platforms/platform_esp32.h" +#include "common/platforms/platform_esp8266.h" +#include "common/platforms/platform_cc3100.h" +#include "common/platforms/platform_cc3200.h" +#include "common/platforms/platform_cc3220.h" +#include "common/platforms/platform_mbed.h" +#include "common/platforms/platform_nrf51.h" +#include "common/platforms/platform_nrf52.h" +#include "common/platforms/platform_wince.h" +#include "common/platforms/platform_nxp_lpc.h" +#include "common/platforms/platform_nxp_kinetis.h" +#include "common/platforms/platform_pic32.h" +#include "common/platforms/platform_stm32.h" + +/* Common stuff */ + +#if !defined(WEAK) +#if (defined(__GNUC__) || defined(__TI_COMPILER_VERSION__)) && !defined(_WIN32) +#define WEAK __attribute__((weak)) +#else +#define WEAK +#endif +#endif + +#ifdef __GNUC__ +#define NORETURN __attribute__((noreturn)) +#define NOINLINE __attribute__((noinline)) +#define WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#define NOINSTR __attribute__((no_instrument_function)) +#define DO_NOT_WARN_UNUSED __attribute__((unused)) +#else +#define NORETURN +#define NOINLINE +#define WARN_UNUSED_RESULT +#define NOINSTR +#define DO_NOT_WARN_UNUSED +#endif /* __GNUC__ */ + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) +#endif + +#endif /* CS_COMMON_PLATFORM_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/arm/arm_exc.c b/src/mongoose-6.11/src/common/platforms/arm/arm_exc.c new file mode 100644 index 0000000..60b50c8 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/arm/arm_exc.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2014-2017 Cesanta Software Limited + * All rights reserved + */ + +#include +#include +#include +#include + +#include "FreeRTOS.h" + +#include "common/platform.h" + +#include "mgos_core_dump.h" +#include "mgos_hal.h" + +struct arm_exc_frame { + uint32_t r0; + uint32_t r1; + uint32_t r2; + uint32_t r3; + uint32_t r12; + uint32_t lr; + uint32_t pc; + uint32_t xpsr; +#ifdef ARM_HAVE_FPU + uint32_t s[16]; + uint32_t fpscr; + uint32_t reserved; +#endif +} __attribute__((packed)); + +struct arm_gdb_reg_file { + uint32_t r[13]; + uint32_t sp; + uint32_t lr; + uint32_t pc; + uint32_t cpsr; + uint64_t d[16]; + uint32_t fpscr; +} __attribute__((packed)); + +#if ARM_HAVE_FPU +static void save_s16_s31(uint32_t *dst) { + __asm volatile( + "\ + vmov r1, s16\n str r1, [%0, 0]\n\ + vmov r1, s17\n str r1, [%0, 4]\n\ + vmov r1, s18\n str r1, [%0, 8]\n\ + vmov r1, s19\n str r1, [%0, 12]\n\ + vmov r1, s20\n str r1, [%0, 16]\n\ + vmov r1, s21\n str r1, [%0, 20]\n\ + vmov r1, s22\n str r1, [%0, 24]\n\ + vmov r1, s23\n str r1, [%0, 28]\n\ + vmov r1, s24\n str r1, [%0, 32]\n\ + vmov r1, s25\n str r1, [%0, 36]\n\ + vmov r1, s26\n str r1, [%0, 40]\n\ + vmov r1, s27\n str r1, [%0, 44]\n\ + vmov r1, s28\n str r1, [%0, 48]\n\ + vmov r1, s29\n str r1, [%0, 52]\n\ + vmov r1, s30\n str r1, [%0, 56]\n\ + vmov r1, s31\n str r1, [%0, 60]\n\ + " + : + : "r"(dst) + : "r1"); +} + +static void print_fpu_regs(const uint32_t *regs, int off, int n) { + for (int i = 0, j = off; i < n; i++, j++) { + if (j % 4 == 0) mgos_cd_putc('\n'); + mgos_cd_printf(" S%d: %s0x%08lx", j, (j < 10 ? " " : ""), regs[i]); + } +} +#endif + +void arm_exc_handler_bottom(uint8_t isr_no, struct arm_exc_frame *ef, + struct arm_gdb_reg_file *rf) { + char buf[8]; + const char *name; + portDISABLE_INTERRUPTS(); + switch (isr_no) { + case 0: + name = "ThreadMode"; + break; + + case 1: + case 7: + case 8: + case 9: + case 10: + case 13: + name = "Reserved"; + break; + + case 2: + name = "NMI"; + break; + case 3: + name = "HardFault"; + break; + case 4: + name = "MemManage"; + break; + case 5: + name = "BusFault"; + break; + case 6: + name = "UsageFault"; + break; + case 11: + name = "SVCall"; + break; + case 12: + name = "ReservedDebug"; + break; + case 14: + name = "PendSV"; + break; + case 15: + name = "SysTick"; + break; + default: { + sprintf(buf, "IRQ%u", isr_no - 16); + name = buf; + } + } + mgos_cd_printf("\n\n--- Exception %u (%s) ---\n", isr_no, name); + if (rf != NULL) { + mgos_cd_printf(" R0: 0x%08lx R1: 0x%08lx R2: 0x%08lx R3: 0x%08lx\n", + rf->r[0], rf->r[1], rf->r[2], rf->r[3]); + mgos_cd_printf(" R4: 0x%08lx R5: 0x%08lx R6: 0x%08lx R7: 0x%08lx\n", + rf->r[4], rf->r[5], rf->r[6], rf->r[7]); + mgos_cd_printf(" R8: 0x%08lx R9: 0x%08lx R10: 0x%08lx R11: 0x%08lx\n", + rf->r[8], rf->r[9], rf->r[10], rf->r[11]); + mgos_cd_printf(" R12: 0x%08lx SP: 0x%08lx LR: 0x%08lx PC: 0x%08lx\n", + rf->r[12], rf->sp, rf->lr, rf->pc); + mgos_cd_printf(" PSR: 0x%08lx\n", rf->cpsr); + } + memset(rf->d, 0, sizeof(rf->d)); +#if ARM_HAVE_FPU + rf->fpscr = ef->fpscr; + memcpy((uint8_t *) rf->d, ef->s, sizeof(ef->s)); + print_fpu_regs((uint32_t *) rf->d, 0, ARRAY_SIZE(ef->s)); + save_s16_s31(ef->s); + memcpy(((uint8_t *) rf->d) + sizeof(ef->s), ef->s, sizeof(ef->s)); + print_fpu_regs((uint32_t *) (((uint8_t *) rf->d) + sizeof(ef->s)), 16, + ARRAY_SIZE(ef->s)); + mgos_cd_putc('\n'); + mgos_cd_printf("FPSCR: 0x%08lx\n", rf->fpscr); +#else + rf->fpscr = 0; +#endif + mgos_cd_emit_header(); + mgos_cd_emit_section(MGOS_CORE_DUMP_SECTION_REGS, rf, sizeof(*rf)); + mgos_cd_emit_section("SRAM", (void *) SRAM_BASE_ADDR, SRAM_SIZE); + mgos_cd_emit_footer(); +#ifdef MGOS_HALT_ON_EXCEPTION + mgos_cd_printf("Halting\n"); + while (1) { + mgos_wdt_feed(); + } +#else + mgos_cd_printf("Rebooting\n"); + mgos_dev_system_restart(); +#endif +} diff --git a/src/mongoose-6.11/src/common/platforms/arm/arm_exc_top.S b/src/mongoose-6.11/src/common/platforms/arm/arm_exc_top.S new file mode 100644 index 0000000..9ffade4 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/arm/arm_exc_top.S @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2014-2017 Cesanta Software Limited + * All rights reserved + */ + +.arch armv7e-m +.syntax unified +.thumb + +/* These are required to satisfy TI linker. */ +.eabi_attribute Tag_ABI_align_needed, 1 +.eabi_attribute Tag_ABI_align_preserved, 1 + +.global arm_exc_handler_top +.global arm_exc_handler_bottom + +/* + * Determines the stack pointer, populates most of the GDB frame + * and hands off to the C routine. + */ +.section .text.arm_exc_handler_top +.type arm_exc_handler_top, %function +.align 8 + +arm_exc_handler_top: + tst lr, #4 + ite eq + mrseq r1, msp + mrsne r1, psp + // r1 -> arm_exc_frame prepared for us by the CPU +#if ARM_HAVE_FPU + add r0, r1, #104 // sizeof(arm_exc_frame) + sub sp, #328 // sizeof(arm_gdb_reg_file) +#else + add r0, r1, #32 // sizeof(arm_exc_frame) + sub sp, #328 // sizeof(arm_gdb_reg_file) +#endif + mov r2, sp + // r0 -> original sp, r2 -> arm_gdb_reg_file to fill + // r3 - scratch + ldr r3, [r1, #0] // r0 + str r3, [r2, #0] + ldr r3, [r1, #4] // r2 + str r3, [r2, #4] + ldr r3, [r1, #8] // r1 + str r3, [r2, #8] + ldr r3, [r1, #12] // r3 + str r3, [r2, #12] + str r4, [r2, #16] // r4 + str r5, [r2, #20] // r5 + str r6, [r2, #24] // r6 + str r7, [r2, #28] // r7 + str r8, [r2, #32] // r8 + str r9, [r2, #36] // r9 + str r10, [r2, #40] // r10 + str r11, [r2, #44] // r11 + ldr r3, [r1, #16] // r12 + str r3, [r2, #48] + str r0, [r2, #52] // sp + ldr r3, [r1, #20] // lr + str r3, [r2, #56] + ldr r3, [r1, #24] // pc + str r3, [r2, #60] + ldr r3, [r1, #28] // xpsr + str r3, [r2, #64] + + mrs r0, ipsr + b arm_exc_handler_bottom +.size arm_exc_handler_top, . - arm_exc_handler_top diff --git a/src/mongoose-6.11/src/common/platforms/cc3200/cc3200.ld b/src/mongoose-6.11/src/common/platforms/cc3200/cc3200.ld new file mode 100644 index 0000000..a2df648 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/cc3200/cc3200.ld @@ -0,0 +1,92 @@ +/***************************************************************************** +* +* GCC Linker script for CC3200. Based on TI's example "blinky.ld". +* +* Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/ +* +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* 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. +* +* Neither the name of Texas Instruments Incorporated nor the names of +* its contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "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 COPYRIGHT +* OWNER OR CONTRIBUTORS 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. +* +******************************************************************************/ + +ORG = DEFINED(ORG) ? ORG : 0x20004000; +RAM_SIZE = DEFINED(RAM_SIZE) ? RAM_SIZE : 0x3C000; + +MEMORY +{ + /* SRAM size of 240KB for cc3200 ES 1.33 device onward */ + SRAM (rwx) : ORIGIN = ORG, LENGTH = RAM_SIZE +} + +SECTIONS +{ + .text : + { + _text = .; + KEEP(*(.intvecs)) + *(.text*) + *(.rodata*) + *(.ARM.extab* .gnu.linkonce.armextab.*) + . = ALIGN(8); + _etext = .; + } > SRAM + + .ARM : { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } > SRAM + + __init_data = .; + + .data : AT(__init_data) + { + _data = .; + *(.data*) + . = ALIGN (8); + _edata = .; + } > SRAM + + .bss : + { + _bss = .; + *(.bss*) + *(COMMON) + _ebss = .; + } > SRAM + + .heap : + { + _heap = .; + . = . + (LENGTH(SRAM) - SIZEOF(.text) - SIZEOF(.ARM) - SIZEOF(.data) - SIZEOF(.bss)); + . = ALIGN(8); + _eheap = .; + + } > SRAM +} diff --git a/src/mongoose-6.11/src/common/platforms/cc3200/cc3200_libc.c b/src/mongoose-6.11/src/common/platforms/cc3200/cc3200_libc.c new file mode 100644 index 0000000..f488725 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/cc3200/cc3200_libc.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if CS_PLATFORM == CS_P_CC3200 + +#include "common/mg_mem.h" +#include +#include + +#ifndef __TI_COMPILER_VERSION__ +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#define CONSOLE_UART UARTA0_BASE + +#ifdef __TI_COMPILER_VERSION__ +int asprintf(char **strp, const char *fmt, ...) { + va_list ap; + int len; + + *strp = MG_MALLOC(BUFSIZ); + if (*strp == NULL) return -1; + + va_start(ap, fmt); + len = vsnprintf(*strp, BUFSIZ, fmt, ap); + va_end(ap); + + if (len > 0) { + *strp = MG_REALLOC(*strp, len + 1); + if (*strp == NULL) return -1; + } + + if (len >= BUFSIZ) { + va_start(ap, fmt); + len = vsnprintf(*strp, len + 1, fmt, ap); + va_end(ap); + } + + return len; +} + +#if MG_TI_NO_HOST_INTERFACE +time_t HOSTtime() { + struct timeval tp; + gettimeofday(&tp, NULL); + return tp.tv_sec; +} +#endif + +#endif /* __TI_COMPILER_VERSION__ */ + +void fprint_str(FILE *fp, const char *str) { + while (*str != '\0') { + if (*str == '\n') MAP_UARTCharPut(CONSOLE_UART, '\r'); + MAP_UARTCharPut(CONSOLE_UART, *str++); + } +} + +void _exit(int status) { + fprint_str(stderr, "_exit\n"); + /* cause an unaligned access exception, that will drop you into gdb */ + *(int *) 1 = status; + while (1) + ; /* avoid gcc warning because stdlib abort() has noreturn attribute */ +} + +void _not_implemented(const char *what) { + fprint_str(stderr, what); + fprint_str(stderr, " is not implemented\n"); + _exit(42); +} + +int _kill(int pid, int sig) { + (void) pid; + (void) sig; + _not_implemented("_kill"); + return -1; +} + +int _getpid() { + fprint_str(stderr, "_getpid is not implemented\n"); + return 42; +} + +int _isatty(int fd) { + /* 0, 1 and 2 are TTYs. */ + return fd < 2; +} + +#endif /* CS_PLATFORM == CS_P_CC3200 */ diff --git a/src/mongoose-6.11/src/common/platforms/cc3200/cc3200v1p32.cmd b/src/mongoose-6.11/src/common/platforms/cc3200/cc3200v1p32.cmd new file mode 100644 index 0000000..3b17d07 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/cc3200/cc3200v1p32.cmd @@ -0,0 +1,82 @@ +//***************************************************************************** +// cc3200v1p32.cmd +// +// CCS linker configuration file for cc3200 ES 1.32. +// +// Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/ +// +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 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. +// +// Neither the name of Texas Instruments Incorporated nor the names of +// its contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "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 COPYRIGHT +// OWNER OR CONTRIBUTORS 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. +// +//***************************************************************************** + + +--retain=g_pfnVectors + +//***************************************************************************** +// The following command line options are set as part of the CCS project. +// If you are building using the command line, or for some reason want to +// define them here, you can uncomment and modify these lines as needed. +// If you are using CCS for building, it is probably better to make any such +// modifications in your CCS project and leave this file alone. +//***************************************************************************** + + +//***************************************************************************** +// The starting address of the application. Normally the interrupt vectors +// must be located at the beginning of the application. +//***************************************************************************** +#define RAM_BASE 0x20004000 + +/* System memory map */ + +MEMORY +{ + /* Application uses internal RAM for program and data */ + SRAM_CODE (RWX) : origin = 0x20004000, length = 0x2F000 + SRAM_DATA (RWX) : origin = 0x20033000, length = 0xD000 +} + +/* Section allocation in memory */ + +SECTIONS +{ + .intvecs: > RAM_BASE + .init_array : > SRAM_CODE + .vtable : > SRAM_CODE + .text : > SRAM_CODE + .const : > SRAM_CODE + .cinit : > SRAM_CODE + .pinit : > SRAM_CODE + .data : > SRAM_DATA + .bss : > SRAM_DATA + .sysmem : > SRAM_DATA + .stack : > SRAM_DATA(HIGH) +} + diff --git a/src/mongoose-6.11/src/common/platforms/cc3200/gcc.mk b/src/mongoose-6.11/src/common/platforms/cc3200/gcc.mk new file mode 100644 index 0000000..ec98b75 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/cc3200/gcc.mk @@ -0,0 +1,52 @@ +APP_LDFLAGS ?= +CC_WRAPPER ?= +GENFILES_LIST ?= +CC = arm-none-eabi-gcc +CXX = arm-none-eabi-g++ +AR = arm-none-eabi-ar +NM = arm-none-eabi-nm + +IPATH += $(SDK_PATH)/third_party/FreeRTOS/source/portable/GCC/ARM_CM4 +VPATH += $(SDK_PATH)/third_party/FreeRTOS/source/portable/GCC/ARM_CM4 + +C_CXX_FLAGS = -mthumb -mcpu=cortex-m4 -ffunction-sections -fdata-sections \ + -MD -Os -ggdb -Wall -Werror -Dgcc +CFLAGS += -std=c99 $(C_CXX_FLAGS) +CXXFLAGS += -std=g++11 $(C_CXX_FLAGS) + +AR = arm-none-eabi-ar +LD = arm-none-eabi-ld +OBJCOPY = arm-none-eabi-objcopy +LIBGCC := ${shell ${CC} -mthumb ${CFLAGS} -print-libgcc-file-name} +LIBC := ${shell ${CC} ${CFLAGS} -print-file-name=libc.a} +LIBM := ${shell ${CC} ${CFLAGS} -print-file-name=libm.a} + +# Disable certain warnings on SDK sources, we have no control over them anyway. +# We also force-include platform.h which resolves some symbol conflicts +# between system includes and simplelink.h +$(SDK_OBJS): CFLAGS += -Wno-missing-braces -Wno-strict-aliasing -Wno-parentheses -Wno-unused-variable -Wno-builtin-macro-redefined +$(SDK_OBJS): CFLAGS += -include common/platform.h + +# cc flags,file +define cc + $(vecho) "GCC $2 -> $@" + $(Q) $(CC_WRAPPER) $(CC) -c $1 -o $@ $2 +endef +define cxx + $(vecho) "G++ $2 -> $@" + $(Q) $(CC_WRAPPER) $(CXX) -c $1 -o $@ $2 +endef + +# ar files +define ar + $(vecho) "AR $@" + $(Q) $(AR) cru $@ $1 +endef + +# link script,flags,objs +define link + $(vecho) "LD $@" + $(Q) $(CC_WRAPPER) $(LD) \ + --gc-sections -o $@ -T $1 $2 $3 \ + $(LIBM) $(LIBC) $(LIBGCC) +endef diff --git a/src/mongoose-6.11/src/common/platforms/cc3200/ti.mk b/src/mongoose-6.11/src/common/platforms/cc3200/ti.mk new file mode 100644 index 0000000..b50407f --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/cc3200/ti.mk @@ -0,0 +1,52 @@ +IPATH += $(SDK_PATH)/third_party/FreeRTOS/source/portable/CCS/ARM_CM3 +VPATH += $(SDK_PATH)/third_party/FreeRTOS/source/portable/CCS/ARM_CM3 + +CC_WRAPPER ?= +CC = $(TOOLCHAIN)/bin/armcl +AR = $(TOOLCHAIN)/bin/armar +NM = nm +GENFILES_LIST ?= + +C_CXX_FLAGS = -Dccs -I$(TOOLCHAIN)/include +TI_C_CXX_FLAGS = -mv7M4 --little_endian --code_state=16 --float_support=vfplib --abi=eabi \ + -O4 --opt_for_speed=0 --unaligned_access=on --small_enum \ + --gen_func_subsections=on --diag_wrap=off --display_error_number \ + --emit_warnings_as_errors +CFLAGS += --c99 $(TI_C_CXX_FLAGS) $(C_CXX_FLAGS) +CXXFLAGS += $(TI_C_CXX_FLAGS) $(C_CXX_FLAGS) + +# cc flags,file +define cc + $(vecho) "TICC $2 -> $@" + $(Q) $(CC_WRAPPER) $(CC) -c --preproc_with_compile -ppd=$@.d $1 --output_file=$@ $2 +endef +define cxx + $(vecho) "TICXX $2 -> $@" + $(Q) $(CC_WRAPPER) $(CC) -c --preproc_with_compile -ppd=$@.d $1 --output_file=$@ $2 +endef + +# asm flags,file +define asm + $(vecho) "TIASM $2 -> $@" + $(Q) $(CC_WRAPPER) $(CC) -c $1 --output_file=$@ $2 +endef + +# ar files +define ar + $(vecho) "TIAR $@" + $(Q) $(AR) qru $@ $1 +endef + +# link script,flags,objs +define link + $(vecho) "TILD $@" + $(Q) $(CC_WRAPPER) $(CC) \ + -mv7M4 --code_state=16 --float_support=vfplib --abi=eabi --little_endian \ + --run_linker \ + --generate_dead_funcs_list=$@.garbage.xml \ + -i $(TOOLCHAIN)/lib \ + --reread_libs --warn_sections --display_error_number \ + --unused_section_elimination=on \ + -o $@ --map_file=$@.map --xml_link_info=$@.map.xml \ + $2 $1 $3 +endef diff --git a/src/mongoose-6.11/src/common/platforms/esp/esptool.py b/src/mongoose-6.11/src/common/platforms/esp/esptool.py new file mode 100755 index 0000000..9592f6d --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp/esptool.py @@ -0,0 +1,2133 @@ +#!/usr/bin/env python +# NB: Before sending a PR to change the above line to '#!/usr/bin/env python2', please read https://github.com/themadinventor/esptool/issues/21 +# +# ESP8266 & ESP32 ROM Bootloader Utility +# https://github.com/themadinventor/esptool +# +# Copyright (C) 2014-2016 Fredrik Ahlberg, Angus Gratton, Espressif Systems (Shanghai) PTE LTD, other contributors as noted. +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 51 Franklin +# Street, Fifth Floor, Boston, MA 02110-1301 USA. + +import argparse +import hashlib +import inspect +import json +import os +import serial +import struct +import subprocess +import sys +import time +import base64 +import zlib + +__version__ = "2.0-dev" + + +MAX_UINT32 = 0xffffffff +MAX_UINT24 = 0xffffff + + +def check_supported_function(func, check_func): + """ + Decorator implementation that wraps a check around an ESPLoader + bootloader function to check if it's supported. + + This is used to capture the multidimensional differences in + functionality between the ESP8266 & ESP32 ROM loaders, and the + software stub that runs on both. Not possible to do this cleanly + via inheritance alone. + """ + def inner(*args, **kwargs): + obj = args[0] + if check_func(obj): + return func(*args, **kwargs) + else: + raise NotImplementedInROMError(obj) + return inner + + +def stub_function_only(func): + """ Attribute for a function only supported in the software stub loader """ + return check_supported_function(func, lambda o: o.IS_STUB) + + +def stub_and_esp32_function_only(func): + """ Attribute for a function only supported by software stubs or ESP32 ROM """ + return check_supported_function(func, lambda o: o.IS_STUB or o.CHIP_NAME == "ESP32") + + +def esp8266_function_only(func): + """ Attribute for a function only supported on ESP8266 """ + return check_supported_function(func, lambda o: o.CHIP_NAME == "ESP8266") + + +class ESPLoader(object): + """ Base class providing access to ESP ROM & softtware stub bootloaders. + Subclasses provide ESP8266 & ESP32 specific functionality. + + Don't instantiate this base class directly, either instantiate a subclass or + call ESPLoader.detect_chip() which will interrogate the chip and return the + appropriate subclass instance. + + """ + CHIP_NAME = "Espressif device" + IS_STUB = False + + DEFAULT_PORT = "/dev/ttyUSB0" + + # Commands supported by ESP8266 ROM bootloader + ESP_FLASH_BEGIN = 0x02 + ESP_FLASH_DATA = 0x03 + ESP_FLASH_END = 0x04 + ESP_MEM_BEGIN = 0x05 + ESP_MEM_END = 0x06 + ESP_MEM_DATA = 0x07 + ESP_SYNC = 0x08 + ESP_WRITE_REG = 0x09 + ESP_READ_REG = 0x0a + + # Some comands supported by ESP32 ROM bootloader (or -8266 w/ stub) + ESP_SPI_SET_PARAMS = 0x0B + ESP_SPI_ATTACH = 0x0D + ESP_CHANGE_BAUDRATE = 0x0F + ESP_FLASH_DEFL_BEGIN = 0x10 + ESP_FLASH_DEFL_DATA = 0x11 + ESP_FLASH_DEFL_END = 0x12 + ESP_SPI_FLASH_MD5 = 0x13 + + # Some commands supported by stub only + ESP_ERASE_FLASH = 0xD0 + ESP_ERASE_REGION = 0xD1 + ESP_READ_FLASH = 0xD2 + ESP_GET_FLASH_ID = 0xD3 + + # Maximum block sized for RAM and Flash writes, respectively. + ESP_RAM_BLOCK = 0x1800 + ESP_FLASH_BLOCK = 0x400 + + FLASH_WRITE_SIZE = ESP_FLASH_BLOCK + + # Default baudrate. The ROM auto-bauds, so we can use more or less whatever we want. + ESP_ROM_BAUD = 115200 + + # First byte of the application image + ESP_IMAGE_MAGIC = 0xe9 + + # Initial state for the checksum routine + ESP_CHECKSUM_MAGIC = 0xef + + # Flash sector size, minimum unit of erase. + ESP_FLASH_SECTOR = 0x1000 + + UART_DATA_REG_ADDR = 0x60000078 + + # Memory addresses + IROM_MAP_START = 0x40200000 + IROM_MAP_END = 0x40300000 + + # The number of bytes in the UART response that signify command status + STATUS_BYTES_LENGTH = 2 + + def __init__(self, port=DEFAULT_PORT, baud=ESP_ROM_BAUD, do_connect=True): + """Base constructor for ESPLoader bootloader interaction + + Don't call this constructor, either instantiate ESP8266ROM + or ESP32ROM, or use ESPLoader.detect_chip(). + + This base class has all of the instance methods for bootloader + functionality supported across various chips & stub + loaders. Subclasses replace the functions they don't support + with ones which throw NotImplementedInROMError(). + + """ + if isinstance(port, serial.Serial): + self._port = port + else: + self._port = serial.Serial(port) + self._slip_reader = slip_reader(self._port) + # setting baud rate in a separate step is a workaround for + # CH341 driver on some Linux versions (this opens at 9600 then + # sets), shouldn't matter for other platforms/drivers. See + # https://github.com/themadinventor/esptool/issues/44#issuecomment-107094446 + self._port.baudrate = baud + if do_connect: + self.connect() + + @staticmethod + def detect_chip(port=DEFAULT_PORT, baud=ESP_ROM_BAUD): + """Use serial access to detect the chip type. + + We use the UART's datecode register for this, it's mapped at + the same address on ESP8266 & ESP32 so we can use one + memory read and compare to the datecode register for each chip + type. + + """ + detect_port = ESPLoader(port, baud, True) + sys.stdout.write('Detecting chip type... ') + date_reg = detect_port.read_reg(ESPLoader.UART_DATA_REG_ADDR) + + for cls in [ESP8266ROM, ESP32ROM]: + if date_reg == cls.DATE_REG_VALUE: + # don't connect a second time + inst = cls(detect_port._port, baud, False) + print '%s' % inst.CHIP_NAME + return inst + print '' + raise FatalError("Unexpected UART datecode value 0x%08x. Failed to autodetect chip type." % date_reg) + + """ Read a SLIP packet from the serial port """ + def read(self): + r = self._slip_reader.next() + return r + + """ Write bytes to the serial port while performing SLIP escaping """ + def write(self, packet): + buf = '\xc0' \ + + (packet.replace('\xdb','\xdb\xdd').replace('\xc0','\xdb\xdc')) \ + + '\xc0' + self._port.write(buf) + + """ Calculate checksum of a blob, as it is defined by the ROM """ + @staticmethod + def checksum(data, state=ESP_CHECKSUM_MAGIC): + for b in data: + state ^= ord(b) + return state + + """ Send a request and read the response """ + def command(self, op=None, data="", chk=0): + if op is not None: + pkt = struct.pack(' self.STATUS_BYTES_LENGTH: + return data[:-self.STATUS_BYTES_LENGTH] + else: # otherwise, just return the 'val' field which comes from the reply header (this is used by read_reg) + return val + + def flush_input(self): + self._port.flushInput() + self._slip_reader = slip_reader(self._port) + + def sync(self): + """ Perform a connection test """ + self.command(self.ESP_SYNC, '\x07\x07\x12\x20' + 32 * '\x55') + for i in xrange(7): + self.command() + + def connect(self): + """ Try connecting repeatedly until successful, or giving up """ + print 'Connecting...' + + for _ in xrange(10): + # issue reset-to-bootloader: + # RTS = either CH_PD or nRESET (both active low = chip in reset) + # DTR = GPIO0 (active low = boot to flasher) + self._port.setDTR(False) + self._port.setRTS(True) + time.sleep(0.05) + self._port.setDTR(True) + self._port.setRTS(False) + time.sleep(0.05) + self._port.setDTR(False) + + self._port.timeout = 0.1 + last_exception = None + for _ in xrange(4): + try: + self.flush_input() + self._port.flushOutput() + self.sync() + self._port.timeout = 5 + return + except FatalError as e: + last_exception = e + time.sleep(0.05) + raise FatalError('Failed to connect to %s: %s' % (self.CHIP_NAME, last_exception)) + + """ Read memory address in target """ + def read_reg(self, addr): + # we don't call check_command here because read_reg() function is called + # when detecting chip type, and the way we check for success (STATUS_BYTES_LENGTH) is different + # for different chip types (!) + val, data = self.command(self.ESP_READ_REG, struct.pack('>sys.stderr, 'Stub executed, reading response:' + while True: + p = self.read() + print hexify(p) + if p == '': + return + + def run_stub(self, stub=None): + if stub is None: + if self.IS_STUB: + raise FatalError("Not possible for a stub to load another stub (memory likely to overlap.)") + stub = self.STUB_CODE + + # Upload + print "Uploading stub..." + for field in ['text', 'data']: + if field in stub: + self._upload_ram(stub[field + "_start"], stub[field]) + print "Running stub..." + self.mem_finish(stub['entry']) + + p = self.read() + if p != 'OHAI': + raise FatalError("Failed to start stub. Unexpected response: %s" % p) + print "Stub running..." + return self.STUB_CLASS(self) + + @stub_and_esp32_function_only + def flash_defl_begin(self, size, compsize, offset): + """ Start downloading compressed data to Flash (performs an erase) """ + old_tmo = self._port.timeout + num_blocks = (compsize + self.ESP_FLASH_BLOCK - 1) / self.ESP_FLASH_BLOCK + erase_blocks = (size + self.ESP_FLASH_BLOCK - 1) / self.ESP_FLASH_BLOCK + + self._port.timeout = 20 + t = time.time() + print "Compressed %d bytes to %d..." % (size, compsize) + self.check_command("enter compressed flash mode", self.ESP_FLASH_DEFL_BEGIN, + struct.pack(' length: + raise FatalError('Read more than expected') + digest_frame = self.read() + if len(digest_frame) != 16: + raise FatalError('Expected digest, got: %s' % hexify(digest_frame)) + expected_digest = hexify(digest_frame).upper() + digest = hashlib.md5(data).hexdigest().upper() + if digest != expected_digest: + raise FatalError('Digest mismatch: expected %s, got %s' % (expected_digest, digest)) + return data + + def flash_spi_attach(self,is_hspi,is_legacy): + """Send SPI attach command to enable the SPI flash pins + + ESP8266 ROM does this when you send flash_begin, ESP32 ROM + has it as a SPI command. + """ + # last 3 bytes in ESP_SPI_ATTACH argument are reserved values + arg = struct.pack(' 0: + self.write_reg(SPI_MOSI_DLEN_REG, mosi_bits - 1) + if miso_bits > 0: + self.write_reg(SPI_MISO_DLEN_REG, miso_bits - 1) + else: + + def set_data_lengths(mosi_bits, miso_bits): + SPI_DATA_LEN_REG = SPI_USR1_REG + SPI_MOSI_BITLEN_S = 17 + SPI_MISO_BITLEN_S = 8 + mosi_mask = 0 if (mosi_bits == 0) else (mosi_bits - 1) + miso_mask = 0 if (miso_bits == 0) else (miso_bits - 1) + self.write_reg(SPI_DATA_LEN_REG, + (miso_mask << SPI_MISO_BITLEN_S) | ( + mosi_mask << SPI_MOSI_BITLEN_S)) + + # SPI peripheral "command" bitmasks for SPI_CMD_REG + SPI_CMD_USR = (1 << 18) + + # shift values + SPI_USR2_DLEN_SHIFT = 28 + + if read_bits > 32: + raise FatalError("Reading more than 32 bits back from a SPI flash operation is unsupported") + if len(data) > 64: + raise FatalError("Writing more than 64 bytes of data with one SPI command is unsupported") + + data_bits = len(data) * 8 + flags = SPI_USR_COMMAND + if read_bits > 0: + flags |= SPI_USR_MISO + if data_bits > 0: + flags |= SPI_USR_MOSI + set_data_lengths(data_bits, read_bits) + self.write_reg(SPI_USR_REG, flags) + self.write_reg(SPI_USR2_REG, + (7 << SPI_USR2_DLEN_SHIFT) | spiflash_command) + if data_bits == 0: + self.write_reg(SPI_W0_REG, 0) # clear data register before we read it + else: + if len(data) % 4 != 0: # pad to 32-bit multiple + data += b'\0' * (4 - (len(data) % 4)) + words = struct.unpack("I" * (len(data) / 4), data) + next_reg = SPI_W0_REG + for word in words: + self.write_reg(next_reg, word) + next_reg += 4 + self.write_reg(SPI_CMD_REG, SPI_CMD_USR) + + def wait_done(): + for _ in xrange(10): + if (self.read_reg(SPI_CMD_REG) & SPI_CMD_USR) == 0: + return + raise FatalError("SPI command did not complete in time") + wait_done() + + status = self.read_reg(SPI_W0_REG) + return status + + def read_status(self, num_bytes=2): + """Read up to 24 bits (num_bytes) of SPI flash status register contents + via RDSR, RDSR2, RDSR3 commands + + Not all SPI flash supports all three commands. The upper 1 or 2 + bytes may be 0xFF. + """ + SPIFLASH_RDSR = 0x05 + SPIFLASH_RDSR2 = 0x35 + SPIFLASH_RDSR3 = 0x15 + + status = 0 + shift = 0 + for cmd in [SPIFLASH_RDSR, SPIFLASH_RDSR2, SPIFLASH_RDSR3][0:num_bytes]: + status += self.run_spiflash_command(cmd, read_bits=8) << shift + shift += 8 + return status + + def write_status(self, new_status, num_bytes=2, set_non_volatile=False): + """Write up to 24 bits (num_bytes) of new status register + + num_bytes can be 1, 2 or 3. + + Not all flash supports the additional commands to write the + second and third byte of the status register. When writing 2 + bytes, esptool also sends a 16-byte WRSR command (as some + flash types use this instead of WRSR2.) + + If the set_non_volatile flag is set, non-volatile bits will + be set as well as volatile ones (WREN used instead of WEVSR). + + """ + SPIFLASH_WRSR = 0x01 + SPIFLASH_WRSR2 = 0x31 + SPIFLASH_WRSR3 = 0x11 + SPIFLASH_WEVSR = 0x50 + SPIFLASH_WREN = 0x06 + SPIFLASH_WRDI = 0x04 + + enable_cmd = SPIFLASH_WREN if set_non_volatile else SPIFLASH_WEVSR + + # try using a 16-bit WRSR (not supported by all chips) + # this may be redundant, but shouldn't hurt + if num_bytes == 2: + self.run_spiflash_command(enable_cmd) + self.run_spiflash_command(SPIFLASH_WRSR, struct.pack(">= 8 + + self.run_spiflash_command(SPIFLASH_WRDI) + + +class ESP8266ROM(ESPLoader): + """ Access class for ESP8266 ROM bootloader + """ + CHIP_NAME = "ESP8266" + IS_STUB = False + + DATE_REG_VALUE = 0x00062000 + + # OTP ROM addresses + ESP_OTP_MAC0 = 0x3ff00050 + ESP_OTP_MAC1 = 0x3ff00054 + ESP_OTP_MAC3 = 0x3ff0005c + + SPI_REG_BASE = 0x60000200 + SPI_W0_OFFS = 0x40 + SPI_HAS_MOSI_DLEN_REG = False + + FLASH_SIZES = { + '512KB':0x00, + '256KB':0x10, + '1MB':0x20, + '2MB':0x30, + '4MB':0x40, + '2MB-c1': 0x50, + '4MB-c1':0x60, + '4MB-c2':0x70} + + def flash_spi_attach(self, is_spi, is_legacy): + pass # not implemented in ROM, but OK to silently skip + + def flash_set_parameters(self, size): + pass # not implemented in ROM, but OK to silently skip + + def chip_id(self): + """ Read Chip ID from OTP ROM - see http://esp8266-re.foogod.com/wiki/System_get_chip_id_%28IoT_RTOS_SDK_0.9.9%29 """ + id0 = self.read_reg(self.ESP_OTP_MAC0) + id1 = self.read_reg(self.ESP_OTP_MAC1) + return (id0 >> 24) | ((id1 & MAX_UINT24) << 8) + + def read_mac(self): + """ Read MAC from OTP ROM """ + mac0 = self.read_reg(self.ESP_OTP_MAC0) + mac1 = self.read_reg(self.ESP_OTP_MAC1) + mac3 = self.read_reg(self.ESP_OTP_MAC3) + if (mac3 != 0): + oui = ((mac3 >> 16) & 0xff, (mac3 >> 8) & 0xff, mac3 & 0xff) + elif ((mac1 >> 16) & 0xff) == 0: + oui = (0x18, 0xfe, 0x34) + elif ((mac1 >> 16) & 0xff) == 1: + oui = (0xac, 0xd0, 0x74) + else: + raise FatalError("Unknown OUI") + return oui + ((mac1 >> 8) & 0xff, mac1 & 0xff, (mac0 >> 24) & 0xff) + + def get_erase_size(self, offset, size): + """ Calculate an erase size given a specific size in bytes. + + Provides a workaround for the bootloader erase bug.""" + + sectors_per_block = 16 + sector_size = self.ESP_FLASH_SECTOR + num_sectors = (size + sector_size - 1) / sector_size + start_sector = offset / sector_size + + head_sectors = sectors_per_block - (start_sector % sectors_per_block) + if num_sectors < head_sectors: + head_sectors = num_sectors + + if num_sectors < 2 * head_sectors: + return (num_sectors + 1) / 2 * sector_size + else: + return (num_sectors - head_sectors) * sector_size + + +class ESP8266StubLoader(ESP8266ROM): + """ Access class for ESP8266 stub loader, runs on top of ROM. + """ + FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c + IS_STUB = True + + def __init__(self, rom_loader): + self._port = rom_loader._port + self.flush_input() # resets _slip_reader + +ESP8266ROM.STUB_CLASS = ESP8266StubLoader + + +class ESP32ROM(ESPLoader): + """Access class for ESP32 ROM bootloader + + """ + CHIP_NAME = "ESP32" + IS_STUB = False + + DATE_REG_VALUE = 0x15122500 + + IROM_MAP_START = 0x400d0000 + IROM_MAP_END = 0x40400000 + DROM_MAP_START = 0x3F400000 + DROM_MAP_END = 0x3F700000 + + # ESP32 uses a 4 byte status reply + STATUS_BYTES_LENGTH = 4 + + SPI_REG_BASE = 0x60002000 + EFUSE_REG_BASE = 0x6001a000 + + SPI_W0_OFFS = 0x80 + SPI_HAS_MOSI_DLEN_REG = True + + FLASH_SIZES = { + '1MB':0x00, + '2MB':0x10, + '4MB':0x20, + '8MB':0x30, + '16MB':0x40 + } + + def read_efuse(self, n): + """ Read the nth word of the ESP3x EFUSE region. """ + return self.read_reg(self.EFUSE_REG_BASE + (4 * n)) + + def chip_id(self): + word16 = self.read_efuse(16) + word17 = self.read_efuse(17) + return ((word17 & MAX_UINT24) << 24) | (word16 >> 8) & MAX_UINT24 + + def read_mac(self): + """ Read MAC from EFUSE region """ + word16 = self.read_efuse(16) + word17 = self.read_efuse(17) + word18 = self.read_efuse(18) + word19 = self.read_efuse(19) + wifi_mac = (((word17 >> 16) & 0xff), ((word17 >> 8) & 0xff), ((word17 >> 0) & 0xff), + ((word16 >> 24) & 0xff), ((word16 >> 16) & 0xff), ((word16 >> 8) & 0xff)) + bt_mac = (((word19 >> 16) & 0xff), ((word19 >> 8) & 0xff), ((word19 >> 0) & 0xff), + ((word18 >> 24) & 0xff), ((word18 >> 16) & 0xff), ((word18 >> 8) & 0xff)) + return (wifi_mac,bt_mac) + + def get_erase_size(self, offset, size): + return size + + +class ESP32StubLoader(ESP32ROM): + """ Access class for ESP32 stub loader, runs on top of ROM. + """ + FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c + STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM + IS_STUB = True + + def __init__(self, rom_loader): + self._port = rom_loader._port + self.flush_input() # resets _slip_reader + +ESP32ROM.STUB_CLASS = ESP32StubLoader + + +class ESPBOOTLOADER(object): + """ These are constants related to software ESP bootloader, working with 'v2' image files """ + + # First byte of the "v2" application image + IMAGE_V2_MAGIC = 0xea + + # First 'segment' value in a "v2" application image, appears to be a constant version value? + IMAGE_V2_SEGMENT = 4 + + +def LoadFirmwareImage(chip, filename): + """ Load a firmware image. Can be for ESP8266 or ESP32. ESP8266 images will be examined to determine if they are + original ROM firmware images (ESPFirmwareImage) or "v2" OTA bootloader images. + + Returns a BaseFirmwareImage subclass, either ESPFirmwareImage (v1) or OTAFirmwareImage (v2). + """ + with open(filename, 'rb') as f: + if chip == 'esp32': + return ESP32FirmwareImage(f) + else: # Otherwise, ESP8266 so look at magic to determine the image type + magic = ord(f.read(1)) + f.seek(0) + if magic == ESPLoader.ESP_IMAGE_MAGIC: + return ESPFirmwareImage(f) + elif magic == ESPBOOTLOADER.IMAGE_V2_MAGIC: + return OTAFirmwareImage(f) + else: + raise FatalError("Invalid image magic number: %d" % magic) + + +class ImageSegment(object): + """ Wrapper class for a segment in an ESP image + (very similar to a section in an ELFImage also) """ + def __init__(self, addr, data, file_offs=None): + self.addr = addr + # pad all ImageSegments to at least 4 bytes length + pad_mod = len(data) % 4 + if pad_mod != 0: + data += "\x00" * (4 - pad_mod) + self.data = data + self.file_offs = file_offs + self.include_in_checksum = True + + def copy_with_new_addr(self, new_addr): + """ Return a new ImageSegment with same data, but mapped at + a new address. """ + return ImageSegment(new_addr, self.data, 0) + + def __repr__(self): + r = "len 0x%05x load 0x%08x" % (len(self.data), self.addr) + if self.file_offs is not None: + r += " file_offs 0x%08x" % (self.file_offs) + return r + + +class ELFSection(ImageSegment): + """ Wrapper class for a section in an ELF image, has a section + name as well as the common properties of an ImageSegment. """ + def __init__(self, name, addr, data): + super(ELFSection, self).__init__(addr, data) + self.name = name + + def __repr__(self): + return "%s %s" % (self.name, super(ELFSection, self).__repr__()) + + +class BaseFirmwareImage(object): + SEG_HEADER_LEN = 8 + + """ Base class with common firmware image functions """ + def __init__(self): + self.segments = [] + self.entrypoint = 0 + + def load_common_header(self, load_file, expected_magic): + (magic, segments, self.flash_mode, self.flash_size_freq, self.entrypoint) = struct.unpack(' 16: + raise FatalError('Invalid firmware image magic=%d segments=%d' % (magic, segments)) + return segments + + def load_segment(self, f, is_irom_segment=False): + """ Load the next segment from the image file """ + file_offs = f.tell() + (offset, size) = struct.unpack(' 0x40200000 or offset < 0x3ffe0000 or size > 65536: + print('WARNING: Suspicious segment 0x%x, length %d' % (offset, size)) + + def save_segment(self, f, segment, checksum=None): + """ Save the next segment to the image file, return next checksum value if provided """ + f.write(struct.pack(' 0: + if len(irom_segments) != 1: + raise FatalError('Found %d segments that could be irom0. Bad ELF file?' % len(irom_segments)) + return irom_segments[0] + return None + + def get_non_irom_segments(self): + irom_segment = self.get_irom_segment() + return [s for s in self.segments if s != irom_segment] + + +class ESPFirmwareImage(BaseFirmwareImage): + """ 'Version 1' firmware image, segments loaded directly by the ROM bootloader. """ + + ROM_LOADER = ESP8266ROM + + def __init__(self, load_file=None): + super(ESPFirmwareImage, self).__init__() + self.flash_mode = 0 + self.flash_size_freq = 0 + self.version = 1 + + if load_file is not None: + segments = self.load_common_header(load_file, ESPLoader.ESP_IMAGE_MAGIC) + + for _ in xrange(segments): + self.load_segment(load_file) + self.checksum = self.read_checksum(load_file) + + def default_output_name(self, input_file): + """ Derive a default output name from the ELF name. """ + return input_file + '-' + + def save(self, basename): + """ Save a set of V1 images for flashing. Parameter is a base filename. """ + # IROM data goes in its own plain binary file + irom_segment = self.get_irom_segment() + if irom_segment is not None: + with open("%s0x%05x.bin" % (basename, irom_segment.addr - ESP8266ROM.IROM_MAP_START), "wb") as f: + f.write(irom_segment.data) + + # everything but IROM goes at 0x00000 in an image file + normal_segments = self.get_non_irom_segments() + with open("%s0x00000.bin" % basename, 'wb') as f: + self.write_common_header(f, normal_segments) + checksum = ESPLoader.ESP_CHECKSUM_MAGIC + for segment in normal_segments: + checksum = self.save_segment(f, segment, checksum) + self.append_checksum(f, checksum) + + +class OTAFirmwareImage(BaseFirmwareImage): + """ 'Version 2' firmware image, segments loaded by software bootloader stub + (ie Espressif bootloader or rboot) + """ + + ROM_LOADER = ESP8266ROM + + def __init__(self, load_file=None): + super(OTAFirmwareImage, self).__init__() + self.version = 2 + if load_file is not None: + segments = self.load_common_header(load_file, ESPBOOTLOADER.IMAGE_V2_MAGIC) + if segments != ESPBOOTLOADER.IMAGE_V2_SEGMENT: + # segment count is not really segment count here, but we expect to see '4' + print 'Warning: V2 header has unexpected "segment" count %d (usually 4)' % segments + + # irom segment comes before the second header + # + # the file is saved in the image with a zero load address + # in the header, so we need to calculate a load address + irom_segment = self.load_segment(load_file, True) + # for actual mapped addr, add ESP8266ROM.IROM_MAP_START + flashing_Addr + 8 + irom_segment.addr = 0 + irom_segment.include_in_checksum = False + + first_flash_mode = self.flash_mode + first_flash_size_freq = self.flash_size_freq + first_entrypoint = self.entrypoint + # load the second header + + segments = self.load_common_header(load_file, ESPLoader.ESP_IMAGE_MAGIC) + + if first_flash_mode != self.flash_mode: + print('WARNING: Flash mode value in first header (0x%02x) disagrees with second (0x%02x). Using second value.' + % (first_flash_mode, self.flash_mode)) + if first_flash_size_freq != self.flash_size_freq: + print('WARNING: Flash size/freq value in first header (0x%02x) disagrees with second (0x%02x). Using second value.' + % (first_flash_size_freq, self.flash_size_freq)) + if first_entrypoint != self.entrypoint: + print('WARNING: Entrypoint address in first header (0x%08x) disagrees with second header (0x%08x). Using second value.' + % (first_entrypoint, self.entrypoint)) + + # load all the usual segments + for _ in xrange(segments): + self.load_segment(load_file) + self.checksum = self.read_checksum(load_file) + + def default_output_name(self, input_file): + """ Derive a default output name from the ELF name. """ + irom_segment = self.get_irom_segment() + if irom_segment is not None: + irom_offs = irom_segment.addr - ESP8266ROM.IROM_MAP_START + else: + irom_offs = 0 + return "%s-0x%05x.bin" % (os.path.splitext(input_file)[0], + irom_offs & ~(ESPLoader.ESP_FLASH_SECTOR - 1)) + + def save(self, filename): + with open(filename, 'wb') as f: + # Save first header for irom0 segment + f.write(struct.pack(' 0: + null = ImageSegment(0, '\x00' * pad_len, f.tell()) + checksum = self.save_segment(f, null, checksum) + padding_segments += 1 + # verify that after the 8 byte header is added, were are at the correct offset relative to the segment's vaddr + assert (f.tell() + 8) % IROM_ALIGN == segment.addr % IROM_ALIGN + checksum = self.save_segment(f, segment, checksum) + self.append_checksum(f, checksum) + # kinda hacky: go back to the initial header and write the new segment count + # that includes padding segments. Luckily(?) this header is not checksummed + f.seek(1) + f.write(chr(len(self.segments) + padding_segments)) + + +class ELFFile(object): + SEC_TYPE_PROGBITS = 0x01 + SEC_TYPE_STRTAB = 0x03 + + def __init__(self, name): + # Load sections from the ELF file + self.name = name + self.symbols = None + with open(self.name, 'rb') as f: + self._read_elf_file(f) + + def get_section(self, section_name): + for s in self.sections: + if s.name == section_name: + return s + raise ValueError("No section %s in ELF file" % section_name) + + def _read_elf_file(self, f): + # read the ELF file header + LEN_FILE_HEADER = 0x34 + try: + (ident,_type,machine,_version, + self.entrypoint,_phoff,shoff,_flags, + _ehsize, _phentsize,_phnum,_shentsize, + _shnum,shstrndx) = struct.unpack("<16sHHLLLLLHHHHHH", f.read(LEN_FILE_HEADER)) + except struct.error as e: + raise FatalError("Failed to read a valid ELF header from %s: %s" % (self.name, e)) + + if ident[0] != '\x7f' or ident[1:4] != 'ELF': + raise FatalError("%s has invalid ELF magic header" % self.name) + if machine != 0x5e: + raise FatalError("%s does not appear to be an Xtensa ELF file. e_machine=%04x" % (self.name, machine)) + self._read_sections(f, shoff, shstrndx) + + def _read_sections(self, f, section_header_offs, shstrndx): + f.seek(section_header_offs) + section_header = f.read() + LEN_SEC_HEADER = 0x28 + if len(section_header) == 0: + raise FatalError("No section header found at offset %04x in ELF file." % section_header_offs) + if len(section_header) % LEN_SEC_HEADER != 0: + print 'WARNING: Unexpected ELF section header length %04x is not mod-%02x' % (len(section_header),LEN_SEC_HEADER) + + # walk through the section header and extract all sections + section_header_offsets = range(0, len(section_header), LEN_SEC_HEADER) + + def read_section_header(offs): + name_offs,sec_type,_flags,lma,sec_offs,size = struct.unpack_from(", ) or a single +# argument. + + +def load_ram(esp, args): + image = LoadFirmwareImage(esp, args.filename) + + print 'RAM boot...' + for (offset, size, data) in image.segments: + print 'Downloading %d bytes at %08x...' % (size, offset), + sys.stdout.flush() + esp.mem_begin(size, div_roundup(size, esp.ESP_RAM_BLOCK), esp.ESP_RAM_BLOCK, offset) + + seq = 0 + while len(data) > 0: + esp.mem_block(data[0:esp.ESP_RAM_BLOCK], seq) + data = data[esp.ESP_RAM_BLOCK:] + seq += 1 + print 'done!' + + print 'All segments done, executing at %08x' % image.entrypoint + esp.mem_finish(image.entrypoint) + + +def read_mem(esp, args): + print '0x%08x = 0x%08x' % (args.address, esp.read_reg(args.address)) + + +def write_mem(esp, args): + esp.write_reg(args.address, args.value, args.mask, 0) + print 'Wrote %08x, mask %08x to %08x' % (args.value, args.mask, args.address) + + +def dump_mem(esp, args): + f = file(args.filename, 'wb') + for i in xrange(args.size / 4): + d = esp.read_reg(args.address + (i * 4)) + f.write(struct.pack(' flash_end: + raise FatalError(("File %s (length %d) at offset %d will not fit in %d bytes of flash. " + + "Use --flash-size argument, or change flashing address.") + % (argfile.name, argfile.tell(), address, flash_end)) + argfile.seek(0) + + for address, argfile in args.addr_filename: + print 'Erasing flash...' + image = argfile.read() + # Update header with flash parameters + if address == 0 and image[0] == '\xe9': + image = image[0:2] + flash_info + image[4:] + calcmd5 = hashlib.md5(image).hexdigest() + uncsize = len(image) + if args.compress: + uncimage = image + image = zlib.compress(uncimage, 9) + blocks = div_roundup(len(image), esp.FLASH_WRITE_SIZE) + esp.flash_defl_begin(len(uncimage),len(image), address) + else: + blocks = div_roundup(len(image), esp.FLASH_WRITE_SIZE) + esp.flash_begin(blocks * esp.FLASH_WRITE_SIZE, address) + argfile.seek(0) # in case we need it again + seq = 0 + written = 0 + t = time.time() + header_block = None + while len(image) > 0: + print '\rWriting at 0x%08x... (%d %%)' % (address + seq * esp.FLASH_WRITE_SIZE, 100 * (seq + 1) / blocks), + sys.stdout.flush() + block = image[0:esp.FLASH_WRITE_SIZE] + if args.compress: + esp.flash_defl_block(block, seq) + else: + # Pad the last block + block = block + '\xff' * (esp.FLASH_WRITE_SIZE - len(block)) + esp.flash_block(block, seq) + image = image[esp.FLASH_WRITE_SIZE:] + seq += 1 + written += len(block) + t = time.time() - t + speed_msg = "" + if args.compress: + if t > 0.0: + speed_msg = " (effective %.1f kbit/s)" % (uncsize / t * 8 / 1000) + print '\rWrote %d bytes (%d compressed) at 0x%08x in %.1f seconds%s...' % (uncsize, written, address, t, speed_msg) + else: + if t > 0.0: + speed_msg = " (%.1f kbit/s)" % (written / t * 8 / 1000) + print '\rWrote %d bytes at 0x%08x in %.1f seconds%s...' % (written, address, t, speed_msg) + res = esp.flash_md5sum(address, uncsize) + if res != calcmd5: + print 'File md5: %s' % calcmd5 + print 'Flash md5: %s' % res + raise FatalError("MD5 of file does not match data in flash!") + else: + print 'Hash of data verified.' + print '\nLeaving...' + if args.flash_mode == 'dio' and esp.CHIP_NAME == "ESP8266": + esp.flash_unlock_dio() + else: + esp.flash_begin(0, 0) + if args.compress: + esp.flash_defl_finish(False) + else: + esp.flash_finish(False) + if args.verify: + print 'Verifying just-written flash...' + verify_flash(esp, args, header_block) + + +def image_info(args): + image = LoadFirmwareImage(args.chip, args.filename) + print('Image version: %d' % image.version) + print('Entry point: %08x' % image.entrypoint) if image.entrypoint != 0 else 'Entry point not set' + print '%d segments' % len(image.segments) + print + idx = 0 + for seg in image.segments: + idx += 1 + print 'Segment %d: %r' % (idx, seg) + calc_checksum = image.calculate_checksum() + print 'Checksum: %02x (%s)' % (image.checksum, + 'valid' if image.checksum == calc_checksum else 'invalid - calculated %02x' % calc_checksum) + + +def make_image(args): + image = ESPFirmwareImage() + if len(args.segfile) == 0: + raise FatalError('No segments specified') + if len(args.segfile) != len(args.segaddr): + raise FatalError('Number of specified files does not match number of specified addresses') + for (seg, addr) in zip(args.segfile, args.segaddr): + data = file(seg, 'rb').read() + image.segments.append(ImageSegment(addr, data)) + image.entrypoint = args.entrypoint + image.save(args.output) + + +def elf2image(args): + e = ELFFile(args.input) + if args.chip == 'auto': # Default to ESP8266 for backwards compatibility + print "Creating image for ESP8266..." + args.chip == 'esp8266' + + if args.chip == 'esp32': + image = ESP32FirmwareImage() + elif args.version == '1': # ESP8266 + image = ESPFirmwareImage() + else: + image = OTAFirmwareImage() + image.entrypoint = e.entrypoint + image.segments = e.sections # ELFSection is a subclass of ImageSegment + image.flash_mode = {'qio':0, 'qout':1, 'dio':2, 'dout': 3}[args.flash_mode] + image.flash_size_freq = image.ROM_LOADER.FLASH_SIZES[args.flash_size] + image.flash_size_freq += {'40m':0, '26m':1, '20m':2, '80m': 0xf}[args.flash_freq] + + if args.output is None: + args.output = image.default_output_name(args.input) + image.save(args.output) + + +def read_mac(esp, args): + mac = esp.read_mac() + print 'MAC: %s' % ':'.join(map(lambda x: '%02x' % x, mac)) + + +def chip_id(esp, args): + chipid = esp.chip_id() + print 'Chip ID: 0x%08x' % chipid + + +def erase_flash(esp, args): + print 'Erasing flash (this may take a while)...' + t = time.time() + esp.erase_flash() + print 'Chip erase completed successfully in %.1fs' % (time.time() - t) + + +def erase_region(esp, args): + print 'Erasing region (may be slow depending on size)...' + t = time.time() + esp.erase_region(args.address, args.size) + print 'Erase completed successfully in %.1f seconds.' % (time.time() - t) + + +def run(esp, args): + esp.run() + + +def flash_id(esp, args): + flash_id = esp.flash_id() + print 'Manufacturer: %02x' % (flash_id & 0xff) + print 'Device: %02x%02x' % ((flash_id >> 8) & 0xff, (flash_id >> 16) & 0xff) + + +def read_flash(esp, args): + if args.no_progress: + flash_progress = None + else: + def flash_progress(progress, length): + msg = '%d (%d %%)' % (progress, progress * 100.0 / length) + padding = '\b' * len(msg) + if progress == length: + padding = '\n' + sys.stdout.write(msg + padding) + sys.stdout.flush() + t = time.time() + data = esp.read_flash(args.address, args.size, flash_progress) + t = time.time() - t + print ('\rRead %d bytes at 0x%x in %.1f seconds (%.1f kbit/s)...' + % (len(data), args.address, t, len(data) / t * 8 / 1000)) + file(args.filename, 'wb').write(data) + + +def verify_flash(esp, args, flash_params=None): + differences = False + for address, argfile in args.addr_filename: + image = argfile.read() + argfile.seek(0) # rewind in case we need it again + if address == 0 and image[0] == '\xe9' and flash_params is not None: + image = image[0:2] + flash_params + image[4:] + image_size = len(image) + print 'Verifying 0x%x (%d) bytes @ 0x%08x in flash against %s...' % (image_size, image_size, address, argfile.name) + # Try digest first, only read if there are differences. + digest = esp.flash_md5sum(address, image_size) + expected_digest = hashlib.md5(image).hexdigest() + if digest == expected_digest: + print '-- verify OK (digest matched)' + continue + else: + differences = True + if getattr(args, 'diff', 'no') != 'yes': + print '-- verify FAILED (digest mismatch)' + continue + + flash = esp.read_flash(address, image_size) + assert flash != image + diff = [i for i in xrange(image_size) if flash[i] != image[i]] + print '-- verify FAILED: %d differences, first @ 0x%08x' % (len(diff), address + diff[0]) + for d in diff: + print ' %08x %02x %02x' % (address + d, ord(flash[d]), ord(image[d])) + if differences: + raise FatalError("Verify failed.") + + +def read_flash_status(esp, args): + print ('Status value: 0x%04x' % esp.read_status(args.bytes)) + + +def write_flash_status(esp, args): + fmt = "0x%%0%dx" % (args.bytes * 2) + args.value = args.value & ((1 << (args.bytes * 8)) - 1) + print (('Initial flash status: ' + fmt) % esp.read_status(args.bytes)) + print (('Setting flash status: ' + fmt) % args.value) + esp.write_status(args.value, args.bytes, args.non_volatile) + print (('After flash status: ' + fmt) % esp.read_status(args.bytes)) + + +def version(args): + print __version__ + + +def wrap_stub(args): + e = ELFFile(args.input) + + stub = { + 'params_start': e.get_symbol_addr('_params_start'), + 'code': e.get_section('.text').data, + 'code_start': e.get_symbol_addr('_code_start'), + 'entry': e.get_symbol_addr(args.entry), + } + try: + stub['data'] = e.get_section('.data').data + stub['data_start'] = e.get_symbol_addr('_data_start') + except ValueError: + pass + # No data section, that's fine + bss_size, bss_start = 0, 0 + try: + bss_start = e.get_symbol_addr('_bss_start') + bss_size = e.get_symbol_addr('_bss_end') - bss_start + except ValueError: + pass + params_len = e.get_symbol_addr('_params_end') - stub['params_start'] + if params_len % 4 != 0: + raise FatalError('Params must be dwords') + stub['num_params'] = params_len / 4 + + # Pad code with NOPs to mod 4. + if len(stub['code']) % 4 != 0: + stub['code'] += (4 - (len(stub['code']) % 4)) * '\0' + + print >>sys.stderr, ( + 'Stub params: %d @ 0x%08x, code: %d @ 0x%08x, bss: %d @ 0x%08x, data: %d @ 0x%08x, entry: %s @ 0x%x' % ( + params_len, stub['params_start'], + len(stub['code']), stub['code_start'], + bss_size, bss_start, + len(stub.get('data', '')), stub.get('data_start', 0), + args.entry, stub['entry'])) + + jstub = dict(stub) + jstub['code'] = hexify(stub['code']) + if 'data' in stub: + jstub['data'] = hexify(stub['data']) + print json.dumps(jstub) + + +def run_stub(esp, args): + esp.run_custom_stub(json.load(open(args.input)), args.params, read_output=True) + +# +# End of operations functions +# + + +def main(): + parser = argparse.ArgumentParser(description='esptool.py v%s - ESP8266 ROM Bootloader Utility' % __version__, prog='esptool') + + parser.add_argument('--chip', '-c', + help='Target chip type', + choices=['auto', 'esp8266', 'esp31', 'esp32'], + default=os.environ.get('ESPTOOL_CHIP', 'auto')) + + parser.add_argument( + '--port', '-p', + help='Serial port device', + default=os.environ.get('ESPTOOL_PORT', ESPLoader.DEFAULT_PORT)) + + parser.add_argument( + '--baud', '-b', + help='Serial port baud rate used when flashing/reading', + type=arg_auto_int, + default=os.environ.get('ESPTOOL_BAUD', ESPLoader.ESP_ROM_BAUD)) + + parser.add_argument( + '--no-stub', + help="Disable launching the flasher stub, only talk to ROM bootloader. Some features will not be available.", + action='store_true') + + subparsers = parser.add_subparsers( + dest='operation', + help='Run esptool {command} -h for additional help') + + parser_load_ram = subparsers.add_parser( + 'load_ram', + help='Download an image to RAM and execute') + parser_load_ram.add_argument('filename', help='Firmware image') + + parser_dump_mem = subparsers.add_parser( + 'dump_mem', + help='Dump arbitrary memory to disk') + parser_dump_mem.add_argument('address', help='Base address', type=arg_auto_int) + parser_dump_mem.add_argument('size', help='Size of region to dump', type=arg_auto_int) + parser_dump_mem.add_argument('filename', help='Name of binary dump') + + parser_read_mem = subparsers.add_parser( + 'read_mem', + help='Read arbitrary memory location') + parser_read_mem.add_argument('address', help='Address to read', type=arg_auto_int) + + parser_write_mem = subparsers.add_parser( + 'write_mem', + help='Read-modify-write to arbitrary memory location') + parser_write_mem.add_argument('address', help='Address to write', type=arg_auto_int) + parser_write_mem.add_argument('value', help='Value', type=arg_auto_int) + parser_write_mem.add_argument('mask', help='Mask of bits to write', type=arg_auto_int) + + def add_spi_flash_subparsers(parent): + """ Add common parser arguments for SPI flash properties """ + parent.add_argument('--flash_freq', '-ff', help='SPI Flash frequency', + choices=['40m', '26m', '20m', '80m'], + default=os.environ.get('ESPTOOL_FF', '40m')) + parent.add_argument('--flash_mode', '-fm', help='SPI Flash mode', + choices=['qio', 'qout', 'dio', 'dout'], + default=os.environ.get('ESPTOOL_FM', 'qio')) + parent.add_argument('--flash_size', '-fs', help='SPI Flash size in MegaBytes (1MB, 2MB, 4MB, 8MB, 16M)' + ' plus ESP8266-only (256KB, 512KB, 2MB-c1, 4MB-c1, 4MB-2)', + action=FlashSizeAction, + default=os.environ.get('ESPTOOL_FS', '1MB')) + parent.add_argument('--ucIsHspi', '-ih', help='Config SPI PORT/PINS (Espressif internal feature)',action='store_true') + parent.add_argument('--ucIsLegacy', '-il', help='Config SPI LEGACY (Espressif internal feature)',action='store_true') + + parser_write_flash = subparsers.add_parser( + 'write_flash', + help='Write a binary blob to flash') + parser_write_flash.add_argument('addr_filename', metavar='
', help='Address followed by binary filename, separated by space', + action=AddrFilenamePairAction) + add_spi_flash_subparsers(parser_write_flash) + parser_write_flash.add_argument('--no-progress', '-p', help='Suppress progress output', action="store_true") + parser_write_flash.add_argument('--verify', help='Verify just-written data (only necessary if very cautious, data is already CRCed', action='store_true') + parser_write_flash.add_argument('--compress', '-z', help='Compress data in transfer',action="store_true") + + subparsers.add_parser( + 'run', + help='Run application code in flash') + + parser_image_info = subparsers.add_parser( + 'image_info', + help='Dump headers from an application image') + parser_image_info.add_argument('filename', help='Image file to parse') + + parser_make_image = subparsers.add_parser( + 'make_image', + help='Create an application image from binary files') + parser_make_image.add_argument('output', help='Output image file') + parser_make_image.add_argument('--segfile', '-f', action='append', help='Segment input file') + parser_make_image.add_argument('--segaddr', '-a', action='append', help='Segment base address', type=arg_auto_int) + parser_make_image.add_argument('--entrypoint', '-e', help='Address of entry point', type=arg_auto_int, default=0) + + parser_elf2image = subparsers.add_parser( + 'elf2image', + help='Create an application image from ELF file') + parser_elf2image.add_argument('input', help='Input ELF file') + parser_elf2image.add_argument('--output', '-o', help='Output filename prefix (for version 1 image), or filename (for version 2 single image)', type=str) + parser_elf2image.add_argument('--version', '-e', help='Output image version', choices=['1','2'], default='1') + add_spi_flash_subparsers(parser_elf2image) + + subparsers.add_parser( + 'read_mac', + help='Read MAC address from OTP ROM') + + subparsers.add_parser( + 'chip_id', + help='Read Chip ID from OTP ROM') + + subparsers.add_parser( + 'flash_id', + help='Read SPI flash manufacturer and device ID') + + parser_read_status = subparsers.add_parser( + 'read_flash_status', + help='Read SPI flash status register') + + parser_read_status.add_argument('--bytes', help='Number of bytes to read (1-3)', type=int, choices=[1,2,3], default=2) + + parser_write_status = subparsers.add_parser( + 'write_flash_status', + help='Write SPI flash status register') + + parser_write_status.add_argument('--non-volatile', help='Write non-volatile bits (use with caution)', action='store_true') + parser_write_status.add_argument('--bytes', help='Number of status bytes to write (1-3)', type=int, choices=[1,2,3], default=2) + parser_write_status.add_argument('value', help='New value', type=arg_auto_int) + + parser_read_flash = subparsers.add_parser( + 'read_flash', + help='Read SPI flash content') + parser_read_flash.add_argument('address', help='Start address', type=arg_auto_int) + parser_read_flash.add_argument('size', help='Size of region to dump', type=arg_auto_int) + parser_read_flash.add_argument('filename', help='Name of binary dump') + parser_read_flash.add_argument('--no-progress', '-p', help='Suppress progress output', action="store_true") + + parser_verify_flash = subparsers.add_parser( + 'verify_flash', + help='Verify a binary blob against flash') + parser_verify_flash.add_argument('addr_filename', help='Address and binary file to verify there, separated by space', + action=AddrFilenamePairAction) + parser_verify_flash.add_argument('--diff', '-d', help='Show differences', + choices=['no', 'yes'], default='no') + + subparsers.add_parser( + 'erase_flash', + help='Perform Chip Erase on SPI flash') + + parser_erase_region = subparsers.add_parser( + 'erase_region', + help='Erase a region of the flash') + parser_erase_region.add_argument('address', help='Start address (must be multiple of 4096)', type=arg_auto_int) + parser_erase_region.add_argument('size', help='Size of region to erase (must be multiple of 4096)', type=arg_auto_int) + + subparsers.add_parser( + 'version', help='Print esptool version') + + parser_wrap_stub = subparsers.add_parser('wrap_stub', help='Wrap stub and output a JSON object') + parser_wrap_stub.add_argument('input') + parser_wrap_stub.add_argument('--entry', default='stub_main') + + parser_run_stub = subparsers.add_parser('run_stub', help='Run stub on a device') + parser_run_stub.add_argument('--entry', default='stub_main') + parser_run_stub.add_argument('input') + parser_run_stub.add_argument('params', nargs='*', type=arg_auto_int) + + # internal sanity check - every operation matches a module function of the same name + for operation in subparsers.choices.keys(): + assert operation in globals(), "%s should be a module function" % operation + + args = parser.parse_args() + + # operation function can take 1 arg (args), 2 args (esp, arg) + # or be a member function of the ESPLoader class. + + operation_func = globals()[args.operation] + operation_args,_,_,_ = inspect.getargspec(operation_func) + if operation_args[0] == 'esp': # operation function takes an ESPLoader connection object + initial_baud = min(ESPLoader.ESP_ROM_BAUD, args.baud) # don't sync faster than the default baud rate + chip_constructor_fun = { + 'auto': ESPLoader.detect_chip, + 'esp8266': ESP8266ROM, + 'esp32': ESP32ROM, + }[args.chip] + esp = chip_constructor_fun(args.port, initial_baud) + + if not args.no_stub and args.operation != 'run_stub': + esp = esp.run_stub() + + if args.baud > initial_baud: + esp.change_baud(args.baud) + # TODO: handle a NotImplementedInROMError + + # override common SPI flash parameter stuff as required + if hasattr(args, "ucIsHspi"): + print "Attaching SPI flash..." + esp.flash_spi_attach(args.ucIsHspi,args.ucIsLegacy) + else: + esp.flash_spi_attach(0, 0) + if hasattr(args, "flash_size"): + print "Configuring flash size..." + esp.flash_set_parameters(flash_size_bytes(args.flash_size)) + + operation_func(esp, args) + else: + operation_func(args) + + +class FlashSizeAction(argparse.Action): + """ Custom flash size parser class to support backwards compatibility with megabit size arguments. + + (At next major relase, remove deprecated sizes and this can become a 'normal' choices= argument again.) + """ + def __init__(self, option_strings, dest, nargs=1, **kwargs): + super(FlashSizeAction, self).__init__(option_strings, dest, nargs, **kwargs) + + def __call__(self, parser, namespace, values, option_string=None): + try: + value = { + '2m': '256KB', + '4m': '512KB', + '8m': '1MB', + '16m': '2MB', + '32m': '4MB', + '16m-c1': '2MB-c1', + '32m-c1': '4MB-c1', + '32m-c2': '4MB-c2' + }[values[0]] + print("WARNING: Flash size arguments in megabits like '%s' are deprecated." % (values[0])) + print("Please use the equivalent size '%s'." % (value)) + print("Megabit arguments may be removed in a future release.") + except KeyError: + value = values[0] + + known_sizes = dict(ESP8266ROM.FLASH_SIZES) + known_sizes.update(ESP32ROM.FLASH_SIZES) + if value not in known_sizes: + raise argparse.ArgumentError(self, '%s is not a known flash size. Known sizes: %s' % (value, ", ".join(known_sizes.keys()))) + setattr(namespace, self.dest, value) + + +class AddrFilenamePairAction(argparse.Action): + """ Custom parser class for the address/filename pairs passed as arguments """ + def __init__(self, option_strings, dest, nargs='+', **kwargs): + super(AddrFilenamePairAction, self).__init__(option_strings, dest, nargs, **kwargs) + + def __call__(self, parser, namespace, values, option_string=None): + # validate pair arguments + pairs = [] + for i in range(0,len(values),2): + try: + address = int(values[i],0) + except ValueError as e: + raise argparse.ArgumentError(self,'Address "%s" must be a number' % values[i]) + try: + argfile = open(values[i + 1], 'rb') + except IOError as e: + raise argparse.ArgumentError(self, e) + except IndexError: + raise argparse.ArgumentError(self,'Must be pairs of an address and the binary filename to write there') + pairs.append((address, argfile)) + setattr(namespace, self.dest, pairs) + +# Binary stub code (see flasher_stub dir for source & details) +ESP8266ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b""" +eNrFXPt/08aW/1ckJ+RhQjsjydIohF7HCSZw4XMhNCm967YZvaBc2k1c74ayufu3r85LGsk2gb72h4BnNJrHmfP4njNn9D/bi/L9Ynvf2569r8zsvQrqP3Uxe6+VU1BLBfkzun4vr/+q2ftceVC7U/8Tet7m+QSq\ +4XkKPw7rf2yn4S409OuqpFP9mMbi0gmUzhedFjDPsP6rx9UBTiPyNtRsXjdSvfnp9rdR3d9V+Te38ZiGkD/q1iUIzEnTEHlgeZK6XZXCynCwRy/qePbewsv16vKRUAHqJ1D50K2ZvU8D+F0TowyEalFnzWmHKrhT\ +579Ai52nRIkqnG1v0HBEmJrgZcZTi5liamfP86LzuhaGKaGJTCKHLl/t1WsocOTxWV2pmxGDLfhXqc3zkCdYwqIqBc1UU2zafwsjwutlp3qSE2md3Q1pd81TL4T69KnZ2x/s7jFz5cZvaa+XN9nI7wteg1pi07qQ\ +1kuySAzY8/wu/zJ9tojc8ngsv57+i1/QnU5N0ylwMa/f5ZMi7dCwriARW0Bd6sNbukMeYK2BUAj+FsAeN/hCWXWaDmCzkfAF1Hvegvgsh32GSVhoX1M/jee8F8QODV/Ur4yPaMHc5ytgXXhu4PmTsOZUHQopgCF1\ +Vlxc0E9+JXsJ/z552M7rGNlF3gSWxjXCmur3aj2Thgd1bZEzH9EPGBVaVPstl7+Beb+QFvgqMDLQIG4l1dEmUZf9piK+neqoq4BgBihz+wOSHBW2TH/K213TK89rsbEwk5gEqohZZAvFUmY6Wy0SCwwM+w6MsIqR\ +1X1WPy3n1DOx0EWGGw+bXBWyWGFAaZNzA9tvwBNaLzsOpy6QYZBOCSkjNXrYjor6Y8T7SjOTZoaH1+vmV1CDlns/eX69OYLENCKFEgRdB/uwf85O48hK1Ay8zhQyIKCsEAuYPinN+kWfNx1/BB/fre60SpyWlbkM\ +hIizxRVaAzZBRd5fPJLtHk20nVj7Pr1mVtHsCnjKyhy8/tRwv8SAqfcnyFyHb5eUE+sgIGekDg7TzcZS70aNKPCfHnl7h9D861OU7t1j3vXRyk194NJipGpVbsLbNbmQtvn9yi1cuoX6tyH9S4KT/x1/RU4dNEpT\ +LqS63fqlrStTgCZ1k8UVsFetb1Ilapu0Ky6moFnTTofr11+50hDPFqwNCqJkXjNeVvH2Ql0FTFACxfQzIIliZl/qFm16M48H3IV65rF8FcvvFLC6khQUSEGuzp+CUPtirYN2WdoOuKMlpmtQCVvYkFsD39Kwwshx\ +R/hzInzBCABoDVRtObgwL2A8IH+rCbxlxnjj7v3CLbx3CzddFjGqW077ZSOCMi5EBdu2jrkm73ONdTDN7eyjbQVWJnZNfZfqaCaaTd8jDaRtSBjN8mSRDaNTVmtV8A7arNOsPvEYVJbAbwF1amKAioo3C17NVr7a\ +oAQ0cMMXMNSxWM74QJpasdi4yjdkJRD1xNQjKNmsdHgdgECGjAdGvZJ3wx1hqrNfwc50hSEL+nO0xNk88Hsa2OQZryuavCNMDSCBQEi9mGIkjKpxPTBS9AAGAdBEEAUp0x3dLklDPXXL0LydxdkrMpCFLeG5bup/\ +hHpgmd6E/RbD1Dywgxu0TRuSwes5DnH5pXBRiD5PZzNbyYKeCMe/YPVOg0/K1gTlq8iYFc2soP+4NvkGN+yFsAHrf9qdE1pkFoP6DxruvAFF6J2p5z8ATROGCiX038ylehIPjlh1Z8c0fWLc+u0yZDSRpVvHgIoN\ ++hgPeHDRTCZewa1GrdBMsKVF4sgTbuUKDSkKer4Hb8qK9DUr6XaNh+2ukneFTDTZhGePEsfeKeVtsJ5JMmercD+nrZ4G5ZL+Js349EkPL7YgRPa5dNQSWzBy73Xuf828m74YIurFIuouNmyoiPJDRn26a51B56Nj\ +Ue3sgec+IPcddtzavju7Q9tSqq8811StMm8mbwBDtDub8Zs1QYb3duH3zgHTA2xZEbCCSQ+YQC3RVhgRdvb/cXL4mFAfO22brAwQLqx0HXULqZqYAE626zZqNV7GMfJSzQrb7pP/qqtSorQzF5oBFiAsYTtPOl6q\ +t+uMUW+pKfCdV05QA8jYvA3WyJZSANhEg9w41Wgqs7Zfi+hMrfSzPYE0Sj0HMXcKOnC6cD1ua5yCrpoV1NtnUycS004oEnnl2SlndqrzMGKvDAoZrGPUw2JVdyUNjbEgoxvX8Gd5U3iFDcd7TxtYKdKXRY/5V55/\ +0QBRgkpYKAzBzzdN3QlZPQIXiqxOXXi6xXWGuwTjwGEZECTLYKHFFQO7gfGmIwxFTUAiLBjswIKfHliIR5AdcDQ/GUeyFkQMjLT9fZCgcmDxM/kQvKD86KGYAJ/CJ6BUoE8UbbTk+HS45wYv2OeVoVR+yuZ/JdBI\ +GINadnLdkJ+KvG7sq9HkK7QHjsdqvkhWPF9Zr085BkbM9IL9n/KqtRXUccZTlJ6WTKkW7eeDLSydEIZAG4jBdOM8ThDTCeztOqHNEEJ4JhJ66sGLI6wZDewAWAP6NU3QImj7MoC/tVYDr5rMFjt7ndkE9ds9PxPB\ +ZrnCeQfh0lAw4vijHhi1BKpk2HDgjs+qr9bDEGfyGUtXZCwAwmiOitrY8XtLBotpumY+MuE06Tcgti4K5igBuBgYDSjoxHSO96F9EqT0NHWeTt/RI1AiFbdXMcKW6khs+T+xuCFP77N+jt912THrjTqb4TPAeDRW\ +2bYt3Dl8x/Ux6y2dd3oy8ZfNJBVpHN1poOJIGtS9NcR54sZkNLyAoC6XKncKm25bJW2p82/baeuC3EqNEaKQcV/8DyFEXW0qmXXz+GlXLNNieSsBOgJqSRlpmfiksT4Qh0AwuCTO56ekHkE8kRFw9l9QE5M/8zAA\ +ef6SxkPsHoAOfuXEuCLXVngeefgwXLKkwM7Z3lUEwGBExCTqS/F+vqKVoBcerByAFnO9YiXzles4Z8EDesWBo/tKak4ystXSPY+N8Cfw+6gSYAvTYVE2TU/KeYzOk4T94mHn6ZTkFx7VzTropt+DlR7uuMHgQqYd\ +tHG7Zv4xea0tP+PgZg8s6iltWl4xiqRYbaMduJs05LCfFqsGrgZGJwKXy44m7Y4A/CnL8QB6Tb6AYU55kqPWseQf4G7iuYcWVZNcnKGCPYbK58ceNMAY0Tj0YHq56M9cy9IBEgMGhCg1YRafrAJQpyWGQyGb5yZw\ +nhkOLIbBRE4XTpnNYgecAlgl6ZbNQ804almnEeZ49vOS8E657ahtR+9N+bBJdZiqbZ0vuQ/VP1qHCOk+WsW+Pcej9xx9FyWHHYbd87KueSjFnrHQS8binNROkQx6lgL8UB34s23Yu3Ir+L6atOtCcAMHRPGrZVSJ\ +epheLyVGHmBxNP3abe6zD0QxwO2luPCeL+6DOjwcoouy2W+E8dV7FKIYrIq7hxTGw9WXK+AQ6NN2SnhiGSDACid4evkKrBXQN0VUMz2S5qutfYimPvQCOn3C8wsSv4dt/EbFD3hzPoJXBgxXFcs5ybC9y7wHPJeO\ +XDXqc5iq1C1dMzlCgygbuARVkTF6CfHULJj7dzN7hx6y4t5OBLJScNkE2CCwwMmjH6Rnf3TFUgcna4F/9/LvrO7tMXkpgJkK+6/M7mIne2MOYwMpwXM2GE+fgqBF14SiicN+pgmAi1bmGJAAvQ3bokc772A0uzW3\ +m9jr8OAliDvETrBB9AZDlS26YNUbAapGh4eUJAWaiBaoVfzSVcdttAfCSYD04X8DWKmAfzBCkvztProamyB2P3v4e4uNIGi1miqg7PMNwO263qEZDQHWPBVsGz3OvuNztYKMKCqFjL07jpKXUSCemH9OvYCeziuc\ +lavruII6m7ocErTHIZ13YQU/0aJpARSihMEPdqYOrsQu57SmO88TXNPjdkkq/FaWouIvWFPL/ON2/g9780/bQysRBeVPnEY5NvLHXIX6Cbz84A1tjVF3QGreDiZWndoos1/CBoHsE9eiR6WX0RVt8cRXp36U+fiS\ +HxJvk8SV5Gt5FR945P/93ItEMo0H1i98/jV4ItmZhMOuGOIzqtUj9JXRcXqdOAdSo/GYI+tkSccgMaGqgG/yKfcWL5HfBx1Y7B2lROopBdQopUH+eBOq+MEYN3HXdXX7zMY8OvB2WDpzWp/KISMFAjRVkWbCrNRT\ +bmnIZncD2c6BrLBhBDFgzlkkbSr/Lsaiq8FHKkFuTMY8GLcxfaWuqGtggdrIbe6C2+z5h7t0lARCV4aHHBIDaFicn26ghdxEbQAnGzUtNj5Ci7ShxS4HLvOXDG4r64hSs2zzpyw77yybFTgsg7OPxrxa2Cx9A1vo\ +0YtatbBMx88IVsAhvYGz4o8ufi4rRx8H+8klOSb665ZuYgmKyslcrkHDlG8HcBJbAr4Dm9RssmFamENZdi/GlRwcfjr3a/aGcj4TRzGY7+bUQ8YAotXJPuc+5If3YZKNins7eDIQ+tCUeVxU7QgVD9FWvw2fhHAa\ +oE1DY59Xlh8G0mcufcryhZwaj63iaQvNJCKTatCyZTZbpIPZAqy4ojBEVSOLxf1gtth30YamPsE4qia7JKb5Ui5Rh3B7v0wllMcYDZnsAOl951OZ7CA8J0Na8hFV1yd0jcefpl4kAq9YoAycqprGjFOgvWG2TnLF\ +wdGUQ/7Bsh7ZpdAMnvGpuQ9xzayaP5QJnjmROjRCzzjfQh0BNLjYQICwzXlYCW9E2TVd6DCV+YoJGJyAaSbAhDJd8jbqbg/w0kJ81V/bo7QVeygpDDFFLZeMjkwB9w4iOsXc330OS6E0rg/wzzWf3ul/w6BQiqUU\ +35ODzxxokpA9nyPe6pr0ed+gz/1ADDmZcDDnMF7+4eyy4qgBjARaA5Us0q6W8vm076D1VoNHErzsFLdhi4Me6MT5LY8NXoNIoe/gszYpJlDXnKCN1i8JZ1YVvUWJ526G15qgPDRpl1ivuF430C2ZSGxy0hwxhtcE\ +NiuIg+XBJZ2TSGg1jSYTaTngs0c8y6Iwr8TcgXPyRd+TnZw0r5LUGSNdjAI6wyjNPh8pgPOdSH/V8GR+3L7MaB1DTPFPPZcXqAucVBVvIVzLAogm/TaFswLeYEQyPGcV8f+kdcpStI7P8fJCFIGzxkM+dC8k0wo7\ +Bdilx1PeaCBbiW6n8trQ2wqccIIcCd3lT0JvC16HaD4md+FZewwqXPt8FiuOECQ+1ms4xUOsHrNmts+ssmM1O3bBNJrmRGzFg/S3QDHAd+ekOPLg/wmSYKy1OGRrmbsMWdNZbQ43D0lnS+iKkGhruRuYWhPdJ0Bd\ +4YGvDpOY4v06D3FnQEJjcFP11Y27I5dzYGuU9PHlezBZkLqRvAK/RB+L/1PbMO1u2dxieFLT4Vlfx8y2D0GdFkSB4WzbSbjBGHPWf8f/mR0/CBLJ1Ia7EzrvNMpJVOixSC2SumaQHYjbobLG3GMe0AQgp3pg7+ES\ +vmjtpMtOpsnCdJcwxEh/6OoklBY12RBGP4asa/hZT+IL2k707SMZB7hN71Lii2OeGRxgeGeOdK5Llmx8asB2QgnO31PIuqgIWKH63eUUPXtckZYDWGuaBAuQh022Iw3R9VfNhi8kA1GHHily+DOc06cx5QS8r1gS\ +rNeY7ZBzb6sMnDloZVIZFncwtYZDCUJBiO4hagTgYClDXaK8E4va8RgOTsO5NZeJx0ForQObBH488RM6op77cWB3xqE38c3lFbL+8dzGE2seUYwHIDQaWMA5SaIvT2gpqTpuQ8njsZ7YndYNpdTHcZutbvVU5j6p\ +FzT+BTqY+DuwNeHkGyjN+YAPj94SSv42sdeYs/o1yK5Mo/E1vsz7C7DB4JAD7MabNNG5UkFyCZGE9QyqlKBe/CUs/vIYmH8uqSLjR8ipUIv91777ol7WhA6WNWcn2qie9+BrVOrOWDUhgXjXNfHqn0of1oQGBZA3\ +Zi1h1GnnfgKMYS5xByjRWxnF0pniJMbIR5chaYQ0eNnqxTR1JUSn7TFGlstpvH9va7cNYttETlcYX4jKDjj7LXCPvxAJDmnmNh7Io122iJhqiOkyMLHoivVPNty4HDDhmvMG5g5UbDGH1fT4sRaheUS5XRLYdg+a\ +sUEypuO+hOuC5ojGCcTxYmyzmF2K3KCXnt/whaHW6GJgB6Qmp6sT0ROgsg2J2EEb/QPWL0O2j9nkbg2FAPoimItYfcbidR5s9jORPu7CUyIYe/HpX+jFtxsfOJ5p4vgViRuTxB4n7QGEye51xgjbpAPGQJs46oZH\ +A9+lWYm51ehCZ10W+tbln8DlH1RZyhP+QWOuUg19o9A7J0G06RjY53RwG/Hm60xQZNRyAGDOhgMGaBtoAgMeCDkAb1Y848T7uL0EVhMX3NBaDrea43saIuwgO+fIFQG2Dn04pYCMQ9hnQClgQxAyaAka5HJc7ziD\ +W32ndMLZNwrdNNCoJrwIRFoSTv7D7LSfxJbAlNeiOp0LrvkPBlSY9ZDL8CkmZNC5R4OsBpYDeH8TIfjUGJbON86JL8RldbF+IwTJaiGYrpCAzi2KwN9cloWWD5Vp5CJ5AEP57Bvlsqkp9ZATNjaWD2t0it1dQ3fL\ +5ICpXcPeX8Mih9eYaANJihD6qnl86wl1s+gwCSl54hPuU8u1hhCAicFrEAhd87fH+8cB3927zQsn3zJ3IV4N6sgV/1C5sG3a9xKuOBszm+CGafQDdTCk7cYz6ulPwCevWVzkjCNYyVov2ROvKv2fMCUfrmfly+cB\ +uAql/7c/7XrO/iuE4zSdCU8n/BD6Z1SPso90w1SFvAts+x65z7nIAKwwop6FKf0CoAhsV9kdPr2OM4rVmPhlq+AbG1nSTUnxgtgKhZzjGY8aEd59nopVbfJyJAsldi5odu3uDofX4XCwwmR2gJpRx9hmf4qxnd5u\ +aVnBYvpfRAeVv9XSVp9sae98jpL5hY8yGSIhxCxWWdv4L7S25o+3tqxPgmWDe8jJpC3vjMXOtbyDLpF3wbxDN2S8XQ23KA3twMJhgPCGLKFx9l1nGx0LiJrt4jWCWzS9+xDxsdUYACrQNY1l86dNtkDfyC5FSjxH\ +T1KexWvBr9PHrSyJNsasEjYv6EeqW4KxmRsFFge5wctwwJyAL5e22Y4tT0iGwYa3ji+W9oadykw1e3PIF+3s/qCzTcrZJqXrrSGpadKPctoJnW3e8N3PHt7B3JBGDuftxqX6A4z3AYVyWTKPBAHJ5uANlZ+cY801\ +O0RkVWEPt3ckQtvJSkKiKvM2vfUC1hj7wKXnZImeV69hcU8Ga50TqMZ8YVt7XY+ZrJWTIWyTJpCis0c3DtRLcOGXYsIxgeoSPcsPMJoGHGUbSn9o0GULMQcY+CnQgiCQA0eArtQP6hGDh246eYfM4x6ZA9GeZz+1\ +e8Xn9tKTvns2fS2iRaJdMbNAzC6zQmWMgA4BE+GxGuSc0QnV2wHn5m5K3Dd7m0FVnAFa0jHA1BhEsjjmechZg0IxN+t09gZCVj7itmzJ03DqOSK4EhcGPXXdRsGDvrpuQM49PkDrg0RWr6S8VQcg2g4iDDtgULSy\ +E+91Id0KxCdxIxcu0KZFgi72e1ghpwTDq01R5eg7NaYUkvRUJaHJnBPetL4Ys04HJlT64kfBA9FrxgPBxAknOsBAJxeY3/ioFWKD+8vZjx1I0KxjSNJgkG9c55uswgXy7gmsDRhQRW+PMfzWBF3SSBTPMbsxWX43\ +hC2gg6e4CweUWmIz167eymmGOc3icR37pQ2TpX8Ak/XZS61CCEi+NgmbrwGuRQiZixCerUUIEdZM8Uwi9NRqE3TFiQTpOp6auDzVxZiGvrrwWDdfpWhwQgqJXRVP1MaNJy7kiVt2IITI7LAWIR7JZkcrQMIa3Xh4\ +LtoLLfP+MR60TOzm4df0ADTAfoR3HNA51jFfEEVEUEw+nbmWEsq6/JUxukWU8ceyWHa7EnsNGiyfL6mvpPMCBRkcJSang62tEwpfOtYHCZdKDmeWofOsKAMyrx23PTmRyS7Q/7nEvtEVCOxQHXnfiP57/tAjRDY8\ +fgQ5mZiaHcg1HLz4v/eSIuR4/e3gpc/GCm9JbKbZ9IfVG+Oc+Jfp3B86Tv4eBuzxCFyTgQLGINylhhuOa6+z6wEa5LkdoqUVCc8ySUSqPYvmRtpEIFLGYYJi8tCt11jUUgzuh1gR3D8DgYnF/xGFx+xuOY8G77tQ\ +/zSpCjZvJJUaK/XbMwbputXbeKRZundTAQSkO4yjE04eQUDxjri1zdP6Cj0wPFT65GDnHs2J3LDpWw6yrAv4jP68tK2y1Ri/IWWLVxdJQs2Dzz3f3uPjIaIDRi3+Qke0l09Dh6KQoL4iXWvaWvdbltg7Ct4jKNB6\ +3JM/cpX6GxJ1nVUnPBAbJBwefft42h73FHI3vDtrH6/Q7W2kWXsF0KznZjBdkQ9fpEgxV4hQY73wIVztnYO4AOyKfoVfP5I1NViFYR/4Mbohkav4Szg5+yj4ISTFF0kiMf9j/qpIAd9bQJBtn/NHDFAj5M+fgxtG\ ++bSdNNqIsGEb9/o34zY5n85/pYRaDvrt9JIrnANGvNxknTr9z+9Jd4AYNbVwPmvPVjwI1z2I1j0YrXsQr3uQ9B5gwSAGteE1AujLjUMg9YDoneGl4YtOpljgKvjBftPTcF/w8TWsBa6o4pVki19CqYmPoUo6ya8N\ +2Ze0EacEs/kafmcvarJLRNbKdfd7fFQuN8xsL+ZJB/K0W5dkjXMKJn4pB2+23tHveJtBr5oHxLwZXNLM5aZS2L8LgveCfDg+QeRnz74lTVCyjgM2BUQHp1A2uCGjgOpBeJYTA2BB+JLp3V1CfuunSI8E9N5AGj7G\ +eW3m393BZOBK7qYN5LICX6TR30O73Q0zm199x2SCv/DyoD1h1aNMbmBg9kyB+uHiDsPU7Pvn33v47YcRnpzMnw9APeZznllyj793h1QtqQ3S/y5/v6Di5LT8hIIE9A2K5mAb7v7jV/MaM12K11QQqMibKK/c/Ul2\ +8QpL3ModJj6V0FfuhC1TDAkN2htV3Vti0JzNCh9jNG3TeoQp55VY+rSe0xbdNOsf8EPE7LZ/KblarpQUBXpriXM9ukoNdDd8JQl5nm7HwTcS1D0PDvHgYK6ZjOYPTOErRubbrHEPXlR4x6Xs0kzRtesIbwiF8GEL\ +Q/eE8IrXK4Y7OUIzSdZoLrVhMn5GH254amYzjzSACZBQo3O+fI261YiHnBaYH3AX4ORm+00Kcqw9SvtP5cNude/13E+evprN3rx7f9NMBT5A1SesfAVDvt5RNncFec9D9qq0WbFV1C8nUcp2m4P+1xmby2p8lcTw\ +OV9lT+VOGuaTfMNhqqydTwqJhk02jgpJMeBXLLY492O0tclpPE2QYCKfY4OkkmCIgGFXPn3XSXyVljm2nC1kZGxN2rLz3SVpX6xq336frnmHwndY3N7ztgu7sD/8srBz+LaoVkmYBCoIFD+p6wa9L140dwwj53Oj\ +oeOks+0T+JRZULH45Y16AvRzRI+pMnQKmp3BuvCEF4Kx29wpyLXx/guomOf48w3tav1rW3KE+m07BWShlc3kZnNduOab2Pj5qcoprOv3O2KHpTYzEnT6GEkozsz4RzpnrX/d5zR6fDHilz++gnWFI7pfttxGGZdw\ +HA/CApp8eQKmoBkeUSrR9Z9MaeaDUsgVO+OVat3gf2Kh1rgysyxtfmJWb7OM3qdy3LvGDQer7mdMOuVRr9xJNG+/uoJ/vc+z6N7YmLzkfmtDud+qaAuHbsH2PvXT6zPXK76tq3vtde950CuHvXLUK8e9sumV825Z\ +9+ajO+09t9Bp6X6vR18sf7znT/vTt5SDz+Sh23jqNh7rl+NbysktZfPR8uIjpZ8/Uup+/mdVOf9oef4x2bn173PlNv4sGi0+Y939mVe3aIHezHVvJrpHRd3pb8Mt3HULnW47GVVHbuGlW+hsyC89TdObp+2V8165\ +DFdIif4LpfjP1gK/V0v8Xi3ye7XM79VCt5U/80+r9nu+jQQmKHmU5zhqr57zQd2cqcZfJGokbZWNW7vSQQ134fP6LhCOklRFCoBw+fNi/mtTGajY/Pv/ALbA9mA=\ +"""))) +ESP32ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b""" +eNqNWnlz1MgV/ypjge0ZY9huaUZquTblA3YwhlQwWYyBSWVbLSlma9cF3qmyIWw+e/pd6pZGTuWPAamP16/f8XuH/O/ddXO33j2Y7K7uWjPx/8wP4UnhU368ulP+sdT+tfa/dnXn1IQGjVmt/b/wpB5enNIsrrT/\ +z0oN9BQtkJ9WwoGKnqKfEY6ahZ920ZkVPSs/ptLu7Bke3PG0tcFcckX0+HUKZ1zc9JZMgQbQ1WYCA3O4RqLGb6DUEbHYBP507nnkY4GXpo74dgNZlSWwEA8Y5Onr/VLrfjo8m8Fc6w6JgPxwYj6J1Lwl3KQJ3RL4\ +Npl/XsBljFxmD0ZWd3YhUpf1IKN2XvgpeMlNcpSBSj0R51iCmT+p1cxBLixHgxpWZpfJtn/Ngsjgym179NYv0HS5csG0QaDtPUKEyVr3Jk/6Cj9F4a5hyemJegbjL05Ok7OsTAbCNixIsEnkUg0MFF/K+SNwlUO6\ +r61Y8magEjOP34+O5OmVv4zs0T26hugGVc2B5+OgGL7QJdg/zIN62rMTG2TLK+AiuVJr0KVbrf2CpqEF1ZgsrSFVWXiGXeB6JZta6Scqy06hmXk27YZNu9sIPlDGpl0hLuDakvdZRIu7MgV34zP4BzfWnr4rgQT9\ +YKwj59LLaEdFEg73SF/wbEQbeU757OgcU4ARHQ7FILfU4ZY1yWuXeNfAf04QpNR3v9QQMZiAS/rJ9abndmg1cH0w4/lSyN2PErPAW512QAjQB/yk8UaRkaqyC68B8LGceK5zhsha9UzgPZ3fR559kmMHd3SMZbXY\ +mvagGFNyWsvzdcUuzabSRM7VrXFLhpSBoExMhImLo7f5/yBY85r55hq6GF3jIGVcSsOZQEPedZVw7EHumQO0zJ54RCg2Egpajh3hEYUF7pBNmhBnN7bm97Eex095voxfvB3W4KQLjzO1eQJX9E+li4YB0UCo/iXf\ +2qLTATkFYtsRxsWWz4Ps6S6vn3pqVU3w7upgnGMq8jHnun8e7fRMOYhPuC8b7jvD4OLUxd9JYjYCnCqiBX5nLUuwGRF+BD6I6pXs2RZ2H0NEElknoBQ3lPxVLO3P8cs6frkbKEX3AvS/0J0oerJvAZ9X7GVbNmA9\ +ykQCsm0Zd8wLNl64R1GJEPZX1+DC5kbW3aOIgB7PEZrJg/MfIyvN+xgTdntzdwiE55LenNFSbX3MNiWlDCb/IYJd5N8Lq8zIyDVjvzFEtQMR9eIec3LlyC0ceZmLcJqwsEwxffkHPDtegeKqIl/nUyo+BZIcSddE\ +Apvm6dSmeZ7denCo/K+eH4PXwYrXnolCLvsMUo/5c38F64hwlxiJMnLRMAnFxVlNPqICSXU8cfCCh5iH/h4RVCFJ1lVEmCVe5T9xSoae8J1dC/FT0XYIdyAYf7Ob1fUH/wBpTvpS4PHt5lXAHcFG24YEAnGyqnd+\ +Ssj7LELyL3D6hFwC/ZVZtvm4z1o95rOsMEx99gFWUDv6PuB8s0MbUR72HbHH97/m4bJYdGNrThIsu27xCSx0STCKETMPCVSMECgKSbrV68eIwBQjjkkV6DlSDIBO9PzD5cHb16vVMWcUiqsKl8uih+98udIWHqiq\ +drPSsEhkAvKfQBjJJveHQOMOEo4289nPU9i492AG/03nVE2VwxBn+vXZESMfJlP28PQhBxlETLgsiFltE7B03qSZJTdk6S+d/FPC9YZzR9ySclDmpBrgRfP/MA/6r1yElGlUb2Vj6a0RxU56qdJVFCNaiS1bwZ9a\ +NWKc6gFVTeMJQcdPI/YFzBv143E57YqVmTlIJvvH7IfZ7BSYAbhQ5aXoaQHxqZQMe/TMjQrwb6fHLyDuUylB0lwf8THDckaqyV7ByABrejnHUS8B2Q1vclLIt7GwUTgMTw0Hvd/20EhMylbj3D4+vaL/AO4XbGUY\ +IeQF6sqS7E+Rjnz+ctmFz1dkK7hUz4lWzsAPNVRTwNW0PMxIPSiNOUxLAZr3a2v6QbBNXyZFSqEYDNJpMB33kmDIoQlaxqNmi1O4huqsuo0aFm5vB/790FnfJw59WtAsNrItStVasCOwTQyLoZxNPfJhZc9jaSWm\ +d75proR0YFGVZYCo681lwHHFZuuiorrWU7aQaJBoNsKdBB8zErEspGTNG4iSj/y1HL5qkd3BTa8xA2VUPuzN4DD5STdahlFd3ADCqhvO4XV6vlrjkC7PW4rHupAMP5NlmHaAQ+nP2aR9ChybpFdBZ+X5Zo6O6NJs\ +liCOs1HgyFRkHoLZZvxwjgJQOVU5Z0oGQBfKHijbdNE/H1HP9msHyHjtyLiLc7Aev0uxzE5g3NfJChIWhoMc195mJU2W8aTJf6VJz+Ki7XYg/kFm6QPme3xGpECPL9kWaWdnmP6aHd3lRxjmlMq/urCq7p/+UYoT\ +XqJdf/5xx52i9EH3r5bKvD/lidDC1oHUkrog6etaRtIeha14seLFdPhFYFtnFK8r85ijYYZRz+WvSDyUCHNBWrE1ufxBaICSS337A2u56+WWRCBIKY9Jw2aj5rmgFEGB0ddgr7iH01LjnhIBagBfXIJ9HPzKGJhF\ +ByhqMeEZxcgZFivLz+iUX+5ggaJszqmiAMQ9+LYckBSei3a0SvRZ1ibL0sNollnfdlTRSXB5gJa9GFlAZc6ywAVdzd+G+geyrVKMLZemRNjpZ2vRzDBKDMhYFSt/uR3FSf00ZASK/HodGZHLb1lpSGpPMpfEvOf2\ +jqG4bzh7dtzm6voxAIlQZyEYcf9LFjRt12H6RlABSC9Vka782RW2nBSqepcSmK4rsuD1KddlaTBaYUdrQZOCaFkbvgxQc0gJuqlbTvLU7eQzNbpc3F3SQUol5m3ZkvyIhPllDyTZyY35KVhi7hSOKWTaQKeJ3Mik\ +zxHZ37GpLwQ8ILEqOPcEdZsiNqHgyi6uJHFs4MGm6m/EY8ymPa5pyrSDhAOX7JwCbnWajTworOHZfou9a04UE2rHblOLFONKaOuOxolPwyBxQVeuTRQkREEIXB1A3sL6aqf4yMj/V3S0D6F2Q/DVRRMjqYaSywFW\ +St5p9Y6k80+G2DBNYPjxJmS4zvomX46/cYJTVeQABLiwc3ZP1zDj5q3eHut8nfWTYovfQFJwhYRTEfkqUps3FIStlB5QZcfFMUX/tB/9gVGN3bBq0vdb2OIqcngM7nNJUw/jwJ1FvpiyY2l2rI4aJFgF1wxYjZDx\ +6UauZiEkYVcYbBbNIIVcVd1YGmoU5kUzOwVAc+VqN9TQSA88uOYuG3ivXdxxOQbup7eIVZSw4RWc02K9xXoqubZzkk05SlThaq2McW9EFZsa8yh9De61Dbj5hVwV+imWRQ3GUpUwC3JyiqEbYBv0Zg2jqisnZFWw\ +qQKm8kmgAGZc84eHqIdQ896WgKXhRhNUbBT4JlRWgftJ10vEhpZTxRKbEV/OPac2EJW9HRRM8cvdCTtyTkqg7IPgThoV1NKVerKY/QzDmIpdQLrTECG42TwJgLkJKTJZAgV/CGbo0+SCineXxc42lnLyeLE5rhgi\ +0brZfLpW+JCGGI8iwZZyLoznz4DIXWJSyKKxE8LRsprD9+jaLp8NHBrS/QpBbUGHi9hiwCW3n4vPk/MvpIWVEqhaF1V6mZQlwGlBbCLcLwR5e0CyuMEDTNoEKzB8yxZzS/RxxP2vV/RWm0flNAQmPMZky+8RnHaX\ +c+aEEaqB+NQiorugraoZ0Qp+Cv+T6kYjV/iPdJ2SEOI6cHO/c15D1QIkAQWpifz7F7BULj8Rzpp+Mwg3u/w31GIaVWJfg0gppt7ILcl6E0IHNH00sK4Pg26WhDCHAT7D73iGIzm3n/A7SSl/WgDel30nV6d2yw67\ +mNmBjtli2e8odzle/OVKTR9QwVAaSOddMmOR52z08QYnSzktcYx05cZ3CYREuYGRGyDtJJxOG76TZOhld3+yW9u1/ecfa3sDfw2iVZEtFnlmFM/QX4hI5wbWw9+NxOvnWabzufEzzfX65ms3mKl0/ud/AYyHY9Q=\ +"""))) + +if __name__ == '__main__': + try: + main() + except FatalError as e: + print '\nA fatal error occurred: %s' % e + sys.exit(2) diff --git a/src/mongoose-6.11/src/common/platforms/esp/slip.c b/src/mongoose-6.11/src/common/platforms/esp/slip.c new file mode 100644 index 0000000..13b980e --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp/slip.c @@ -0,0 +1,35 @@ +#include "rom_functions.h" + +void SLIP_send(uint8_t *pkt, uint32_t size) { + send_packet(pkt, size); +} + +uint32_t SLIP_recv(void *pkt, uint32_t max_len) { + uint8_t c; + uint32_t len = 0; + uint8_t *p = (uint8_t *) pkt; + do { + c = uart_rx_one_char_block(); + } while (c != '\xc0'); + while (len < max_len) { + c = uart_rx_one_char_block(); + if (c == '\xc0') return len; + if (c == '\xdb') { + c = uart_rx_one_char_block(); + if (c == '\xdc') { + c = '\xc0'; + } else if (c == '\xdd') { + c = '\xdb'; + } else { + len = 0; + break; /* Bad esc sequence. */ + } + } + *p++ = c; + len++; + } + do { + c = uart_rx_one_char_block(); + } while (c != '\xc0'); + return len; +} diff --git a/src/mongoose-6.11/src/common/platforms/esp/slip.h b/src/mongoose-6.11/src/common/platforms/esp/slip.h new file mode 100644 index 0000000..53100a0 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp/slip.h @@ -0,0 +1,9 @@ +#ifndef CS_COMMON_PLATFORMS_ESP8266_STUBS_SLIP_H_ +#define CS_COMMON_PLATFORMS_ESP8266_STUBS_SLIP_H_ + +#include + +void SLIP_send(const void *pkt, uint32_t size); +uint32_t SLIP_recv(void *pkt, uint32_t max_len); + +#endif /* CS_COMMON_PLATFORMS_ESP8266_STUBS_SLIP_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp/stub_flasher.c b/src/mongoose-6.11/src/common/platforms/esp/stub_flasher.c new file mode 100644 index 0000000..be7398c --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp/stub_flasher.c @@ -0,0 +1,559 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + * + * Spiffy flasher. Implements strong checksums (MD5) and can use higher + * baud rates. Actual max baud rate will differe from device to device, + * but 921K seems to be common. + * + * SLIP protocol is used for communication. + * First packet is a single byte - command number. + * After that, a packet with a variable number of 32-bit (LE) arguments, + * depending on command. + * + * Then command produces variable number of packets of output, but first + * packet of length 1 is the response code: 0 for success, non-zero - error. + * + * See individual command description below. + */ + +#include "stub_flasher.h" + +#include +#include + +#include "rom_functions.h" + +#if defined(ESP8266) +#include "eagle_soc.h" +#include "ets_sys.h" +#include "../../../miniz.c" +#elif defined(ESP32) +#include "rom/efuse.h" +#include "rom/miniz.h" +#include "rom/spi_flash.h" +#include "soc/uart_reg.h" +#include "led.h" +#endif + +#include "slip.h" +#include "uart.h" + +/* Param: baud rate. */ +uint32_t params[1] __attribute__((section(".params"))); + +#define FLASH_BLOCK_SIZE 65536 +#define FLASH_SECTOR_SIZE 4096 +#define FLASH_PAGE_SIZE 256 + +/* These consts should be in sync with flasher_client.go */ +#define NUM_BUFS 4 +#define BUF_SIZE 4096 +#define FLASH_WRITE_SIZE BUF_SIZE + +#define UART_RX_INTS (UART_RXFIFO_FULL_INT_ENA | UART_RXFIFO_TOUT_INT_ENA) + +extern uint32_t _bss_start, _bss_end; + +#ifdef ESP8266 +#define REG_SPI_BASE(i) (0x60000200 - i * 0x100) + +#define SPI_CMD_REG(i) (REG_SPI_BASE(i) + 0x0) +#define SPI_FLASH_WREN (BIT(30)) +#define SPI_FLASH_RDID (BIT(28)) +#define SPI_FLASH_SE (BIT(24)) +#define SPI_FLASH_BE (BIT(23)) + +#define SPI_ADDR_REG(i) (REG_SPI_BASE(i) + 0x4) + +#define SPI_USER_REG(i) (REG_SPI_BASE(i) + 0x1C) + +#define SPI_W0_REG(i) (REG_SPI_BASE(i) + 0x40) +#endif + +enum read_state { + READ_WAIT_START = 0, + READ_FLAGS, + READ_DATA, + READ_UNESCAPE, + READ_ERROR, +}; + +struct data_buf { + uint32_t len; + uint8_t data[BUF_SIZE]; + uint8_t flags; +}; + +#define FLAG_COMPRESSED 1 + +struct uart_buf { + enum read_state state; + struct data_buf bufs[NUM_BUFS]; + uint32_t bri, bwi; + uint32_t ps; +}; + +static inline uint32_t ccount(void) { + uint32_t r; + __asm volatile("rsr.ccount %0" : "=a"(r)); + return r; +} + +struct write_progress { + uint32_t num_written; + uint32_t buf_level; + uint8_t digest[16]; +}; + +struct write_result { + uint32_t wait_time; + uint32_t decomp_time; + uint32_t write_time; + uint32_t erase_time; + uint32_t total_time; + uint8_t digest[16]; +}; + +static struct uart_buf ub; +static uint32_t inflate_buf[TINFL_LZ_DICT_SIZE / sizeof(uint32_t)]; + +static void next_write_buf(void) { + ub.bwi++; + if (ub.bwi == NUM_BUFS) ub.bwi = 0; + ub.bufs[ub.bwi].len = 0; + ub.bufs[ub.bwi].flags = 0; +} + +static void add_byte(uint8_t byte) { + struct data_buf *buf = &ub.bufs[ub.bwi]; + if (buf->len < BUF_SIZE) { + buf->data[buf->len++] = byte; + ub.ps++; + } +} + +void uart_isr(void *arg) { + uint32_t int_st = READ_PERI_REG(UART_INT_ST_REG(0)); + uint8_t fifo_len, i; + while ((fifo_len = READ_PERI_REG(UART_STATUS_REG(0))) > 0) { + for (i = 0; i < fifo_len; i++) { + uint8_t byte = READ_PERI_REG(UART_FIFO_REG(0)); + switch (ub.state) { + case READ_WAIT_START: { + if (byte == 0xc0) { + ub.state = READ_FLAGS; + ub.ps = 0; + } + break; + } + case READ_FLAGS: + case READ_DATA: { + struct data_buf *buf = &ub.bufs[ub.bwi]; + if (byte == 0xdb) { + ub.state = READ_UNESCAPE; + } else if (byte == 0xc0) { + next_write_buf(); + if (ub.ps == 0) { + /* Empty packet, sender aborted. */ + ub.state = READ_ERROR; + SET_PERI_REG_MASK(UART_INT_ENA_REG(0), 0); + goto out; + } else { + ub.state = READ_WAIT_START; + } + } else { + if (ub.state == READ_FLAGS) { + buf->flags = byte; + ub.state = READ_DATA; + } else { + add_byte(byte); + } + } + break; + } + case READ_UNESCAPE: { + if (byte == 0xdc) { + byte = 0xc0; + } else if (byte == 0xdd) { + byte = 0xdb; + } else { + ub.state = READ_ERROR; + SET_PERI_REG_MASK(UART_INT_ENA_REG(0), 0); + goto out; + } + add_byte(byte); + ub.state = READ_DATA; + break; + } + case READ_ERROR: { + goto out; + } + } + } + } +out: + WRITE_PERI_REG(UART_INT_CLR_REG(0), int_st); + (void) arg; +} + +size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, + const void *pSrc_buf, size_t src_buf_len, + int flags); +#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) +#define TINFL_FLAG_PARSE_ZLIB_HEADER 1 + +#if defined(ESP8266) +int esp_rom_spiflash_erase_start(uint32_t addr, uint32_t cmd) { + SPI_write_enable(flashchip); + WRITE_PERI_REG(SPI_ADDR_REG(0), addr); + WRITE_PERI_REG(SPI_CMD_REG(0), cmd); + while (READ_PERI_REG(SPI_CMD_REG(0)) & cmd) { + } + return 0; +} +#elif defined(ESP32) +extern esp_rom_spiflash_chip_t g_rom_spiflash_chip; + +esp_rom_spiflash_result_t esp_rom_spiflash_erase_start(uint32_t addr, + uint32_t cmd) { + esp_rom_spiflash_chip_t *spi = &g_rom_spiflash_chip; + esp_rom_spiflash_wait_idle(spi); + + REG_CLR_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_USR_DUMMY); + REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG1, SPI_USR_ADDR_BITLEN, + ESP_ROM_SPIFLASH_W_SIO_ADDR_BITSLEN); + + WRITE_PERI_REG(PERIPHS_SPI_FLASH_CMD, SPI_FLASH_WREN); + while (READ_PERI_REG(PERIPHS_SPI_FLASH_CMD) != 0) { + } + + WRITE_PERI_REG(PERIPHS_SPI_FLASH_ADDR, addr); + WRITE_PERI_REG(PERIPHS_SPI_FLASH_CMD, cmd); + + return ESP_ROM_SPIFLASH_RESULT_OK; +} +#endif + +int do_flash_write(uint32_t addr, uint32_t len, uint32_t erase) { + int ret = 0; + uint32_t num_erased = 0; + struct MD5Context ctx; + MD5Init(&ctx); + + if (addr % FLASH_SECTOR_SIZE != 0) return 0x32; + if (len % FLASH_SECTOR_SIZE != 0) return 0x33; + if (esp_rom_spiflash_unlock() != 0) return 0x34; + + memset(&ub, 0, sizeof(ub)); + memset(&inflate_buf, 0, sizeof(inflate_buf)); + ets_isr_attach(ETS_UART0_INUM, uart_isr, &ub); + uint32_t saved_conf1 = READ_PERI_REG(UART_CONF1_REG(0)); + /* Reduce frequency of UART interrupts */ + WRITE_PERI_REG(UART_CONF1_REG(0), UART_RX_TOUT_EN | + (20 << UART_RX_TOUT_THRHD_S) | + (100 << UART_RXFIFO_FULL_THRHD_S)); + SET_PERI_REG_MASK(UART_INT_ENA_REG(0), UART_RX_INTS); + ets_isr_unmask(1 << ETS_UART0_INUM); + + struct write_result wr; + memset(&wr, 0, sizeof(wr)); + + struct write_progress wp = {.num_written = 0, .buf_level = 0}; + wp.buf_level = (uint32_t) &addr; + SLIP_send(&wp, sizeof(wp)); + wr.total_time = ccount(); + while (wp.num_written < len) { + /* Prepare the space ahead. */ + uint32_t start_count = ccount(); + while (erase && num_erased < wp.num_written + FLASH_WRITE_SIZE) { + const uint32_t num_left = (len - num_erased); + if (num_left >= FLASH_BLOCK_SIZE && addr % FLASH_BLOCK_SIZE == 0) { + if (esp_rom_spiflash_erase_start(addr, SPI_FLASH_BE) != 0) { + ret = 0x35; + goto out; + } + num_erased += FLASH_BLOCK_SIZE; + } else { + /* len % FLASH_SECTOR_SIZE == 0 is enforced, no further checks needed */ + if (esp_rom_spiflash_erase_start(addr, SPI_FLASH_SE) != 0) { + ret = 0x36; + goto out; + } + num_erased += FLASH_SECTOR_SIZE; + } + } + wr.erase_time += ccount() - start_count; + start_count = ccount(); + /* Wait for data to arrive. */ + wp.buf_level = 0; + for (int i = 0; i < NUM_BUFS; i++) wp.buf_level += ub.bufs[i].len; + volatile uint32_t *bwi = &ub.bwi; + while (*bwi == ub.bri && ub.state != READ_ERROR) { + } + struct data_buf *buf = &ub.bufs[ub.bri]; + if (ub.state == READ_ERROR) { + ret = 0x37; + goto out; + } + wr.wait_time += ccount() - start_count; + start_count = ccount(); + uint32_t *data = (uint32_t *) buf->data; + uint32_t write_len = buf->len; + if (buf->flags & FLAG_COMPRESSED) { + data = inflate_buf; + write_len = tinfl_decompress_mem_to_mem( + &inflate_buf[0], sizeof(inflate_buf), buf->data, write_len, + TINFL_FLAG_PARSE_ZLIB_HEADER); + if (write_len == TINFL_DECOMPRESS_MEM_TO_MEM_FAILED) { + ret = 0x40; + goto out; + } + } + wr.decomp_time += ccount() - start_count; + MD5Update(&ctx, (uint8_t *) data, write_len); + start_count = ccount(); + wr.erase_time += ccount() - start_count; + start_count = ccount(); + if (esp_rom_spiflash_write(addr, data, write_len) != 0) { + ret = 0x38; + goto out; + } + wr.write_time += ccount() - start_count; + buf->len = 0; + ub.bri++; + if (ub.bri == NUM_BUFS) ub.bri = 0; + addr += write_len; + wp.num_written += write_len; + struct MD5Context ctx2; + memcpy(&ctx2, &ctx, sizeof(ctx)); + MD5Final(wp.digest, &ctx2); + SLIP_send(&wp, sizeof(wp)); + } + + MD5Final(wr.digest, &ctx); + + wr.total_time = ccount() - wr.total_time; + SLIP_send(&wr, sizeof(wr)); + +out: + WRITE_PERI_REG(UART_CONF1_REG(0), saved_conf1); + ets_isr_mask(1 << ETS_UART0_INUM); + return ret; +} + +int do_flash_read(uint32_t addr, uint32_t len, uint32_t block_size, + uint32_t max_in_flight) { + uint8_t buf[FLASH_SECTOR_SIZE]; + uint8_t digest[16]; + struct MD5Context ctx; + uint32_t num_sent = 0, num_acked = 0; + if (block_size > sizeof(buf)) return 0x52; + MD5Init(&ctx); + while (num_acked < len) { + while (num_sent < len && num_sent - num_acked < max_in_flight) { + uint32_t n = len - num_sent; + if (n > block_size) n = block_size; + if (esp_rom_spiflash_read(addr, (uint32_t *) buf, n) != 0) return 0x53; + send_packet(buf, n); + MD5Update(&ctx, buf, n); + addr += n; + num_sent += n; + } + { + if (SLIP_recv(&num_acked, sizeof(num_acked)) != 4) return 0x54; + if (num_acked > num_sent) return 0x55; + } + } + MD5Final(digest, &ctx); + send_packet(digest, sizeof(digest)); + return 0; +} + +int do_flash_digest(uint32_t addr, uint32_t len, uint32_t digest_block_size) { + uint8_t buf[FLASH_SECTOR_SIZE]; + uint8_t digest[16]; + uint32_t read_block_size = + digest_block_size ? digest_block_size : sizeof(buf); + struct MD5Context ctx; + if (digest_block_size > sizeof(buf)) return 0x62; + MD5Init(&ctx); + while (len > 0) { + uint32_t n = len; + struct MD5Context block_ctx; + MD5Init(&block_ctx); + if (n > read_block_size) n = read_block_size; + if (esp_rom_spiflash_read(addr, (uint32_t *) buf, n) != 0) return 0x63; + MD5Update(&ctx, buf, n); + if (digest_block_size > 0) { + MD5Update(&block_ctx, buf, n); + MD5Final(digest, &block_ctx); + send_packet(digest, sizeof(digest)); + } + addr += n; + len -= n; + } + MD5Final(digest, &ctx); + send_packet(digest, sizeof(digest)); + return 0; +} + +int do_flash_read_chip_id(void) { + uint32_t chip_id = 0; + WRITE_PERI_REG(SPI_CMD_REG(0), SPI_FLASH_RDID); + while (READ_PERI_REG(SPI_CMD_REG(0)) & SPI_FLASH_RDID) { + } + chip_id = READ_PERI_REG(SPI_W0_REG(0)) & 0xFFFFFF; + send_packet((uint8_t *) &chip_id, sizeof(chip_id)); + return 0; +} + +uint8_t cmd_loop(void) { + uint8_t cmd = 0x55; + do { + /* Reset FIFO to re-sync */ + SET_PERI_REG_MASK(UART_CONF0_REG(0), UART_RXFIFO_RST); + CLEAR_PERI_REG_MASK(UART_CONF0_REG(0), UART_RXFIFO_RST); + uint32_t args[4]; + uint32_t len = SLIP_recv(&cmd, 1); + if (len != 1) { + continue; + } + uint8_t resp = 0xff; + switch (cmd) { + case CMD_FLASH_WRITE: { + len = SLIP_recv(args, sizeof(args)); + if (len == 12) { + resp = do_flash_write(args[0] /* addr */, args[1] /* len */, + args[2] /* erase */); + } else { + resp = 0x41; + } + break; + } + case CMD_FLASH_READ: { + len = SLIP_recv(args, sizeof(args)); + if (len == 16) { + resp = do_flash_read(args[0] /* addr */, args[1], /* len */ + args[2] /* block_size */, + args[3] /* max_in_flight */); + } else { + resp = 0x51; + } + break; + } + case CMD_FLASH_DIGEST: { + len = SLIP_recv(args, sizeof(args)); + if (len == 12) { + resp = do_flash_digest(args[0] /* addr */, args[1], /* len */ + args[2] /* digest_block_size */); + } else { + resp = 0x61; + } + break; + } + case CMD_FLASH_READ_CHIP_ID: { + resp = do_flash_read_chip_id(); + break; + } + case CMD_FLASH_ERASE_CHIP: { + resp = esp_rom_spiflash_erase_chip(); + break; + } + case CMD_BOOT_FW: + case CMD_REBOOT: { + resp = 0; + SLIP_send(&resp, 1); + return cmd; + } + case CMD_ECHO: { + len = SLIP_recv(args, sizeof(args)); + SLIP_send(args, len); + resp = 0; + break; + } + } + SLIP_send(&resp, 1); + } while (cmd != CMD_BOOT_FW && cmd != CMD_REBOOT); + return cmd; +} + +void stub_main1(void) { + uint32_t baud_rate = params[0]; + uint32_t greeting = 0x4941484f; /* OHAI */ + uint8_t last_cmd; + + /* This points at us right now, reset for next boot. */ + ets_set_user_start(0); + +/* Selects SPI functions for flash pins. */ +#if defined(ESP8266) + SelectSpiFunction(); + SET_PERI_REG_MASK(0x3FF00014, 1); /* Switch to 160 MHz */ +#elif defined(ESP32) + esp_rom_spiflash_attach(ets_efuse_get_spiconfig(), 0 /* legacy */); +#endif + + esp_rom_spiflash_config_param( + 0 /* deviceId */, 16 * 1024 * 1024 /* chip_size */, FLASH_BLOCK_SIZE, + FLASH_SECTOR_SIZE, FLASH_PAGE_SIZE, 0xffff /* status_mask */); + + if (baud_rate > 0) { + ets_delay_us(10000); + set_baud_rate(0, baud_rate); + } + + /* Give host time to get ready too. */ + ets_delay_us(50000); + +#ifdef BAUD_TEST + while (1) { + WRITE_PERI_REG(UART_FIFO_REG(0), 0x55); + } +#else + SLIP_send(&greeting, 4); +#endif + + last_cmd = cmd_loop(); + + ets_delay_us(10000); + + if (last_cmd == CMD_BOOT_FW) { +#if defined(ESP8266) + /* + * Find the return address in our own stack and change it. + * "flash_finish" it gets to the same point, except it doesn't need to + * patch up its RA: it returns from UartDwnLdProc, then from f_400011ac, + * then jumps to 0x4000108a, then checks strapping bits again (which will + * not have changed), and then proceeds to 0x400010a8. + */ + volatile uint32_t *sp = &baud_rate; + while (*sp != (uint32_t) 0x40001100) sp++; + *sp = 0x400010a8; + /* + * The following dummy asm fragment acts as a barrier, to make sure function + * epilogue, including return address loading, is added after our stack + * patching. + */ + __asm volatile("nop.n"); + return; /* To 0x400010a8 */ +#elif defined(ESP32) +/* TODO(rojer) */ +#endif + } else { + software_reset(); + } + /* Not reached */ +} + +/* miniz requires at least 12K of stack */ +uint32_t stack[3071]; +uint32_t stack_end; + +void stub_main(void) { + memset(&_bss_start, 0, (&_bss_end - &_bss_start)); + __asm volatile("movi a1, stack_end\n"); + stub_main1(); + // Keep the stack vars alive. + stack[0] = stack_end = 0xff; +} diff --git a/src/mongoose-6.11/src/common/platforms/esp/stub_flasher.h b/src/mongoose-6.11/src/common/platforms/esp/stub_flasher.h new file mode 100644 index 0000000..4bae4c6 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp/stub_flasher.h @@ -0,0 +1,96 @@ +#ifndef CS_COMMON_PLATFORMS_ESP8266_STUBS_STUB_FLASHER_H_ +#define CS_COMMON_PLATFORMS_ESP8266_STUBS_STUB_FLASHER_H_ + +enum stub_cmd { + /* + * Write to the SPI flash. + * + * Args: addr, len, erase; addr and len must be SECTOR_SIZE-aligned. + * If erase != 0, perform erase before writing. + * Input: Stream of data to be written, note: no SLIP encapsulation here. + * Output: SLIP packets with number of bytes written after every write. + * This can (and should) be used for flow control. Flasher will + * write in 1K chunks but will buffer up to 4K of data + * Use this feedback to keep buffer above 1K but below 4K. + * Final packet will contain MD5 digest of the data written. + */ + CMD_FLASH_WRITE = 1, + + /* + * Read from the SPI flash. + * + * Args: addr, len, block_size; no alignment requirements, block_size <= 4K. + * Input: None. + * Output: Packets of up to block_size with data. An acknowledgement is + *expected + * after every packet, in the form of a packet with total number of + * bytes received so far. + * Last packet is the MD5 digest of the data sent. + * + * Note: No flow control is performed, it is assumed that the host can cope + * with the inbound stream. + */ + CMD_FLASH_READ = 2, + + /* + * Compute MD5 digest of the specified flash region. + * + * Args: addr, len, digest_block_size; no alignment requirements. + * Input: None. + * Output: If block digests are not enabled (digest_block_size == 0), + * only overall digest is produced. + * Otherwise, there will be a separate digest for each block, + * the remainder (if any) and the overall digest at the end. + */ + CMD_FLASH_DIGEST = 3, + + /* + * Read flash chip ID. + * This is the JEDEC ID, containinf manufactirer, SPI mode and capacity. + * + * Args: None. + * Input: None. + * Output: 32 bit chip id (only 24 bits are meaningful). + */ + CMD_FLASH_READ_CHIP_ID = 4, + + /* + * Zap the whole chip at once. + * + * Args: None. + * Input: None. + * Output: None. + */ + CMD_FLASH_ERASE_CHIP = 5, + + /* + * Boots the firmware from flash. + * + * Args: None. + * Input: None. + * Output: None. + */ + CMD_BOOT_FW = 6, + + /* + * Reboot the CPU. + * Since strapping settings are not reset, this will reboot into whatever mode + * got us here, most likely UART loader. + * + * Args: None. + * Input: None. + * Output: None. + */ + CMD_REBOOT = 7, + + /* + * Echo the arguments back to the host. + * + * Args: variable. + * Input: None. + * Output: arguments. + */ + CMD_ECHO = 8, +}; + +#endif /* CS_COMMON_PLATFORMS_ESP8266_STUBS_STUB_FLASHER_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp31/rom/.gitattributes b/src/mongoose-6.11/src/common/platforms/esp31/rom/.gitattributes new file mode 100644 index 0000000..1260bec --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp31/rom/.gitattributes @@ -0,0 +1 @@ +rom.bin -nodiff diff --git a/src/mongoose-6.11/src/common/platforms/esp31/rom/ESP31B_ROM.txt b/src/mongoose-6.11/src/common/platforms/esp31/rom/ESP31B_ROM.txt new file mode 100644 index 0000000..f4640cc --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp31/rom/ESP31B_ROM.txt @@ -0,0 +1,11917 @@ + +rom.elf: file format elf32-xtensa-le + + +Disassembly of section .text: + +40000000 <_start>: +40000000: 49c500 s32e a0, a5, -16 +40000003: 49d510 s32e a1, a5, -12 +40000006: 49e520 s32e a2, a5, -8 +40000009: 49f530 s32e a3, a5, -4 +4000000c: 003400 rfwo +4000000f: 412800 srli a2, a0, 8 +40000012: 5138 l32i.n a3, a1, 20 +40000014: 6148 l32i.n a4, a1, 24 +40000016: 01d112 addmi a1, a1, 0x100 +40000019: 13d100 wsr.excsave1 a0 +4000001c: 034800 rsr.windowbase a0 +4000001f: 4080f0 rotw -1 +40000022: 03e620 rsr.ps a2 +40000025: 343820 extui a3, a2, 8, 4 +40000028: 303340 xor a3, a3, a4 +4000002b: 000846 j 40000050 <_XX_ExcVec50> + ... + +40000040 <_XX_Vec40>: +40000040: 09c500 l32e a0, a5, -16 +40000043: 09d510 l32e a1, a5, -12 +40000046: 09e520 l32e a2, a5, -8 +40000049: 09f530 l32e a3, a5, -4 +4000004c: 003500 rfwu + ... + +40000050 <_XX_ExcVec50>: +40000050: 03d140 rsr.excsave1 a4 +40000053: 113380 slli a3, a3, 8 +40000056: 302230 xor a2, a2, a3 +40000059: 13e620 wsr.ps a2 +4000005c: 002010 rsync +4000005f: dd74f7 bbci a4, 31, 40000040 <_XX_Vec40> +40000062: 4080f0 rotw -1 +40000065: 5778e7 bbci a8, 30, 400000c0 <_XX_ExcVec80+0x40> +40000068: 4080f0 rotw -1 +4000006b: 003446 j 40000140 <_WindowUnderflowHandler> + ... + +40000080 <_XX_ExcVec80>: +40000080: 49c900 s32e a0, a9, -16 +40000083: 09d100 l32e a0, a1, -12 +40000086: 49d910 s32e a1, a9, -12 +40000089: 49e920 s32e a2, a9, -8 +4000008c: 49f930 s32e a3, a9, -4 +4000008f: 498040 s32e a4, a0, -32 +40000092: 499050 s32e a5, a0, -28 +40000095: 49a060 s32e a6, a0, -24 +40000098: 49b070 s32e a7, a0, -20 +4000009b: 003400 rfwo + ... +400000be: 000000 ill +400000c1: 09c9 s32i.n a12, a9, 0 +400000c3: 09d910 l32e a1, a9, -12 +400000c6: 09e920 l32e a2, a9, -8 +400000c9: 09d170 l32e a7, a1, -12 +400000cc: 09f930 l32e a3, a9, -4 +400000cf: 098740 l32e a4, a7, -32 +400000d2: 099750 l32e a5, a7, -28 +400000d5: 09a760 l32e a6, a7, -24 +400000d8: 09b770 l32e a7, a7, -20 +400000db: 003500 rfwu + ... + +40000100 <_WindowOverflowHandler>: +40000100: 49cd00 s32e a0, a13, -16 +40000103: 09d100 l32e a0, a1, -12 +40000106: 49dd10 s32e a1, a13, -12 +40000109: 49ed20 s32e a2, a13, -8 +4000010c: 49fd30 s32e a3, a13, -4 +4000010f: 494040 s32e a4, a0, -48 +40000112: 495050 s32e a5, a0, -44 +40000115: 496060 s32e a6, a0, -40 +40000118: 497070 s32e a7, a0, -36 +4000011b: 498080 s32e a8, a0, -32 +4000011e: 499090 s32e a9, a0, -28 +40000121: 49a0a0 s32e a10, a0, -24 +40000124: 49b0b0 s32e a11, a0, -20 +40000127: 003400 rfwo + ... + +40000140 <_WindowUnderflowHandler>: +40000140: 09cd00 l32e a0, a13, -16 +40000143: 09dd10 l32e a1, a13, -12 +40000146: 09ed20 l32e a2, a13, -8 +40000149: 09d1b0 l32e a11, a1, -12 +4000014c: 09fd30 l32e a3, a13, -4 +4000014f: 094b40 l32e a4, a11, -48 +40000152: 095b50 l32e a5, a11, -44 +40000155: 096b60 l32e a6, a11, -40 +40000158: 097b70 l32e a7, a11, -36 +4000015b: 098b80 l32e a8, a11, -32 +4000015e: 099b90 l32e a9, a11, -28 +40000161: 09aba0 l32e a10, a11, -24 +40000164: 09bbb0 l32e a11, a11, -20 +40000167: 003500 rfwu + ... +4000017e: 200000 or a0, a0, a0 +40000181: a061d2 s32i a13, a1, 0x280 +40000184: 000002 l8ui a0, a0, 0 + ... +400001bf: d32000 movt a2, a0, b0 +400001c2: 02a061 l32r a6, 3ffc0c44 <_start-0x3f3bc> + ... +400001fd: 000000 ill +40000200: 61d420 xsr.excsave4 a2 +40000203: 0002a0 jx a2 + ... +4000023e: 200000 or a0, a0, a0 +40000241: a061d5 call4 3ffa0860 <_start-0x5f7a0> +40000244: 000002 l8ui a0, a0, 0 + ... +4000027f: d62000 excw +40000282: 827c13 excw +40000285: 005100 simcall +40000288: ffff06 j 40000288 <_WindowUnderflowHandler+0x148> + ... +400002bf: 371000 excw + ... + +40000300 <_XX_Vec400>: +40000300: 004100 break 1, 0 +40000303: fffe46 j 40000300 <_XX_Vec400> + ... +4000033e: 120000 andbc b0, b0, b0 +40000341: 29ffd1 l32r a13, 3ffcab40 <_start-0x354c0> +40000344: 513941 l32r a4, 3ffd4828 <_start-0x2b7d8> +40000347: f27e31 l32r a3, 3fffcd40 <_start-0x32c0> +4000034a: 03e820 rsr.exccause a2 +4000034d: a03230 addx4 a3, a2, a3 +40000350: 0338 l32i.n a3, a3, 0 +40000352: 6149 s32i.n a4, a1, 24 +40000354: 0003a0 jx a3 + ... +400003bf: 414000 srli a4, a0, 0 +400003c2: fe4600 excw +400003c5: ff .byte 0xff + ... +400003fe: 7c0000 excw +40000401: 6100d0 xsr.lbeg a13 +40000404: 0dc613 excw + ... +4000040f: 211f40 srai a1, a4, 15 +40000412: 002222 l32i a2, a2, 0 +40000415: e00000 subx4 a0, a0, a0 +40000418: 000494 excw +4000041b: 70d440 excw +4000041e: dc4000 excw +40000421: 0009 s32i.n a0, a0, 0 +40000423: 0ab040 depbits a4, a0, 0, 12 +40000426: 804000 add a4, a0, a0 +40000429: 000b addi.n a0, a0, -1 +4000042b: c20840 quou a0, a8, a4 +4000042e: ff .byte 0xff +4000042f: 3f .byte 0x3f +40000430: 000cc0 callx0 a12 +40000433: c20c40 quou a0, a12, a4 +40000436: ff .byte 0xff +40000437: 3f .byte 0x3f + ... +40000440: 000c movi.n a0, 0 +40000442: 13e400 wsr.intenable a0 +40000445: fff021 l32r a2, 40000408 <_XX_Vec400+0x108> +40000448: 03eb30 rsr.prid a3 +4000044b: 743030 extui a3, a3, 0, 8 +4000044e: 228c beqz.n a2, 40000454 <_XX_Vec400+0x154> +40000450: 03cc bnez.n a3, 40000454 <_XX_Vec400+0x154> +40000452: 0209 s32i.n a0, a2, 0 +40000454: ffee21 l32r a2, 4000040c <_XX_Vec400+0x10c> +40000457: 13e720 wsr.vecbase a2 +4000045a: 531c movi.n a3, 21 +4000045c: 136330 wsr.atomctl a3 +4000045f: 006120 rsil a2, 1 +40000462: ffeb21 l32r a2, 40000410 <_XX_Vec400+0x110> +40000465: ffeb51 l32r a5, 40000414 <_XX_Vec400+0x114> +40000468: ffec61 l32r a6, 40000418 <_XX_Vec400+0x118> +4000046b: 030c movi.n a3, 0 +4000046d: 027d mov.n a7, a2 +4000046f: 106650 and a6, a6, a5 +40000472: 000786 j 40000494 <_XX_Vec400+0x194> + ... +4000047d: 000000 ill +40000480: 506340 witlb a4, a3 +40000483: 002000 isync +40000486: f03d nop.n +40000488: 0020f0 nop +4000048b: c03350 sub a3, a3, a5 +4000048e: 14b3b6 bltui a3, 16, 400004a6 <_XX_Vec400+0x1a6> +40000491: 417470 srli a7, a7, 4 +40000494: 344070 extui a4, a7, 0, 4 +40000497: e51367 beq a3, a6, 40000480 <_XX_Vec400+0x180> +4000049a: 506340 witlb a4, a3 +4000049d: c03350 sub a3, a3, a5 +400004a0: edb3f6 bgeui a3, 16, 40000491 <_XX_Vec400+0x191> +400004a3: 002000 isync +400004a6: ffdb51 l32r a5, 40000414 <_XX_Vec400+0x114> +400004a9: 030c movi.n a3, 0 +400004ab: 027d mov.n a7, a2 +400004ad: 344070 extui a4, a7, 0, 4 +400004b0: 50e340 wdtlb a4, a3 +400004b3: c03350 sub a3, a3, a5 +400004b6: 417470 srli a7, a7, 4 +400004b9: f0b3f6 bgeui a3, 16, 400004ad <_XX_Vec400+0x1ad> +400004bc: 002030 dsync +400004bf: 03a032 movi a3, 3 +400004c2: 036120 rsr.memctl a2 +400004c5: 202230 or a2, a2, a3 +400004c8: 136120 wsr.memctl a2 +400004cb: ffd421 l32r a2, 4000041c <_XX_Vec400+0x11c> +400004ce: 02ac beqz.n a2, 400004f2 <_XX_Vec400+0x1f2> +400004d0: 0238 l32i.n a3, a2, 0 +400004d2: 1248 l32i.n a4, a2, 4 +400004d4: 2258 l32i.n a5, a2, 8 +400004d6: 0cc222 addi a2, a2, 12 +400004d9: 0fb347 bgeu a3, a4, 400004ec <_XX_Vec400+0x1ec> +400004dc: 0568 l32i.n a6, a5, 0 +400004de: 554b addi.n a5, a5, 4 +400004e0: 0369 s32i.n a6, a3, 0 +400004e2: 334b addi.n a3, a3, 4 +400004e4: f43347 bltu a3, a4, 400004dc <_XX_Vec400+0x1dc> +400004e7: fff946 j 400004d0 <_XX_Vec400+0x1d0> +400004ea: 560000 excw +400004ed: 56fe03 excw +400004f0: 00fdd5 call4 400014d0 <_l_strap_0x0x11_loader+0x7> +400004f3: 0c0020 excw +400004f6: 491011 l32r a1, 3ffd2938 <_start-0x2d6c8> +400004f9: 480013 excw +400004fc: 201013 excw + ... + +40000500 <_X_ResetVector>: +40000500: 000c movi.n a0, 0 +40000502: ffc741 l32r a4, 40000420 <_XX_Vec400+0x120> +40000505: 13d240 wsr.excsave2 a4 +40000508: ffc741 l32r a4, 40000424 <_XX_Vec400+0x124> +4000050b: 13d340 wsr.excsave3 a4 +4000050e: ffc641 l32r a4, 40000428 <_XX_Vec400+0x128> +40000511: 13d440 wsr.excsave4 a4 +40000514: ffc651 l32r a5, 4000042c <_XX_Vec400+0x12c> +40000517: 006542 s32i a4, a5, 0 +4000051a: ffc541 l32r a4, 40000430 <_XX_Vec400+0x130> +4000051d: 13d540 wsr.excsave5 a4 +40000520: ffc551 l32r a5, 40000434 <_XX_Vec400+0x134> +40000523: 006542 s32i a4, a5, 0 +40000526: 002845 call0 400007ac <_X_start> + ... + +40000700 <_c_stack>: +40000700: 000000 ill +40000703: 002040 excw +40000706: 480004 mula.da.ll.ldinc m0, a0, m0, a0 + +40000708 <_c_bss_start>: +40000708: cd48 l32i.n a4, a13, 48 +4000070a: ff .byte 0xff +4000070b: 3f .byte 0x3f + +4000070c <_c_bss_end>: +4000070c: ffe1d0 excw +4000070f: 3f .byte 0x3f +40000710: 6668 l32i.n a6, a6, 24 +40000712: 084000 excw +40000715: 3fffc1 l32r a12, 3ffd0714 <_start-0x2f8ec> +40000718: 000e40 excw +4000071b: c00840 sub a0, a8, a4 +4000071e: ff .byte 0xff +4000071f: 3f .byte 0x3f +40000720: 66b8 l32i.n a11, a6, 24 +40000722: 234000 sext a4, a0, 7 +40000725: 000500 excw +40000728: 000007 bnone a0, a0, 4000072c <_c_bss_end+0x20> +4000072b: 000580 ret +4000072e: 000000 ill +40000731: c00000 sub a0, a0, a0 + +40000734 <_c_0x3fffc210>: +40000734: ffc210 excw +40000737: 3f .byte 0x3f + +40000738 <_c_0x80000000>: +40000738: 000000 ill +4000073b: c21880 quou a1, a8, a8 +4000073e: ff .byte 0xff +4000073f: 3f .byte 0x3f +40000740: 66c8 l32i.n a12, a6, 24 +40000742: 004000 break 0, 0 +40000745: 078000 excw +40000748: 000aa1 l32r a10, 3ffc0770 <_start-0x3f890> +4000074b: 800040 add a0, a0, a4 +4000074e: 723800 excw +40000751: 000b addi.n a0, a0, -1 +40000753: d15040 mul16s a5, a0, a4 +40000756: ff .byte 0xff +40000757: 3f .byte 0x3f +40000758: c208 l32i.n a0, a2, 48 +4000075a: ff .byte 0xff +4000075b: 3f .byte 0x3f +4000075c: 050024 excw + ... + +40000760 <_c_0x40000000>: +40000760: 000000 ill +40000763: d6b040 excw +40000766: ff .byte 0xff +40000767: 3f .byte 0x3f +40000768: c20c movi.n a2, 12 +4000076a: ff .byte 0xff +4000076b: 3f .byte 0x3f +4000076c: 050025 call8 40005770 <_XX_unk5734+0x3c> +4000076f: 000000 ill +40000772: 408001 l32r a0, 3ffd0974 <_start-0x2f68c> +40000775: ffc8 l32i.n a12, a15, 60 +40000777: 3f .byte 0x3f +40000778: f42d break.n 4 +4000077a: 2d5851 l32r a5, 3ffcbcdc <_start-0x34324> +4000077d: 7f .byte 0x7f +4000077e: ff4c95 call4 3ffffc48 <_start-0x3b8> + +40000780 <_c_0x7fffffff>: +40000780: ff .byte 0xff +40000781: ff .byte 0xff +40000782: ff .byte 0xff +40000783: 7f .byte 0x7f +40000784: ffca30 excw +40000787: 3f .byte 0x3f +40000788: 0066b0 rsil a11, 6 +4000078b: fff040 excw +4000078e: fffb addi.n a15, a15, 15 +40000790: ff .byte 0xff +40000791: 000000 ill +40000794: 00ff00 excw + ... + +40000798 <_c_0x00ff0000>: +40000798: ff0000 excw +4000079b: 000000 ill +4000079e: 40ff00 nsau a0, a15 +400007a1: 404040 excw +400007a4: 808080 add a8, a0, a8 +400007a7: feff80 excw +400007aa: fe .byte 0xfe +400007ab: fe .byte 0xfe + +400007ac <_X_start>: +400007ac: 00a002 movi a0, 0 +400007af: ffd411 l32r a1, 40000700 <_c_stack> +400007b2: ffd431 l32r a3, 40000704 <_c_stack+0x4> +400007b5: f03d nop.n +400007b7: 13e630 wsr.ps a3 +400007ba: 002010 rsync +400007bd: ffd261 l32r a6, 40000708 <_c_bss_start> +400007c0: ffd371 l32r a7, 4000070c <_c_bss_end> +400007c3: 06b677 bgeu a6, a7, 400007cd <_X_start+0x21> +400007c6: 0609 s32i.n a0, a6, 0 +400007c8: 664b addi.n a6, a6, 4 +400007ca: f83677 bltu a6, a7, 400007c6 <_X_start+0x1a> +400007cd: 0098d5 call4 4000115c <_X_main> +400007d0: 120c movi.n a2, 1 +400007d2: 005100 simcall +400007d5: 0041f0 break 1, 15 +400007d8: fffd06 j 400007d0 <_X_start+0x24> +400007db: 413600 srli a3, a0, 6 +400007de: d2f600 quos a15, a6, a0 +400007e1: 2e .byte 0x2e +400007e2: 090c movi.n a9, 0 +400007e4: ffcd61 l32r a6, 40000718 <_c_bss_end+0xc> +400007e7: ffce41 l32r a4, 40000720 <_c_bss_end+0x14> +400007ea: ffcc81 l32r a8, 4000071c <_c_bss_end+0x10> +400007ed: ffc8a1 l32r a10, 40000710 <_c_bss_end+0x4> +400007f0: ffc9b1 l32r a11, 40000714 <_c_bss_end+0x8> +400007f3: 1152e0 slli a5, a2, 2 +400007f6: b5ba add.n a11, a5, a11 +400007f8: 833a30 moveqz a3, a10, a3 +400007fb: 558a add.n a5, a5, a8 +400007fd: 0b28 l32i.n a2, a11, 0 +400007ff: c073a0 sub a7, a3, a10 +40000802: 934670 movnez a4, a6, a7 +40000805: 0b39 s32i.n a3, a11, 0 +40000807: 0549 s32i.n a4, a5, 0 +40000809: c0a2a0 sub a10, a2, a10 +4000080c: 8329a0 moveqz a2, a9, a10 +4000080f: f01d retw.n +40000811: 020c movi.n a2, 0 +40000813: f01d retw.n +40000815: 000000 ill +40000818: 03b130 rsr.epc1 a3 +4000081b: 030120 rsr.lend a2 +4000081e: 333b addi.n a3, a3, 3 +40000820: 0c9237 bne a2, a3, 40000830 <_X_start+0x84> +40000823: 030220 rsr.lcount a2 +40000826: 628c beqz.n a2, 40000830 <_X_start+0x84> +40000828: 220b addi.n a2, a2, -1 +4000082a: 130220 wsr.lcount a2 +4000082d: 030030 rsr.lbeg a3 +40000830: 4128 l32i.n a2, a1, 16 +40000832: 13b130 wsr.epc1 a3 +40000835: 5138 l32i.n a3, a1, 20 +40000837: 056256 bnez a2, 40000891 <_X_start+0xe5> +4000083a: 396132 s32i a3, a1, 228 +4000083d: 3a6142 s32i a4, a1, 232 +40000840: 3b6152 s32i a5, a1, 236 +40000843: ffb831 l32r a3, 40000724 <_c_bss_end+0x18> +40000846: 03e620 rsr.ps a2 +40000849: 13e630 wsr.ps a3 +4000084c: 03b130 rsr.epc1 a3 +4000084f: 01d112 addmi a1, a1, 0x100 +40000852: 002010 rsync +40000855: 040c movi.n a4, 0 +40000857: 008136 entry a1, 64 +4000085a: 0001f5 call12 40000878 <_X_start+0xcc> +4000085d: ffb2e1 l32r a14, 40000728 <_c_bss_end+0x1c> +40000860: 0cea add.n a0, a12, a14 +40000862: f01d retw.n +40000864: 13e620 wsr.ps a2 +40000867: 020c movi.n a2, 0 +40000869: a548 l32i.n a4, a5, 40 +4000086b: 002010 rsync +4000086e: 13b130 wsr.epc1 a3 +40000871: 9538 l32i.n a3, a5, 36 +40000873: b558 l32i.n a5, a5, 44 +40000875: 003000 rfe +40000878: 006136 entry a1, 48 +4000087b: ffacc1 l32r a12, 4000072c <_c_bss_end+0x20> +4000087e: c0ca add.n a12, a0, a12 +40000880: 006136 entry a1, 48 +40000883: 00cd mov.n a12, a0 +40000885: 006136 entry a1, 48 +40000888: 00cd mov.n a12, a0 +4000088a: 002136 entry a1, 16 +4000088d: 0bbd mov.n a11, a11 +4000088f: f01d retw.n +40000891: f27c movi.n a2, -1 +40000893: 01d112 addmi a1, a1, 0x100 +40000896: 003000 rfe +40000899: 000000 ill +4000089c: 7159 s32i.n a5, a1, 28 +4000089e: ffa121 l32r a2, 40000724 <_c_bss_end+0x18> +400008a1: 03b130 rsr.epc1 a3 +400008a4: 61e620 xsr.ps a2 +400008a7: 0139 s32i.n a3, a1, 0 +400008a9: 1129 s32i.n a2, a1, 4 +400008ab: 3b2122 l32i a2, a1, 236 +400008ae: 01d112 addmi a1, a1, 0x100 +400008b1: 002010 rsync +400008b4: ff9f41 l32r a4, 40000730 <_c_bss_end+0x24> +400008b7: 203430 or a3, a4, a3 +400008ba: 904430 addx2 a4, a4, a3 +400008bd: 020136 entry a1, 0x100 +400008c0: 0061f0 rsil a15, 1 +400008c3: ff9ce1 l32r a14, 40000734 <_c_0x3fffc210> +400008c6: 03e2f0 rsr.interrupt a15 +400008c9: 03e4c0 rsr.intenable a12 +400008cc: 1ed8 l32i.n a13, a14, 4 +400008ce: 10ffc0 and a15, a15, a12 +400008d1: 0303c0 rsr.sar a12 +400008d4: 31d9 s32i.n a13, a1, 12 +400008d6: 092f16 beqz a15, 4000096c <_X_start+0x1c0> +400008d9: 21c9 s32i.n a12, a1, 8 +400008db: 8129 s32i.n a2, a1, 32 +400008dd: ffaf22 movi a2, -1 +400008e0: 40ffc0 nsau a12, a15 +400008e3: ff95f1 l32r a15, 40000738 <_c_0x80000000> +400008e6: 400c00 ssr a12 +400008e9: 91f0f0 srl a15, a15 +400008ec: 3022f0 xor a2, a2, a15 +400008ef: ff93d1 l32r a13, 4000073c <_c_0x80000000+0x4> +400008f2: 13e3f0 wsr.intclear a15 +400008f5: b0ccd0 addx8 a12, a12, a13 +400008f8: 402cd2 l32i a13, a12, 0x100 +400008fb: 0066f0 rsil a15, 6 +400008fe: 0ef8 l32i.n a15, a14, 0 +40000900: 1ed9 s32i.n a13, a14, 4 +40000902: 10ffd0 and a15, a15, a13 +40000905: 13e4f0 wsr.intenable a15 +40000908: 002010 rsync +4000090b: 0060f0 rsil a15, 0 +4000090e: 0302d0 rsr.lcount a13 +40000911: 0300f0 rsr.lbeg a15 +40000914: 91d9 s32i.n a13, a1, 36 +40000916: 0301d0 rsr.lend a13 +40000919: a1f9 s32i.n a15, a1, 40 +4000091b: b1d9 s32i.n a13, a1, 44 +4000091d: 0310d0 rsr.acclo a13 +40000920: 0311f0 rsr.acchi a15 +40000923: c1d9 s32i.n a13, a1, 48 +40000925: d1f9 s32i.n a15, a1, 52 +40000927: 0cd8 l32i.n a13, a12, 0 +40000929: 01fd mov.n a15, a1 +4000092b: 012ce2 l32i a14, a12, 4 +4000092e: 000df0 callx12 a13 +40000931: 91d8 l32i.n a13, a1, 36 +40000933: a1e8 l32i.n a14, a1, 40 +40000935: b1f8 l32i.n a15, a1, 44 +40000937: 1302d0 wsr.lcount a13 +4000093a: 1300e0 wsr.lbeg a14 +4000093d: 1301f0 wsr.lend a15 +40000940: c1d8 l32i.n a13, a1, 48 +40000942: d1e8 l32i.n a14, a1, 52 +40000944: 1310d0 wsr.acclo a13 +40000947: 1311e0 wsr.acchi a14 +4000094a: ff7ae1 l32r a14, 40000734 <_c_0x3fffc210> +4000094d: 03e2f0 rsr.interrupt a15 +40000950: 0066d0 rsil a13, 6 +40000953: 0ed8 l32i.n a13, a14, 0 +40000955: 31c8 l32i.n a12, a1, 12 +40000957: 10ddc0 and a13, a13, a12 +4000095a: 10ffd0 and a15, a15, a13 +4000095d: 01ff56 bnez a15, 40000980 <_X_start+0x1d4> +40000960: 8128 l32i.n a2, a1, 32 +40000962: 1ec9 s32i.n a12, a14, 4 +40000964: 21c8 l32i.n a12, a1, 8 +40000966: 13e4d0 wsr.intenable a13 +40000969: 0063d0 rsil a13, 3 +4000096c: ff7501 l32r a0, 40000740 <_c_0x80000000+0x8> +4000096f: ff70d1 l32r a13, 40000730 <_c_bss_end+0x24> +40000972: 1303c0 wsr.sar a12 +40000975: 2000d0 or a0, a0, a13 +40000978: 900d00 addx2 a0, a13, a0 +4000097b: 0063d0 rsil a13, 3 +4000097e: f01d retw.n +40000980: 40ffd0 nsau a13, a15 +40000983: ff6ec1 l32r a12, 4000073c <_c_0x80000000+0x4> +40000986: b0cdc0 addx8 a12, a13, a12 +40000989: 412ce2 l32i a14, a12, 0x104 +4000098c: 10ff20 and a15, a15, a2 +4000098f: 10ffe0 and a15, a15, a14 +40000992: ff68e1 l32r a14, 40000734 <_c_0x3fffc210> +40000995: f47f56 bnez a15, 400008e0 <_X_start+0x134> +40000998: ff68f1 l32r a15, 40000738 <_c_0x80000000> +4000099b: 400d00 ssr a13 +4000099e: 91d0f0 srl a13, a15 +400009a1: 412cf2 l32i a15, a12, 0x104 +400009a4: 13e3d0 wsr.intclear a13 +400009a7: 2022f0 or a2, a2, a15 +400009aa: 3022d0 xor a2, a2, a13 +400009ad: ffd1c6 j 400008f8 <_X_start+0x14c> +400009b0: 002136 entry a1, 16 +400009b3: 13e620 wsr.ps a2 +400009b6: 002010 rsync +400009b9: f01d retw.n +400009bb: 213600 srai a3, a0, 6 +400009be: 023d00 andb b3, b13, b0 +400009c1: ff5c41 l32r a4, 40000734 <_c_0x3fffc210> +400009c4: 006670 rsil a7, 6 +400009c7: 1428 l32i.n a2, a4, 4 +400009c9: 0458 l32i.n a5, a4, 0 +400009cb: 1439 s32i.n a3, a4, 4 +400009cd: 105530 and a5, a5, a3 +400009d0: 13e450 wsr.intenable a5 +400009d3: 13e670 wsr.ps a7 +400009d6: 002010 rsync +400009d9: f01d retw.n +400009db: d22000 quos a2, a0, a0 +400009de: d11261 l32r a6, 3fff4e28 <_start-0xb1d8> +400009e1: ff .byte 0xff +400009e2: 4129 s32i.n a2, a1, 16 +400009e4: ff5021 l32r a2, 40000724 <_c_bss_end+0x18> +400009e7: 6149 s32i.n a4, a1, 24 +400009e9: 7159 s32i.n a5, a1, 28 +400009eb: 13e620 wsr.ps a2 +400009ee: 002010 rsync +400009f1: 3b2122 l32i a2, a1, 236 +400009f4: 01d112 addmi a1, a1, 0x100 +400009f7: 03b240 rsr.epc2 a4 +400009fa: ff4d51 l32r a5, 40000730 <_c_bss_end+0x24> +400009fd: 204540 or a4, a5, a4 +40000a00: 904540 addx2 a4, a5, a4 +40000a03: 020136 entry a1, 0x100 +40000a06: 0062f0 rsil a15, 2 +40000a09: 03e2f0 rsr.interrupt a15 +40000a0c: 03e4c0 rsr.intenable a12 +40000a0f: ff4dd1 l32r a13, 40000744 <_c_0x80000000+0xc> +40000a12: 10ffc0 and a15, a15, a12 +40000a15: 10ffd0 and a15, a15, a13 +40000a18: 0303e0 rsr.sar a14 +40000a1b: 06ef16 beqz a15, 40000a8d <_X_start+0x2e1> +40000a1e: 21e9 s32i.n a14, a1, 8 +40000a20: 0302d0 rsr.lcount a13 +40000a23: 0300e0 rsr.lbeg a14 +40000a26: 91d9 s32i.n a13, a1, 36 +40000a28: 0301d0 rsr.lend a13 +40000a2b: a1e9 s32i.n a14, a1, 40 +40000a2d: b1d9 s32i.n a13, a1, 44 +40000a2f: 0310d0 rsr.acclo a13 +40000a32: 0311e0 rsr.acchi a14 +40000a35: c1d9 s32i.n a13, a1, 48 +40000a37: d1e9 s32i.n a14, a1, 52 +40000a39: 60c0f0 neg a12, a15 +40000a3c: 10ccf0 and a12, a12, a15 +40000a3f: 13e3c0 wsr.intclear a12 +40000a42: ff3ed1 l32r a13, 4000073c <_c_0x80000000+0x4> +40000a45: fe1c movi.n a14, 31 +40000a47: 40fcc0 nsau a12, a12 +40000a4a: c0fec0 sub a15, a14, a12 +40000a4d: 60f0f0 neg a15, a15 +40000a50: 1fcff2 addi a15, a15, 31 +40000a53: b0cfd0 addx8 a12, a15, a13 +40000a56: 0cd8 l32i.n a13, a12, 0 +40000a58: 1ce8 l32i.n a14, a12, 4 +40000a5a: 20f110 or a15, a1, a1 +40000a5d: 000df0 callx12 a13 +40000a60: 03e2f0 rsr.interrupt a15 +40000a63: 03e4c0 rsr.intenable a12 +40000a66: ff37d1 l32r a13, 40000744 <_c_0x80000000+0xc> +40000a69: 10ffc0 and a15, a15, a12 +40000a6c: 10ffd0 and a15, a15, a13 +40000a6f: fc6f56 bnez a15, 40000a39 <_X_start+0x28d> +40000a72: 91d8 l32i.n a13, a1, 36 +40000a74: a1e8 l32i.n a14, a1, 40 +40000a76: b1f8 l32i.n a15, a1, 44 +40000a78: 1302d0 wsr.lcount a13 +40000a7b: 1300e0 wsr.lbeg a14 +40000a7e: 1301f0 wsr.lend a15 +40000a81: c1d8 l32i.n a13, a1, 48 +40000a83: d1e8 l32i.n a14, a1, 52 +40000a85: 1310d0 wsr.acclo a13 +40000a88: 1311e0 wsr.acchi a14 +40000a8b: 21e8 l32i.n a14, a1, 8 +40000a8d: ff2e01 l32r a0, 40000748 <_c_0x80000000+0x10> +40000a90: ff28d1 l32r a13, 40000730 <_c_bss_end+0x24> +40000a93: 1303e0 wsr.sar a14 +40000a96: 2000d0 or a0, a0, a13 +40000a99: 900d00 addx2 a0, a13, a0 +40000a9c: 0063e0 rsil a14, 3 +40000a9f: f01d retw.n +40000aa1: 3b6522 s32i a2, a5, 236 +40000aa4: 4528 l32i.n a2, a5, 16 +40000aa6: 6548 l32i.n a4, a5, 24 +40000aa8: 7558 l32i.n a5, a5, 28 +40000aaa: 003210 rfi 2 +40000aad: 000000 ill +40000ab0: 61d320 xsr.excsave3 a2 +40000ab3: ffd112 addmi a1, a1, 0xffffff00 +40000ab6: 4129 s32i.n a2, a1, 16 +40000ab8: ff1b21 l32r a2, 40000724 <_c_bss_end+0x18> +40000abb: 6149 s32i.n a4, a1, 24 +40000abd: 7159 s32i.n a5, a1, 28 +40000abf: 13e620 wsr.ps a2 +40000ac2: 002010 rsync +40000ac5: 3b2122 l32i a2, a1, 236 +40000ac8: 01d112 addmi a1, a1, 0x100 +40000acb: 03b340 rsr.epc3 a4 +40000ace: ff1851 l32r a5, 40000730 <_c_bss_end+0x24> +40000ad1: 204540 or a4, a5, a4 +40000ad4: 904540 addx2 a4, a5, a4 +40000ad7: 020136 entry a1, 0x100 +40000ada: 0063f0 rsil a15, 3 +40000add: 03e2f0 rsr.interrupt a15 +40000ae0: 03e4c0 rsr.intenable a12 +40000ae3: ff1ad1 l32r a13, 4000074c <_c_0x80000000+0x14> +40000ae6: 10ffc0 and a15, a15, a12 +40000ae9: 10ffd0 and a15, a15, a13 +40000aec: 0303e0 rsr.sar a14 +40000aef: 06ef16 beqz a15, 40000b61 <_X_start+0x3b5> +40000af2: 21e9 s32i.n a14, a1, 8 +40000af4: 0302d0 rsr.lcount a13 +40000af7: 0300e0 rsr.lbeg a14 +40000afa: 91d9 s32i.n a13, a1, 36 +40000afc: 0301d0 rsr.lend a13 +40000aff: a1e9 s32i.n a14, a1, 40 +40000b01: b1d9 s32i.n a13, a1, 44 +40000b03: 0310d0 rsr.acclo a13 +40000b06: 0311e0 rsr.acchi a14 +40000b09: c1d9 s32i.n a13, a1, 48 +40000b0b: d1e9 s32i.n a14, a1, 52 +40000b0d: 60c0f0 neg a12, a15 +40000b10: 10ccf0 and a12, a12, a15 +40000b13: 13e3c0 wsr.intclear a12 +40000b16: ff09d1 l32r a13, 4000073c <_c_0x80000000+0x4> +40000b19: fe1c movi.n a14, 31 +40000b1b: 40fcc0 nsau a12, a12 +40000b1e: c0fec0 sub a15, a14, a12 +40000b21: 60f0f0 neg a15, a15 +40000b24: 1fcff2 addi a15, a15, 31 +40000b27: b0cfd0 addx8 a12, a15, a13 +40000b2a: 0cd8 l32i.n a13, a12, 0 +40000b2c: 1ce8 l32i.n a14, a12, 4 +40000b2e: 20f110 or a15, a1, a1 +40000b31: 000df0 callx12 a13 +40000b34: 03e2f0 rsr.interrupt a15 +40000b37: 03e4c0 rsr.intenable a12 +40000b3a: ff04d1 l32r a13, 4000074c <_c_0x80000000+0x14> +40000b3d: 10ffc0 and a15, a15, a12 +40000b40: 10ffd0 and a15, a15, a13 +40000b43: fc6f56 bnez a15, 40000b0d <_X_start+0x361> +40000b46: 91d8 l32i.n a13, a1, 36 +40000b48: a1e8 l32i.n a14, a1, 40 +40000b4a: b1f8 l32i.n a15, a1, 44 +40000b4c: 1302d0 wsr.lcount a13 +40000b4f: 1300e0 wsr.lbeg a14 +40000b52: 1301f0 wsr.lend a15 +40000b55: c1d8 l32i.n a13, a1, 48 +40000b57: d1e8 l32i.n a14, a1, 52 +40000b59: 1310d0 wsr.acclo a13 +40000b5c: 1311e0 wsr.acchi a14 +40000b5f: 21e8 l32i.n a14, a1, 8 +40000b61: fefb01 l32r a0, 40000750 <_c_0x80000000+0x18> +40000b64: fef3d1 l32r a13, 40000730 <_c_bss_end+0x24> +40000b67: 1303e0 wsr.sar a14 +40000b6a: 2000d0 or a0, a0, a13 +40000b6d: 900d00 addx2 a0, a13, a0 +40000b70: f01d retw.n +40000b72: 3b6522 s32i a2, a5, 236 +40000b75: 4528 l32i.n a2, a5, 16 +40000b77: 6548 l32i.n a4, a5, 24 +40000b79: 7558 l32i.n a5, a5, 28 +40000b7b: 003310 rfi 3 +40000b7e: 210000 srai a0, a0, 0 +40000b81: 09fef5 call12 4000ab70 <__umoddi3+0x3d0c> +40000b84: f401d2 l8ui a13, a1, 244 +40000b87: fe .byte 0xfe +40000b88: e219 s32i.n a1, a2, 56 +40000b8a: 0008 l32i.n a0, a0, 0 +40000b8c: 106232 s32i a3, a2, 64 +40000b8f: 61d400 xsr.excsave4 a0 +40000b92: 116242 s32i a4, a2, 68 +40000b95: f209 s32i.n a0, a2, 60 +40000b97: 03b130 rsr.epc1 a3 +40000b9a: 03e840 rsr.exccause a4 +40000b9d: 3239 s32i.n a3, a2, 12 +40000b9f: 4249 s32i.n a4, a2, 16 +40000ba1: 03ee30 rsr.excvaddr a3 +40000ba4: 056232 s32i a3, a2, 20 +40000ba7: 03d140 rsr.excsave1 a4 +40000baa: 066242 s32i a4, a2, 24 +40000bad: 803c movi.n a0, 56 +40000baf: 126252 s32i a5, a2, 72 +40000bb2: 136262 s32i a6, a2, 76 +40000bb5: 146272 s32i a7, a2, 80 +40000bb8: 156282 s32i a8, a2, 84 +40000bbb: 166292 s32i a9, a2, 88 +40000bbe: 1762a2 s32i a10, a2, 92 +40000bc1: 1862b2 s32i a11, a2, 96 +40000bc4: 1962c2 s32i a12, a2, 100 +40000bc7: 1a62d2 s32i a13, a2, 104 +40000bca: 1b62e2 s32i a14, a2, 108 +40000bcd: 1c62f2 s32i a15, a2, 112 +40000bd0: f8c082 addi a8, a0, -8 +40000bd3: 20c2a2 addi a10, a2, 32 +40000bd6: 408020 rotw 2 +40000bd9: fdb056 bnez a0, 40000bb8 <_X_start+0x40c> +40000bdc: 408020 rotw 2 +40000bdf: 034930 rsr.windowstart a3 +40000be2: 034840 rsr.windowbase a4 +40000be5: 1239 s32i.n a3, a2, 4 +40000be7: 2249 s32i.n a4, a2, 8 +40000be9: 130c movi.n a3, 1 +40000beb: 040c movi.n a4, 0 +40000bed: 134930 wsr.windowstart a3 +40000bf0: 134840 wsr.windowbase a4 +40000bf3: 002010 rsync +40000bf6: fed711 l32r a1, 40000754 <_c_0x80000000+0x1c> +40000bf9: 000c movi.n a0, 0 +40000bfb: fed821 l32r a2, 4000075c <_c_0x80000000+0x24> +40000bfe: 13e620 wsr.ps a2 +40000c01: 002010 rsync +40000c04: 0303e0 rsr.sar a14 +40000c07: 01e9 s32i.n a14, a1, 0 +40000c09: 0302e0 rsr.lcount a14 +40000c0c: a1e9 s32i.n a14, a1, 40 +40000c0e: 0300e0 rsr.lbeg a14 +40000c11: b1e9 s32i.n a14, a1, 44 +40000c13: 0301e0 rsr.lend a14 +40000c16: c1e9 s32i.n a14, a1, 48 +40000c18: 0310e0 rsr.acclo a14 +40000c1b: 81e9 s32i.n a14, a1, 32 +40000c1d: 0311e0 rsr.acchi a14 +40000c20: 91e9 s32i.n a14, a1, 36 +40000c22: fecfd1 l32r a13, 40000760 <_c_0x40000000> +40000c25: fec5c1 l32r a12, 4000073c <_c_0x80000000+0x4> +40000c28: 13e3d0 wsr.intclear a13 +40000c2b: 2cd8 l32i.n a13, a12, 8 +40000c2d: 3c68 l32i.n a6, a12, 12 +40000c2f: 000dd0 callx4 a13 +40000c32: a1d8 l32i.n a13, a1, 40 +40000c34: b1e8 l32i.n a14, a1, 44 +40000c36: c1f8 l32i.n a15, a1, 48 +40000c38: 1302d0 wsr.lcount a13 +40000c3b: 1300e0 wsr.lbeg a14 +40000c3e: 1301f0 wsr.lend a15 +40000c41: 81d8 l32i.n a13, a1, 32 +40000c43: 91e8 l32i.n a14, a1, 36 +40000c45: 1310d0 wsr.acclo a13 +40000c48: 1311e0 wsr.acchi a14 +40000c4b: 01f8 l32i.n a15, a1, 0 +40000c4d: 1303f0 wsr.sar a15 +40000c50: 423c movi.n a2, 52 +40000c52: 13e620 wsr.ps a2 +40000c55: 002010 rsync +40000c58: 1128 l32i.n a2, a1, 4 +40000c5a: 2138 l32i.n a3, a1, 8 +40000c5c: 134920 wsr.windowstart a2 +40000c5f: 134830 wsr.windowbase a3 +40000c62: 002010 rsync +40000c65: febb11 l32r a1, 40000754 <_c_0x80000000+0x1c> +40000c68: 863c movi.n a6, 56 +40000c6a: e0c172 addi a7, a1, -32 +40000c6d: f8c6e2 addi a14, a6, -8 +40000c70: 20c7f2 addi a15, a7, 32 +40000c73: 112f42 l32i a4, a15, 68 +40000c76: 122f52 l32i a5, a15, 72 +40000c79: 132f62 l32i a6, a15, 76 +40000c7c: 142f72 l32i a7, a15, 80 +40000c7f: 152f82 l32i a8, a15, 84 +40000c82: 162f92 l32i a9, a15, 88 +40000c85: 172fa2 l32i a10, a15, 92 +40000c88: 182fb2 l32i a11, a15, 96 +40000c8b: 408020 rotw 2 +40000c8e: fdb656 bnez a6, 40000c6d <_X_start+0x4c1> +40000c91: 192742 l32i a4, a7, 100 +40000c94: 1a2752 l32i a5, a7, 104 +40000c97: 1b2762 l32i a6, a7, 108 +40000c9a: 1c2772 l32i a7, a7, 112 +40000c9d: 408020 rotw 2 +40000ca0: 3128 l32i.n a2, a1, 12 +40000ca2: 4138 l32i.n a3, a1, 16 +40000ca4: 13b120 wsr.epc1 a2 +40000ca7: 13e830 wsr.exccause a3 +40000caa: 5128 l32i.n a2, a1, 20 +40000cac: 13ee20 wsr.excvaddr a2 +40000caf: 6138 l32i.n a3, a1, 24 +40000cb1: 13d130 wsr.excsave1 a3 +40000cb4: d108 l32i.n a0, a1, 52 +40000cb6: f128 l32i.n a2, a1, 60 +40000cb8: 102132 l32i a3, a1, 64 +40000cbb: e118 l32i.n a1, a1, 56 +40000cbd: 003410 rfi 4 +40000cc0: fea921 l32r a2, 40000764 <_c_0x40000000+0x4> +40000cc3: d209 s32i.n a0, a2, 52 +40000cc5: fea801 l32r a0, 40000768 <_c_0x40000000+0x8> +40000cc8: e219 s32i.n a1, a2, 56 +40000cca: 0008 l32i.n a0, a0, 0 +40000ccc: 106232 s32i a3, a2, 64 +40000ccf: 61d500 xsr.excsave5 a0 +40000cd2: 116242 s32i a4, a2, 68 +40000cd5: f209 s32i.n a0, a2, 60 +40000cd7: 03b130 rsr.epc1 a3 +40000cda: 03e840 rsr.exccause a4 +40000cdd: 3239 s32i.n a3, a2, 12 +40000cdf: 4249 s32i.n a4, a2, 16 +40000ce1: 03ee30 rsr.excvaddr a3 +40000ce4: 056232 s32i a3, a2, 20 +40000ce7: 03d140 rsr.excsave1 a4 +40000cea: 6249 s32i.n a4, a2, 24 +40000cec: 38a002 movi a0, 56 +40000cef: 126252 s32i a5, a2, 72 +40000cf2: 136262 s32i a6, a2, 76 +40000cf5: 146272 s32i a7, a2, 80 +40000cf8: 156282 s32i a8, a2, 84 +40000cfb: 166292 s32i a9, a2, 88 +40000cfe: 1762a2 s32i a10, a2, 92 +40000d01: 1862b2 s32i a11, a2, 96 +40000d04: 1962c2 s32i a12, a2, 100 +40000d07: 1a62d2 s32i a13, a2, 104 +40000d0a: 1b62e2 s32i a14, a2, 108 +40000d0d: 1c62f2 s32i a15, a2, 112 +40000d10: f8c082 addi a8, a0, -8 +40000d13: 20c2a2 addi a10, a2, 32 +40000d16: 408020 rotw 2 +40000d19: fdb056 bnez a0, 40000cf8 <_X_start+0x54c> +40000d1c: 408020 rotw 2 +40000d1f: 034930 rsr.windowstart a3 +40000d22: 034840 rsr.windowbase a4 +40000d25: 1239 s32i.n a3, a2, 4 +40000d27: 2249 s32i.n a4, a2, 8 +40000d29: 130c movi.n a3, 1 +40000d2b: 040c movi.n a4, 0 +40000d2d: 134930 wsr.windowstart a3 +40000d30: 134840 wsr.windowbase a4 +40000d33: 002010 rsync +40000d36: fe8b11 l32r a1, 40000764 <_c_0x40000000+0x4> +40000d39: 000c movi.n a0, 0 +40000d3b: fe8c21 l32r a2, 4000076c <_c_0x40000000+0xc> +40000d3e: 13e620 wsr.ps a2 +40000d41: 002010 rsync +40000d44: 0303e0 rsr.sar a14 +40000d47: 01e9 s32i.n a14, a1, 0 +40000d49: 0302e0 rsr.lcount a14 +40000d4c: a1e9 s32i.n a14, a1, 40 +40000d4e: 0300e0 rsr.lbeg a14 +40000d51: b1e9 s32i.n a14, a1, 44 +40000d53: 0301e0 rsr.lend a14 +40000d56: c1e9 s32i.n a14, a1, 48 +40000d58: 0310e0 rsr.acclo a14 +40000d5b: 81e9 s32i.n a14, a1, 32 +40000d5d: 0311e0 rsr.acchi a14 +40000d60: 91e9 s32i.n a14, a1, 36 +40000d62: 03e2f0 rsr.interrupt a15 +40000d65: 03e4c0 rsr.intenable a12 +40000d68: fe82d1 l32r a13, 40000770 <_c_0x40000000+0x10> +40000d6b: 10ffc0 and a15, a15, a12 +40000d6e: 10ffd0 and a15, a15, a13 +40000d71: 02af16 beqz a15, 40000d9f <_X_start+0x5f3> +40000d74: 60e0f0 neg a14, a15 +40000d77: 10eef0 and a14, a14, a15 +40000d7a: 13e3e0 wsr.intclear a14 +40000d7d: fe6fc1 l32r a12, 4000073c <_c_0x80000000+0x4> +40000d80: 40fee0 nsau a14, a14 +40000d83: b0cec0 addx8 a12, a14, a12 +40000d86: 0cd8 l32i.n a13, a12, 0 +40000d88: 1c68 l32i.n a6, a12, 4 +40000d8a: 000dd0 callx4 a13 +40000d8d: 03e2f0 rsr.interrupt a15 +40000d90: 03e4c0 rsr.intenable a12 +40000d93: fe77d1 l32r a13, 40000770 <_c_0x40000000+0x10> +40000d96: 10ffc0 and a15, a15, a12 +40000d99: 10ffd0 and a15, a15, a13 +40000d9c: fd4f56 bnez a15, 40000d74 <_X_start+0x5c8> +40000d9f: a1d8 l32i.n a13, a1, 40 +40000da1: b1e8 l32i.n a14, a1, 44 +40000da3: c1f8 l32i.n a15, a1, 48 +40000da5: 1302d0 wsr.lcount a13 +40000da8: 1300e0 wsr.lbeg a14 +40000dab: 1301f0 wsr.lend a15 +40000dae: 81d8 l32i.n a13, a1, 32 +40000db0: 91e8 l32i.n a14, a1, 36 +40000db2: 1310d0 wsr.acclo a13 +40000db5: 1311e0 wsr.acchi a14 +40000db8: 01f8 l32i.n a15, a1, 0 +40000dba: 1303f0 wsr.sar a15 +40000dbd: 523c movi.n a2, 53 +40000dbf: 13e620 wsr.ps a2 +40000dc2: 002010 rsync +40000dc5: 1128 l32i.n a2, a1, 4 +40000dc7: 022132 l32i a3, a1, 8 +40000dca: 134920 wsr.windowstart a2 +40000dcd: 134830 wsr.windowbase a3 +40000dd0: 002010 rsync +40000dd3: fe6411 l32r a1, 40000764 <_c_0x40000000+0x4> +40000dd6: 38a062 movi a6, 56 +40000dd9: e0c172 addi a7, a1, -32 +40000ddc: f8c6e2 addi a14, a6, -8 +40000ddf: 20c7f2 addi a15, a7, 32 +40000de2: 112f42 l32i a4, a15, 68 +40000de5: 122f52 l32i a5, a15, 72 +40000de8: 132f62 l32i a6, a15, 76 +40000deb: 142f72 l32i a7, a15, 80 +40000dee: 152f82 l32i a8, a15, 84 +40000df1: 162f92 l32i a9, a15, 88 +40000df4: 172fa2 l32i a10, a15, 92 +40000df7: 182fb2 l32i a11, a15, 96 +40000dfa: 408020 rotw 2 +40000dfd: fdb656 bnez a6, 40000ddc <_X_start+0x630> +40000e00: 192742 l32i a4, a7, 100 +40000e03: 1a2752 l32i a5, a7, 104 +40000e06: 1b2762 l32i a6, a7, 108 +40000e09: 1c2772 l32i a7, a7, 112 +40000e0c: 408020 rotw 2 +40000e0f: 3128 l32i.n a2, a1, 12 +40000e11: 4138 l32i.n a3, a1, 16 +40000e13: 13b120 wsr.epc1 a2 +40000e16: 13e830 wsr.exccause a3 +40000e19: 5128 l32i.n a2, a1, 20 +40000e1b: 13ee20 wsr.excvaddr a2 +40000e1e: 6138 l32i.n a3, a1, 24 +40000e20: 13d130 wsr.excsave1 a3 +40000e23: d108 l32i.n a0, a1, 52 +40000e25: f128 l32i.n a2, a1, 60 +40000e27: 102132 l32i a3, a1, 64 +40000e2a: e118 l32i.n a1, a1, 56 +40000e2c: 003510 rfi 5 +40000e2f: 320c00 orbc b0, b12, b0 +40000e32: 000286 j 40000e40 <_X_start+0x694> +40000e35: 000000 ill +40000e38: fdc332 addi a3, a3, -3 +40000e3b: 332a add.n a3, a3, a2 +40000e3d: 0004c6 j 40000e54 <_X_start+0x6a8> +40000e40: 7159 s32i.n a5, a1, 28 +40000e42: 086122 s32i a2, a1, 32 +40000e45: 03e620 rsr.ps a2 +40000e48: fe3731 l32r a3, 40000724 <_c_bss_end+0x18> +40000e4b: 016122 s32i a2, a1, 4 +40000e4e: 342020 extui a2, a2, 0, 4 +40000e51: e342f6 bgeui a2, 4, 40000e38 <_X_start+0x68c> +40000e54: 03b120 rsr.epc1 a2 +40000e57: 13e630 wsr.ps a3 +40000e5a: 0129 s32i.n a2, a1, 0 +40000e5c: 3b2122 l32i a2, a1, 236 +40000e5f: 01d112 addmi a1, a1, 0x100 +40000e62: 002010 rsync +40000e65: fe3241 l32r a4, 40000730 <_c_bss_end+0x24> +40000e68: 203430 or a3, a4, a3 +40000e6b: 904430 addx2 a4, a4, a3 +40000e6e: 020136 entry a1, 0x100 +40000e71: 0303c0 rsr.sar a12 +40000e74: 11d8 l32i.n a13, a1, 4 +40000e76: 03e6e0 rsr.ps a14 +40000e79: 34d0d0 extui a13, a13, 0, 4 +40000e7c: 34f0e0 extui a15, a14, 0, 4 +40000e7f: 30eef0 xor a14, a14, a15 +40000e82: 30eed0 xor a14, a14, a13 +40000e85: 13e6e0 wsr.ps a14 +40000e88: fe23d1 l32r a13, 40000714 <_c_bss_end+0x8> +40000e8b: 81f8 l32i.n a15, a1, 32 +40000e8d: 21c9 s32i.n a12, a1, 8 +40000e8f: 0302c0 rsr.lcount a12 +40000e92: 0300e0 rsr.lbeg a14 +40000e95: 91c9 s32i.n a12, a1, 36 +40000e97: 0301c0 rsr.lend a12 +40000e9a: a1e9 s32i.n a14, a1, 40 +40000e9c: b1c9 s32i.n a12, a1, 44 +40000e9e: 0310c0 rsr.acclo a12 +40000ea1: 0311e0 rsr.acchi a14 +40000ea4: c1c9 s32i.n a12, a1, 48 +40000ea6: d1e9 s32i.n a14, a1, 52 +40000ea8: a0cfd0 addx4 a12, a15, a13 +40000eab: 0cc8 l32i.n a12, a12, 0 +40000ead: 01ed mov.n a14, a1 +40000eaf: 1c8c beqz.n a12, 40000eb4 <_X_start+0x708> +40000eb1: 000cf0 callx12 a12 +40000eb4: 91d8 l32i.n a13, a1, 36 +40000eb6: a1e8 l32i.n a14, a1, 40 +40000eb8: b1f8 l32i.n a15, a1, 44 +40000eba: 1302d0 wsr.lcount a13 +40000ebd: 1300e0 wsr.lbeg a14 +40000ec0: 1301f0 wsr.lend a15 +40000ec3: c1d8 l32i.n a13, a1, 48 +40000ec5: d1e8 l32i.n a14, a1, 52 +40000ec7: 1310d0 wsr.acclo a13 +40000eca: 1311e0 wsr.acchi a14 +40000ecd: 21e8 l32i.n a14, a1, 8 +40000ecf: 0063c0 rsil a12, 3 +40000ed2: 1303e0 wsr.sar a14 +40000ed5: fe1a01 l32r a0, 40000740 <_c_0x80000000+0x8> +40000ed8: fe16d1 l32r a13, 40000730 <_c_bss_end+0x24> +40000edb: 2000d0 or a0, a0, a13 +40000ede: 900d00 addx2 a0, a13, a0 +40000ee1: f01d retw.n +40000ee3: 413600 srli a3, a0, 6 +40000ee6: 234100 sext a4, a1, 7 +40000ee9: fe .byte 0xfe +40000eea: 0448 l32i.n a4, a4, 0 +40000eec: 030c movi.n a3, 0 +40000eee: 2a6422 s32i a2, a4, 168 +40000ef1: 2b6432 s32i a3, a4, 172 +40000ef4: f01d retw.n +40000ef6: 360000 excw +40000ef9: 610041 l32r a4, 3ffd92fc <_start-0x26d04> +40000efc: 1e .byte 0x1e +40000efd: fe .byte 0xfe +40000efe: fe1e81 l32r a8, 40000778 <_c_0x40000000+0x18> +40000f01: 0668 l32i.n a6, a6, 0 +40000f03: fe1e51 l32r a5, 4000077c <_c_0x40000000+0x1c> +40000f06: 2b2672 l32i a7, a6, 172 +40000f09: 2a2642 l32i a4, a6, 168 +40000f0c: 827750 mull a7, a7, a5 +40000f0f: 828480 mull a8, a4, a8 +40000f12: 822450 mull a2, a4, a5 +40000f15: a24450 muluh a4, a4, a5 +40000f18: 521b addi.n a5, a2, 1 +40000f1a: 448a add.n a4, a4, a8 +40000f1c: 447a add.n a4, a4, a7 +40000f1e: 0fb527 bgeu a5, a2, 40000f31 <_X_start+0x785> +40000f21: 2a6652 s32i a5, a6, 168 +40000f24: 241b addi.n a2, a4, 1 +40000f26: fe1631 l32r a3, 40000780 <_c_0x7fffffff> +40000f29: 2b6622 s32i a2, a6, 172 +40000f2c: 102230 and a2, a2, a3 +40000f2f: f01d retw.n +40000f31: 2a6652 s32i a5, a6, 168 +40000f34: fe1321 l32r a2, 40000780 <_c_0x7fffffff> +40000f37: 2b6642 s32i a4, a6, 172 +40000f3a: 102420 and a2, a4, a2 +40000f3d: f01d retw.n +40000f3f: d81000 excw +40000f42: ff .byte 0xff +40000f43: 3f .byte 0x3f +40000f44: 004136 entry a1, 32 +40000f47: fffe41 l32r a4, 40000f40 <_X_start+0x794> +40000f4a: 1439 s32i.n a3, a4, 4 +40000f4c: 0429 s32i.n a2, a4, 0 +40000f4e: f01d retw.n + +40000f50 <_c_0x3fffd820>: +40000f50: ffd820 excw +40000f53: 3f .byte 0x3f + +40000f54 <_X_ets_task>: +40000f54: 004136 entry a1, 32 +40000f57: 160c movi.n a6, 1 +40000f59: 090c movi.n a9, 0 +40000f5b: fffd71 l32r a7, 40000f50 <_c_0x3fffd820> +40000f5e: 1183c0 slli a8, a3, 4 +40000f61: 778a add.n a7, a7, a8 +40000f63: 830b addi.n a8, a3, -1 +40000f65: f0c772 addi a7, a7, -16 +40000f68: 0729 s32i.n a2, a7, 0 +40000f6a: 1749 s32i.n a4, a7, 4 +40000f6c: 084752 s8i a5, a7, 8 +40000f6f: 094792 s8i a9, a7, 9 +40000f72: 0a4792 s8i a9, a7, 10 +40000f75: 0b4792 s8i a9, a7, 11 +40000f78: 401800 ssl a8 +40000f7b: a16600 sll a6, a6 +40000f7e: 3769 s32i.n a6, a7, 12 +40000f80: f01d retw.n + ... + +40000f84 <_XX_unk0f84>: +40000f84: 004136 entry a1, 32 +40000f87: f01d retw.n +40000f89: 000000 ill +40000f8c: 004136 entry a1, 32 +40000f8f: ffff65 call8 40000f84 <_XX_unk0f84> +40000f92: 0008a5 call8 4000101c <_XX_unk0f96+0x84> +40000f95: f01d retw.n + ... + +40000f98 <_XX_unk0f96>: +40000f98: 004136 entry a1, 32 +40000f9b: 0010e5 call8 400010a8 <_X_ets_enter_critical> +40000f9e: ffeca1 l32r a10, 40000f50 <_c_0x3fffd820> +40000fa1: 11b2c0 slli a11, a2, 4 +40000fa4: aaba add.n a10, a10, a11 +40000fa6: ffdac2 addmi a12, a10, 0xffffff00 +40000fa9: f80cd2 l8ui a13, a12, 248 +40000fac: fb0c82 l8ui a8, a12, 251 +40000faf: f0cab2 addi a11, a10, -16 +40000fb2: 4eb8d7 bgeu a8, a13, 40001004 <_XX_unk0f96+0x6c> +40000fb5: 090b82 l8ui a8, a11, 9 +40000fb8: 1b98 l32i.n a9, a11, 4 +40000fba: f81b addi.n a15, a8, 1 +40000fbc: b08890 addx8 a8, a8, a9 +40000fbf: 0839 s32i.n a3, a8, 0 +40000fc1: 1849 s32i.n a4, a8, 4 +40000fc3: 094bf2 s8i a15, a11, 9 +40000fc6: f90ce2 l8ui a14, a12, 249 +40000fc9: ffdaa2 addmi a10, a10, 0xffffff00 +40000fcc: 049de7 bne a13, a14, 40000fd4 <_XX_unk0f96+0x3c> +40000fcf: 0c0c movi.n a12, 0 +40000fd1: 094bc2 s8i a12, a11, 9 +40000fd4: 0b0be2 l8ui a14, a11, 11 +40000fd7: 01cee2 addi a14, a14, 1 +40000fda: 0b4be2 s8i a14, a11, 11 +40000fdd: fb0ad2 l8ui a13, a10, 251 +40000fe0: 091d26 beqi a13, 1, 40000fed <_XX_unk0f96+0x55> +40000fe3: 000da5 call8 400010bc <_X_ets_exit_critical> +40000fe6: 00a022 movi a2, 0 +40000fe9: 000090 retw +40000fec: d48100 extui a8, a0, 1, 14 +40000fef: ff .byte 0xff +40000ff0: 3f2af2 l32i a15, a10, 252 +40000ff3: 022892 l32i a9, a8, 8 +40000ff6: 20ff90 or a15, a15, a9 +40000ff9: 0268f2 s32i a15, a8, 8 +40000ffc: 000be5 call8 400010bc <_X_ets_exit_critical> +40000fff: 00a022 movi a2, 0 +40001002: f01d retw.n +40001004: 000b65 call8 400010bc <_X_ets_exit_critical> +40001007: 120c movi.n a2, 1 +40001009: f01d retw.n +4000100b: 413600 srli a3, a0, 6 +4000100e: f23000 rems a3, a0, a0 +40001011: 022c40 andb b2, b12, b4 +40001014: c02230 sub a2, a2, a3 +40001017: 742020 extui a2, a2, 0, 8 +4000101a: f01d retw.n +4000101c: 004136 entry a1, 32 +4000101f: f77c movi.n a7, -1 +40001021: ffc731 l32r a3, 40000f40 <_X_start+0x794> +40001024: 060c movi.n a6, 0 +40001026: 10c352 addi a5, a3, 16 +40001029: 000406 j 4000103d <_XX_unk0f96+0xa5> +4000102c: 201110 or a1, a1, a1 +4000102f: 0008e5 call8 400010bc <_X_ets_exit_critical> +40001032: ffd282 addmi a8, a2, 0xffffff00 +40001035: 3c2882 l32i a8, a8, 240 +40001038: 04ad mov.n a10, a4 +4000103a: 0008e0 callx8 a8 +4000103d: 0006a5 call8 400010a8 <_X_ets_enter_critical> +40001040: 23a8 l32i.n a10, a3, 8 +40001042: fffca5 call8 4000100c <_XX_unk0f96+0x74> +40001045: 048a16 beqz a10, 40001091 <_XX_unk0f96+0xf9> +40001048: 112ac0 slli a2, a10, 4 +4000104b: 252a add.n a2, a5, a2 +4000104d: f0c2a2 addi a10, a2, -16 +40001050: ffd2b2 addmi a11, a2, 0xffffff00 +40001053: f80b92 l8ui a9, a11, 248 +40001056: 3d2b82 l32i a8, a11, 244 +40001059: 0a0ac2 l8ui a12, a10, 10 +4000105c: fa0b42 l8ui a4, a11, 250 +4000105f: cc1b addi.n a12, a12, 1 +40001061: 0a4ac2 s8i a12, a10, 10 +40001064: fa0bb2 l8ui a11, a11, 250 +40001067: b04480 addx8 a4, a4, a8 +4000106a: 0299b7 bne a9, a11, 40001070 <_XX_unk0f96+0xd8> +4000106d: 0a4a62 s8i a6, a10, 10 +40001070: 0b0ab2 l8ui a11, a10, 11 +40001073: ffd292 addmi a9, a2, 0xffffff00 +40001076: bb0b addi.n a11, a11, -1 +40001078: 0b4ab2 s8i a11, a10, 11 +4000107b: fb0982 l8ui a8, a9, 251 +4000107e: 3f29d2 l32i a13, a9, 252 +40001081: fa7856 bnez a8, 4000102c <_XX_unk0f96+0x94> +40001084: 23c8 l32i.n a12, a3, 8 +40001086: 30dd70 xor a13, a13, a7 +40001089: 10ccd0 and a12, a12, a13 +4000108c: 23c9 s32i.n a12, a3, 8 +4000108e: ffe686 j 4000102c <_XX_unk0f96+0x94> +40001091: 03b8 l32i.n a11, a3, 0 +40001093: 005b16 beqz a11, 4000109c <_XX_unk0f96+0x104> +40001096: 0123a2 l32i a10, a3, 4 +40001099: 000be0 callx8 a11 +4000109c: 000365 call8 400010d4 <_X_ets_exit_critical_and_wait_int> +4000109f: ffe686 j 4000103d <_XX_unk0f96+0xa5> + ... + +400010a4 <_c_ets_critical_level>: +400010a4: ffda20 excw +400010a7: 3f .byte 0x3f + +400010a8 <_X_ets_enter_critical>: +400010a8: 004136 entry a1, 32 +400010ab: fffe31 l32r a3, 400010a4 <_c_ets_critical_level> +400010ae: 006220 rsil a2, 2 +400010b1: 0388 l32i.n a8, a3, 0 +400010b3: 881b addi.n a8, a8, 1 +400010b5: 0389 s32i.n a8, a3, 0 +400010b7: f01d retw.n +400010b9: 000000 ill + +400010bc <_X_ets_exit_critical>: +400010bc: 004136 entry a1, 32 +400010bf: fff931 l32r a3, 400010a4 <_c_ets_critical_level> +400010c2: 0328 l32i.n a2, a3, 0 +400010c4: 220b addi.n a2, a2, -1 +400010c6: 0329 s32i.n a2, a3, 0 +400010c8: 028c beqz.n a2, 400010cc <_X_ets_exit_critical+0x10> +400010ca: f01d retw.n +400010cc: 006080 rsil a8, 0 +400010cf: f01d retw.n +400010d1: 000000 ill + +400010d4 <_X_ets_exit_critical_and_wait_int>: +400010d4: 004136 entry a1, 32 +400010d7: fff331 l32r a3, 400010a4 <_c_ets_critical_level> +400010da: 0328 l32i.n a2, a3, 0 +400010dc: 220b addi.n a2, a2, -1 +400010de: 0329 s32i.n a2, a3, 0 +400010e0: 007000 waiti 0 +400010e3: f01d retw.n +400010e5: 000000 ill + +400010e8 <_X_ets_isr_attach>: +400010e8: 004136 entry a1, 32 +400010eb: 04cd mov.n a12, a4 +400010ed: 03bd mov.n a11, a3 +400010ef: 02ad mov.n a10, a2 +400010f1: 048e65 call8 400059d8 <_X_xtos_set_interrupt_handler_arg> +400010f4: f01d retw.n + ... + +400010f8 <_X_ets_isr_mask>: +400010f8: 004136 entry a1, 32 +400010fb: 02ad mov.n a10, a2 +400010fd: 0495a5 call8 40005a58 <_X_xtos_ints_off> +40001100: f01d retw.n + ... + +40001104 <_X_ets_isr_unmask>: +40001104: 004136 entry a1, 32 +40001107: 02ad mov.n a10, a2 +40001109: 0492a5 call8 40005a34 <_X_xtos_ints_on> +4000110c: f01d retw.n + ... + +40001110 <_c_0x3fffda30>: +40001110: ffda30 excw +40001113: 3f .byte 0x3f + +40001114 <_XX_set_0x3fffda30_0>: +40001114: 004136 entry a1, 32 +40001117: fffe31 l32r a3, 40001110 <_c_0x3fffda30> +4000111a: 0329 s32i.n a2, a3, 0 +4000111c: f01d retw.n + ... + +40001120 <_XX_set_0x3fffda30_4>: +40001120: 004136 entry a1, 32 +40001123: fffb31 l32r a3, 40001110 <_c_0x3fffda30> +40001126: 1329 s32i.n a2, a3, 4 +40001128: f01d retw.n + ... + +4000112c <_c_0xfffdffff>: +4000112c: ff .byte 0xff +4000112d: ff .byte 0xff +4000112e: fffd excw + +40001130 <_c_0x60003e00>: +40001130: 003e00 excw +40001133: 820060 mull a0, a0, a6 + +40001134 <_c_0x60008200>: +40001134: 008200 any4 b0, b0:b1:b2:b3 +40001137: 7e0060 excw + +40001138 <_c_0x60007e00>: +40001138: 007e00 waiti 14 +4000113b: 100060 and a0, a0, a6 + +4000113c <_c_0x1000>: +4000113c: 001000 movsp a0, a0 + ... + +40001140 <_s_fw_build>: +40001140: ca7c movi.n a10, -4 +40001142: ff .byte 0xff +40001143: 3f .byte 0x3f + +40001144 <_s_boot_banner>: +40001144: ffca54 excw +40001147: 3f .byte 0x3f + +40001148 <_s_pct_s_pct_u>: +40001148: ca88 l32i.n a8, a10, 48 +4000114a: ff .byte 0xff +4000114b: 3f .byte 0x3f + +4000114c <_s_ets_main_c>: +4000114c: ffca90 excw +4000114f: 3f .byte 0x3f +40001150: 1d18 l32i.n a1, a13, 4 +40001152: 904000 addx2 a4, a0, a0 +40001155: 001d mov.n a1, a0 +40001157: ca9c40 depbits a4, a12, 12, 10 +4000115a: ff .byte 0xff +4000115b: 3f .byte 0x3f + +4000115c <_X_main>: +4000115c: 004136 entry a1, 32 +4000115f: 0177a5 call8 400028d8 <_X_get_rst_cause> +40001162: fff281 l32r a8, 4000112c <_c_0xfffdffff> +40001165: 00afe2 movi a14, 0xffffff00 +40001168: fff221 l32r a2, 40001130 <_c_0x60003e00> +4000116b: 871c movi.n a7, 24 +4000116d: fff1d1 l32r a13, 40001134 <_c_0x60008200> +40001170: 063c movi.n a6, 48 +40001172: 203aa0 or a3, a10, a10 +40001175: fff0b1 l32r a11, 40001138 <_c_0x60007e00> +40001178: 1aa0a2 movi a10, 26 +4000117b: 0020c0 memw +4000117e: a02bf2 l32i a15, a11, 0x280 +40001181: 10ff80 and a15, a15, a8 +40001184: 0020c0 memw +40001187: a06bf2 s32i a15, a11, 0x280 +4000118a: ffecc1 l32r a12, 4000113c <_c_0x1000> +4000118d: 0020c0 memw +40001190: a02b92 l32i a9, a11, 0x280 +40001193: 2099c0 or a9, a9, a12 +40001196: 0020c0 memw +40001199: a06b92 s32i a9, a11, 0x280 +4000119c: 0020c0 memw +4000119f: 8e2282 l32i a8, a2, 0x238 +400011a2: 174867 ball a8, a6, 400011bd <_X_main+0x61> +400011a5: 0020c0 memw +400011a8: 8e2292 l32i a9, a2, 0x238 +400011ab: 549090 extui a9, a9, 0, 6 +400011ae: 0b1977 beq a9, a7, 400011bd <_X_main+0x61> +400011b1: 0020c0 memw +400011b4: 8e22b2 l32i a11, a2, 0x238 +400011b7: 54b0b0 extui a11, a11, 0, 6 +400011ba: 0e9ba7 bne a11, a10, 400011cc <_X_main+0x70> +400011bd: 0020c0 memw +400011c0: 9d2dc2 l32i a12, a13, 0x274 +400011c3: 10cce0 and a12, a12, a14 +400011c6: 0020c0 memw +400011c9: 9d6dc2 s32i a12, a13, 0x274 +400011cc: 0020c0 memw +400011cf: 8e22f2 l32i a15, a2, 0x238 +400011d2: f03d nop.n +400011d4: 10ff60 and a15, a15, a6 +400011d7: 6bcf66 bnei a15, 32, 40001246 <_X_main+0xea> +400011da: fd6991 l32r a9, 40000780 <_c_0x7fffffff> +400011dd: 0020c0 memw +400011e0: a52dc2 l32i a12, a13, 0x294 +400011e3: 10cc90 and a12, a12, a9 +400011e6: 0020c0 memw +400011e9: a56dc2 s32i a12, a13, 0x294 +400011ec: 0020c0 memw +400011ef: a62db2 l32i a11, a13, 0x298 +400011f2: 10bb90 and a11, a11, a9 +400011f5: 0020c0 memw +400011f8: a66db2 s32i a11, a13, 0x298 +400011fb: 0020c0 memw +400011fe: a72d82 l32i a8, a13, 0x29c +40001201: 108890 and a8, a8, a9 +40001204: 0020c0 memw +40001207: a76d82 s32i a8, a13, 0x29c +4000120a: 0020c0 memw +4000120d: a82df2 l32i a15, a13, 0x2a0 +40001210: 10ff90 and a15, a15, a9 +40001213: 0020c0 memw +40001216: a86df2 s32i a15, a13, 0x2a0 +40001219: 0020c0 memw +4000121c: a92dc2 l32i a12, a13, 0x2a4 +4000121f: 10cc90 and a12, a12, a9 +40001222: 0020c0 memw +40001225: a96dc2 s32i a12, a13, 0x2a4 +40001228: 0020c0 memw +4000122b: aa2db2 l32i a11, a13, 0x2a8 +4000122e: 10bb90 and a11, a11, a9 +40001231: 0020c0 memw +40001234: aa6db2 s32i a11, a13, 0x2a8 +40001237: 0020c0 memw +4000123a: ab2d82 l32i a8, a13, 0x2ac +4000123d: 108890 and a8, a8, a9 +40001240: 0020c0 memw +40001243: ab6d82 s32i a8, a13, 0x2ac +40001246: 950c movi.n a5, 9 +40001248: b40c movi.n a4, 11 +4000124a: 8c2c movi.n a12, 40 +4000124c: eb2c movi.n a11, 46 +4000124e: 0020c0 memw +40001251: 8e22f2 l32i a15, a2, 0x238 +40001254: 020fc7 bnone a15, a12, 4000125a <_l_strap_0x0xxx> +40001257: 006a86 j 40001405 <_l_strap_NxNxxx> + +4000125a <_l_strap_0x0xxx>: +4000125a: 0020c0 memw +4000125d: 9d2d82 l32i a8, a13, 0x274 +40001260: 1088e0 and a8, a8, a14 +40001263: 0020c0 memw +40001266: 9d6d82 s32i a8, a13, 0x274 + +40001269 <_l_strap_init_uart0>: +40001269: 020da5 call8 40003344 <_X_uart_attach> +4000126c: 00a0a2 movi a10, 0 +4000126f: 022c65 call8 40003534 <_X_uart_init> +40001272: 015125 call8 40002784 <_X_ets_install_uart_printf> +40001275: ba2c movi.n a10, 43 +40001277: 0020c0 memw +4000127a: 8e2292 l32i a9, a2, 0x238 +4000127d: 1099a0 and a9, a9, a10 +40001280: fec992 addi a9, a9, -2 +40001283: 25f916 beqz a9, 400014e6 <_l_strap_0x0x10> +40001286: 0020c0 memw +40001289: 8e22b2 l32i a11, a2, 0x238 +4000128c: 54b0b0 extui a11, a11, 0, 6 +4000128f: c0bb40 sub a11, a11, a4 +40001292: 233b16 beqz a11, 400014c9 <_l_strap_0x0x11_loader> +40001295: 0020c0 memw +40001298: 8e22c2 l32i a12, a2, 0x238 +4000129b: 10cca0 and a12, a12, a10 +4000129e: fdccc2 addi a12, a12, -3 +400012a1: 224c16 beqz a12, 400014c9 <_l_strap_0x0x11_loader> +400012a4: 0020c0 memw +400012a7: 8e22d2 l32i a13, a2, 0x238 +400012aa: 340da7 bnone a13, a10, 400012e2 <_l_strap_0x0x00> +400012ad: 0020c0 memw +400012b0: 8e22e2 l32i a14, a2, 0x238 +400012b3: 54e0e0 extui a14, a14, 0, 6 +400012b6: c0ee50 sub a14, a14, a5 +400012b9: 217e16 beqz a14, 400014d4 <_l_strap_0x0x01> +400012bc: 0020c0 memw +400012bf: 8e22f2 l32i a15, a2, 0x238 +400012c2: 10ffa0 and a15, a15, a10 +400012c5: ff0b addi.n a15, a15, -1 +400012c7: 209f16 beqz a15, 400014d4 <_l_strap_0x0x01> +400012ca: 0020c0 memw +400012cd: 8e2282 l32i a8, a2, 0x238 +400012d0: 548080 extui a8, a8, 0, 6 +400012d3: 139866 bnei a8, 10, 400012ea <_l_boot> +400012d6: 04a0a2 movi a10, 4 +400012d9: 03e965 call8 40005170 <_X_sip_init_attach> +400012dc: 000286 j 400012ea <_l_boot> +400012df: 000000 ill + +400012e2 <_l_strap_0x0x00>: +400012e2: 0a0c movi.n a10, 0 +400012e4: 201110 or a1, a1, a1 +400012e7: 03e8a5 call8 40005170 <_X_sip_init_attach> + +400012ea <_l_boot>: +400012ea: 20c330 or a12, a3, a3 +400012ed: ff94b1 l32r a11, 40001140 <_s_fw_build> +400012f0: ff95a1 l32r a10, 40001144 <_s_boot_banner> +400012f3: 0020c0 memw +400012f6: 8e22d2 l32i a13, a2, 0x238 +400012f9: 54d0d0 extui a13, a13, 0, 6 +400012fc: 015065 call8 40002804 +400012ff: 009ea5 call8 40001ce8 <_X_print_mem_banner> +40001302: 0da0a2 movi a10, 13 +40001305: ff8251 l32r a5, 40001110 <_c_0x3fffda30> +40001308: 0233f6 bgeui a3, 3, 4000130e <_l_boot+0x24> +4000130b: 033356 bnez a3, 40001342 <_l_rst_cause_12> +4000130e: 0263f6 bgeui a3, 6, 40001314 <_l_boot+0x2a> +40001311: 2133f6 bgeui a3, 3, 40001336 <_l_rst_cause_345> +40001314: ae0c movi.n a14, 10 +40001316: 023e37 bltu a14, a3, 4000131c <_l_boot+0x32> +40001319: 1973f6 bgeui a3, 7, 40001336 <_l_rst_cause_345> +4000131c: 023a37 bltu a10, a3, 40001322 <_l_boot+0x38> +4000131f: 24b347 bgeu a3, a4, 40001347 <_l_rst_cause_12+0x5> +40001322: 4f1c movi.n a15, 20 +40001324: 0e13f7 beq a3, a15, 40001336 <_l_rst_cause_345> +40001327: ff88a1 l32r a10, 40001148 <_s_pct_s_pct_u> +4000132a: ff88b1 l32r a11, 4000114c <_s_ets_main_c> +4000132d: 31a1c2 movi a12, 0x131 +40001330: 014d25 call8 40002804 +40001333: ffff06 j 40001333 <_l_boot+0x49> + +40001336 <_l_rst_cause_345>: +40001336: 05ad mov.n a10, a5 +40001338: 03bd mov.n a11, a3 +4000133a: 0160e5 call8 40002948 <_XX_unk2948> +4000133d: 0588 l32i.n a8, a5, 0 +4000133f: 11b856 bnez a8, 4000145e <_l_strap_0010xx+0x12> + +40001342 <_l_rst_cause_12>: +40001342: 05ad mov.n a10, a5 +40001344: 001f65 call8 4000153c <_XX_unk153c> +40001347: 071347 beq a3, a4, 40001352 <_l_rst_cause_12+0x10> +4000134a: 04a326 beqi a3, 12, 40001352 <_l_rst_cause_12+0x10> +4000134d: d90c movi.n a9, 13 +4000134f: 4d9397 bne a3, a9, 400013a0 <_l_rst_cause_12+0x5e> +40001352: 1b2c movi.n a11, 33 +40001354: 0020c0 memw +40001357: 8e22a2 l32i a10, a2, 0x238 +4000135a: 02cab7 bnall a10, a11, 40001360 <_l_rst_cause_12+0x1e> +4000135d: 0058c6 j 400014c4 <_l_strap_001000_0x110x+0x14> +40001360: 0020c0 memw +40001363: 8e22c2 l32i a12, a2, 0x238 +40001366: 54c0c0 extui a12, a12, 0, 6 +40001369: c0cc70 sub a12, a12, a7 +4000136c: 154c16 beqz a12, 400014c4 <_l_strap_001000_0x110x+0x14> +4000136f: 0020c0 memw +40001372: 8e22d2 l32i a13, a2, 0x238 +40001375: 54d0d0 extui a13, a13, 0, 6 +40001378: e6cdd2 addi a13, a13, -26 +4000137b: 145d16 beqz a13, 400014c4 <_l_strap_001000_0x110x+0x14> +4000137e: 1b0c movi.n a11, 1 +40001380: 00a032 movi a3, 0 +40001383: 20a330 or a10, a3, a3 +40001386: 0020c0 memw +40001389: 8e22d2 l32i a13, a2, 0x238 +4000138c: 01a0c2 movi a12, 1 +4000138f: 10dd60 and a13, a13, a6 +40001392: e0cdd2 addi a13, a13, -32 +40001395: 83acd0 moveqz a10, a12, a13 +40001398: 74a0a0 extui a10, a10, 0, 8 +4000139b: 02fd65 call8 40004370 <_X_spi_flash_attach> +4000139e: 0539 s32i.n a3, a5, 0 +400013a0: 0020c0 memw +400013a3: 8e22e2 l32i a14, a2, 0x238 +400013a6: 46ee57 bbsi a14, 5, 400013f0 <_l_rst_cause_12+0xae> +400013a9: 0020c0 memw +400013ac: 8e22f2 l32i a15, a2, 0x238 +400013af: 54f0f0 extui a15, a15, 0, 6 +400013b2: 3a1f77 beq a15, a7, 400013f0 <_l_rst_cause_12+0xae> +400013b5: 0020c0 memw +400013b8: 8e2282 l32i a8, a2, 0x238 +400013bb: 1aa092 movi a9, 26 +400013be: 548080 extui a8, a8, 0, 6 +400013c1: 2b1897 beq a8, a9, 400013f0 <_l_rst_cause_12+0xae> +400013c4: 0020c0 memw +400013c7: 8e2292 l32i a9, a2, 0x238 +400013ca: 549090 extui a9, a9, 0, 6 +400013cd: 1a8926 beqi a9, 8, 400013eb <_l_rst_cause_12+0xa9> +400013d0: 0020c0 memw +400013d3: 8e22a2 l32i a10, a2, 0x238 +400013d6: 2ea0b2 movi a11, 46 +400013d9: 10aab0 and a10, a10, a11 +400013dc: 0baa26 beqi a10, 12, 400013eb <_l_rst_cause_12+0xa9> +400013df: 0020c0 memw +400013e2: 8e22c2 l32i a12, a2, 0x238 +400013e5: 28a0d2 movi a13, 40 +400013e8: 728cd7 bany a12, a13, 4000145e <_l_strap_0010xx+0x12> +400013eb: 05e8 l32i.n a14, a5, 0 +400013ed: 06de56 bnez a14, 4000145e <_l_strap_0010xx+0x12> +400013f0: 002f65 call8 400016e8 <_XX_unk153c+0x1ac> +400013f3: 067a16 beqz a10, 4000145e <_l_strap_0010xx+0x12> +400013f6: ff54a1 l32r a10, 40001148 <_s_pct_s_pct_u> +400013f9: ff54b1 l32r a11, 4000114c <_s_ets_main_c> +400013fc: 2ca1c2 movi a12, 0x12c +400013ff: 014065 call8 40002804 +40001402: ffff06 j 40001402 <_l_rst_cause_12+0xc0> + +40001405 <_l_strap_NxNxxx>: +40001405: 0020c0 memw +40001408: 8e22f2 l32i a15, a2, 0x238 +4000140b: f03d nop.n +4000140d: 54f0f0 extui a15, a15, 0, 6 +40001410: f8cff2 addi a15, a15, -8 +40001413: 099f16 beqz a15, 400014b0 <_l_strap_001000_0x110x> +40001416: 0020c0 memw +40001419: 8e2282 l32i a8, a2, 0x238 +4000141c: 1088b0 and a8, a8, a11 +4000141f: f4c882 addi a8, a8, -12 +40001422: 08a816 beqz a8, 400014b0 <_l_strap_001000_0x110x> +40001425: 0020c0 memw +40001428: 8e2292 l32i a9, a2, 0x238 +4000142b: 549090 extui a9, a9, 0, 6 +4000142e: 1a1947 beq a9, a4, 4000144c <_l_strap_0010xx> +40001431: 0020c0 memw +40001434: 8e22b2 l32i a11, a2, 0x238 +40001437: 54b0b0 extui a11, a11, 0, 6 +4000143a: 0e9b26 beqi a11, 10, 4000144c <_l_strap_0010xx> +4000143d: 0020c0 memw +40001440: 8e22c2 l32i a12, a2, 0x238 +40001443: 54c0c0 extui a12, a12, 0, 6 +40001446: c0cc50 sub a12, a12, a5 +40001449: e1cc56 bnez a12, 40001269 <_l_strap_init_uart0> + +4000144c <_l_strap_0010xx>: +4000144c: 0020c0 memw +4000144f: 9d2df2 l32i a15, a13, 0x274 +40001452: 10ffe0 and a15, a15, a14 +40001455: 0020c0 memw +40001458: 9d6df2 s32i a15, a13, 0x274 +4000145b: ff8286 j 40001269 <_l_strap_init_uart0> +4000145e: 09a0a2 movi a10, 9 +40001461: ff3bb1 l32r a11, 40001150 <_s_ets_main_c+0x4> +40001464: ff3765 call8 400007dc <_X_start+0x30> +40001467: ff3b21 l32r a2, 40001154 <_s_ets_main_c+0x8> +4000146a: 0a0c movi.n a10, 0 +4000146c: 02bd mov.n a11, a2 +4000146e: ff36e5 call8 400007dc <_X_start+0x30> +40001471: 2a0c movi.n a10, 2 +40001473: 02bd mov.n a11, a2 +40001475: ff3665 call8 400007dc <_X_start+0x30> +40001478: 3a0c movi.n a10, 3 +4000147a: 02bd mov.n a11, a2 +4000147c: ff35e5 call8 400007dc <_X_start+0x30> +4000147f: ca1c movi.n a10, 28 +40001481: 02bd mov.n a11, a2 +40001483: ff35a5 call8 400007dc <_X_start+0x30> +40001486: da1c movi.n a10, 29 +40001488: 02bd mov.n a11, a2 +4000148a: ff3525 call8 400007dc <_X_start+0x30> +4000148d: 02bd mov.n a11, a2 +4000148f: 08a0a2 movi a10, 8 +40001492: ff34a5 call8 400007dc <_X_start+0x30> +40001495: 05a8 l32i.n a10, a5, 0 +40001497: 1a8c beqz.n a10, 4000149c <_l_strap_0010xx+0x50> +40001499: 000ae0 callx8 a10 +4000149c: ff2fa1 l32r a10, 40001158 <_s_ets_main_c+0xc> +4000149f: 013665 call8 40002804 +400014a2: 0125a2 l32i a10, a5, 4 +400014a5: 033a16 beqz a10, 400014dc <_l_strap_0x0x01+0x8> +400014a8: 000ae0 callx8 a10 +400014ab: 00a022 movi a2, 0 +400014ae: f01d retw.n + +400014b0 <_l_strap_001000_0x110x>: +400014b0: c97c movi.n a9, -4 +400014b2: 0020c0 memw +400014b5: 9d2d82 l32i a8, a13, 0x274 +400014b8: 108890 and a8, a8, a9 +400014bb: 0020c0 memw +400014be: 9d6d82 s32i a8, a13, 0x274 +400014c1: ff6906 j 40001269 <_l_strap_init_uart0> +400014c4: 0b0c movi.n a11, 0 +400014c6: ffad86 j 40001380 <_l_rst_cause_12+0x3e> + +400014c9 <_l_strap_0x0x11_loader>: +400014c9: 3a0c movi.n a10, 3 +400014cb: 03ca65 call8 40005170 <_X_sip_init_attach> +400014ce: ff8606 j 400012ea <_l_boot> +400014d1: 000000 ill + +400014d4 <_l_strap_0x0x01>: +400014d4: 1a0c movi.n a10, 1 +400014d6: 03c9a5 call8 40005170 <_X_sip_init_attach> +400014d9: ff8346 j 400012ea <_l_boot> +400014dc: ffaae5 call8 40000f8c <_XX_unk0f84+0x8> +400014df: 020c movi.n a2, 0 +400014e1: f01d retw.n +400014e3: 000000 ill + +400014e6 <_l_strap_0x0x10>: +400014e6: 2a0c movi.n a10, 2 +400014e8: 03c865 call8 40005170 <_X_sip_init_attach> +400014eb: ff7ec6 j 400012ea <_l_boot> + ... + +400014f0 <_c_0xffff8fff>: +400014f0: ff .byte 0xff +400014f1: 8f .byte 0x8f +400014f2: ff .byte 0xff +400014f3: ff .byte 0xff + +400014f4 <_c_0x60008e00>: +400014f4: 008e00 any4 b0, b12:b13:b14:b15 +400014f7: 413660 srli a3, a6, 6 +400014fa: fdb100 excw +400014fd: ff .byte 0xff +400014fe: fffd91 l32r a9, 400014f4 <_c_0x60008e00> +40001501: 0020c0 memw +40001504: 9a29a2 l32i a10, a9, 0x268 +40001507: 10aab0 and a10, a10, a11 +4000150a: 0020c0 memw +4000150d: 9a69a2 s32i a10, a9, 0x268 +40001510: 0020c0 memw +40001513: 9a2982 l32i a8, a9, 0x268 +40001516: 0020c0 memw +40001519: 9a6982 s32i a8, a9, 0x268 +4000151c: 000106 j 40001524 <_c_0x60008e00+0x30> +4000151f: 000000 ill +40001522: 250070 extui a0, a7, 16, 3 +40001525: 6603c3 excw +40001528: f62a add.n a15, a6, a2 +4000152a: f01d retw.n + +4000152c <_s_waiting_for_host>: +4000152c: caac beqz.n a10, 4000155c <_XX_unk153c+0x20> +4000152e: ff .byte 0xff +4000152f: 3f .byte 0x3f +40001530: ffa000 excw +40001533: 3f .byte 0x3f +40001534: 002000 isync +40001537: 000000 ill +4000153a: 364004 excw + +4000153c <_XX_unk153c>: +4000153c: 004136 entry a1, 32 +4000153f: fefc31 l32r a3, 40001130 <_c_0x60003e00> +40001542: 1d0c movi.n a13, 1 +40001544: 8b1c movi.n a11, 24 +40001546: ac1c movi.n a12, 26 +40001548: 040c movi.n a4, 0 +4000154a: 852c movi.n a5, 40 +4000154c: 04ad mov.n a10, a4 +4000154e: 0020c0 memw +40001551: 8e2392 l32i a9, a3, 0x238 +40001554: 109950 and a9, a9, a5 +40001557: 83ad90 moveqz a10, a13, a9 +4000155a: 0020c0 memw +4000155d: 8e2382 l32i a8, a3, 0x238 +40001560: 048580 extui a8, a8, 5, 1 +40001563: 0ad856 bnez a8, 40001614 <_XX_unk153c+0xd8> +40001566: 0020c0 memw +40001569: 8e23e2 l32i a14, a3, 0x238 +4000156c: 54e0e0 extui a14, a14, 0, 6 +4000156f: c0eeb0 sub a14, a14, a11 +40001572: 09ee16 beqz a14, 40001614 <_XX_unk153c+0xd8> +40001575: 0020c0 memw +40001578: 8e23f2 l32i a15, a3, 0x238 +4000157b: 54f0f0 extui a15, a15, 0, 6 +4000157e: c0ffc0 sub a15, a15, a12 +40001581: 08ff16 beqz a15, 40001614 <_XX_unk153c+0xd8> +40001584: 0020c0 memw +40001587: 8e2382 l32i a8, a3, 0x238 +4000158a: 548080 extui a8, a8, 0, 6 +4000158d: e5c882 addi a8, a8, -27 +40001590: 124816 beqz a8, 400016b8 <_XX_unk153c+0x17c> +40001593: 0020c0 memw +40001596: 8e2392 l32i a9, a3, 0x238 +40001599: ec2c movi.n a12, 46 +4000159b: 549090 extui a9, a9, 0, 6 +4000159e: f8c992 addi a9, a9, -8 +400015a1: 0f5916 beqz a9, 4000169a <_XX_unk153c+0x15e> +400015a4: 0020c0 memw +400015a7: 8e23b2 l32i a11, a3, 0x238 +400015aa: 040c movi.n a4, 0 +400015ac: 10bbc0 and a11, a11, a12 +400015af: f4cbb2 addi a11, a11, -12 +400015b2: 0e4b16 beqz a11, 4000169a <_XX_unk153c+0x15e> +400015b5: 0ba0e2 movi a14, 11 +400015b8: 0020c0 memw +400015bb: 8e23d2 l32i a13, a3, 0x238 +400015be: 54d0d0 extui a13, a13, 0, 6 +400015c1: 1a1de7 beq a13, a14, 400015df <_XX_unk153c+0xa3> +400015c4: 0020c0 memw +400015c7: 8e23e2 l32i a14, a3, 0x238 +400015ca: 09a082 movi a8, 9 +400015cd: 54e0e0 extui a14, a14, 0, 6 +400015d0: 0b9e26 beqi a14, 10, 400015df <_XX_unk153c+0xa3> +400015d3: 0020c0 memw +400015d6: 8e23f2 l32i a15, a3, 0x238 +400015d9: 54f0f0 extui a15, a15, 0, 6 +400015dc: 089f87 bne a15, a8, 400015e8 <_XX_unk153c+0xac> +400015df: ffd3a1 l32r a10, 4000152c <_s_waiting_for_host> +400015e2: 012225 call8 40002804 +400015e5: 01a042 movi a4, 1 +400015e8: 0020c0 memw +400015eb: 8e2382 l32i a8, a3, 0x238 +400015ee: 178857 bany a8, a5, 40001609 <_XX_unk153c+0xcd> +400015f1: 01a0a2 movi a10, 1 +400015f4: 01f3e5 call8 40003534 <_X_uart_init> +400015f7: 0a0c movi.n a10, 0 +400015f9: 020a25 call8 4000369c <_X_uart_wait_tx_empty> +400015fc: 0a2c movi.n a10, 32 +400015fe: ffb065 call8 40001104 <_X_ets_isr_unmask> +40001601: 06b416 beqz a4, 40001670 <_XX_unk153c+0x134> +40001604: 0a2c movi.n a10, 32 +40001606: ffaf25 call8 400010f8 <_X_ets_isr_mask> +40001609: 532426 beqi a4, 2, 40001660 <_XX_unk153c+0x124> +4000160c: ffeea5 call8 400014f8 <_c_0x60008e00+0x4> +4000160f: f01d retw.n +40001611: 000000 ill +40001614: 1a2c movi.n a10, 33 +40001616: 0020c0 memw +40001619: 8e2392 l32i a9, a3, 0x238 +4000161c: 3949a7 ball a9, a10, 40001659 <_XX_unk153c+0x11d> +4000161f: 0020c0 memw +40001622: 8e23e2 l32i a14, a3, 0x238 +40001625: 54e0e0 extui a14, a14, 0, 6 +40001628: 2d1eb7 beq a14, a11, 40001659 <_XX_unk153c+0x11d> +4000162b: 0020c0 memw +4000162e: 8e23f2 l32i a15, a3, 0x238 +40001631: 1b0c movi.n a11, 1 +40001633: 54f0f0 extui a15, a15, 0, 6 +40001636: 1f1fc7 beq a15, a12, 40001659 <_XX_unk153c+0x11d> +40001639: 30a0e2 movi a14, 48 +4000163c: 20a440 or a10, a4, a4 +4000163f: 0020c0 memw +40001642: 8e23c2 l32i a12, a3, 0x238 +40001645: 10cce0 and a12, a12, a14 +40001648: e0ccc2 addi a12, a12, -32 +4000164b: 83adc0 moveqz a10, a13, a12 +4000164e: 74a0a0 extui a10, a10, 0, 8 +40001651: 02d1e5 call8 40004370 <_X_spi_flash_attach> +40001654: 006242 s32i a4, a2, 0 +40001657: f01d retw.n +40001659: 0b0c movi.n a11, 0 +4000165b: fff686 j 40001639 <_XX_unk153c+0xfd> +4000165e: a10000 sll a0, a0 +40001661: b1ffb4 excw +40001664: cdffb4 excw +40001667: 902502 l32i a0, a5, 0x240 +4000166a: f01d01 l32r a0, 3fffd6e0 <_start-0x2920> +4000166d: 000000 ill +40001670: 00a0a2 movi a10, 0 +40001673: 017e65 call8 40002e58 <_XX_unk2e58> +40001676: 7440a0 extui a4, a10, 0, 8 +40001679: f87456 bnez a4, 40001604 <_XX_unk153c+0xc8> +4000167c: 1a0c movi.n a10, 1 +4000167e: 017da5 call8 40002e58 <_XX_unk2e58> +40001681: 7440a0 extui a4, a10, 0, 8 +40001684: f7c456 bnez a4, 40001604 <_XX_unk153c+0xc8> +40001687: 03ad25 call8 40005158 <_XX_unk500c+0x14c> +4000168a: feca82 addi a8, a10, -2 +4000168d: 1f0c movi.n a15, 1 +4000168f: 040c movi.n a4, 0 +40001691: 834f80 moveqz a4, a15, a8 +40001694: fd8416 beqz a4, 40001670 <_XX_unk153c+0x134> +40001697: ffda46 j 40001604 <_XX_unk153c+0xc8> +4000169a: 00a0b2 movi a11, 0 +4000169d: 7440a0 extui a4, a10, 0, 8 +400016a0: 20a440 or a10, a4, a4 +400016a3: 01daa5 call8 4000344c <_c_0x000fffff+0x4> +400016a6: 20baa0 or a11, a10, a10 +400016a9: 04ad mov.n a10, a4 +400016ab: f4b0b0 extui a11, a11, 0, 16 +400016ae: 01e3a5 call8 400034e8 +400016b1: 240c movi.n a4, 2 +400016b3: ffbf86 j 400015b5 <_XX_unk153c+0x79> +400016b6: c10000 mul16u a0, a0, a0 +400016b9: c9ffa0 excw +400016bc: f01d02 l16ui a0, a13, 0x1e0 +400016bf: cae400 depbits a0, a4, 12, 15 +400016c2: ff .byte 0xff +400016c3: 3f .byte 0x3f +400016c4: cb18 l32i.n a1, a11, 48 +400016c6: ff .byte 0xff +400016c7: 3f .byte 0x3f +400016c8: ffcb24 excw +400016cb: 3f .byte 0x3f +400016cc: ffcb04 excw +400016cf: 3f .byte 0x3f +400016d0: ffc880 excw +400016d3: 3f .byte 0x3f +400016d4: ffcb50 excw +400016d7: 3f .byte 0x3f +400016d8: ffcac0 excw +400016db: 3f .byte 0x3f +400016dc: ffcad0 excw +400016df: 3f .byte 0x3f +400016e0: ffcb34 excw +400016e3: 3f .byte 0x3f +400016e4: ffcb44 excw +400016e7: 3f .byte 0x3f +400016e8: 00e136 entry a1, 112 +400016eb: 1a2c movi.n a10, 33 +400016ed: fe9081 l32r a8, 40001130 <_c_0x60003e00> +400016f0: 0020c0 memw +400016f3: 8e2892 l32i a9, a8, 0x238 +400016f6: 4249a7 ball a9, a10, 4000173c <_XX_unk153c+0x200> +400016f9: 8c1c movi.n a12, 24 +400016fb: 0020c0 memw +400016fe: 8e28b2 l32i a11, a8, 0x238 +40001701: 54b0b0 extui a11, a11, 0, 6 +40001704: 341bc7 beq a11, a12, 4000173c <_XX_unk153c+0x200> +40001707: 1aa0d2 movi a13, 26 +4000170a: 0020c0 memw +4000170d: 8e28c2 l32i a12, a8, 0x238 +40001710: 54c0c0 extui a12, a12, 0, 6 +40001713: 251cd7 beq a12, a13, 4000173c <_XX_unk153c+0x200> +40001716: 01a022 movi a2, 1 +40001719: 00a0a2 movi a10, 0 +4000171c: 20b110 or a11, a1, a1 +4000171f: 10a0c2 movi a12, 16 +40001722: 031765 call8 40004898 +40001725: facc bnez.n a10, 40001738 <_XX_unk153c+0x1fc> +40001727: 010162 l8ui a6, a1, 1 +4000172a: 030132 l8ui a3, a1, 3 +4000172d: 11c8 l32i.n a12, a1, 4 +4000172f: 0001d2 l8ui a13, a1, 0 +40001732: e9a0e2 movi a14, 233 +40001735: 081de7 beq a13, a14, 40001741 <_XX_unk153c+0x205> +40001738: 120c movi.n a2, 1 +4000173a: f01d retw.n +4000173c: 020c movi.n a2, 0 +4000173e: fff5c6 j 40001719 <_XX_unk153c+0x1dd> +40001741: 20b220 or a11, a2, a2 +40001744: 0201a2 l8ui a10, a1, 2 +40001747: 0e61c2 s32i a12, a1, 56 +4000174a: 02dee5 call8 40004538 <_X_SPIReadModeConfig> +4000174d: 348030 extui a8, a3, 0, 4 +40001750: 0fa032 movi a3, 15 +40001753: 0738f6 bgeui a8, 3, 4000175e <_XX_unk153c+0x222> +40001756: a82b addi.n a10, a8, 2 +40001758: 74a0a0 extui a10, a10, 0, 8 +4000175b: 000046 j 40001760 <_XX_unk153c+0x224> +4000175e: 1a0c movi.n a10, 1 +40001760: 02f165 call8 40004678 <_X_SPIReadModeConfig+0x140> +40001763: 0c0182 l8ui a8, a1, 12 +40001766: 0e0192 l8ui a9, a1, 14 +40001769: 0f0152 l8ui a5, a1, 15 +4000176c: 0b0122 l8ui a2, a1, 11 +4000176f: 115580 slli a5, a5, 8 +40001772: 112280 slli a2, a2, 8 +40001775: 205590 or a5, a5, a9 +40001778: 0d0192 l8ui a9, a1, 13 +4000177b: 115580 slli a5, a5, 8 +4000177e: 205590 or a5, a5, a9 +40001781: 115580 slli a5, a5, 8 +40001784: 0a0192 l8ui a9, a1, 10 +40001787: 205580 or a5, a5, a8 +4000178a: 080182 l8ui a8, a1, 8 +4000178d: 202290 or a2, a2, a9 +40001790: 090192 l8ui a9, a1, 9 +40001793: 112280 slli a2, a2, 8 +40001796: 202290 or a2, a2, a9 +40001799: 112280 slli a2, a2, 8 +4000179c: 202280 or a2, a2, a8 +4000179f: 3c1296 bltz a2, 40001b64 <_XX_unk153c+0x628> +400017a2: 031c movi.n a3, 16 +400017a4: 20a330 or a10, a3, a3 +400017a7: 20b110 or a11, a1, a1 +400017aa: 10a0c2 movi a12, 16 +400017ad: 030ea5 call8 40004898 +400017b0: f84a56 bnez a10, 40001738 <_XX_unk153c+0x1fc> +400017b3: 10c332 addi a3, a3, 16 +400017b6: 278616 beqz a6, 40001a32 <_XX_unk153c+0x4f6> +400017b9: f119 s32i.n a1, a1, 60 +400017bb: efa042 movi a4, 239 +400017be: 017d mov.n a7, a1 +400017c0: c159 s32i.n a5, a1, 48 +400017c2: 6169 s32i.n a6, a1, 24 +400017c4: 02bd mov.n a11, a2 +400017c6: 0d0c movi.n a13, 0 +400017c8: d1d9 s32i.n a13, a1, 52 +400017ca: 0b2d mov.n a2, a11 +400017cc: ffbda1 l32r a10, 400016c0 <_XX_unk153c+0x184> +400017cf: c1c8 l32i.n a12, a1, 48 +400017d1: d188 l32i.n a8, a1, 52 +400017d3: 051c movi.n a5, 16 +400017d5: c05580 sub a5, a5, a8 +400017d8: 745050 extui a5, a5, 0, 8 +400017db: 05dd mov.n a13, a5 +400017dd: 010265 call8 40002804 +400017e0: 1a0c movi.n a10, 1 +400017e2: c198 l32i.n a9, a1, 48 +400017e4: a1a9 s32i.n a10, a1, 40 +400017e6: 03b957 bgeu a9, a5, 400017ed <_XX_unk153c+0x2b1> +400017e9: 0b0c movi.n a11, 0 +400017eb: a1b9 s32i.n a11, a1, 40 +400017ed: 0f0c movi.n a15, 0 +400017ef: d1e8 l32i.n a14, a1, 52 +400017f1: a1c8 l32i.n a12, a1, 40 +400017f3: 81c9 s32i.n a12, a1, 32 +400017f5: 93efc0 movnez a14, a15, a12 +400017f8: 91e9 s32i.n a14, a1, 36 +400017fa: 3b2c16 beqz a12, 40001bb0 <_XX_unk153c+0x674> +400017fd: 380c movi.n a8, 3 +400017ff: 028857 bany a8, a5, 40001805 <_XX_unk153c+0x2c9> +40001802: 008d86 j 40001a3c <_XX_unk153c+0x500> +40001805: 0215e6 bgei a5, 1, 4000180b <_XX_unk153c+0x2cf> +40001808: 002206 j 40001894 <_XX_unk153c+0x358> +4000180b: 249050 extui a9, a5, 0, 3 +4000180e: 139976 loopnez a9, 40001825 <_XX_unk153c+0x2e9> +40001811: f188 l32i.n a8, a1, 60 +40001813: 000892 l8ui a9, a8, 0 +40001816: 004292 s8i a9, a2, 0 +40001819: 881b addi.n a8, a8, 1 +4000181b: f189 s32i.n a8, a1, 60 +4000181d: 221b addi.n a2, a2, 1 +4000181f: 304940 xor a4, a9, a4 +40001822: 744040 extui a4, a4, 0, 8 +40001825: f188 l32i.n a8, a1, 60 +40001827: 419350 srli a9, a5, 3 +4000182a: 639976 loopnez a9, 40001891 <_XX_unk153c+0x355> +4000182d: 000892 l8ui a9, a8, 0 +40001830: 004292 s8i a9, a2, 0 +40001833: 309940 xor a9, a9, a4 +40001836: 749090 extui a9, a9, 0, 8 +40001839: 010842 l8ui a4, a8, 1 +4000183c: 014242 s8i a4, a2, 1 +4000183f: 309490 xor a9, a4, a9 +40001842: 749090 extui a9, a9, 0, 8 +40001845: 020842 l8ui a4, a8, 2 +40001848: 024242 s8i a4, a2, 2 +4000184b: 309490 xor a9, a4, a9 +4000184e: 749090 extui a9, a9, 0, 8 +40001851: 030842 l8ui a4, a8, 3 +40001854: 034242 s8i a4, a2, 3 +40001857: 309490 xor a9, a4, a9 +4000185a: 749090 extui a9, a9, 0, 8 +4000185d: 040842 l8ui a4, a8, 4 +40001860: 044242 s8i a4, a2, 4 +40001863: 309490 xor a9, a4, a9 +40001866: 749090 extui a9, a9, 0, 8 +40001869: 050842 l8ui a4, a8, 5 +4000186c: 054242 s8i a4, a2, 5 +4000186f: 309490 xor a9, a4, a9 +40001872: 749090 extui a9, a9, 0, 8 +40001875: 060842 l8ui a4, a8, 6 +40001878: 064242 s8i a4, a2, 6 +4000187b: 309490 xor a9, a4, a9 +4000187e: 749090 extui a9, a9, 0, 8 +40001881: 070842 l8ui a4, a8, 7 +40001884: 074242 s8i a4, a2, 7 +40001887: 309490 xor a9, a4, a9 +4000188a: 888b addi.n a8, a8, 8 +4000188c: 228b addi.n a2, a2, 8 +4000188e: 744090 extui a4, a9, 0, 8 +40001891: 0f6182 s32i a8, a1, 60 +40001894: c168 l32i.n a6, a1, 48 +40001896: a1a8 l32i.n a10, a1, 40 +40001898: c06650 sub a6, a6, a5 +4000189b: 416460 srli a6, a6, 4 +4000189e: 35ea16 beqz a10, 40001c00 <_XX_unk153c+0x6c4> +400018a1: d198 l32i.n a9, a1, 52 +400018a3: c188 l32i.n a8, a1, 48 +400018a5: 889a add.n a8, a8, a9 +400018a7: f0c882 addi a8, a8, -16 +400018aa: 348080 extui a8, a8, 0, 4 +400018ad: 076182 s32i a8, a1, 28 +400018b0: 91a8 l32i.n a10, a1, 36 +400018b2: 74a0a0 extui a10, a10, 0, 8 +400018b5: d1a9 s32i.n a10, a1, 52 +400018b7: 3e16a6 blti a6, 1, 400018f9 <_XX_unk153c+0x3bd> +400018ba: 050c movi.n a5, 0 +400018bc: 20a330 or a10, a3, a3 +400018bf: 20b110 or a11, a1, a1 +400018c2: 10a0c2 movi a12, 16 +400018c5: 02fd25 call8 40004898 +400018c8: 1fca56 bnez a10, 40001ac8 <_XX_unk153c+0x58c> +400018cb: 080c movi.n a8, 0 +400018cd: 10c332 addi a3, a3, 16 +400018d0: 091c movi.n a9, 16 +400018d2: 10a976 loopgtz a9, 400018e6 <_XX_unk153c+0x3aa> +400018d5: 987a add.n a9, a8, a7 +400018d7: 01c882 addi a8, a8, 1 +400018da: 000992 l8ui a9, a9, 0 +400018dd: 748080 extui a8, a8, 0, 8 +400018e0: 309940 xor a9, a9, a4 +400018e3: 744090 extui a4, a9, 0, 8 +400018e6: 20a220 or a10, a2, a2 +400018e9: 20b110 or a11, a1, a1 +400018ec: 0c1c movi.n a12, 16 +400018ee: 050865 call8 40006974 +400018f1: 10c222 addi a2, a2, 16 +400018f4: 551b addi.n a5, a5, 1 +400018f6: c29567 bne a5, a6, 400018bc <_XX_unk153c+0x380> +400018f9: 0821b2 l32i a11, a1, 32 +400018fc: 011b16 beqz a11, 40001911 <_XX_unk153c+0x3d5> +400018ff: 20a330 or a10, a3, a3 +40001902: 01bd mov.n a11, a1 +40001904: 0c1c movi.n a12, 16 +40001906: 02f925 call8 40004898 +40001909: 3afa56 bnez a10, 40001cbc <_XX_unk153c+0x780> +4000190c: 10c332 addi a3, a3, 16 +4000190f: f119 s32i.n a1, a1, 60 +40001911: 072152 l32i a5, a1, 28 +40001914: ff6ca1 l32r a10, 400016c4 <_XX_unk153c+0x188> +40001917: 745050 extui a5, a5, 0, 8 +4000191a: 20b550 or a11, a5, a5 +4000191d: 00ee65 call8 40002804 +40001920: 03a082 movi a8, 3 +40001923: 028857 bany a8, a5, 40001929 <_XX_unk153c+0x3ed> +40001926: 006b86 j 40001ad8 <_XX_unk153c+0x59c> +40001929: 0215e6 bgei a5, 1, 4000192f <_XX_unk153c+0x3f3> +4000192c: 002206 j 400019b8 <_XX_unk153c+0x47c> +4000192f: 249050 extui a9, a5, 0, 3 +40001932: 139976 loopnez a9, 40001949 <_XX_unk153c+0x40d> +40001935: f188 l32i.n a8, a1, 60 +40001937: 000892 l8ui a9, a8, 0 +4000193a: 004292 s8i a9, a2, 0 +4000193d: 881b addi.n a8, a8, 1 +4000193f: f189 s32i.n a8, a1, 60 +40001941: 221b addi.n a2, a2, 1 +40001943: 304940 xor a4, a9, a4 +40001946: 744040 extui a4, a4, 0, 8 +40001949: f188 l32i.n a8, a1, 60 +4000194b: 419350 srli a9, a5, 3 +4000194e: 639976 loopnez a9, 400019b5 <_XX_unk153c+0x479> +40001951: 000892 l8ui a9, a8, 0 +40001954: 004292 s8i a9, a2, 0 +40001957: 309940 xor a9, a9, a4 +4000195a: 749090 extui a9, a9, 0, 8 +4000195d: 010842 l8ui a4, a8, 1 +40001960: 014242 s8i a4, a2, 1 +40001963: 309490 xor a9, a4, a9 +40001966: 749090 extui a9, a9, 0, 8 +40001969: 020842 l8ui a4, a8, 2 +4000196c: 024242 s8i a4, a2, 2 +4000196f: 309490 xor a9, a4, a9 +40001972: 749090 extui a9, a9, 0, 8 +40001975: 030842 l8ui a4, a8, 3 +40001978: 034242 s8i a4, a2, 3 +4000197b: 309490 xor a9, a4, a9 +4000197e: 749090 extui a9, a9, 0, 8 +40001981: 040842 l8ui a4, a8, 4 +40001984: 044242 s8i a4, a2, 4 +40001987: 309490 xor a9, a4, a9 +4000198a: 749090 extui a9, a9, 0, 8 +4000198d: 050842 l8ui a4, a8, 5 +40001990: 054242 s8i a4, a2, 5 +40001993: 309490 xor a9, a4, a9 +40001996: 749090 extui a9, a9, 0, 8 +40001999: 060842 l8ui a4, a8, 6 +4000199c: 064242 s8i a4, a2, 6 +4000199f: 309490 xor a9, a4, a9 +400019a2: 749090 extui a9, a9, 0, 8 +400019a5: 070842 l8ui a4, a8, 7 +400019a8: 074242 s8i a4, a2, 7 +400019ab: 309490 xor a9, a4, a9 +400019ae: 888b addi.n a8, a8, 8 +400019b0: 228b addi.n a2, a2, 8 +400019b2: 744090 extui a4, a9, 0, 8 +400019b5: 0f6182 s32i a8, a1, 60 +400019b8: ff44a1 l32r a10, 400016c8 <_XX_unk153c+0x18c> +400019bb: 04bd mov.n a11, a4 +400019bd: 00e465 call8 40002804 +400019c0: 61a8 l32i.n a10, a1, 24 +400019c2: aa0b addi.n a10, a10, -1 +400019c4: 2d5a16 beqz a10, 40001c9d <_XX_unk153c+0x761> +400019c7: d128 l32i.n a2, a1, 52 +400019c9: 061c movi.n a6, 16 +400019cb: 252a add.n a2, a5, a2 +400019cd: c06620 sub a6, a6, a2 +400019d0: 746060 extui a6, a6, 0, 8 +400019d3: 0286f6 bgeui a6, 8, 400019d9 <_XX_unk153c+0x49d> +400019d6: 0078c6 j 40001bbd <_XX_unk153c+0x681> +400019d9: f158 l32i.n a5, a1, 60 +400019db: a28b addi.n a10, a2, 8 +400019dd: 74a0a0 extui a10, a10, 0, 8 +400019e0: d1a9 s32i.n a10, a1, 52 +400019e2: 040562 l8ui a6, a5, 4 +400019e5: 0005c2 l8ui a12, a5, 0 +400019e8: 050582 l8ui a8, a5, 5 +400019eb: 060592 l8ui a9, a5, 6 +400019ee: 0105d2 l8ui a13, a5, 1 +400019f1: 0305b2 l8ui a11, a5, 3 +400019f4: 0205e2 l8ui a14, a5, 2 +400019f7: 11bb80 slli a11, a11, 8 +400019fa: 20bbe0 or a11, a11, a14 +400019fd: 11bb80 slli a11, a11, 8 +40001a00: 20bbd0 or a11, a11, a13 +40001a03: 11bb80 slli a11, a11, 8 +40001a06: 070552 l8ui a5, a5, 7 +40001a09: 20bbc0 or a11, a11, a12 +40001a0c: 115580 slli a5, a5, 8 +40001a0f: 205590 or a5, a5, a9 +40001a12: 115580 slli a5, a5, 8 +40001a15: 205580 or a5, a5, a8 +40001a18: 115580 slli a5, a5, 8 +40001a1b: 205560 or a5, a5, a6 +40001a1e: c159 s32i.n a5, a1, 48 +40001a20: 6188 l32i.n a8, a1, 24 +40001a22: d198 l32i.n a9, a1, 52 +40001a24: a80b addi.n a10, a8, -1 +40001a26: 997a add.n a9, a9, a7 +40001a28: f199 s32i.n a9, a1, 60 +40001a2a: 7480a0 extui a8, a10, 0, 8 +40001a2d: 6189 s32i.n a8, a1, 24 +40001a2f: d97856 bnez a8, 400017ca <_XX_unk153c+0x28e> +40001a32: e1a8 l32i.n a10, a1, 56 +40001a34: ff6de5 call8 40001114 <_XX_set_0x3fffda30_0> +40001a37: 020c movi.n a2, 0 +40001a39: f01d retw.n +40001a3b: 451600 extui a1, a0, 22, 5 +40001a3e: 02ade5 call8 4000451c +40001a41: 41d250 srli a13, a5, 2 +40001a44: 7a1da6 blti a13, 1, 40001ac2 <_XX_unk153c+0x586> +40001a47: 276527 bbci a5, 2, 40001a72 <_XX_unk153c+0x536> +40001a4a: f198 l32i.n a9, a1, 60 +40001a4c: a24b addi.n a10, a2, 4 +40001a4e: 0009c2 l8ui a12, a9, 0 +40001a51: 0988 l32i.n a8, a9, 0 +40001a53: 0289 s32i.n a8, a2, 0 +40001a55: 75b080 extui a11, a8, 16, 8 +40001a58: 74e880 extui a14, a8, 8, 8 +40001a5b: 756880 extui a6, a8, 24, 8 +40001a5e: 994b addi.n a9, a9, 4 +40001a60: 0f6192 s32i a9, a1, 60 +40001a63: 306640 xor a6, a6, a4 +40001a66: 304ce0 xor a4, a12, a14 +40001a69: 304b40 xor a4, a11, a4 +40001a6c: 304460 xor a4, a4, a6 +40001a6f: 744040 extui a4, a4, 0, 8 +40001a72: 0afd mov.n a15, a10 +40001a74: f188 l32i.n a8, a1, 60 +40001a76: 4121d0 srli a2, a13, 1 +40001a79: 419276 loopnez a2, 40001abe <_XX_unk153c+0x582> +40001a7c: 030892 l8ui a9, a8, 3 +40001a7f: 0108b2 l8ui a11, a8, 1 +40001a82: 0008c2 l8ui a12, a8, 0 +40001a85: 0208d2 l8ui a13, a8, 2 +40001a88: 08e8 l32i.n a14, a8, 0 +40001a8a: 309940 xor a9, a9, a4 +40001a8d: 30bcb0 xor a11, a12, a11 +40001a90: 0fe9 s32i.n a14, a15, 0 +40001a92: 30bdb0 xor a11, a13, a11 +40001a95: 0708c2 l8ui a12, a8, 7 +40001a98: 0508d2 l8ui a13, a8, 5 +40001a9b: 0408e2 l8ui a14, a8, 4 +40001a9e: 060842 l8ui a4, a8, 6 +40001aa1: 1828 l32i.n a2, a8, 4 +40001aa3: 309b90 xor a9, a11, a9 +40001aa6: 30bed0 xor a11, a14, a13 +40001aa9: 1f29 s32i.n a2, a15, 4 +40001aab: 888b addi.n a8, a8, 8 +40001aad: 749090 extui a9, a9, 0, 8 +40001ab0: 30b4b0 xor a11, a4, a11 +40001ab3: ff8b addi.n a15, a15, 8 +40001ab5: 309c90 xor a9, a12, a9 +40001ab8: 309b90 xor a9, a11, a9 +40001abb: 744090 extui a4, a9, 0, 8 +40001abe: f189 s32i.n a8, a1, 60 +40001ac0: 0fad mov.n a10, a15 +40001ac2: 0a2d mov.n a2, a10 +40001ac4: ff7306 j 40001894 <_XX_unk153c+0x358> +40001ac7: 01a100 slli a10, a1, 32 +40001aca: ff .byte 0xff +40001acb: ff01b1 l32r a11, 400016d0 <_XX_unk153c+0x194> +40001ace: 00d365 call8 40002804 +40001ad1: 120c movi.n a2, 1 +40001ad3: f01d retw.n +40001ad5: 000000 ill +40001ad8: edc516 beqz a5, 400019b8 <_XX_unk153c+0x47c> +40001adb: 02ad mov.n a10, a2 +40001add: 41d250 srli a13, a5, 2 +40001ae0: 021de6 bgei a13, 1, 40001ae6 <_XX_unk153c+0x5aa> +40001ae3: ffb446 j 400019b8 <_XX_unk153c+0x47c> +40001ae6: 286527 bbci a5, 2, 40001b12 <_XX_unk153c+0x5d6> +40001ae9: f198 l32i.n a9, a1, 60 +40001aeb: a24b addi.n a10, a2, 4 +40001aed: 0009c2 l8ui a12, a9, 0 +40001af0: 0988 l32i.n a8, a9, 0 +40001af2: 0289 s32i.n a8, a2, 0 +40001af4: 75b080 extui a11, a8, 16, 8 +40001af7: 74e880 extui a14, a8, 8, 8 +40001afa: 756880 extui a6, a8, 24, 8 +40001afd: 04c992 addi a9, a9, 4 +40001b00: 0f6192 s32i a9, a1, 60 +40001b03: 306640 xor a6, a6, a4 +40001b06: 304ce0 xor a4, a12, a14 +40001b09: 304b40 xor a4, a11, a4 +40001b0c: 304460 xor a4, a4, a6 +40001b0f: 744040 extui a4, a4, 0, 8 +40001b12: 0afd mov.n a15, a10 +40001b14: f188 l32i.n a8, a1, 60 +40001b16: 4121d0 srli a2, a13, 1 +40001b19: 419276 loopnez a2, 40001b5e <_XX_unk153c+0x622> +40001b1c: 030892 l8ui a9, a8, 3 +40001b1f: 0108b2 l8ui a11, a8, 1 +40001b22: 0008c2 l8ui a12, a8, 0 +40001b25: 0208d2 l8ui a13, a8, 2 +40001b28: 08e8 l32i.n a14, a8, 0 +40001b2a: 309940 xor a9, a9, a4 +40001b2d: 30bcb0 xor a11, a12, a11 +40001b30: 0fe9 s32i.n a14, a15, 0 +40001b32: 30bdb0 xor a11, a13, a11 +40001b35: 0708c2 l8ui a12, a8, 7 +40001b38: 0508d2 l8ui a13, a8, 5 +40001b3b: 0408e2 l8ui a14, a8, 4 +40001b3e: 060842 l8ui a4, a8, 6 +40001b41: 1828 l32i.n a2, a8, 4 +40001b43: 309b90 xor a9, a11, a9 +40001b46: 30bed0 xor a11, a14, a13 +40001b49: 1f29 s32i.n a2, a15, 4 +40001b4b: 888b addi.n a8, a8, 8 +40001b4d: 749090 extui a9, a9, 0, 8 +40001b50: 30b4b0 xor a11, a4, a11 +40001b53: ff8b addi.n a15, a15, 8 +40001b55: 309c90 xor a9, a12, a9 +40001b58: 309b90 xor a9, a11, a9 +40001b5b: 744090 extui a4, a9, 0, 8 +40001b5e: f189 s32i.n a8, a1, 60 +40001b60: ff9506 j 400019b8 <_XX_unk153c+0x47c> +40001b63: 402000 ssa8l a0 +40001b66: c58275 call12 3ffc738c <_start-0x38c74> +40001b69: 87f8 l32i.n a15, a7, 32 +40001b6b: 060203 excw +40001b6e: 500032 l8ui a3, a0, 80 +40001b71: 16f474 excw +40001b74: 0c0917 bnone a9, a1, 40001b84 <_XX_unk153c+0x648> +40001b77: 031c05 call0 40004d38 +40001b7a: 918b addi.n a9, a1, 8 +40001b7c: b199 s32i.n a9, a1, 44 +40001b7e: 03ad mov.n a10, a3 +40001b80: 01bd mov.n a11, a1 +40001b82: 0c1c movi.n a12, 16 +40001b84: 02d125 call8 40004898 +40001b87: ea7a56 bnez a10, 40001a32 <_XX_unk153c+0x4f6> +40001b8a: 10c332 addi a3, a3, 16 +40001b8d: 449c beqz.n a4, 40001ba5 <_XX_unk153c+0x669> +40001b8f: 01ad mov.n a10, a1 +40001b91: 0247e5 call8 40004010 <_XX_unk4010> +40001b94: 440b addi.n a4, a4, -1 +40001b96: f44040 extui a4, a4, 0, 16 +40001b99: 848c beqz.n a4, 40001ba5 <_XX_unk153c+0x669> +40001b9b: b1a8 l32i.n a10, a1, 44 +40001b9d: 024725 call8 40004010 <_XX_unk4010> +40001ba0: 440b addi.n a4, a4, -1 +40001ba2: f44040 extui a4, a4, 0, 16 +40001ba5: 551b addi.n a5, a5, 1 +40001ba7: f45050 extui a5, a5, 0, 16 +40001baa: d09757 bne a7, a5, 40001b7e <_XX_unk153c+0x642> +40001bad: 001646 j 40001c0a <_XX_unk153c+0x6ce> +40001bb0: 7480e0 extui a8, a14, 0, 8 +40001bb3: c198 l32i.n a9, a1, 48 +40001bb5: 7199 s32i.n a9, a1, 28 +40001bb7: d189 s32i.n a8, a1, 52 +40001bb9: ff4f06 j 400018f9 <_XX_unk153c+0x3bd> +40001bbc: 05cd00 extui a12, a0, 29, 1 +40001bbf: fec5a1 l32r a10, 400016d4 <_XX_unk153c+0x198> +40001bc2: d1b8 l32i.n a11, a1, 52 +40001bc4: 06dd mov.n a13, a6 +40001bc6: 00c3e5 call8 40002804 +40001bc9: b27a add.n a11, a2, a7 +40001bcb: 10c1a2 addi a10, a1, 16 +40001bce: 06cd mov.n a12, a6 +40001bd0: 04da25 call8 40006974 +40001bd3: 03ad mov.n a10, a3 +40001bd5: 01bd mov.n a11, a1 +40001bd7: 0c1c movi.n a12, 16 +40001bd9: 02cbe5 call8 40004898 +40001bdc: 0eca56 bnez a10, 40001ccc <_XX_unk153c+0x790> +40001bdf: 10c332 addi a3, a3, 16 +40001be2: 01bd mov.n a11, a1 +40001be4: 8c0c movi.n a12, 8 +40001be6: 10c1a2 addi a10, a1, 16 +40001be9: a6aa add.n a10, a6, a10 +40001beb: c0cc60 sub a12, a12, a6 +40001bee: 74c0c0 extui a12, a12, 0, 8 +40001bf1: d1c9 s32i.n a12, a1, 52 +40001bf3: 04d825 call8 40006974 +40001bf6: 41b8 l32i.n a11, a1, 16 +40001bf8: 51c8 l32i.n a12, a1, 20 +40001bfa: c1c9 s32i.n a12, a1, 48 +40001bfc: ff8806 j 40001a20 <_XX_unk153c+0x4e4> +40001bff: c1d800 mul16u a13, a8, a0 +40001c02: 71d9 s32i.n a13, a1, 28 +40001c04: ff2a06 j 400018b0 <_XX_unk153c+0x374> +40001c07: 031c00 excw +40001c0a: 03ad mov.n a10, a3 +40001c0c: 01bd mov.n a11, a1 +40001c0e: 0c1c movi.n a12, 16 +40001c10: 02c865 call8 40004898 +40001c13: b21a56 bnez a10, 40001738 <_XX_unk153c+0x1fc> +40001c16: 10c332 addi a3, a3, 16 +40001c19: 448c beqz.n a4, 40001c21 <_XX_unk153c+0x6e5> +40001c1b: 20a110 or a10, a1, a1 +40001c1e: 023f25 call8 40004010 <_XX_unk4010> +40001c21: 744020 extui a4, a2, 0, 8 +40001c24: 04ad mov.n a10, a4 +40001c26: 0299a5 call8 400045c0 <_X_SPIReadModeConfig+0x88> +40001c29: 348820 extui a8, a2, 8, 4 +40001c2c: 1838f6 bgeui a8, 3, 40001c48 <_XX_unk153c+0x70c> +40001c2f: 282b addi.n a2, a8, 2 +40001c31: 742020 extui a2, a2, 0, 8 +40001c34: 000486 j 40001c4a <_XX_unk153c+0x70e> +40001c37: a10000 sll a0, a0 +40001c3a: 50fea7 bbsi a14, 26, 40001c8e <_XX_unk153c+0x752> +40001c3d: 6520b5 call12 40066e48 <__bss_start+0x56e48> +40001c40: 00bc beqz.n a0, 40001c74 <_XX_unk153c+0x738> +40001c42: 01a022 movi a2, 1 +40001c45: 000090 retw +40001c48: 120c movi.n a2, 1 +40001c4a: 20c440 or a12, a4, a4 +40001c4d: fea3a1 l32r a10, 400016dc <_XX_unk153c+0x1a0> +40001c50: 20b220 or a11, a2, a2 +40001c53: 00bb25 call8 40002804 +40001c56: 20a220 or a10, a2, a2 +40001c59: 02a1e5 call8 40004678 <_X_SPIReadModeConfig+0x140> +40001c5c: 0e0192 l8ui a9, a1, 14 +40001c5f: 080152 l8ui a5, a1, 8 +40001c62: 0b0122 l8ui a2, a1, 11 +40001c65: 0a0182 l8ui a8, a1, 10 +40001c68: 112280 slli a2, a2, 8 +40001c6b: 202280 or a2, a2, a8 +40001c6e: 090182 l8ui a8, a1, 9 +40001c71: 112280 slli a2, a2, 8 +40001c74: 202280 or a2, a2, a8 +40001c77: 112280 slli a2, a2, 8 +40001c7a: 202250 or a2, a2, a5 +40001c7d: 0f0152 l8ui a5, a1, 15 +40001c80: 0c0182 l8ui a8, a1, 12 +40001c83: 115580 slli a5, a5, 8 +40001c86: 205590 or a5, a5, a9 +40001c89: 0d0192 l8ui a9, a1, 13 +40001c8c: 115580 slli a5, a5, 8 +40001c8f: 205590 or a5, a5, a9 +40001c92: 115580 slli a5, a5, 8 +40001c95: 205580 or a5, a5, a8 +40001c98: fec206 j 400017a4 <_XX_unk153c+0x268> +40001c9b: a10000 sll a0, a0 +40001c9e: 40fe90 nsau a9, a14 +40001ca1: 2520b4 excw +40001ca4: 8200b6 bltui a0, 0x8000, 40001c2a <_XX_unk153c+0x6ee> +40001ca7: 400f01 l32r a0, 3ffd1ce4 <_start-0x2e31c> +40001caa: c088 l32i.n a8, a0, 48 +40001cac: d82816 beqz a8, 40001a32 <_XX_unk153c+0x4f6> +40001caf: fe8da1 l32r a10, 400016e4 <_XX_unk153c+0x1a8> +40001cb2: 00b525 call8 40002804 +40001cb5: 01a022 movi a2, 1 +40001cb8: f01d retw.n +40001cba: a10000 sll a0, a0 +40001cbd: b2fe84 excw +40001cc0: 25efa2 s32c1i a10, a15, 148 +40001cc3: 0c00b4 excw +40001cc6: f01d12 l16ui a1, a13, 0x1e0 +40001cc9: 000000 ill +40001ccc: fe80a1 l32r a10, 400016cc <_XX_unk153c+0x190> +40001ccf: 2ca3b2 movi a11, 0x32c +40001cd2: 00b325 call8 40002804 +40001cd5: ff5646 j 40001a32 <_XX_unk153c+0x4f6> + +40001cd8 <_c_data_end>: +40001cd8: ffc864 excw +40001cdb: 3f .byte 0x3f + +40001cdc <_s_mem_banner>: +40001cdc: cb68 l32i.n a6, a11, 48 +40001cde: ff .byte 0xff +40001cdf: 3f .byte 0x3f + +40001ce0 <_c_stack_sentry>: +40001ce0: ffe1d0 excw +40001ce3: 3f .byte 0x3f + +40001ce4 <_c_data_start>: +40001ce4: ffc000 excw +40001ce7: 3f .byte 0x3f + +40001ce8 <_X_print_mem_banner>: +40001ce8: 006136 entry a1, 48 +40001ceb: fffca1 l32r a10, 40001cdc <_s_mem_banner> +40001cee: fffcb1 l32r a11, 40001ce0 <_c_stack_sentry> +40001cf1: fa83c1 l32r a12, 40000700 <_c_stack> +40001cf4: fa85d1 l32r a13, 40000708 <_c_bss_start> +40001cf7: fa85e1 l32r a14, 4000070c <_c_bss_end> +40001cfa: fffaf1 l32r a15, 40001ce4 <_c_data_start> +40001cfd: fff681 l32r a8, 40001cd8 <_c_data_end> +40001d00: 0189 s32i.n a8, a1, 0 +40001d02: 00b025 call8 40002804 +40001d05: f01d retw.n +40001d07: da4000 depbits a0, a0, 13, 5 +40001d0a: ff .byte 0xff +40001d0b: 3f .byte 0x3f + +40001d0c <_s_exc_sp_fmt>: +40001d0c: ffcbe4 excw +40001d0f: 3f .byte 0x3f + +40001d10 <_s_exc_sf_dump_fmt>: +40001d10: cbec bnez.n a11, 40001d40 <_X_exc_handler+0x28> +40001d12: ff .byte 0xff +40001d13: 3f .byte 0x3f + +40001d14 <_s_exc_regs_fmt>: +40001d14: cc2c movi.n a12, 44 +40001d16: ff .byte 0xff +40001d17: 3f .byte 0x3f + +40001d18 <_X_exc_handler>: +40001d18: 004136 entry a1, 32 +40001d1b: 012d mov.n a2, a1 +40001d1d: 03e765 call8 40005b94 <_XX_xtos_exc_unk5b94> +40001d20: 030c movi.n a3, 0 +40001d22: f8add2 movi a13, 0xfffffdf8 +40001d25: fff841 l32r a4, 40001d08 <_X_print_mem_banner+0x20> +40001d28: 580c movi.n a8, 5 +40001d2a: 209440 or a9, a4, a4 +40001d2d: 1ea876 loopgtz a8, 40001d4f <_X_exc_handler+0x37> +40001d30: c2da add.n a12, a2, a13 +40001d32: 7e2cb2 l32i a11, a12, 0x1f8 +40001d35: 09b9 s32i.n a11, a9, 0 +40001d37: 7f2ca2 l32i a10, a12, 0x1fc +40001d3a: 19a9 s32i.n a10, a9, 4 +40001d3c: 802ce2 l32i a14, a12, 0x200 +40001d3f: 29e9 s32i.n a14, a9, 8 +40001d41: 812cc2 l32i a12, a12, 0x204 +40001d44: 39c9 s32i.n a12, a9, 12 +40001d46: 5b8c beqz.n a11, 40001d4f <_X_exc_handler+0x37> +40001d48: 10c992 addi a9, a9, 16 +40001d4b: 0a2d mov.n a2, a10 +40001d4d: 331b addi.n a3, a3, 1 +40001d4f: ffefa1 l32r a10, 40001d0c <_s_exc_sp_fmt> +40001d52: 02bd mov.n a11, a2 +40001d54: 00aae5 call8 40002804 +40001d57: 020c movi.n a2, 0 +40001d59: 1713a6 blti a3, 1, 40001d74 <_X_exc_handler+0x5c> +40001d5c: ffeda1 l32r a10, 40001d10 <_s_exc_sf_dump_fmt> +40001d5f: 02bd mov.n a11, a2 +40001d61: 04c8 l32i.n a12, a4, 0 +40001d63: 14d8 l32i.n a13, a4, 4 +40001d65: 24e8 l32i.n a14, a4, 8 +40001d67: 34f8 l32i.n a15, a4, 12 +40001d69: 00a9a5 call8 40002804 +40001d6c: 10c442 addi a4, a4, 16 +40001d6f: 221b addi.n a2, a2, 1 +40001d71: e79237 bne a2, a3, 40001d5c <_X_exc_handler+0x44> +40001d74: ffe8a1 l32r a10, 40001d14 <_s_exc_regs_fmt> +40001d77: 03b1b0 rsr.epc1 a11 +40001d7a: 03b2c0 rsr.epc2 a12 +40001d7d: 03b3d0 rsr.epc3 a13 +40001d80: 03eee0 rsr.excvaddr a14 +40001d83: 03c0f0 rsr.depc a15 +40001d86: 00a7e5 call8 40002804 +40001d89: ffff06 j 40001d89 <_X_exc_handler+0x71> +40001d8c: ffcc74 excw +40001d8f: 3f .byte 0x3f + +40001d90 <_XX_unk1d90>: +40001d90: 004136 entry a1, 32 +40001d93: 03e8b0 rsr.exccause a11 +40001d96: fffda1 l32r a10, 40001d8c <_X_exc_handler+0x74> +40001d99: 00a6a5 call8 40002804 +40001d9c: ffdea1 l32r a10, 40001d14 <_s_exc_regs_fmt> +40001d9f: 03b1b0 rsr.epc1 a11 +40001da2: 03b2c0 rsr.epc2 a12 +40001da5: 03b3d0 rsr.epc3 a13 +40001da8: 03eee0 rsr.excvaddr a14 +40001dab: 03c0f0 rsr.depc a15 +40001dae: 00a565 call8 40002804 +40001db1: ffff06 j 40001db1 <_XX_unk1d90+0x21> + +40001db4 <_X_ets_memset>: +40001db4: 004136 entry a1, 32 +40001db7: 04cd mov.n a12, a4 +40001db9: 03bd mov.n a11, a3 +40001dbb: 02ad mov.n a10, a2 +40001dbd: 04e265 call8 40006be4 +40001dc0: 0a2d mov.n a2, a10 +40001dc2: f01d retw.n + +40001dc4 <_X_ets_memcpy>: +40001dc4: 004136 entry a1, 32 +40001dc7: 04cd mov.n a12, a4 +40001dc9: 03bd mov.n a11, a3 +40001dcb: 02ad mov.n a10, a2 +40001dcd: 04ba65 call8 40006974 +40001dd0: 0a2d mov.n a2, a10 +40001dd2: f01d retw.n + +40001dd4 <_X_ets_memmove>: +40001dd4: 004136 entry a1, 32 +40001dd7: 04cd mov.n a12, a4 +40001dd9: 03bd mov.n a11, a3 +40001ddb: 02ad mov.n a10, a2 +40001ddd: 04c8e5 call8 40006a6c +40001de0: 0a2d mov.n a2, a10 +40001de2: f01d retw.n + +40001de4 <_X_ets_memcmp>: +40001de4: 004136 entry a1, 32 +40001de7: 04cd mov.n a12, a4 +40001de9: 03bd mov.n a11, a3 +40001deb: 02ad mov.n a10, a2 +40001ded: 04afe5 call8 400068ec +40001df0: 0a2d mov.n a2, a10 +40001df2: f01d retw.n +40001df4: ffda90 excw +40001df7: 3f .byte 0x3f +40001df8: 004136 entry a1, 32 +40001dfb: 000292 l8ui a9, a2, 0 +40001dfe: fffd81 l32r a8, 40001df4 <_X_ets_memcmp+0x10> +40001e01: 010262 l8ui a6, a2, 1 +40001e04: 0858 l32i.n a5, a8, 0 +40001e06: 116680 slli a6, a6, 8 +40001e09: 206690 or a6, a6, a9 +40001e0c: 665a add.n a6, a6, a5 +40001e0e: 071267 beq a2, a6, 40001e19 <_X_ets_memcmp+0x35> +40001e11: 040692 l8ui a9, a6, 4 +40001e14: 18e8 l32i.n a14, a8, 4 +40001e16: 04e916 beqz a9, 40001e68 <_X_ets_memcmp+0x84> +40001e19: 030272 l8ui a7, a2, 3 +40001e1c: 020292 l8ui a9, a2, 2 +40001e1f: 117780 slli a7, a7, 8 +40001e22: 207790 or a7, a7, a9 +40001e25: 675a add.n a6, a7, a5 +40001e27: 391267 beq a2, a6, 40001e64 <_X_ets_memcmp+0x80> +40001e2a: 040692 l8ui a9, a6, 4 +40001e2d: 28a8 l32i.n a10, a8, 8 +40001e2f: 19fc bnez.n a9, 40001e64 <_X_ets_memcmp+0x80> +40001e31: 41b870 srli a11, a7, 8 +40001e34: 0102d2 l8ui a13, a2, 1 +40001e37: 0002e2 l8ui a14, a2, 0 +40001e3a: 11dd80 slli a13, a13, 8 +40001e3d: 20dde0 or a13, a13, a14 +40001e40: 0046d2 s8i a13, a6, 0 +40001e43: 41d8d0 srli a13, a13, 8 +40001e46: 0146d2 s8i a13, a6, 1 +40001e49: 0102c2 l8ui a12, a2, 1 +40001e4c: 0002d2 l8ui a13, a2, 0 +40001e4f: 11cc80 slli a12, a12, 8 +40001e52: 20ccd0 or a12, a12, a13 +40001e55: cc5a add.n a12, a12, a5 +40001e57: 024c72 s8i a7, a12, 2 +40001e5a: 034cb2 s8i a11, a12, 3 +40001e5d: 019a27 bne a10, a2, 40001e62 <_X_ets_memcmp+0x7e> +40001e60: 2869 s32i.n a6, a8, 8 +40001e62: f01d retw.n +40001e64: f01d retw.n +40001e66: 670000 excw +40001e69: 1e .byte 0x1e +40001e6a: 50ad excw +40001e6c: a2c032 addi a3, a0, -94 +40001e6f: 920006 j 3ffe6673 <_start-0x1998d> +40001e72: f20106 j 3fffe67a <_start-0x1986> +40001e75: 0228 l32i.n a2, a2, 0 +40001e77: 119980 slli a9, a9, 8 +40001e7a: 029f67 bne a15, a6, 40001e80 <_X_ets_memcmp+0x9c> +40001e7d: 026822 s32i a2, a8, 8 +40001e80: 2099a0 or a9, a9, a10 +40001e83: 004292 s8i a9, a2, 0 +40001e86: 419890 srli a9, a9, 8 +40001e89: 014292 s8i a9, a2, 1 +40001e8c: 010642 l8ui a4, a6, 1 +40001e8f: 000692 l8ui a9, a6, 0 +40001e92: 114480 slli a4, a4, 8 +40001e95: 204490 or a4, a4, a9 +40001e98: 445a add.n a4, a4, a5 +40001e9a: 024432 s8i a3, a4, 2 +40001e9d: 413830 srli a3, a3, 8 +40001ea0: 034432 s8i a3, a4, 3 +40001ea3: ffdc86 j 40001e19 <_X_ets_memcmp+0x35> +40001ea6: 360000 excw +40001ea9: 1c0041 l32r a4, 3ffc8eac <_start-0x37154> +40001eac: 051c03 excw +40001eaf: 180c movi.n a8, 1 +40001eb1: fca291 l32r a9, 4000113c <_c_0x1000> +40001eb4: 0d0c movi.n a13, 0 +40001eb6: 0c1c movi.n a12, 16 +40001eb8: cb7c movi.n a11, -4 +40001eba: a23b addi.n a10, a2, 3 +40001ebc: 10aab0 and a10, a10, a11 +40001ebf: 014ac2 s8i a12, a10, 1 +40001ec2: 024ad2 s8i a13, a10, 2 +40001ec5: 004a92 s8i a9, a10, 0 +40001ec8: 034ad2 s8i a13, a10, 3 +40001ecb: 044ad2 s8i a13, a10, 4 +40001ece: ffc9b1 l32r a11, 40001df4 <_X_ets_memcmp+0x10> +40001ed1: 10da42 addmi a4, a10, 0x1000 +40001ed4: 0ba9 s32i.n a10, a11, 0 +40001ed6: 2ba9 s32i.n a10, a11, 8 +40001ed8: 004492 s8i a9, a4, 0 +40001edb: 1b49 s32i.n a4, a11, 4 +40001edd: 024492 s8i a9, a4, 2 +40001ee0: 044482 s8i a8, a4, 4 +40001ee3: 014452 s8i a5, a4, 1 +40001ee6: 034432 s8i a3, a4, 3 +40001ee9: f01d retw.n +40001eeb: 413600 srli a3, a0, 6 +40001eee: b28c00 mulsh a8, a12, a0 +40001ef1: ffc0a1 l32r a10, 40001df4 <_X_ets_memcmp+0x10> +40001ef4: 0a88 l32i.n a8, a10, 0 +40001ef6: 1a98 l32i.n a9, a10, 4 +40001ef8: 023287 bltu a2, a8, 40001efe <_X_ets_memcmp+0x11a> +40001efb: 013297 bltu a2, a9, 40001f00 <_X_ets_memcmp+0x11c> +40001efe: f01d retw.n +40001f00: 0c0c movi.n a12, 0 +40001f02: 2ad8 l32i.n a13, a10, 8 +40001f04: f8c2b2 addi a11, a2, -8 +40001f07: 63ddb0 minu a13, a13, a11 +40001f0a: 044bc2 s8i a12, a11, 4 +40001f0d: 2ad9 s32i.n a13, a10, 8 +40001f0f: 0bad mov.n a10, a11 +40001f11: ffee65 call8 40001df8 <_X_ets_memcmp+0x14> +40001f14: f01d retw.n +40001f16: fc0000 excw +40001f19: ff .byte 0xff +40001f1a: 360000 excw +40001f1d: c10041 l32r a4, 3fff2320 <_start-0xdce0> +40001f20: 51fc87 bbsi a12, 24, 40001f75 <_X_ets_memcmp+0x191> +40001f23: fffd excw +40001f25: 433b addi.n a4, a3, 3 +40001f27: 104450 and a4, a4, a5 +40001f2a: c50c movi.n a5, 12 +40001f2c: 734450 maxu a4, a4, a5 +40001f2f: f43040 extui a3, a4, 0, 16 +40001f32: 02bc37 bgeu a12, a3, 40001f38 <_X_ets_memcmp+0x154> +40001f35: 002246 j 40001fc2 <_X_ets_memcmp+0x1de> +40001f38: ffaf91 l32r a9, 40001df4 <_X_ets_memcmp+0x10> +40001f3b: 0968 l32i.n a6, a9, 0 +40001f3d: 1988 l32i.n a8, a9, 4 +40001f3f: 7d3267 bltu a2, a6, 40001fc0 <_X_ets_memcmp+0x1dc> +40001f42: 7ab287 bgeu a2, a8, 40001fc0 <_X_ets_memcmp+0x1dc> +40001f45: f8c2b2 addi a11, a2, -8 +40001f48: 010b82 l8ui a8, a11, 1 +40001f4b: 000ba2 l8ui a10, a11, 0 +40001f4e: 118880 slli a8, a8, 8 +40001f51: 2088a0 or a8, a8, a10 +40001f54: c0a260 sub a10, a2, a6 +40001f57: f8caa2 addi a10, a10, -8 +40001f5a: c078a0 sub a7, a8, a10 +40001f5d: f8c772 addi a7, a7, -8 +40001f60: f47070 extui a7, a7, 0, 16 +40001f63: 5b3737 bltu a7, a3, 40001fc2 <_X_ets_memcmp+0x1de> +40001f66: 561737 beq a7, a3, 40001fc0 <_X_ets_memcmp+0x1dc> +40001f69: d86a add.n a13, a8, a6 +40001f6b: 040df2 l8ui a15, a13, 4 +40001f6e: 0e0c movi.n a14, 0 +40001f70: 052f16 beqz a15, 40001fc6 <_X_ets_memcmp+0x1e2> +40001f73: 14c342 addi a4, a3, 20 +40001f76: 463747 bltu a7, a4, 40001fc0 <_X_ets_memcmp+0x1dc> +40001f79: 2958 l32i.n a5, a9, 8 +40001f7b: 41f880 srli a15, a8, 8 +40001f7e: 4148a0 srli a4, a10, 8 +40001f81: 3a3a add.n a3, a10, a3 +40001f83: 338b addi.n a3, a3, 8 +40001f85: f4d030 extui a13, a3, 0, 16 +40001f88: d6da add.n a13, a6, a13 +40001f8a: 034d42 s8i a4, a13, 3 +40001f8d: 024da2 s8i a10, a13, 2 +40001f90: 014df2 s8i a15, a13, 1 +40001f93: 044de2 s8i a14, a13, 4 +40001f96: 004d82 s8i a8, a13, 0 +40001f99: 6355d0 minu a5, a5, a13 +40001f9c: 2959 s32i.n a5, a9, 8 +40001f9e: 418830 srli a8, a3, 8 +40001fa1: 004b32 s8i a3, a11, 0 +40001fa4: 014b82 s8i a8, a11, 1 +40001fa7: 010d72 l8ui a7, a13, 1 +40001faa: 000dd2 l8ui a13, a13, 0 +40001fad: 117780 slli a7, a7, 8 +40001fb0: 2077d0 or a7, a7, a13 +40001fb3: 0917c7 beq a7, a12, 40001fc0 <_X_ets_memcmp+0x1dc> +40001fb6: 476a add.n a4, a7, a6 +40001fb8: 024432 s8i a3, a4, 2 +40001fbb: 034482 s8i a8, a4, 3 +40001fbe: f01d retw.n +40001fc0: f01d retw.n +40001fc2: 020c movi.n a2, 0 +40001fc4: f01d retw.n +40001fc6: 2958 l32i.n a5, a9, 8 +40001fc8: 4148a0 srli a4, a10, 8 +40001fcb: 000db2 l8ui a11, a13, 0 +40001fce: 010d82 l8ui a8, a13, 1 +40001fd1: 3a3a add.n a3, a10, a3 +40001fd3: 338b addi.n a3, a3, 8 +40001fd5: f47030 extui a7, a3, 0, 16 +40001fd8: 118880 slli a8, a8, 8 +40001fdb: 2088b0 or a8, a8, a11 +40001fde: 807670 add a7, a6, a7 +40001fe1: 0247a2 s8i a10, a7, 2 +40001fe4: 0447e2 s8i a14, a7, 4 +40001fe7: 034742 s8i a4, a7, 3 +40001fea: 41f880 srli a15, a8, 8 +40001fed: 004782 s8i a8, a7, 0 +40001ff0: 0147f2 s8i a15, a7, 1 +40001ff3: 418830 srli a8, a3, 8 +40001ff6: 0295d7 bne a5, a13, 40001ffc <_X_ets_memcmp+0x218> +40001ff9: 026972 s32i a7, a9, 8 +40001ffc: f8c2d2 addi a13, a2, -8 +40001fff: 014d82 s8i a8, a13, 1 +40002002: 004d32 s8i a3, a13, 0 +40002005: 0107b2 l8ui a11, a7, 1 +40002008: 000792 l8ui a9, a7, 0 +4000200b: 117b80 slli a7, a11, 8 +4000200e: 207790 or a7, a7, a9 +40002011: ab17c7 beq a7, a12, 40001fc0 <_X_ets_memcmp+0x1dc> +40002014: 976a add.n a9, a7, a6 +40002016: 024932 s8i a3, a9, 2 +40002019: 034982 s8i a8, a9, 3 +4000201c: f01d retw.n +4000201e: 360000 excw +40002021: 160041 l32r a4, 3ffc7824 <_start-0x387dc> +40002024: b105d2 l8ui a13, a5, 177 +40002027: ffbc beqz.n a15, 4000206a <_X_ets_memcmp+0x286> +40002029: a23b addi.n a10, a2, 3 +4000202b: 10aab0 and a10, a10, a11 +4000202e: cb0c movi.n a11, 12 +40002030: 73aab0 maxu a10, a10, a11 +40002033: fc42b1 l32r a11, 4000113c <_c_0x1000> +40002036: f4a0a0 extui a10, a10, 0, 16 +40002039: 473ba7 bltu a11, a10, 40002084 <_X_ets_memcmp+0x2a0> +4000203c: ff6ed1 l32r a13, 40001df4 <_X_ets_memcmp+0x10> +4000203f: 002d62 l32i a6, a13, 0 +40002042: 022d92 l32i a9, a13, 8 +40002045: c07ba0 sub a7, a11, a10 +40002048: c02960 sub a2, a9, a6 +4000204b: f42020 extui a2, a2, 0, 16 +4000204e: 32a277 bge a2, a7, 40002084 <_X_ets_memcmp+0x2a0> +40002051: 188076 loop a0, 4000206d <_X_ets_memcmp+0x289> +40002054: 862a add.n a8, a6, a2 +40002056: 040832 l8ui a3, a8, 4 +40002059: 010852 l8ui a5, a8, 1 +4000205c: 0008c2 l8ui a12, a8, 0 +4000205f: 115580 slli a5, a5, 8 +40002062: 2055c0 or a5, a5, a12 +40002065: 007316 beqz a3, 40002070 <_X_ets_memcmp+0x28c> +40002068: 052d mov.n a2, a5 +4000206a: 16a577 bge a5, a7, 40002084 <_X_ets_memcmp+0x2a0> +4000206d: fff806 j 40002051 <_X_ets_memcmp+0x26d> +40002070: c0c520 sub a12, a5, a2 +40002073: f8ccc2 addi a12, a12, -8 +40002076: ee3ca7 bltu a12, a10, 40002068 <_X_ets_memcmp+0x284> +40002079: 1e0c movi.n a14, 1 +4000207b: 14caf2 addi a15, a10, 20 +4000207e: 4f3cf7 bltu a12, a15, 400020d1 <_X_ets_memcmp+0x2ed> +40002081: 0000c6 j 40002088 <_X_ets_memcmp+0x2a4> +40002084: 020c movi.n a2, 0 +40002086: f01d retw.n +40002088: 41f820 srli a15, a2, 8 +4000208b: 41c850 srli a12, a5, 8 +4000208e: 040c movi.n a4, 0 +40002090: 72aa add.n a7, a2, a10 +40002092: 778b addi.n a7, a7, 8 +40002094: f43070 extui a3, a7, 0, 16 +40002097: 363a add.n a3, a6, a3 +40002099: 044342 s8i a4, a3, 4 +4000209c: 0143c2 s8i a12, a3, 1 +4000209f: 024322 s8i a2, a3, 2 +400020a2: 004352 s8i a5, a3, 0 +400020a5: 0343f2 s8i a15, a3, 3 +400020a8: 415870 srli a5, a7, 8 +400020ab: 0448e2 s8i a14, a8, 4 +400020ae: 004872 s8i a7, a8, 0 +400020b1: 014852 s8i a5, a8, 1 +400020b4: 010322 l8ui a2, a3, 1 +400020b7: 000332 l8ui a3, a3, 0 +400020ba: 112280 slli a2, a2, 8 +400020bd: 202230 or a2, a2, a3 +400020c0: 1012b7 beq a2, a11, 400020d4 <_X_ets_memcmp+0x2f0> +400020c3: a62a add.n a10, a6, a2 +400020c5: 024a72 s8i a7, a10, 2 +400020c8: 034a52 s8i a5, a10, 3 +400020cb: 000146 j 400020d4 <_X_ets_memcmp+0x2f0> +400020ce: 000000 ill +400020d1: 0448e2 s8i a14, a8, 4 +400020d4: 219987 bne a9, a8, 400020f9 <_X_ets_memcmp+0x315> +400020d7: 0409b2 l8ui a11, a9, 4 +400020da: bb9c beqz.n a11, 400020f9 <_X_ets_memcmp+0x315> +400020dc: 1d28 l32i.n a2, a13, 4 +400020de: 171297 beq a2, a9, 400020f9 <_X_ets_memcmp+0x315> +400020e1: 0109b2 l8ui a11, a9, 1 +400020e4: 0009a2 l8ui a10, a9, 0 +400020e7: 119b80 slli a9, a11, 8 +400020ea: 2099a0 or a9, a9, a10 +400020ed: 969a add.n a9, a6, a9 +400020ef: 0409c2 l8ui a12, a9, 4 +400020f2: 2d99 s32i.n a9, a13, 8 +400020f4: 1c8c beqz.n a12, 400020f9 <_X_ets_memcmp+0x315> +400020f6: e79297 bne a2, a9, 400020e1 <_X_ets_memcmp+0x2fd> +400020f9: 288b addi.n a2, a8, 8 +400020fb: f01d retw.n +400020fd: 000000 ill +40002100: 004136 entry a1, 32 +40002103: c13230 mul16u a3, a2, a3 +40002106: f4a030 extui a10, a3, 0, 16 +40002109: fff165 call8 40002020 <_X_ets_memcmp+0x23c> +4000210c: 202aa0 or a2, a10, a10 +4000210f: 007a16 beqz a10, 4000211a <_X_ets_memcmp+0x336> +40002112: 20c330 or a12, a3, a3 +40002115: 0b0c movi.n a11, 0 +40002117: ffc9e5 call8 40001db4 <_X_ets_memset> +4000211a: f01d retw.n +4000211c: 004136 entry a1, 32 +4000211f: 02bd mov.n a11, a2 +40002121: 1a0c movi.n a10, 1 +40002123: fffde5 call8 40002100 <_X_ets_memcmp+0x31c> +40002126: 0a2d mov.n a2, a10 +40002128: f01d retw.n +4000212a: 360000 excw +4000212d: 4d0041 l32r a4, 3ffd5530 <_start-0x2aad0> +40002130: 03ad02 movi a0, 0xfffffd03 +40002133: ffeee5 call8 40002020 <_X_ets_memcmp+0x23c> +40002136: 0a2d mov.n a2, a10 +40002138: 2acc bnez.n a10, 4000213e <_X_ets_memcmp+0x35a> +4000213a: f01d retw.n +4000213c: 300000 xor a0, a0, a0 +4000213f: 4020c3 excw +40002142: e520b4 excw +40002145: 40ffc7 bbsi a15, 28, 40002189 <_st_0x3fffda9c+0x39> +40002148: 2520a4 excw +4000214b: ffda add.n a15, a15, a13 +4000214d: f01d retw.n + ... + +40002150 <_st_0x3fffda9c>: +40002150: da9c beqz.n a10, 40002171 <_st_0x3fffda9c+0x21> +40002152: ff .byte 0xff +40002153: 3f .byte 0x3f +40002154: 00fff2 s32ri a15, a15, 0 +40002157: 413600 srli a3, a0, 6 +4000215a: fd4100 excw +4000215d: ff .byte 0xff +4000215e: 1258 l32i.n a5, a2, 4 +40002160: 0429 s32i.n a2, a4, 0 +40002162: 55eb addi.n a5, a5, 14 +40002164: 1459 s32i.n a5, a4, 4 +40002166: fffb51 l32r a5, 40002154 <_st_0x3fffda9c+0x4> +40002169: 0020c0 memw +4000216c: 0238 l32i.n a3, a2, 0 +4000216e: b43030 extui a3, a3, 0, 12 +40002171: 335a add.n a3, a3, a5 +40002173: 045432 s16i a3, a4, 8 +40002176: f01d retw.n +40002178: cc8c beqz.n a12, 40002188 <_st_0x3fffda9c+0x38> +4000217a: ff .byte 0xff +4000217b: 3f .byte 0x3f +4000217c: 004136 entry a1, 32 +4000217f: fff4b1 l32r a11, 40002150 <_st_0x3fffda9c> +40002182: 041b92 l16ui a9, a11, 8 +40002185: 1b88 l32i.n a8, a11, 4 +40002187: d98c beqz.n a9, 40002198 <_st_0x3fffda9c+0x48> +40002189: 004822 s8i a2, a8, 0 +4000218c: a90b addi.n a10, a9, -1 +4000218e: 045ba2 s16i a10, a11, 8 +40002191: 881b addi.n a8, a8, 1 +40002193: 1b89 s32i.n a8, a11, 4 +40002195: f01d retw.n +40002197: f8a100 excw +4000219a: ff .byte 0xff +4000219b: 0066a5 call8 40002804 +4000219e: f01d retw.n +400021a0: 217c movi.n a1, -14 +400021a2: 364000 excw +400021a5: bd00a1 l32r a10, 3fff15a8 <_start-0xea58> +400021a8: 412902 l32i a0, a9, 0x104 +400021ab: 5139 s32i.n a3, a1, 20 +400021ad: 6149 s32i.n a4, a1, 24 +400021af: 7159 s32i.n a5, a1, 28 +400021b1: 8169 s32i.n a6, a1, 32 +400021b3: 9179 s32i.n a7, a1, 36 +400021b5: fffaa1 l32r a10, 400021a0 <_st_0x3fffda9c+0x50> +400021b8: 30c1c2 addi a12, a1, 48 +400021bb: 4e0c movi.n a14, 4 +400021bd: 10c1d2 addi a13, a1, 16 +400021c0: 11d9 s32i.n a13, a1, 4 +400021c2: 21e9 s32i.n a14, a1, 8 +400021c4: 01c9 s32i.n a12, a1, 0 +400021c6: 0015e5 call8 40002324 <_X_ets_unk225c+0xc8> +400021c9: 0a2d mov.n a2, a10 +400021cb: f01d retw.n +400021cd: 000000 ill +400021d0: 004136 entry a1, 32 +400021d3: ffdfa1 l32r a10, 40002150 <_st_0x3fffda9c> +400021d6: 002ab2 l32i a11, a10, 0 +400021d9: 012bd2 l32i a13, a11, 4 +400021dc: 0ccd92 addi a9, a13, 12 +400021df: 0020c0 memw +400021e2: f0a0f2 movi a15, 240 +400021e5: 0be8 l32i.n a14, a11, 0 +400021e7: cd2b addi.n a12, a13, 2 +400021e9: b480e0 extui a8, a14, 0, 12 +400021ec: 34e8e0 extui a14, a14, 8, 4 +400021ef: 004c82 s8i a8, a12, 0 +400021f2: 014ce2 s8i a14, a12, 1 +400021f5: 8c0c movi.n a12, 8 +400021f7: 000de2 l8ui a14, a13, 0 +400021fa: 014dc2 s8i a12, a13, 1 +400021fd: 10eef0 and a14, a14, a15 +40002200: 004de2 s8i a14, a13, 0 +40002203: 041aa2 l16ui a10, a10, 8 +40002206: 0020c0 memw +40002209: 0b88 l32i.n a8, a11, 0 +4000220b: b48080 extui a8, a8, 0, 12 +4000220e: c088a0 sub a8, a8, a10 +40002211: 0bad mov.n a10, a11 +40002213: f2c882 addi a8, a8, -14 +40002216: 005982 s16i a8, a9, 0 +40002219: 02fb25 call8 400051cc <_XX_unk51ac+0x20> +4000221c: f01d retw.n +4000221e: 360000 excw +40002221: 310041 l32r a4, 3ffce624 <_start-0x319dc> +40002224: ffcb addi.n a15, a15, 12 +40002226: 33b8 l32i.n a11, a3, 12 +40002228: 4b8c beqz.n a11, 40002230 <_st_0x3fffda9c+0xe0> +4000222a: 20a220 or a10, a2, a2 +4000222d: 000be0 callx8 a11 +40002230: 43b8 l32i.n a11, a3, 16 +40002232: 3b8c beqz.n a11, 40002239 <_st_0x3fffda9c+0xe9> +40002234: 02ad mov.n a10, a2 +40002236: 000be0 callx8 a11 +40002239: f01d retw.n + ... + +4000223c <_X_ets_uart_putc>: +4000223c: 004136 entry a1, 32 +4000223f: 0b9226 beqi a2, 10, 4000224e <_X_ets_uart_putc+0x12> +40002242: d80c movi.n a8, 13 +40002244: 041287 beq a2, a8, 4000224c <_X_ets_uart_putc+0x10> +40002247: 02ad mov.n a10, a2 +40002249: 013e25 call8 4000362c +4000224c: f01d retw.n +4000224e: 0da0a2 movi a10, 13 +40002251: 013da5 call8 4000362c +40002254: 0aa0a2 movi a10, 10 +40002257: 013d65 call8 4000362c +4000225a: f01d retw.n + +4000225c <_X_ets_unk225c>: +4000225c: 00a136 entry a1, 80 +4000225f: 068d mov.n a8, a6 +40002261: 090c movi.n a9, 0 +40002263: 1b0c movi.n a11, 1 +40002265: 0a0c movi.n a10, 0 +40002267: 83ab30 moveqz a10, a11, a3 +4000226a: 839b20 moveqz a9, a11, a2 +4000226d: 0909a7 bnone a9, a10, 4000227a <_X_ets_unk225c+0x1e> +40002270: 711b addi.n a7, a1, 1 +40002272: 0c3c movi.n a12, 48 +40002274: 0041c2 s8i a12, a1, 0 +40002277: 001046 j 400022bc <_X_ets_unk225c+0x60> +4000227a: 017d mov.n a7, a1 +4000227c: 316f50 srai a6, a5, 31 +4000227f: 086182 s32i a8, a1, 32 +40002282: 20b330 or a11, a3, a3 +40002285: 20a220 or a10, a2, a2 +40002288: 06dd mov.n a13, a6 +4000228a: 05cd mov.n a12, a5 +4000228c: 04bd65 call8 40006e64 <__umoddi3> +4000228f: 03bd mov.n a11, a3 +40002291: 81d8 l32i.n a13, a1, 32 +40002293: 05cd mov.n a12, a5 +40002295: ddaa add.n a13, a13, a10 +40002297: 000dd2 l8ui a13, a13, 0 +4000229a: 02ad mov.n a10, a2 +4000229c: 0047d2 s8i a13, a7, 0 +4000229f: 771b addi.n a7, a7, 1 +400022a1: 06dd mov.n a13, a6 +400022a3: 0498e5 call8 40006c30 <__udivdi3> +400022a6: 0b3d mov.n a3, a11 +400022a8: 0a2d mov.n a2, a10 +400022aa: 0aed mov.n a14, a10 +400022ac: 180c movi.n a8, 1 +400022ae: 0bfd mov.n a15, a11 +400022b0: 93f8b0 movnez a15, a8, a11 +400022b3: 93e8a0 movnez a14, a8, a10 +400022b6: 20eef0 or a14, a14, a15 +400022b9: fc5e56 bnez a14, 40002282 <_X_ets_unk225c+0x26> +400022bc: 441717 beq a7, a1, 40002304 <_X_ets_unk225c+0xa8> +400022bf: c02710 sub a2, a7, a1 +400022c2: 149020 extui a9, a2, 0, 2 +400022c5: 099976 loopnez a9, 400022d2 <_X_ets_unk225c+0x76> +400022c8: 770b addi.n a7, a7, -1 +400022ca: 0007a2 l8ui a10, a7, 0 +400022cd: 0044a2 s8i a10, a4, 0 +400022d0: 441b addi.n a4, a4, 1 +400022d2: 419220 srli a9, a2, 2 +400022d5: 249976 loopnez a9, 400022fd <_X_ets_unk225c+0xa1> +400022d8: 970b addi.n a9, a7, -1 +400022da: fec7b2 addi a11, a7, -2 +400022dd: fdc7c2 addi a12, a7, -3 +400022e0: 000992 l8ui a9, a9, 0 +400022e3: 000bb2 l8ui a11, a11, 0 +400022e6: 000cc2 l8ui a12, a12, 0 +400022e9: fcc772 addi a7, a7, -4 +400022ec: 004492 s8i a9, a4, 0 +400022ef: 0144b2 s8i a11, a4, 1 +400022f2: 0244c2 s8i a12, a4, 2 +400022f5: 000792 l8ui a9, a7, 0 +400022f8: 034492 s8i a9, a4, 3 +400022fb: 444b addi.n a4, a4, 4 +400022fd: 0b0c movi.n a11, 0 +400022ff: 0044b2 s8i a11, a4, 0 +40002302: f01d retw.n +40002304: 020c movi.n a2, 0 +40002306: 0c0c movi.n a12, 0 +40002308: 0044c2 s8i a12, a4, 0 +4000230b: f01d retw.n +4000230d: 000000 ill +40002310: ffc8a0 excw +40002313: 3f .byte 0x3f +40002314: cca8 l32i.n a10, a12, 48 +40002316: ff .byte 0xff +40002317: 3f .byte 0x3f +40002318: cc9c beqz.n a12, 40002338 <_X_ets_unk225c+0xdc> +4000231a: ff .byte 0xff +4000231b: 3f .byte 0x3f +4000231c: ffccd0 excw +4000231f: 3f .byte 0x3f +40002320: ccbc beqz.n a12, 40002360 <_X_ets_unk225c+0x104> +40002322: ff .byte 0xff +40002323: 3f .byte 0x3f +40002324: 00e136 entry a1, 112 +40002327: 106162 s32i a6, a1, 64 +4000232a: 04dd mov.n a13, a4 +4000232c: 05cd mov.n a12, a5 +4000232e: 000352 l8ui a5, a3, 0 +40002331: 431b addi.n a4, a3, 1 +40002333: 42b516 beqz a5, 40002762 <_X_ets_unk225c+0x506> +40002336: 030c movi.n a3, 0 +40002338: a1d9 s32i.n a13, a1, 40 +4000233a: 91c9 s32i.n a12, a1, 36 +4000233c: 080c movi.n a8, 0 +4000233e: 060c movi.n a6, 0 +40002340: 090c movi.n a9, 0 +40002342: 116192 s32i a9, a1, 68 +40002345: d169 s32i.n a6, a1, 52 +40002347: e189 s32i.n a8, a1, 56 +40002349: 016d mov.n a6, a1 +4000234b: 582c movi.n a8, 37 +4000234d: 1b1587 beq a5, a8, 4000236c <_X_ets_unk225c+0x110> +40002350: 05ad mov.n a10, a5 +40002352: 0002e0 callx8 a2 +40002355: 1121a2 l32i a10, a1, 68 +40002358: aa1b addi.n a10, a10, 1 +4000235a: 1161a2 s32i a10, a1, 68 +4000235d: 000452 l8ui a5, a4, 0 +40002360: 01c442 addi a4, a4, 1 +40002363: 19e516 beqz a5, 40002505 <_X_ets_unk225c+0x2a9> +40002366: 25a0b2 movi a11, 37 +40002369: e395b7 bne a5, a11, 40002350 <_X_ets_unk225c+0xf4> +4000236c: 000452 l8ui a5, a4, 0 +4000236f: 441b addi.n a4, a4, 1 +40002371: d3c5c2 addi a12, a5, -45 +40002374: 192c16 beqz a12, 4000250a <_X_ets_unk225c+0x2ae> +40002377: d0c5d2 addi a13, a5, -48 +4000237a: 0e0c movi.n a14, 0 +4000237c: f1e9 s32i.n a14, a1, 60 +4000237e: 197d16 beqz a13, 40002519 <_X_ets_unk225c+0x2bd> +40002381: 0f0c movi.n a15, 0 +40002383: c1f9 s32i.n a15, a1, 48 +40002385: f82c movi.n a8, 47 +40002387: 27b857 bgeu a8, a5, 400023b2 <_X_ets_unk225c+0x156> +4000238a: 993c movi.n a9, 57 +4000238c: 223957 bltu a9, a5, 400023b2 <_X_ets_unk225c+0x156> +4000238f: 070c movi.n a7, 0 +40002391: 178076 loop a0, 400023ac <_X_ets_unk225c+0x150> +40002394: fc2c movi.n a12, 47 +40002396: a07770 addx4 a7, a7, a7 +40002399: 907750 addx2 a7, a7, a5 +4000239c: d0c772 addi a7, a7, -48 +4000239f: 000452 l8ui a5, a4, 0 +400023a2: 441b addi.n a4, a4, 1 +400023a4: 0cbc57 bgeu a12, a5, 400023b4 <_X_ets_unk225c+0x158> +400023a7: 983c movi.n a8, 57 +400023a9: 073857 bltu a8, a5, 400023b4 <_X_ets_unk225c+0x158> +400023ac: fff846 j 40002391 <_X_ets_unk225c+0x135> +400023af: 000000 ill +400023b2: 070c movi.n a7, 0 +400023b4: d2c592 addi a9, a5, -46 +400023b7: 301916 beqz a9, 400026bc <_X_ets_unk225c+0x460> +400023ba: 94c5a2 addi a10, a5, -108 +400023bd: 21ca16 beqz a10, 400025dd <_X_ets_unk225c+0x381> +400023c0: 4b4c movi.n a11, 68 +400023c2: 4615b7 beq a5, a11, 4000240c <_X_ets_unk225c+0x1b0> +400023c5: 5c5c movi.n a12, 85 +400023c7: 4115c7 beq a5, a12, 4000240c <_X_ets_unk225c+0x1b0> +400023ca: 8d5c movi.n a13, 88 +400023cc: 3c15d7 beq a5, a13, 4000240c <_X_ets_unk225c+0x1b0> +400023cf: 64a0e2 movi a14, 100 +400023d2: 3615e7 beq a5, a14, 4000240c <_X_ets_unk225c+0x1b0> +400023d5: 0e0c movi.n a14, 0 +400023d7: 0c0c movi.n a12, 0 +400023d9: 90c5f2 addi a15, a5, -112 +400023dc: 30cf16 beqz a15, 400026ec <_X_ets_unk225c+0x490> +400023df: 75a082 movi a8, 117 +400023e2: 051587 beq a5, a8, 400023eb <_X_ets_unk225c+0x18f> +400023e5: 78a092 movi a9, 120 +400023e8: 649597 bne a5, a9, 40002450 <_X_ets_unk225c+0x1f4> +400023eb: 31ac16 beqz a12, 40002709 <_X_ets_unk225c+0x4ad> +400023ee: 1021c2 l32i a12, a1, 64 +400023f1: 8b7c movi.n a11, -8 +400023f3: ac7b addi.n a10, a12, 7 +400023f5: 10aab0 and a10, a10, a11 +400023f8: 8b1c movi.n a11, 24 +400023fa: ca8b addi.n a12, a10, 8 +400023fc: 1061c2 s32i a12, a1, 64 +400023ff: 02abc7 bge a11, a12, 40002405 <_X_ets_unk225c+0x1a9> +40002402: 006106 j 4000258a <_X_ets_unk225c+0x32e> +40002405: 9198 l32i.n a9, a1, 36 +40002407: 0062c6 j 40002596 <_X_ets_unk225c+0x33a> +4000240a: d20000 quos a0, a0, a0 +4000240d: 1c1021 l32r a2, 3ffc9450 <_start-0x36bb0> +40002410: ad8c beqz.n a13, 4000241e <_X_ets_unk225c+0x1c2> +40002412: 4b0d excw +40002414: d2dd excw +40002416: d71061 l32r a6, 3fff8058 <_start-0x7fa8> +40002419: 042c movi.n a4, 32 +4000241b: 9198 l32i.n a9, a1, 36 +4000241d: 0002c6 j 4000242c <_X_ets_unk225c+0x1d0> +40002420: 8e1c movi.n a14, 24 +40002422: 042ea7 blt a14, a10, 4000242a <_X_ets_unk225c+0x1ce> +40002425: 4f2c movi.n a15, 36 +40002427: 1061f2 s32i a15, a1, 64 +4000242a: a198 l32i.n a9, a1, 40 +4000242c: 102182 l32i a8, a1, 64 +4000242f: 889a add.n a8, a8, a9 +40002431: ffd882 addmi a8, a8, 0xffffff00 +40002434: 3f2882 l32i a8, a8, 252 +40002437: 0d6182 s32i a8, a1, 52 +4000243a: 318f80 srai a8, a8, 31 +4000243d: 0e6182 s32i a8, a1, 56 +40002440: 9cc592 addi a9, a5, -100 +40002443: 167916 beqz a9, 400025ae <_X_ets_unk225c+0x352> +40002446: bcc5a2 addi a10, a5, -68 +40002449: 161a16 beqz a10, 400025ae <_X_ets_unk225c+0x352> +4000244c: 0b0c movi.n a11, 0 +4000244e: e1b9 s32i.n a11, a1, 56 +40002450: 0f0c movi.n a15, 0 +40002452: 81f9 s32i.n a15, a1, 32 +40002454: 485c movi.n a8, 84 +40002456: dbc5c2 addi a12, a5, -37 +40002459: 0bbc87 bgeu a12, a8, 40002468 <_X_ets_unk225c+0x20c> +4000245c: ffad91 l32r a9, 40002310 <_X_ets_unk225c+0xb4> +4000245f: a09c90 addx4 a9, a12, a9 +40002462: 0998 l32i.n a9, a9, 0 +40002464: 0009a0 jx a9 +40002467: 5a2c00 depbits a0, a12, 5, 3 +4000246a: 0002e0 callx8 a2 +4000246d: 05ad mov.n a10, a5 +4000246f: 0002e0 callx8 a2 +40002472: 1121a2 l32i a10, a1, 68 +40002475: aa2b addi.n a10, a10, 2 +40002477: 1161a2 s32i a10, a1, 68 +4000247a: b139 s32i.n a3, a1, 44 +4000247c: c1b8 l32i.n a11, a1, 48 +4000247e: 81c8 l32i.n a12, a1, 32 +40002480: c07730 sub a7, a7, a3 +40002483: ffc7e2 addi a14, a7, -1 +40002486: 937ec0 movnez a7, a14, a12 +40002489: 17cb16 beqz a11, 40002609 <_X_ets_unk225c+0x3ad> +4000248c: 00ec16 beqz a12, 4000249e <_X_ets_unk225c+0x242> +4000248f: 0cad mov.n a10, a12 +40002491: 0002e0 callx8 a2 +40002494: 1121f2 l32i a15, a1, 68 +40002497: 0c0c movi.n a12, 0 +40002499: ff1b addi.n a15, a15, 1 +4000249b: 1161f2 s32i a15, a1, 68 +4000249e: 053c movi.n a5, 48 +400024a0: f188 l32i.n a8, a1, 60 +400024a2: 0861c2 s32i a12, a1, 32 +400024a5: 167816 beqz a8, 40002610 <_X_ets_unk225c+0x3b4> +400024a8: 81c8 l32i.n a12, a1, 32 +400024aa: bc8c beqz.n a12, 400024b9 <_X_ets_unk225c+0x25d> +400024ac: 0cad mov.n a10, a12 +400024ae: 0002e0 callx8 a2 +400024b1: 112192 l32i a9, a1, 68 +400024b4: 991b addi.n a9, a9, 1 +400024b6: 116192 s32i a9, a1, 68 +400024b9: b1a8 l32i.n a10, a1, 44 +400024bb: ffca52 addi a5, a10, -1 +400024be: 203550 or a3, a5, a5 +400024c1: 181aa6 blti a10, 1, 400024dd <_X_ets_unk225c+0x281> +400024c4: 0006a2 l8ui a10, a6, 0 +400024c7: 661b addi.n a6, a6, 1 +400024c9: 0002e0 callx8 a2 +400024cc: 330b addi.n a3, a3, -1 +400024ce: f20366 bnei a3, -1, 400024c4 <_X_ets_unk225c+0x268> +400024d1: f37c movi.n a3, -1 +400024d3: 1121b2 l32i a11, a1, 68 +400024d6: c51b addi.n a12, a5, 1 +400024d8: bbca add.n a11, a11, a12 +400024da: 1161b2 s32i a11, a1, 68 +400024dd: f1d8 l32i.n a13, a1, 60 +400024df: e7ad16 beqz a13, 4000235d <_X_ets_unk225c+0x101> +400024e2: 07ed mov.n a14, a7 +400024e4: 570b addi.n a5, a7, -1 +400024e6: 057d mov.n a7, a5 +400024e8: 021ee6 bgei a14, 1, 400024ee <_X_ets_unk225c+0x292> +400024eb: ff9b86 j 4000235d <_X_ets_unk225c+0x101> +400024ee: 0a2c movi.n a10, 32 +400024f0: 0002e0 callx8 a2 +400024f3: 770b addi.n a7, a7, -1 +400024f5: f50766 bnei a7, -1, 400024ee <_X_ets_unk225c+0x292> +400024f8: 1121f2 l32i a15, a1, 68 +400024fb: 851b addi.n a8, a5, 1 +400024fd: ff8a add.n a15, a15, a8 +400024ff: 1161f2 s32i a15, a1, 68 +40002502: ff95c6 j 4000235d <_X_ets_unk225c+0x101> +40002505: 112122 l32i a2, a1, 68 +40002508: f01d retw.n +4000250a: 1a0c movi.n a10, 1 +4000250c: 000452 l8ui a5, a4, 0 +4000250f: 441b addi.n a4, a4, 1 +40002511: f1a9 s32i.n a10, a1, 60 +40002513: d0c592 addi a9, a5, -48 +40002516: e67956 bnez a9, 40002381 <_X_ets_unk225c+0x125> +40002519: 000452 l8ui a5, a4, 0 +4000251c: 1b0c movi.n a11, 1 +4000251e: 441b addi.n a4, a4, 1 +40002520: c1b9 s32i.n a11, a1, 48 +40002522: ff97c6 j 40002385 <_X_ets_unk225c+0x129> +40002525: 3c0000 excw +40002528: e00a add.n a14, a0, a0 +4000252a: a20002 l8ui a0, a0, 162 +4000252d: e078a0 subx4 a7, a8, a10 +40002530: 0c0002 l8ui a0, a0, 12 +40002533: 1c0c87 bnone a12, a8, 40002553 <_X_ets_unk225c+0x2f7> +40002536: c1c9 s32i.n a12, a1, 48 +40002538: 4d4c movi.n a13, 68 +4000253a: 3615d7 beq a5, a13, 40002574 <_X_ets_unk225c+0x318> +4000253d: 5e5c movi.n a14, 85 +4000253f: 3115e7 beq a5, a14, 40002574 <_X_ets_unk225c+0x318> +40002542: a8c5f2 addi a15, a5, -88 +40002545: 201f16 beqz a15, 4000274a <_X_ets_unk225c+0x4ee> +40002548: 64a082 movi a8, 100 +4000254b: 251587 beq a5, a8, 40002574 <_X_ets_unk225c+0x318> +4000254e: 70a092 movi a9, 112 +40002551: 0b1597 beq a5, a9, 40002560 <_X_ets_unk225c+0x304> +40002554: 75a0a2 movi a10, 117 +40002557: 1915a7 beq a5, a10, 40002574 <_X_ets_unk225c+0x318> +4000255a: 78a0b2 movi a11, 120 +4000255d: 2395b7 bne a5, a11, 40002584 <_X_ets_unk225c+0x328> +40002560: e1b8 l32i.n a11, a1, 56 +40002562: d1a8 l32i.n a10, a1, 52 +40002564: 01cd mov.n a12, a1 +40002566: 0d1c movi.n a13, 16 +40002568: ff6be1 l32r a14, 40002314 <_X_ets_unk225c+0xb8> +4000256b: ffcf25 call8 4000225c <_X_ets_unk225c> +4000256e: 0a3d mov.n a3, a10 +40002570: 000406 j 40002584 <_X_ets_unk225c+0x328> +40002573: e1b800 excw +40002576: d1a8 l32i.n a10, a1, 52 +40002578: 01cd mov.n a12, a1 +4000257a: ad0c movi.n a13, 10 +4000257c: ff67e1 l32r a14, 40002318 <_X_ets_unk225c+0xbc> +4000257f: ffcde5 call8 4000225c <_X_ets_unk225c> +40002582: 0a3d mov.n a3, a10 +40002584: 016d mov.n a6, a1 +40002586: ffbc06 j 4000247a <_X_ets_unk225c+0x21e> +40002589: 8c1c00 excw +4000258c: 042ca7 blt a12, a10, 40002594 <_X_ets_unk225c+0x338> +4000258f: 8d2c movi.n a13, 40 +40002591: 1061d2 s32i a13, a1, 64 +40002594: a198 l32i.n a9, a1, 40 +40002596: 1021f2 l32i a15, a1, 64 +40002599: 9cc5e2 addi a14, a5, -100 +4000259c: ff9a add.n a15, a15, a9 +4000259e: ffdff2 addmi a15, a15, 0xffffff00 +400025a1: 3f2f82 l32i a8, a15, 252 +400025a4: e189 s32i.n a8, a1, 56 +400025a6: 3e2ff2 l32i a15, a15, 248 +400025a9: d1f9 s32i.n a15, a1, 52 +400025ab: e97e56 bnez a14, 40002446 <_X_ets_unk225c+0x1ea> +400025ae: d1a8 l32i.n a10, a1, 52 +400025b0: 0d0c movi.n a13, 0 +400025b2: e188 l32i.n a8, a1, 56 +400025b4: 0c0c movi.n a12, 0 +400025b6: 0218e6 bgei a8, 1, 400025bc <_X_ets_unk225c+0x360> +400025b9: 006146 j 40002742 <_X_ets_unk225c+0x4e6> +400025bc: e90c16 beqz a12, 40002450 <_X_ets_unk225c+0x1f4> +400025bf: d1a8 l32i.n a10, a1, 52 +400025c1: fe7c movi.n a14, -1 +400025c3: e1c8 l32i.n a12, a1, 56 +400025c5: d92c movi.n a9, 45 +400025c7: 8199 s32i.n a9, a1, 32 +400025c9: 30cce0 xor a12, a12, a14 +400025cc: 60b0a0 neg a11, a10 +400025cf: d1b9 s32i.n a11, a1, 52 +400025d1: dc1b addi.n a13, a12, 1 +400025d3: 83cda0 moveqz a12, a13, a10 +400025d6: e1c9 s32i.n a12, a1, 56 +400025d8: ff9e06 j 40002454 <_X_ets_unk225c+0x1f8> +400025db: 520000 excw +400025de: 1b0004 mula.dd.hh.lddec m0, a0, m0, m2 +400025e1: c5f244 excw +400025e4: ff1694 excw +400025e7: 0e .byte 0xe +400025e8: 0c0c movi.n a12, 0 +400025ea: bcc582 addi a8, a5, -68 +400025ed: 0f4816 beqz a8, 400026e5 <_X_ets_unk225c+0x489> +400025f0: abc592 addi a9, a5, -85 +400025f3: 0ee916 beqz a9, 400026e5 <_X_ets_unk225c+0x489> +400025f6: a8c5a2 addi a10, a5, -88 +400025f9: 0e8a16 beqz a10, 400026e5 <_X_ets_unk225c+0x489> +400025fc: 9cc5b2 addi a11, a5, -100 +400025ff: 0e2b16 beqz a11, 400026e5 <_X_ets_unk225c+0x489> +40002602: 1e0c movi.n a14, 1 +40002604: ff7446 j 400023d9 <_X_ets_unk225c+0x17d> +40002607: c80000 excw +4000260a: 052cf1 l32r a15, 3ffc3abc <_start-0x3c544> +4000260d: e97c56 bnez a12, 400024a8 <_X_ets_unk225c+0x24c> +40002610: 07dd mov.n a13, a7 +40002612: 370b addi.n a3, a7, -1 +40002614: 037d mov.n a7, a3 +40002616: 021de6 bgei a13, 1, 4000261c <_X_ets_unk225c+0x3c0> +40002619: ffa2c6 j 400024a8 <_X_ets_unk225c+0x24c> +4000261c: 05ad mov.n a10, a5 +4000261e: 0002e0 callx8 a2 +40002621: 770b addi.n a7, a7, -1 +40002623: f50766 bnei a7, -1, 4000261c <_X_ets_unk225c+0x3c0> +40002626: f77c movi.n a7, -1 +40002628: 1121e2 l32i a14, a1, 68 +4000262b: f31b addi.n a15, a3, 1 +4000262d: eefa add.n a14, a14, a15 +4000262f: 1161e2 s32i a14, a1, 68 +40002632: ff9c86 j 400024a8 <_X_ets_unk225c+0x24c> +40002635: 102192 l32i a9, a1, 64 +40002638: 881c movi.n a8, 24 +4000263a: 09cd mov.n a12, a9 +4000263c: 994b addi.n a9, a9, 4 +4000263e: 106192 s32i a9, a1, 64 +40002641: 052897 blt a8, a9, 4000264a <_X_ets_unk225c+0x3ee> +40002644: 9198 l32i.n a9, a1, 36 +40002646: 000306 j 40002656 <_X_ets_unk225c+0x3fa> +40002649: 8a1c00 depbits a0, a12, 8, 2 +4000264c: 042ac7 blt a10, a12, 40002654 <_X_ets_unk225c+0x3f8> +4000264f: 4b2c movi.n a11, 36 +40002651: 1061b2 s32i a11, a1, 64 +40002654: a198 l32i.n a9, a1, 40 +40002656: 102162 l32i a6, a1, 64 +40002659: ff30d1 l32r a13, 4000231c <_X_ets_unk225c+0xc0> +4000265c: 669a add.n a6, a6, a9 +4000265e: ffd662 addmi a6, a6, 0xffffff00 +40002661: 3f2662 l32i a6, a6, 252 +40002664: 836d60 moveqz a6, a13, a6 +40002667: 0006c2 l8ui a12, a6, 0 +4000266a: 0efc16 beqz a12, 4000275d <_X_ets_unk225c+0x501> +4000266d: 069d mov.n a9, a6 +4000266f: 030c movi.n a3, 0 +40002671: 098076 loop a0, 4000267e <_X_ets_unk225c+0x422> +40002674: 331b addi.n a3, a3, 1 +40002676: 010982 l8ui a8, a9, 1 +40002679: 991b addi.n a9, a9, 1 +4000267b: dfb816 beqz a8, 4000247a <_X_ets_unk225c+0x21e> +4000267e: fffbc6 j 40002671 <_X_ets_unk225c+0x415> +40002681: 1021a2 l32i a10, a1, 64 +40002684: 891c movi.n a9, 24 +40002686: 0acd mov.n a12, a10 +40002688: aa4b addi.n a10, a10, 4 +4000268a: 1061a2 s32i a10, a1, 64 +4000268d: 0529a7 blt a9, a10, 40002696 <_X_ets_unk225c+0x43a> +40002690: 91c8 l32i.n a12, a1, 36 +40002692: 000306 j 400026a2 <_X_ets_unk225c+0x446> +40002695: 8b1c00 depbits a0, a12, 24, 2 +40002698: 042bc7 blt a11, a12, 400026a0 <_X_ets_unk225c+0x444> +4000269b: 4c2c movi.n a12, 36 +4000269d: 1061c2 s32i a12, a1, 64 +400026a0: a1c8 l32i.n a12, a1, 40 +400026a2: 1021a2 l32i a10, a1, 64 +400026a5: aaca add.n a10, a10, a12 +400026a7: ffdaa2 addmi a10, a10, 0xffffff00 +400026aa: fc0aa2 l8ui a10, a10, 252 +400026ad: 0002e0 callx8 a2 +400026b0: ff2846 j 40002355 <_X_ets_unk225c+0xf9> +400026b3: 5a2c movi.n a10, 37 +400026b5: 0002e0 callx8 a2 +400026b8: ff6f86 j 4000247a <_X_ets_unk225c+0x21e> +400026bb: fb2c00 depbits a0, a12, 31, 3 +400026be: 000452 l8ui a5, a4, 0 +400026c1: c1c8 l32i.n a12, a1, 48 +400026c3: 441b addi.n a4, a4, 1 +400026c5: cc1b addi.n a12, a12, 1 +400026c7: c1c9 s32i.n a12, a1, 48 +400026c9: 023b57 bltu a11, a5, 400026cf <_X_ets_unk225c+0x473> +400026cc: ff3a86 j 400023ba <_X_ets_unk225c+0x15e> +400026cf: 9d3c movi.n a13, 57 +400026d1: 25bd57 bgeu a13, a5, 400026fa <_X_ets_unk225c+0x49e> +400026d4: ff3886 j 400023ba <_X_ets_unk225c+0x15e> +400026d7: 1c0c00 excw +400026da: 000452 l8ui a5, a4, 0 +400026dd: 441b addi.n a4, a4, 1 +400026df: bcc5e2 addi a14, a5, -68 +400026e2: f0ae56 bnez a14, 400025f0 <_X_ets_unk225c+0x394> +400026e5: d05c56 bnez a12, 400023ee <_X_ets_unk225c+0x192> +400026e8: 000806 j 4000270c <_X_ets_unk225c+0x4b0> +400026eb: ec5600 excw +400026ee: cf .byte 0xcf +400026ef: 000646 j 4000270c <_X_ets_unk225c+0x4b0> +400026f2: 9f3c movi.n a15, 57 +400026f4: 02bf57 bgeu a15, a5, 400026fa <_X_ets_unk225c+0x49e> +400026f7: ff2fc6 j 400023ba <_X_ets_unk225c+0x15e> +400026fa: f82c movi.n a8, 47 +400026fc: 000452 l8ui a5, a4, 0 +400026ff: 441b addi.n a4, a4, 1 +40002701: ed3857 bltu a8, a5, 400026f2 <_X_ets_unk225c+0x496> +40002704: ff2c86 j 400023ba <_X_ets_unk225c+0x15e> +40002707: 160000 excw +4000270a: fe .byte 0xfe +4000270b: cf .byte 0xcf +4000270c: 1021b2 l32i a11, a1, 64 +4000270f: 891c movi.n a9, 24 +40002711: 0bad mov.n a10, a11 +40002713: bb4b addi.n a11, a11, 4 +40002715: 1061b2 s32i a11, a1, 64 +40002718: 0429b7 blt a9, a11, 40002720 <_X_ets_unk225c+0x4c4> +4000271b: 9198 l32i.n a9, a1, 36 +4000271d: 0002c6 j 4000272c <_X_ets_unk225c+0x4d0> +40002720: 8c1c movi.n a12, 24 +40002722: 042ca7 blt a12, a10, 4000272a <_X_ets_unk225c+0x4ce> +40002725: 4d2c movi.n a13, 36 +40002727: 1061d2 s32i a13, a1, 64 +4000272a: a198 l32i.n a9, a1, 40 +4000272c: 1021e2 l32i a14, a1, 64 +4000272f: ee9a add.n a14, a14, a9 +40002731: ffdee2 addmi a14, a14, 0xffffff00 +40002734: 3f2ee2 l32i a14, a14, 252 +40002737: d1e9 s32i.n a14, a1, 52 +40002739: 31efe0 srai a14, a14, 31 +4000273c: e1e9 s32i.n a14, a1, 56 +4000273e: ff3f86 j 40002440 <_X_ets_unk225c+0x1e4> +40002741: 28ac00 excw +40002744: 1c0c movi.n a12, 1 +40002746: ff9c86 j 400025bc <_X_ets_unk225c+0x360> +40002749: e1b800 excw +4000274c: d1a8 l32i.n a10, a1, 52 +4000274e: 01cd mov.n a12, a1 +40002750: 0d1c movi.n a13, 16 +40002752: fef3e1 l32r a14, 40002320 <_X_ets_unk225c+0xc4> +40002755: ffb065 call8 4000225c <_X_ets_unk225c> +40002758: 0a3d mov.n a3, a10 +4000275a: ff8986 j 40002584 <_X_ets_unk225c+0x328> +4000275d: 030c movi.n a3, 0 +4000275f: ff45c6 j 4000247a <_X_ets_unk225c+0x21e> +40002762: 020c movi.n a2, 0 +40002764: f01d retw.n +40002766: d70000 excw +40002769: 023a add.n a0, a2, a3 +4000276b: ff9346 j 400025bc <_X_ets_unk225c+0x360> +4000276e: fff486 j 40002744 <_X_ets_unk225c+0x4e8> +40002771: 000000 ill + +40002774 : +40002774: 004136 entry a1, 32 +40002777: fe7631 l32r a3, 40002150 <_st_0x3fffda9c> +4000277a: 3329 s32i.n a2, a3, 12 +4000277c: f01d retw.n + ... + +40002780 <_c_0x4000223c_ets_uart_putc>: +40002780: 223c movi.n a2, 50 +40002782: 364000 excw + +40002784 <_X_ets_install_uart_printf>: +40002784: 004136 entry a1, 32 +40002787: fffea1 l32r a10, 40002780 <_c_0x4000223c_ets_uart_putc> +4000278a: fffea5 call8 40002774 +4000278d: f01d retw.n + ... + +40002790 <_c_0x400027dc>: +40002790: 27dc bnez.n a7, 400027a6 <_X_ets_install_external_printf+0x12> +40002792: 364000 excw + +40002794 <_X_ets_install_external_printf>: +40002794: 004136 entry a1, 32 +40002797: fe6e51 l32r a5, 40002150 <_st_0x3fffda9c> +4000279a: 5529 s32i.n a2, a5, 20 +4000279c: 838c beqz.n a3, 400027a8 <_X_ets_install_external_printf+0x14> +4000279e: 20a330 or a10, a3, a3 +400027a1: 000125 call8 400027b4 <_X_ets_install_putc2> +400027a4: 6549 s32i.n a4, a5, 24 +400027a6: f01d retw.n +400027a8: fffaa1 l32r a10, 40002790 <_c_0x400027dc> +400027ab: 0000a5 call8 400027b4 <_X_ets_install_putc2> +400027ae: 6549 s32i.n a4, a5, 24 +400027b0: f01d retw.n + ... + +400027b4 <_X_ets_install_putc2>: +400027b4: 004136 entry a1, 32 +400027b7: fe6631 l32r a3, 40002150 <_st_0x3fffda9c> +400027ba: 4329 s32i.n a2, a3, 16 +400027bc: f01d retw.n + ... + +400027c0 <_X_ets_get_printf_buf_remain_len>: +400027c0: 004136 entry a1, 32 +400027c3: fe6321 l32r a2, 40002150 <_st_0x3fffda9c> +400027c6: 0e1222 l16ui a2, a2, 28 +400027c9: f01d retw.n + ... + +400027cc <_X_ets_reset_printf_buf_len>: +400027cc: 004136 entry a1, 32 +400027cf: fe6031 l32r a3, 40002150 <_st_0x3fffda9c> +400027d2: 020c movi.n a2, 0 +400027d4: 0e5322 s16i a2, a3, 28 +400027d7: f01d retw.n +400027d9: 000000 ill + +400027dc <_X_ets_putc>: +400027dc: 004136 entry a1, 32 +400027df: fe5c61 l32r a6, 40002150 <_st_0x3fffda9c> +400027e2: 0e1642 l16ui a4, a6, 28 +400027e5: 8638 l32i.n a3, a6, 32 +400027e7: d48c beqz.n a4, 400027f8 <_X_ets_putc+0x1c> +400027e9: 004322 s8i a2, a3, 0 +400027ec: 540b addi.n a5, a4, -1 +400027ee: 331b addi.n a3, a3, 1 +400027f0: 0e5652 s16i a5, a6, 28 +400027f3: 8639 s32i.n a3, a6, 32 +400027f5: f01d retw.n +400027f7: f01d00 subx8 a1, a13, a0 +400027fa: bc0000 excw +400027fd: ffda add.n a15, a15, a13 +400027ff: 3f .byte 0x3f +40002800: 002220 excw +40002803: a13640 excw + +40002804 : +40002804: 00a136 entry a1, 80 +40002807: 4129 s32i.n a2, a1, 16 +40002809: 5139 s32i.n a3, a1, 20 +4000280b: 6149 s32i.n a4, a1, 24 +4000280d: 7159 s32i.n a5, a1, 28 +4000280f: 079d mov.n a9, a7 +40002811: fe4f71 l32r a7, 40002150 <_st_0x3fffda9c> +40002814: 8169 s32i.n a6, a1, 32 +40002816: 3788 l32i.n a8, a7, 12 +40002818: 9199 s32i.n a9, a1, 36 +4000281a: 38cc bnez.n a8, 40002821 +4000281c: 0427b2 l32i a11, a7, 16 +4000281f: 5bbc beqz.n a11, 40002858 +40002821: 57d8 l32i.n a13, a7, 20 +40002823: 00dd16 beqz a13, 40002834 +40002826: fff5a1 l32r a10, 400027fc <_X_ets_putc+0x20> +40002829: fccab2 addi a11, a10, -4 +4000282c: 04cac2 addi a12, a10, 4 +4000282f: 000de0 callx8 a13 +40002832: 4128 l32i.n a2, a1, 16 +40002834: fff3a1 l32r a10, 40002800 <_X_ets_putc+0x24> +40002837: 10c1d2 addi a13, a1, 16 +4000283a: 30c1c2 addi a12, a1, 48 +4000283d: 4e0c movi.n a14, 4 +4000283f: 02bd mov.n a11, a2 +40002841: 21e9 s32i.n a14, a1, 8 +40002843: 01c9 s32i.n a12, a1, 0 +40002845: 11d9 s32i.n a13, a1, 4 +40002847: ffade5 call8 40002324 <_X_ets_unk225c+0xc8> +4000284a: 67b8 l32i.n a11, a7, 24 +4000284c: 0a2d mov.n a2, a10 +4000284e: 3b8c beqz.n a11, 40002855 +40002850: 97a8 l32i.n a10, a7, 36 +40002852: 000be0 callx8 a11 +40002855: f01d retw.n +40002857: 020c00 andb b0, b12, b0 +4000285a: f01d retw.n +4000285c: 00a136 entry a1, 80 +4000285f: 02bd mov.n a11, a2 +40002861: 4129 s32i.n a2, a1, 16 +40002863: 5139 s32i.n a3, a1, 20 +40002865: 6149 s32i.n a4, a1, 24 +40002867: 7159 s32i.n a5, a1, 28 +40002869: 8169 s32i.n a6, a1, 32 +4000286b: 9179 s32i.n a7, a1, 36 +4000286d: ffc4a1 l32r a10, 40002780 <_c_0x4000223c_ets_uart_putc> +40002870: 30c1c2 addi a12, a1, 48 +40002873: 4e0c movi.n a14, 4 +40002875: 10c1d2 addi a13, a1, 16 +40002878: 11d9 s32i.n a13, a1, 4 +4000287a: 21e9 s32i.n a14, a1, 8 +4000287c: 01c9 s32i.n a12, a1, 0 +4000287e: ffaa65 call8 40002324 <_X_ets_unk225c+0xc8> +40002881: 0a2d mov.n a2, a10 +40002883: f01d retw.n +40002885: 000000 ill +40002888: 00a136 entry a1, 80 +4000288b: 5139 s32i.n a3, a1, 20 +4000288d: 6149 s32i.n a4, a1, 24 +4000288f: 7159 s32i.n a5, a1, 28 +40002891: 8169 s32i.n a6, a1, 32 +40002893: 02bd mov.n a11, a2 +40002895: fe2e21 l32r a2, 40002150 <_st_0x3fffda9c> +40002898: 9179 s32i.n a7, a1, 36 +4000289a: 52d8 l32i.n a13, a2, 20 +4000289c: 0461b2 s32i a11, a1, 16 +4000289f: 00ed16 beqz a13, 400028b1 +400028a2: 20c2a2 addi a10, a2, 32 +400028a5: 1cc2b2 addi a11, a2, 28 +400028a8: 24c2c2 addi a12, a2, 36 +400028ab: 000de0 callx8 a13 +400028ae: 0421b2 l32i a11, a1, 16 +400028b1: ffb7a1 l32r a10, 40002790 <_c_0x400027dc> +400028b4: 10c1d2 addi a13, a1, 16 +400028b7: 30c1c2 addi a12, a1, 48 +400028ba: 4e0c movi.n a14, 4 +400028bc: 21e9 s32i.n a14, a1, 8 +400028be: 01c9 s32i.n a12, a1, 0 +400028c0: 11d9 s32i.n a13, a1, 4 +400028c2: ffa625 call8 40002324 <_X_ets_unk225c+0xc8> +400028c5: 62b8 l32i.n a11, a2, 24 +400028c7: 0a3d mov.n a3, a10 +400028c9: 3b8c beqz.n a11, 400028d0 +400028cb: 92a8 l32i.n a10, a2, 36 +400028cd: 000be0 callx8 a11 +400028d0: 032d mov.n a2, a3 +400028d2: f01d retw.n + +400028d4 <_c_0xdfffffff>: +400028d4: ff .byte 0xff +400028d5: ff .byte 0xff +400028d6: ff .byte 0xff +400028d7: df .byte 0xdf + +400028d8 <_X_get_rst_cause>: +400028d8: 004136 entry a1, 32 +400028db: fa1741 l32r a4, 40001138 <_c_0x60007e00> +400028de: 0020c0 memw +400028e1: 8d2422 l32i a2, a4, 0x234 +400028e4: fffcc1 l32r a12, 400028d4 <_c_0xdfffffff> +400028e7: 542020 extui a2, a2, 0, 6 +400028ea: 325266 bnei a2, 5, 40002920 <_X_get_rst_cause+0x48> +400028ed: 0020c0 memw +400028f0: 8d2432 l32i a3, a4, 0x234 +400028f3: 943c30 extui a3, a3, 12, 10 +400028f6: 371326 beqi a3, 1, 40002931 <_X_get_rst_cause+0x59> +400028f9: 0020c0 memw +400028fc: fff681 l32r a8, 400028d4 <_c_0xdfffffff> +400028ff: 8d24a2 l32i a10, a4, 0x234 +40002902: 090c movi.n a9, 0 +40002904: 94aca0 extui a10, a10, 12, 10 +40002907: f8caa2 addi a10, a10, -8 +4000290a: 9329a0 movnez a2, a9, a10 +4000290d: 0020c0 memw +40002910: 862452 l32i a5, a4, 0x218 +40002913: 105580 and a5, a5, a8 +40002916: 0020c0 memw +40002919: 866452 s32i a5, a4, 0x218 +4000291c: f01d retw.n +4000291e: c00000 sub a0, a0, a0 +40002921: b20020 mulsh a0, a0, a2 +40002924: c08624 excw +40002927: 10bb addi.n a1, a0, 11 +40002929: 0020c0 memw +4000292c: 8664b2 s32i a11, a4, 0x218 +4000292f: f01d retw.n +40002931: ffe8e1 l32r a14, 400028d4 <_c_0xdfffffff> +40002934: 421c movi.n a2, 20 +40002936: 0020c0 memw +40002939: 8624d2 l32i a13, a4, 0x218 +4000293c: 10dde0 and a13, a13, a14 +4000293f: 0020c0 memw +40002942: 8664d2 s32i a13, a4, 0x218 +40002945: f01d retw.n + ... + +40002948 <_XX_unk2948>: +40002948: 004136 entry a1, 32 +4000294b: 070c movi.n a7, 0 +4000294d: f9fa51 l32r a5, 40001138 <_c_0x60007e00> +40002950: 0020c0 memw +40002953: a82562 l32i a6, a5, 0x2a0 +40002956: 0269 s32i.n a6, a2, 0 +40002958: 0020c0 memw +4000295b: a92542 l32i a4, a5, 0x2a4 +4000295e: 400300 ssr a3 +40002961: 914040 srl a4, a4 +40002964: 086407 bbci a4, 0, 40002970 <_l_2970> +40002967: 0020c0 memw +4000296a: a86572 s32i a7, a5, 0x2a0 +4000296d: f01d retw.n + ... + +40002970 <_l_2970>: +40002970: 0279 s32i.n a7, a2, 0 +40002972: 0020c0 memw +40002975: a86572 s32i a7, a5, 0x2a0 +40002978: f01d retw.n + ... + +4000297c <_X_sw_sys_rst>: +4000297c: 004136 entry a1, 32 +4000297f: f76e41 l32r a4, 40000738 <_c_0x80000000> +40002982: f9ed31 l32r a3, 40001138 <_c_0x60007e00> +40002985: 0020c0 memw +40002988: 802322 l32i a2, a3, 0x200 +4000298b: 202240 or a2, a2, a4 +4000298e: 0020c0 memw +40002991: 806322 s32i a2, a3, 0x200 +40002994: f01d retw.n + ... + +40002998 : +40002998: 004136 entry a1, 32 +4000299b: 80a042 movi a4, 128 +4000299e: f9e631 l32r a3, 40001138 <_c_0x60007e00> +400029a1: 0020c0 memw +400029a4: 802322 l32i a2, a3, 0x200 +400029a7: 202240 or a2, a2, a4 +400029aa: 0020c0 memw +400029ad: 806322 s32i a2, a3, 0x200 +400029b0: f01d retw.n + ... + +400029b4 <_c_0x00400000>: +400029b4: 400000 ssr a0 + ... + +400029b8 <_c_0xffbfffff>: +400029b8: ff .byte 0xff +400029b9: ff .byte 0xff +400029ba: bf .byte 0xbf +400029bb: ff .byte 0xff + +400029bc <_XX_apb_bridge_toggle>: +400029bc: 004136 entry a1, 32 +400029bf: f9de41 l32r a4, 40001138 <_c_0x60007e00> +400029c2: 229c beqz.n a2, 400029d8 <_XX_apb_bridge_toggle+0x1c> +400029c4: fffc31 l32r a3, 400029b4 <_c_0x00400000> +400029c7: 0020c0 memw +400029ca: 862422 l32i a2, a4, 0x218 +400029cd: 202230 or a2, a2, a3 +400029d0: 0020c0 memw +400029d3: 866422 s32i a2, a4, 0x218 +400029d6: f01d retw.n +400029d8: fff891 l32r a9, 400029b8 <_c_0xffbfffff> +400029db: 0020c0 memw +400029de: 862482 l32i a8, a4, 0x218 +400029e1: 108890 and a8, a8, a9 +400029e4: 0020c0 memw +400029e7: 866482 s32i a8, a4, 0x218 +400029ea: f01d retw.n + +400029ec <_X_ets_strcpy>: +400029ec: 004136 entry a1, 32 +400029ef: 20b330 or a11, a3, a3 +400029f2: 20a220 or a10, a2, a2 +400029f5: 032e65 call8 40005cdc +400029f8: 202aa0 or a2, a10, a10 +400029fb: f01d retw.n +400029fd: 000000 ill + +40002a00 <_X_ets_strncpy>: +40002a00: 004136 entry a1, 32 +40002a03: 04cd mov.n a12, a4 +40002a05: 03bd mov.n a11, a3 +40002a07: 02ad mov.n a10, a2 +40002a09: 034865 call8 40005e90 +40002a0c: 0a2d mov.n a2, a10 +40002a0e: f01d retw.n + +40002a10 <_X_ets_strcmp>: +40002a10: 004136 entry a1, 32 +40002a13: 20b330 or a11, a3, a3 +40002a16: 20a220 or a10, a2, a2 +40002a19: 0319e5 call8 40005bb8 +40002a1c: 202aa0 or a2, a10, a10 +40002a1f: f01d retw.n +40002a21: 000000 ill + +40002a24 <_X_ets_strncmp>: +40002a24: 004136 entry a1, 32 +40002a27: 04cd mov.n a12, a4 +40002a29: 03bd mov.n a11, a3 +40002a2b: 02ad mov.n a10, a2 +40002a2d: 033a25 call8 40005dd0 +40002a30: 0a2d mov.n a2, a10 +40002a32: f01d retw.n + +40002a34 <_X_ets_strlen>: +40002a34: 004136 entry a1, 32 +40002a37: 02ad mov.n a10, a2 +40002a39: 033325 call8 40005d6c +40002a3c: 0a2d mov.n a2, a10 +40002a3e: f01d retw.n + +40002a40 <_X_ets_strstr>: +40002a40: 004136 entry a1, 32 +40002a43: 20b330 or a11, a3, a3 +40002a46: 20a220 or a10, a2, a2 +40002a49: 035225 call8 40005f6c +40002a4c: 202aa0 or a2, a10, a10 +40002a4f: f01d retw.n +40002a51: 000000 ill + +40002a54 : +40002a54: 004136 entry a1, 32 +40002a57: 20b330 or a11, a3, a3 +40002a5a: 20a220 or a10, a2, a2 +40002a5d: 03e4e5 call8 400068ac +40002a60: 000090 retw + ... + +40002a64 : +40002a64: 004136 entry a1, 32 +40002a67: 0003a2 l8ui a10, a3, 0 +40002a6a: 9b0c movi.n a11, 9 +40002a6c: 02ca26 beqi a10, 32, 40002a72 +40002a6f: 139ab7 bne a10, a11, 40002a86 +40002a72: 0a8076 loop a0, 40002a80 +40002a75: 0103a2 l8ui a10, a3, 1 +40002a78: 331b addi.n a3, a3, 1 +40002a7a: f7ca26 beqi a10, 32, 40002a75 +40002a7d: 059ab7 bne a10, a11, 40002a86 +40002a80: fffb86 j 40002a72 +40002a83: 000000 ill +40002a86: 040c movi.n a4, 0 +40002a88: 0003a2 l8ui a10, a3, 0 +40002a8b: 0002e5 call8 40002ab8 +40002a8e: 0a5d mov.n a5, a10 +40002a90: 01ea96 bltz a10, 40002ab2 +40002a93: 0103a2 l8ui a10, a3, 1 +40002a96: 000225 call8 40002ab8 +40002a99: 015a96 bltz a10, 40002ab2 +40002a9c: 333b addi.n a3, a3, 3 +40002a9e: 924a add.n a9, a2, a4 +40002aa0: 1185c0 slli a8, a5, 4 +40002aa3: 441b addi.n a4, a4, 1 +40002aa5: 8a8a add.n a8, a10, a8 +40002aa7: 004982 s8i a8, a9, 0 +40002aaa: 744040 extui a4, a4, 0, 8 +40002aad: d76466 bnei a4, 6, 40002a88 +40002ab0: f01d retw.n +40002ab2: 020c movi.n a2, 0 +40002ab4: f01d retw.n +40002ab6: 360000 excw +40002ab9: 3c0041 l32r a4, 3ffd1abc <_start-0x2e544> +40002abc: f62c95 call4 3fff8d88 <_start-0x7278> +40002abf: 02b627 bgeu a6, a2, 40002ac5 +40002ac2: 3ab527 bgeu a5, a2, 40002b00 +40002ac5: 60a032 movi a3, 96 +40002ac8: 05b327 bgeu a3, a2, 40002ad1 +40002acb: 66a042 movi a4, 102 +40002ace: 0ab427 bgeu a4, a2, 40002adc +40002ad1: 084c movi.n a8, 64 +40002ad3: 1db827 bgeu a8, a2, 40002af4 +40002ad6: 46a092 movi a9, 70 +40002ad9: 173927 bltu a9, a2, 40002af4 +40002adc: 02b627 bgeu a6, a2, 40002ae2 +40002adf: 1db527 bgeu a5, a2, 40002b00 +40002ae2: 0a4c movi.n a10, 64 +40002ae4: 10ba27 bgeu a10, a2, 40002af8 +40002ae7: ab5c movi.n a11, 90 +40002ae9: 0b3b27 bltu a11, a2, 40002af8 +40002aec: c9c222 addi a2, a2, -55 +40002aef: 232200 sext a2, a2, 7 +40002af2: f01d retw.n +40002af4: f27c movi.n a2, -1 +40002af6: f01d retw.n +40002af8: a9c222 addi a2, a2, -87 +40002afb: 232200 sext a2, a2, 7 +40002afe: f01d retw.n +40002b00: d0c222 addi a2, a2, -48 +40002b03: 232200 sext a2, a2, 7 +40002b06: f01d retw.n +40002b08: 004136 entry a1, 32 +40002b0b: 00bbe5 call8 400036c8 <_X_uart_wait_tx_empty+0x2c> +40002b0e: 0042a2 s8i a10, a2, 0 +40002b11: f01d retw.n + ... + +40002b14 : +40002b14: 004136 entry a1, 32 +40002b17: 02ad mov.n a10, a2 +40002b19: 00b125 call8 4000362c +40002b1c: f01d retw.n +40002b1e: 000000 ill +40002b21: 6e .byte 0x6e +40002b22: 366000 excw +40002b25: 310041 l32r a4, 3ffcef28 <_start-0x310d8> +40002b28: fe .byte 0xfe +40002b29: ff .byte 0xff +40002b2a: 0020c0 memw +40002b2d: 8c6322 s32i a2, a3, 0x230 +40002b30: f01d retw.n +40002b32: 360000 excw +40002b35: c20041 l32r a4, 3fff3338 <_start-0xccc8> +40002b38: b180a2 excw +40002b3b: fff9 s32i.n a15, a15, 60 +40002b3d: 0020c0 memw +40002b40: 892bb2 l32i a11, a11, 0x224 +40002b43: c0a2b0 sub a10, a2, a11 +40002b46: 0e2ca7 blt a12, a10, 40002b58 +40002b49: 131aa6 blti a10, 1, 40002b60 +40002b4c: 03d2a2 addmi a10, a2, 0x300 +40002b4f: 84caa2 addi a10, a10, -124 +40002b52: fffd25 call8 40002b24 +40002b55: f01d retw.n +40002b57: 02ad00 andb b10, b13, b0 +40002b5a: fffca5 call8 40002b24 +40002b5d: f01d retw.n +40002b5f: abca00 depbits a0, a10, 26, 13 +40002b62: fffc25 call8 40002b24 +40002b65: f01d retw.n +40002b67: 413600 srli a3, a0, 6 +40002b6a: 323900 orbc b3, b9, b0 +40002b6d: 4249 s32i.n a4, a2, 16 +40002b6f: 080c movi.n a8, 0 +40002b71: f57c movi.n a5, -1 +40002b73: 0259 s32i.n a5, a2, 0 +40002b75: 1289 s32i.n a8, a2, 4 +40002b77: 2289 s32i.n a8, a2, 8 +40002b79: f01d retw.n +40002b7b: dad000 depbits a0, a0, 13, 14 +40002b7e: ff .byte 0xff +40002b7f: 3f .byte 0x3f +40002b80: ca88 l32i.n a8, a10, 48 +40002b82: ff .byte 0xff +40002b83: 3f .byte 0x3f +40002b84: ccd8 l32i.n a13, a12, 48 +40002b86: ff .byte 0xff +40002b87: 3f .byte 0x3f +40002b88: 004136 entry a1, 32 +40002b8b: fffcb1 l32r a11, 40002b7c +40002b8e: 0b98 l32i.n a9, a11, 0 +40002b90: 69bc beqz.n a9, 40002bca +40002b92: 00a0a2 movi a10, 0 +40002b95: 0d8076 loop a0, 40002ba6 +40002b98: 1988 l32i.n a8, a9, 4 +40002b9a: c08280 sub a8, a2, a8 +40002b9d: 0818a6 blti a8, 1, 40002ba9 +40002ba0: 09ad mov.n a10, a9 +40002ba2: 0998 l32i.n a9, a9, 0 +40002ba4: 198c beqz.n a9, 40002ba9 +40002ba6: fffac6 j 40002b95 +40002ba9: 0399 s32i.n a9, a3, 0 +40002bab: 1329 s32i.n a2, a3, 4 +40002bad: da9c beqz.n a10, 40002bce +40002baf: 0a39 s32i.n a3, a10, 0 +40002bb1: 0398 l32i.n a9, a3, 0 +40002bb3: 119937 bne a9, a3, 40002bc8 +40002bb6: fff2a1 l32r a10, 40002b80 +40002bb9: fff2b1 l32r a11, 40002b84 +40002bbc: b1a0c2 movi a12, 177 +40002bbf: 201110 or a1, a1, a1 +40002bc2: ffc425 call8 40002804 +40002bc5: ffff06 j 40002bc5 +40002bc8: f01d retw.n +40002bca: 0399 s32i.n a9, a3, 0 +40002bcc: 1329 s32i.n a2, a3, 4 +40002bce: 02ad mov.n a10, a2 +40002bd0: 0b39 s32i.n a3, a11, 0 +40002bd2: fff625 call8 40002b34 +40002bd5: fff606 j 40002bb1 +40002bd8: 1388 l32i.n a8, a3, 4 +40002bda: 360000 excw +40002bdd: 880041 l32r a4, 3ffe4be0 <_start-0x1b420> +40002be0: fd9102 l16si a0, a1, 0x1fa +40002be3: ff .byte 0xff +40002be4: 110826 beqi a8, -1, 40002bf9 +40002be7: ffe6a1 l32r a10, 40002b80 +40002bea: ffe6b1 l32r a11, 40002b84 +40002bed: bda0c2 movi a12, 189 +40002bf0: ffc125 call8 40002804 +40002bf3: ffff06 j 40002bf3 +40002bf6: 000000 ill +40002bf9: 823390 mull a3, a3, a9 +40002bfc: 148c beqz.n a4, 40002c01 +40002bfe: 026232 s32i a3, a2, 8 +40002c01: fe4a65 call8 400010a8 <_X_ets_enter_critical> +40002c04: 20b220 or a11, a2, a2 +40002c07: ffc6a1 l32r a10, 40002b20 +40002c0a: 0020c0 memw +40002c0d: 892aa2 l32i a10, a10, 0x224 +40002c10: f03d nop.n +40002c12: 80a3a0 add a10, a3, a10 +40002c15: fff725 call8 40002b88 +40002c18: fe4a25 call8 400010bc <_X_ets_exit_critical> +40002c1b: f01d retw.n +40002c1d: 000000 ill +40002c20: 004136 entry a1, 32 +40002c23: 0288 l32i.n a8, a2, 0 +40002c25: a03330 addx4 a3, a3, a3 +40002c28: 110826 beqi a8, -1, 40002c3d +40002c2b: ffd5a1 l32r a10, 40002b80 +40002c2e: ffd5b1 l32r a11, 40002b84 +40002c31: d3a0c2 movi a12, 211 +40002c34: ffbce5 call8 40002804 +40002c37: ffff06 j 40002c37 +40002c3a: 000000 ill +40002c3d: 048c beqz.n a4, 40002c41 +40002c3f: 2239 s32i.n a3, a2, 8 +40002c41: fe4665 call8 400010a8 <_X_ets_enter_critical> +40002c44: 20b220 or a11, a2, a2 +40002c47: ffb6a1 l32r a10, 40002b20 +40002c4a: 0020c0 memw +40002c4d: 892aa2 l32i a10, a10, 0x224 +40002c50: 80a3a0 add a10, a3, a10 +40002c53: fff365 call8 40002b88 +40002c56: fe4665 call8 400010bc <_X_ets_exit_critical> +40002c59: 000090 retw +40002c5c: 004136 entry a1, 32 +40002c5f: fe44a5 call8 400010a8 <_X_ets_enter_critical> +40002c62: ffc6c1 l32r a12, 40002b7c +40002c65: 0020f0 nop +40002c68: 002ca2 l32i a10, a12, 0 +40002c6b: 019a16 beqz a10, 40002c88 +40002c6e: 00a0b2 movi a11, 0 +40002c71: 088076 loop a0, 40002c7d +40002c74: 0812a7 beq a2, a10, 40002c80 +40002c77: 0abd mov.n a11, a10 +40002c79: 0aa8 l32i.n a10, a10, 0 +40002c7b: 9a8c beqz.n a10, 40002c88 +40002c7d: fffc06 j 40002c71 +40002c80: 4a8c beqz.n a10, 40002c88 +40002c82: 2b9c beqz.n a11, 40002c98 +40002c84: 0a88 l32i.n a8, a10, 0 +40002c86: 0b89 s32i.n a8, a11, 0 +40002c88: fe4325 call8 400010bc <_X_ets_exit_critical> +40002c8b: 00a092 movi a9, 0 +40002c8e: ffafa2 movi a10, -1 +40002c91: 0062a2 s32i a10, a2, 0 +40002c94: 2299 s32i.n a9, a2, 8 +40002c96: f01d retw.n +40002c98: 0ab8 l32i.n a11, a10, 0 +40002c9a: 0cb9 s32i.n a11, a12, 0 +40002c9c: fffa06 j 40002c88 +40002c9f: 413600 srli a3, a0, 6 +40002ca2: 028800 andb b8, b8, b0 +40002ca5: 0f0826 beqi a8, -1, 40002cb8 +40002ca8: ffb6a1 l32r a10, 40002b80 +40002cab: ffb6b1 l32r a11, 40002b84 +40002cae: 02a1c2 movi a12, 0x102 +40002cb1: ffb525 call8 40002804 +40002cb4: ffff06 j 40002cb4 +40002cb7: 02ad00 andb b10, b13, b0 +40002cba: 0b0c movi.n a11, 0 +40002cbc: 0c0c movi.n a12, 0 +40002cbe: ffeaa5 call8 40002b68 +40002cc1: f01d retw.n +40002cc3: 413600 srli a3, a0, 6 +40002cc6: 3e2500 excw +40002cc9: fe .byte 0xfe +40002cca: ff9521 l32r a2, 40002b20 +40002ccd: ffab31 l32r a3, 40002b7c +40002cd0: 0020c0 memw +40002cd3: 03b8 l32i.n a11, a3, 0 +40002cd5: 892292 l32i a9, a2, 0x224 +40002cd8: 0b9c beqz.n a11, 40002cec +40002cda: 1b88 l32i.n a8, a11, 4 +40002cdc: ffaf42 movi a4, -1 +40002cdf: c08890 sub a8, a8, a9 +40002ce2: 0e18a6 blti a8, 1, 40002cf4 +40002ce5: 3b8c beqz.n a11, 40002cec +40002ce7: 1ba8 l32i.n a10, a11, 4 +40002ce9: ffe4a5 call8 40002b34 +40002cec: fe3ce5 call8 400010bc <_X_ets_exit_critical> +40002cef: f01d retw.n +40002cf1: 000000 ill +40002cf4: 000506 j 40002d0c +40002cf7: c00000 sub a0, a0, a0 +40002cfa: b80020 excw +40002cfd: 229203 excw +40002d00: 1689 s32i.n a8, a6, 4 +40002d02: fe7b addi.n a15, a14, 7 +40002d04: 1ba8 l32i.n a10, a11, 4 +40002d06: c0aa90 sub a10, a10, a9 +40002d09: d81ae6 bgei a10, 1, 40002ce5 +40002d0c: 0b5d mov.n a5, a11 +40002d0e: 13b9 s32i.n a11, a3, 4 +40002d10: 3bd8 l32i.n a13, a11, 12 +40002d12: 23d9 s32i.n a13, a3, 8 +40002d14: 0bc8 l32i.n a12, a11, 0 +40002d16: 03c9 s32i.n a12, a3, 0 +40002d18: 0b49 s32i.n a4, a11, 0 +40002d1a: fe3a25 call8 400010bc <_X_ets_exit_critical> +40002d1d: 3588 l32i.n a8, a5, 12 +40002d1f: 45a8 l32i.n a10, a5, 16 +40002d21: 0008e0 callx8 a8 +40002d24: fe3825 call8 400010a8 <_X_ets_enter_critical> +40002d27: 0598 l32i.n a9, a5, 0 +40002d29: 25a8 l32i.n a10, a5, 8 +40002d2b: ca0966 bnei a9, -1, 40002cf9 +40002d2e: fc7a16 beqz a10, 40002cf9 +40002d31: 15b8 l32i.n a11, a5, 4 +40002d33: abaa add.n a10, a11, a10 +40002d35: 05bd mov.n a11, a5 +40002d37: 15a9 s32i.n a10, a5, 4 +40002d39: ffe4e5 call8 40002b88 +40002d3c: ffee46 j 40002cf9 +40002d3f: 413600 srli a3, a0, 6 +40002d42: fa1c00 depbits a0, a12, 15, 2 +40002d45: 0b0c movi.n a11, 0 +40002d47: 0c0c movi.n a12, 0 +40002d49: fe24e5 call8 40000f98 <_XX_unk0f96> +40002d4c: f01d retw.n +40002d4e: 360000 excw +40002d51: 880041 l32r a4, 3ffe4d54 <_start-0x1b2ac> +40002d54: 088c02 excw +40002d57: f01d retw.n +40002d59: 0a0c movi.n a10, 0 +40002d5b: fff6a5 call8 40002cc4 +40002d5e: f01d retw.n +40002d60: 002d40 excw +40002d63: 2d5040 excw +40002d66: e04000 subx4 a4, a0, a0 +40002d69: ffda add.n a15, a15, a13 +40002d6b: 3f .byte 0x3f +40002d6c: 004136 entry a1, 32 +40002d6f: 0aa0a2 movi a10, 10 +40002d72: fffbb1 l32r a11, 40002d60 +40002d75: ff8181 l32r a8, 40002b7c +40002d78: 020c movi.n a2, 0 +40002d7a: 02cd mov.n a12, a2 +40002d7c: 006822 s32i a2, a8, 0 +40002d7f: fe36a5 call8 400010e8 <_X_ets_isr_attach> +40002d82: 00a4a2 movi a10, 0x400 +40002d85: fe37e5 call8 40001104 <_X_ets_isr_unmask> +40002d88: fff7a1 l32r a10, 40002d64 +40002d8b: fb1c movi.n a11, 31 +40002d8d: fff6c1 l32r a12, 40002d68 +40002d90: 4d0c movi.n a13, 4 +40002d92: fe1c25 call8 40000f54 <_X_ets_task> +40002d95: ff6291 l32r a9, 40002b20 +40002d98: 0020c0 memw +40002d9b: 8c6922 s32i a2, a9, 0x230 +40002d9e: 88a0a2 movi a10, 136 +40002da1: 0020c0 memw +40002da4: 8a69a2 s32i a10, a9, 0x228 +40002da7: 0020c0 memw +40002daa: 886922 s32i a2, a9, 0x220 +40002dad: f01d retw.n +40002daf: c84400 excw +40002db2: ff .byte 0xff +40002db3: 3f .byte 0x3f + +40002db4 : +40002db4: 004136 entry a1, 32 +40002db7: 0392e5 call8 400066e4 <_xtos_set_intlevel+0x74> +40002dba: fffd81 l32r a8, 40002db0 +40002dbd: 002882 l32i a8, a8, 0 +40002dc0: 0a3d mov.n a3, a10 +40002dc2: 822820 mull a2, a8, a2 +40002dc5: 0391e5 call8 400066e4 <_xtos_set_intlevel+0x74> +40002dc8: c09a30 sub a9, a10, a3 +40002dcb: 08b927 bgeu a9, a2, 40002dd7 +40002dce: 039165 call8 400066e4 <_xtos_set_intlevel+0x74> +40002dd1: c0aa30 sub a10, a10, a3 +40002dd4: f63a27 bltu a10, a2, 40002dce +40002dd7: f01d retw.n +40002dd9: 000000 ill + +40002ddc : +40002ddc: 004136 entry a1, 32 +40002ddf: fff431 l32r a3, 40002db0 +40002de2: 0329 s32i.n a2, a3, 0 +40002de4: f01d retw.n + ... + +40002de8 : +40002de8: 004136 entry a1, 32 +40002deb: fff121 l32r a2, 40002db0 +40002dee: 0228 l32i.n a2, a2, 0 +40002df0: f01d retw.n +40002df2: 360000 excw +40002df5: a60041 l32r a4, 3ffec5f8 <_start-0x13a08> +40002df8: 424913 excw +40002dfb: 30efa0 xor a14, a15, a10 +40002dfe: 6d1450 excw +40002e01: 326003 excw +40002e04: a57621 l32r a2, 3ffec3dc <_start-0x13c24> +40002e07: 520a add.n a5, a2, a0 +40002e09: 1b0002 l8ui a0, a0, 27 +40002e0c: 454022 s8i a2, a0, 69 +40002e0f: 404030 excw +40002e12: f03d74 excw +40002e15: 25a376 loopgtz a3, 40002e3e +40002e18: 000232 l8ui a3, a2, 0 +40002e1b: 010252 l8ui a5, a2, 1 +40002e1e: 020262 l8ui a6, a2, 2 +40002e21: 030272 l8ui a7, a2, 3 +40002e24: 303340 xor a3, a3, a4 +40002e27: 224b addi.n a2, a2, 4 +40002e29: 743030 extui a3, a3, 0, 8 +40002e2c: 303530 xor a3, a5, a3 +40002e2f: 743030 extui a3, a3, 0, 8 +40002e32: 303630 xor a3, a6, a3 +40002e35: 743030 extui a3, a3, 0, 8 +40002e38: 303730 xor a3, a7, a3 +40002e3b: 744030 extui a4, a3, 0, 8 +40002e3e: 042d mov.n a2, a4 +40002e40: f01d retw.n +40002e42: 220000 orb b0, b0, b0 +40002e45: 1defa0 excw +40002e48: 0000f0 callx12 a0 + ... + +40002e4c <_st_0x3fffdb10_uartdev>: +40002e4c: ffdb10 excw +40002e4f: 3f .byte 0x3f + +40002e50 <_c_0x3fffdb00>: +40002e50: ffdb00 excw +40002e53: 3f .byte 0x3f +40002e54: 004000 break 0, 0 + ... + +40002e58 <_XX_unk2e58>: +40002e58: 006136 entry a1, 48 +40002e5b: 02ad mov.n a10, a2 +40002e5d: 005765 call8 400033d4 <_c_0x0000ffff+0x4> +40002e60: fffb41 l32r a4, 40002e4c <_st_0x3fffdb10_uartdev> +40002e63: c498 l32i.n a9, a4, 48 +40002e65: 230c movi.n a3, 2 +40002e67: 39ac beqz.n a9, 40002e8e <_XX_unk2e58+0x36> +40002e69: 211926 beqi a9, 1, 40002e8e <_XX_unk2e58+0x36> +40002e6c: f9b1a1 l32r a10, 40001530 <_s_waiting_for_host+0x4> +40002e6f: f9b1b1 l32r a11, 40001534 <_s_waiting_for_host+0x8> +40002e72: 01a0c2 movi a12, 1 +40002e75: 00b125 call8 40003988 <_X_RcvMsg> +40002e78: 00ea56 bnez a10, 40002e8a <_XX_unk2e58+0x32> +40002e7b: f9ada1 l32r a10, 40001530 <_s_waiting_for_host+0x4> +40002e7e: 000a82 l8ui a8, a10, 0 +40002e81: 010a92 l8ui a9, a10, 1 +40002e84: 002856 bnez a8, 40002e8a <_XX_unk2e58+0x32> +40002e87: 1e8926 beqi a9, 8, 40002ea9 <_XX_unk2e58+0x51> +40002e8a: 020c movi.n a2, 0 +40002e8c: f01d retw.n +40002e8e: 20a220 or a10, a2, a2 +40002e91: 01a0b2 movi a11, 1 +40002e94: 005b65 call8 4000344c <_c_0x000fffff+0x4> +40002e97: f4b0a0 extui a11, a10, 0, 16 +40002e9a: fecb16 beqz a11, 40002e8a <_XX_unk2e58+0x32> +40002e9d: 02ad mov.n a10, a2 +40002e9f: 0064a5 call8 400034e8 +40002ea2: c439 s32i.n a3, a4, 48 +40002ea4: fff106 j 40002e6c <_XX_unk2e58+0x14> +40002ea7: 0c0000 excw +40002eaa: 411c movi.n a1, 20 +40002eac: ffe9 s32i.n a14, a15, 60 +40002eae: 8b0c movi.n a11, 8 +40002eb0: 015432 s16i a3, a4, 2 +40002eb3: 0144b2 s8i a11, a4, 1 +40002eb6: 0044c2 s8i a12, a4, 0 +40002eb9: 01bd mov.n a11, a1 +40002ebb: c44b addi.n a12, a4, 4 +40002ebd: 004065 call8 400032c4 <_X_UartConnectProc> +40002ec0: f98d61 l32r a6, 400014f4 <_c_0x60008e00> +40002ec3: f98b51 l32r a5, 400014f0 <_c_0xffff8fff> +40002ec6: 00a0d2 movi a13, 0 +40002ec9: 0844d2 s8i a13, a4, 8 +40002ecc: 0944d2 s8i a13, a4, 9 +40002ecf: 053216 beqz a2, 40002f26 <_XX_unk2e58+0xce> +40002ed2: f897f1 l32r a15, 40001130 <_c_0x60003e00> +40002ed5: ffdfa1 l32r a10, 40002e54 <_c_0x3fffdb00+0x4> +40002ed8: 0020c0 memw +40002edb: 9626d2 l32i a13, a6, 0x258 +40002ede: 10dd50 and a13, a13, a5 +40002ee1: 0020c0 memw +40002ee4: 9666d2 s32i a13, a6, 0x258 +40002ee7: 0020c0 memw +40002eea: 9626c2 l32i a12, a6, 0x258 +40002eed: 20cca0 or a12, a12, a10 +40002ef0: 0020c0 memw +40002ef3: 9666c2 s32i a12, a6, 0x258 +40002ef6: 0020c0 memw +40002ef9: 9526b2 l32i a11, a6, 0x254 +40002efc: 10bb50 and a11, a11, a5 +40002eff: 0020c0 memw +40002f02: 9566b2 s32i a11, a6, 0x254 +40002f05: 0020c0 memw +40002f08: 952692 l32i a9, a6, 0x254 +40002f0b: 2099a0 or a9, a9, a10 +40002f0e: 0020c0 memw +40002f11: 956692 s32i a9, a6, 0x254 +40002f14: f88681 l32r a8, 4000112c <_c_0xfffdffff> +40002f17: 0020c0 memw +40002f1a: f02fe2 l32i a14, a15, 0x3c0 +40002f1d: 10ee80 and a14, a14, a8 +40002f20: 0020c0 memw +40002f23: f06fe2 s32i a14, a15, 0x3c0 +40002f26: 830c movi.n a3, 8 +40002f28: 20a440 or a10, a4, a4 +40002f2b: 0aa0b2 movi a11, 10 +40002f2e: 008fa5 call8 40003828 <_X_SendMsg> +40002f31: 0b1a26 beqi a10, 1, 40002f40 <_XX_unk2e58+0xe8> +40002f34: ffc332 addi a3, a3, -1 +40002f37: fed356 bnez a3, 40002f28 <_XX_unk2e58+0xd0> +40002f3a: 220c movi.n a2, 2 +40002f3c: f01d retw.n +40002f3e: 160000 excw +40002f41: c0f462 s32ri a6, a4, 0x300 +40002f44: f20020 rems a0, a0, a2 +40002f47: 509626 beqi a6, 10, 40002f9b <_X_UartDwnLdProc+0x2f> +40002f4a: ff .byte 0xff +40002f4b: 20c010 or a12, a0, a1 +40002f4e: 66f200 excw +40002f51: 20c096 bltz a0, 40003161 <_XX_unk313c+0x25> +40002f54: 26e200 excw +40002f57: 20c096 bltz a0, 40003167 <_XX_unk313c+0x2b> +40002f5a: 66e200 excw +40002f5d: 020c96 bltz a12, 40002f81 <_X_UartDwnLdProc+0x15> +40002f60: f01d retw.n + ... + +40002f64 <_c_0x3fffdb04>: +40002f64: ffdb04 excw +40002f67: 3f .byte 0x3f +40002f68: ffc9f0 excw +40002f6b: 3f .byte 0x3f + +40002f6c <_X_UartDwnLdProc>: +40002f6c: 00a136 entry a1, 80 +40002f6f: 0a2c movi.n a10, 32 +40002f71: 280c movi.n a8, 2 +40002f73: ffb751 l32r a5, 40002e50 <_c_0x3fffdb00> +40002f76: 046d mov.n a6, a4 +40002f78: 070c movi.n a7, 0 +40002f7a: 6179 s32i.n a7, a1, 24 +40002f7c: 140c movi.n a4, 1 +40002f7e: 004542 s8i a4, a5, 0 +40002f81: 015582 s16i a8, a5, 2 +40002f84: fe17e5 call8 40001104 <_X_ets_isr_unmask> +40002f87: 6198 l32i.n a9, a1, 24 +40002f89: 5c2926 beqi a9, 2, 40002fe9 <_X_UartDwnLdProc+0x7d> +40002f8c: f43030 extui a3, a3, 0, 16 +40002f8f: 870c movi.n a7, 8 +40002f91: 8169 s32i.n a6, a1, 32 +40002f93: fff491 l32r a9, 40002f64 <_c_0x3fffdb04> +40002f96: 9199 s32i.n a9, a1, 36 +40002f98: 20b330 or a11, a3, a3 +40002f9b: 20a220 or a10, a2, a2 +40002f9e: 00a0c2 movi a12, 0 +40002fa1: 009e65 call8 40003988 <_X_RcvMsg> +40002fa4: aa0b addi.n a10, a10, -1 +40002fa6: 134a16 beqz a10, 400030de <_X_UartDwnLdProc+0x172> +40002fa9: 0002b2 l8ui a11, a2, 0 +40002fac: 1a0c movi.n a10, 1 +40002fae: 04fb16 beqz a11, 40003001 <_X_UartDwnLdProc+0x95> +40002fb1: 590c movi.n a9, 5 +40002fb3: 7199 s32i.n a9, a1, 28 +40002fb5: 094592 s8i a9, a5, 9 +40002fb8: 04cd mov.n a12, a4 +40002fba: 0105d2 l8ui a13, a5, 1 +40002fbd: 01a0e2 movi a14, 1 +40002fc0: 93aea0 movnez a10, a14, a10 +40002fc3: 0845a2 s8i a10, a5, 8 +40002fc6: f8cdd2 addi a13, a13, -8 +40002fc9: 83c7d0 moveqz a12, a7, a13 +40002fcc: 206cc0 or a6, a12, a12 +40002fcf: 011c16 beqz a12, 40002fe4 <_X_UartDwnLdProc+0x78> +40002fd2: 20a550 or a10, a5, a5 +40002fd5: 0aa0b2 movi a11, 10 +40002fd8: 0084e5 call8 40003828 <_X_SendMsg> +40002fdb: 161a26 beqi a10, 1, 40002ff5 <_X_UartDwnLdProc+0x89> +40002fde: ffc662 addi a6, a6, -1 +40002fe1: fed656 bnez a6, 40002fd2 <_X_UartDwnLdProc+0x66> +40002fe4: 61f8 l32i.n a15, a1, 24 +40002fe6: ae2f66 bnei a15, 2, 40002f98 <_X_UartDwnLdProc+0x2c> +40002fe9: 20a0a2 movi a10, 32 +40002fec: fe10a5 call8 400010f8 <_X_ets_isr_mask> +40002fef: 00a022 movi a2, 0 +40002ff2: 000090 retw +40002ff5: 20a0a2 movi a10, 32 +40002ff8: fe0fe5 call8 400010f8 <_X_ets_isr_mask> +40002ffb: 01a022 movi a2, 1 +40002ffe: 000090 retw +40003001: 010292 l8ui a9, a2, 1 +40003004: ffd981 l32r a8, 40002f68 <_c_0x3fffdb04+0x4> +40003007: 07a9f6 bgeui a9, 12, 40003012 <_X_UartDwnLdProc+0xa6> +4000300a: a08980 addx4 a8, a9, a8 +4000300d: 0888 l32i.n a8, a8, 0 +4000300f: 0008a0 jx a8 +40003012: 1a0c movi.n a10, 1 +40003014: 590c movi.n a9, 5 +40003016: 7199 s32i.n a9, a1, 28 +40003018: 7198 l32i.n a9, a1, 28 +4000301a: ffe5c6 j 40002fb5 <_X_UartDwnLdProc+0x49> +4000301d: 02ad00 andb b10, b13, b0 +40003020: 1cc1b2 addi a11, a1, 28 +40003023: 01cd mov.n a12, a1 +40003025: 001ea5 call8 40003210 <_XX_unk3210> +40003028: 5b0c movi.n a11, 5 +4000302a: 0145b2 s8i a11, a5, 1 +4000302d: fff9c6 j 40003018 <_X_UartDwnLdProc+0xac> +40003030: 02ad mov.n a10, a2 +40003032: 1cc1b2 addi a11, a1, 28 +40003035: 001ae5 call8 400031e4 <_XX_unk31e4> +40003038: bc0c movi.n a12, 11 +4000303a: 0145c2 s8i a12, a5, 1 +4000303d: fff5c6 j 40003018 <_X_UartDwnLdProc+0xac> +40003040: 02ad mov.n a10, a2 +40003042: 1cc1b2 addi a11, a1, 28 +40003045: 91c8 l32i.n a12, a1, 36 +40003047: 002d25 call8 40003318 <_X_UartRegReadProc> +4000304a: ad0c movi.n a13, 10 +4000304c: 0145d2 s8i a13, a5, 1 +4000304f: fff146 j 40003018 <_X_UartDwnLdProc+0xac> +40003052: 02ad mov.n a10, a2 +40003054: 1cc1b2 addi a11, a1, 28 +40003057: 0027e5 call8 400032d4 <_X_UartRegWriteProc> +4000305a: 9e0c movi.n a14, 9 +4000305c: 0145e2 s8i a14, a5, 1 +4000305f: ffed46 j 40003018 <_X_UartDwnLdProc+0xac> +40003062: 02ad mov.n a10, a2 +40003064: 1cc1b2 addi a11, a1, 28 +40003067: 91c8 l32i.n a12, a1, 36 +40003069: 0025a5 call8 400032c4 <_X_UartConnectProc> +4000306c: 014572 s8i a7, a5, 1 +4000306f: ffe946 j 40003018 <_X_UartDwnLdProc+0xac> +40003072: 02ad mov.n a10, a2 +40003074: 1cc1b2 addi a11, a1, 28 +40003077: 01cd mov.n a12, a1 +40003079: 001c65 call8 40003240 <_XX_unk3240> +4000307c: 7f0c movi.n a15, 7 +4000307e: 0145f2 s8i a15, a5, 1 +40003081: ffe4c6 j 40003018 <_X_UartDwnLdProc+0xac> +40003084: 02ad mov.n a10, a2 +40003086: 1cc1b2 addi a11, a1, 28 +40003089: 01cd mov.n a12, a1 +4000308b: 81d8 l32i.n a13, a1, 32 +4000308d: 0020e5 call8 4000329c <_X_MemDwnLdStopReqMsgProc> +40003090: 680c movi.n a8, 6 +40003092: 014582 s8i a8, a5, 1 +40003095: ffdfc6 j 40003018 <_X_UartDwnLdProc+0xac> +40003098: 02ad mov.n a10, a2 +4000309a: 1cc1b2 addi a11, a1, 28 +4000309d: 01cd mov.n a12, a1 +4000309f: 0011e5 call8 400031bc <_XX_unk31bc> +400030a2: 490c movi.n a9, 4 +400030a4: 81c8 l32i.n a12, a1, 32 +400030a6: 0b0c movi.n a11, 0 +400030a8: 0cb9 s32i.n a11, a12, 0 +400030aa: 014592 s8i a9, a5, 1 +400030ad: ffd9c6 j 40003018 <_X_UartDwnLdProc+0xac> +400030b0: 02ad mov.n a10, a2 +400030b2: 1cc1b2 addi a11, a1, 28 +400030b5: 01cd mov.n a12, a1 +400030b7: 000865 call8 4000313c <_XX_unk313c> +400030ba: 3d0c movi.n a13, 3 +400030bc: 0145d2 s8i a13, a5, 1 +400030bf: ffd546 j 40003018 <_X_UartDwnLdProc+0xac> +400030c2: 00a0a2 movi a10, 0 +400030c5: 00a0b2 movi a11, 0 +400030c8: 012a65 call8 40004370 <_X_spi_flash_attach> +400030cb: 20a220 or a10, a2, a2 +400030ce: 1cc1b2 addi a11, a1, 28 +400030d1: 01cd mov.n a12, a1 +400030d3: 0001e5 call8 400030f0 <_X_FlashDwnLdStartMsgProc> +400030d6: 2e0c movi.n a14, 2 +400030d8: 0145e2 s8i a14, a5, 1 +400030db: ffce46 j 40003018 <_X_UartDwnLdProc+0xac> +400030de: 20a0a2 movi a10, 32 +400030e1: fe0165 call8 400010f8 <_X_ets_isr_mask> +400030e4: 01a022 movi a2, 1 +400030e7: 000090 retw + ... + +400030ec <_c_0x00001800>: +400030ec: 001800 movsp a0, a8 + ... + +400030f0 <_X_FlashDwnLdStartMsgProc>: +400030f0: 004136 entry a1, 32 +400030f3: 011282 l16ui a8, a2, 2 +400030f6: 05a0a2 movi a10, 5 +400030f9: 05b826 beqi a8, 16, 40003102 <_X_FlashDwnLdStartMsgProc+0x12> +400030fc: 120c movi.n a2, 1 +400030fe: 03a9 s32i.n a10, a3, 0 +40003100: f01d retw.n +40003102: 0422b2 l32i a11, a2, 16 +40003105: fff991 l32r a9, 400030ec <_c_0x00001800> +40003108: f039b7 bltu a9, a11, 400030fc <_X_FlashDwnLdStartMsgProc+0xc> +4000310b: 0264b2 s32i a11, a4, 8 +4000310e: 0a0c movi.n a10, 0 +40003110: 32c8 l32i.n a12, a2, 12 +40003112: 22d8 l32i.n a13, a2, 8 +40003114: 04d9 s32i.n a13, a4, 0 +40003116: 14c9 s32i.n a12, a4, 4 +40003118: 34a9 s32i.n a10, a4, 12 +4000311a: 44a9 s32i.n a10, a4, 16 +4000311c: 52b8 l32i.n a11, a2, 20 +4000311e: 54b9 s32i.n a11, a4, 20 +40003120: 0132a5 call8 4000444c +40003123: 54a8 l32i.n a10, a4, 20 +40003125: 04b8 l32i.n a11, a4, 0 +40003127: 0178e5 call8 400048b4 <_X_SPIEraseArea> +4000312a: 6a8c beqz.n a10, 40003134 <_X_FlashDwnLdStartMsgProc+0x44> +4000312c: 120c movi.n a2, 1 +4000312e: 6e0c movi.n a14, 6 +40003130: 03e9 s32i.n a14, a3, 0 +40003132: f01d retw.n +40003134: 020c movi.n a2, 0 +40003136: f01d retw.n +40003138: 001810 movsp a1, a8 + ... + +4000313c <_XX_unk313c>: +4000313c: 004136 entry a1, 32 +4000313f: 011292 l16ui a9, a2, 2 +40003142: fffd81 l32r a8, 40003138 <_X_FlashDwnLdStartMsgProc+0x48> +40003145: 5a0c movi.n a10, 5 +40003147: 07b897 bgeu a8, a9, 40003152 <_XX_unk313c+0x16> +4000314a: 120c movi.n a2, 1 +4000314c: 03a9 s32i.n a10, a3, 0 +4000314e: f01d retw.n +40003150: c80000 excw +40003153: 32b834 excw +40003156: 24d8 l32i.n a13, a4, 8 +40003158: ee9bc7 bne a11, a12, 4000314a <_XX_unk313c+0xe> +4000315b: 22b8 l32i.n a11, a2, 8 +4000315d: 3e0c movi.n a14, 3 +4000315f: e73db7 bltu a13, a11, 4000314a <_XX_unk313c+0xe> +40003162: e48eb7 bany a14, a11, 4000314a <_XX_unk313c+0xe> +40003165: 040252 l8ui a5, a2, 4 +40003168: 18c262 addi a6, a2, 24 +4000316b: fc1b addi.n a15, a12, 1 +4000316d: 34f9 s32i.n a15, a4, 12 +4000316f: 06ad mov.n a10, a6 +40003171: ffc825 call8 40002df4 +40003174: 0915a7 beq a5, a10, 40003181 <_XX_unk313c+0x45> +40003177: 720c movi.n a2, 7 +40003179: 0329 s32i.n a2, a3, 0 +4000317b: 120c movi.n a2, 1 +4000317d: f01d retw.n +4000317f: a20000 muluh a0, a0, a0 +40003182: 600524 excw +40003185: c820b6 bltui a0, 2, 40003151 <_XX_unk313c+0x15> +40003188: 5ae522 s32c1i a2, a5, 0x168 +4000318b: 54a801 l32r a0, 3ffd842c <_start-0x27bd4> +4000318e: 06bd mov.n a11, a6 +40003190: 22c8 l32i.n a12, a2, 8 +40003192: 017065 call8 40004898 +40003195: 06ad mov.n a10, a6 +40003197: 22b8 l32i.n a11, a2, 8 +40003199: ffc5a5 call8 40002df4 +4000319c: 0815a7 beq a5, a10, 400031a8 <_XX_unk313c+0x6c> +4000319f: 120c movi.n a2, 1 +400031a1: 880c movi.n a8, 8 +400031a3: 0389 s32i.n a8, a3, 0 +400031a5: f01d retw.n +400031a7: 44b800 extui a11, a0, 8, 5 +400031aa: 5498 l32i.n a9, a4, 20 +400031ac: 22a8 l32i.n a10, a2, 8 +400031ae: 020c movi.n a2, 0 +400031b0: 99aa add.n a9, a9, a10 +400031b2: bbaa add.n a11, a11, a10 +400031b4: 44b9 s32i.n a11, a4, 16 +400031b6: 5499 s32i.n a9, a4, 20 +400031b8: f01d retw.n + ... + +400031bc <_XX_unk31bc>: +400031bc: 004136 entry a1, 32 +400031bf: 3498 l32i.n a9, a4, 12 +400031c1: 2a0c movi.n a10, 2 +400031c3: 1488 l32i.n a8, a4, 4 +400031c5: 64a9 s32i.n a10, a4, 24 +400031c7: 6a0c movi.n a10, 6 +400031c9: 079897 bne a8, a9, 400031d4 <_XX_unk31bc+0x18> +400031cc: 44c8 l32i.n a12, a4, 16 +400031ce: 0024b2 l32i a11, a4, 0 +400031d1: 071bc7 beq a11, a12, 400031dc <_XX_unk31bc+0x20> +400031d4: 120c movi.n a2, 1 +400031d6: 03a9 s32i.n a10, a3, 0 +400031d8: f01d retw.n +400031da: 650000 extui a0, a0, 16, 7 +400031dd: 0c0131 l32r a3, 3ffc61e4 <_start-0x39e1c> +400031e0: f01d02 l16ui a0, a13, 0x1e0 + ... + +400031e4 <_XX_unk31e4>: +400031e4: 004136 entry a1, 32 +400031e7: 011282 l16ui a8, a2, 2 +400031ea: 891c movi.n a9, 24 +400031ec: 081897 beq a8, a9, 400031f8 <_XX_unk31e4+0x14> +400031ef: 120c movi.n a2, 1 +400031f1: 5a0c movi.n a10, 5 +400031f3: 03a9 s32i.n a10, a3, 0 +400031f5: f01d retw.n +400031f7: 22a200 orb b10, b2, b0 +400031fa: 22b202 l32ai a0, a2, 136 +400031fd: 22c203 excw +40003200: 52d804 excw +40003203: 62e8 l32i.n a14, a2, 24 +40003205: 72f8 l32i.n a15, a2, 28 +40003207: 0173a5 call8 40004940 <_XX_unk4940> +4000320a: 020c movi.n a2, 0 +4000320c: f01d retw.n + ... + +40003210 <_XX_unk3210>: +40003210: 004136 entry a1, 32 +40003213: 011252 l16ui a5, a2, 2 +40003216: 05a072 movi a7, 5 +40003219: 05b526 beqi a5, 16, 40003222 <_XX_unk3210+0x12> +4000321c: 120c movi.n a2, 1 +4000321e: 0379 s32i.n a7, a3, 0 +40003220: f01d retw.n +40003222: 4288 l32i.n a8, a2, 16 +40003224: ffb261 l32r a6, 400030ec <_c_0x00001800> +40003227: 5298 l32i.n a9, a2, 20 +40003229: ef3687 bltu a6, a8, 4000321c <_XX_unk3210+0xc> +4000322c: 2489 s32i.n a8, a4, 8 +4000322e: 5499 s32i.n a9, a4, 20 +40003230: 32a8 l32i.n a10, a2, 12 +40003232: 22b8 l32i.n a11, a2, 8 +40003234: 04b9 s32i.n a11, a4, 0 +40003236: 14a9 s32i.n a10, a4, 4 +40003238: 020c movi.n a2, 0 +4000323a: 3429 s32i.n a2, a4, 12 +4000323c: 4429 s32i.n a2, a4, 16 +4000323e: f01d retw.n + +40003240 <_XX_unk3240>: +40003240: 004136 entry a1, 32 +40003243: 011292 l16ui a9, a2, 2 +40003246: ffbc81 l32r a8, 40003138 <_X_FlashDwnLdStartMsgProc+0x48> +40003249: 5a0c movi.n a10, 5 +4000324b: 05b897 bgeu a8, a9, 40003254 <_XX_unk3240+0x14> +4000324e: 120c movi.n a2, 1 +40003250: 03a9 s32i.n a10, a3, 0 +40003252: f01d retw.n +40003254: 34c8 l32i.n a12, a4, 12 +40003256: 32b8 l32i.n a11, a2, 12 +40003258: 24d8 l32i.n a13, a4, 8 +4000325a: f09bc7 bne a11, a12, 4000324e <_XX_unk3240+0xe> +4000325d: 22b8 l32i.n a11, a2, 8 +4000325f: eb3db7 bltu a13, a11, 4000324e <_XX_unk3240+0xe> +40003262: 040252 l8ui a5, a2, 4 +40003265: 18c262 addi a6, a2, 24 +40003268: ec1b addi.n a14, a12, 1 +4000326a: 34e9 s32i.n a14, a4, 12 +4000326c: 06ad mov.n a10, a6 +4000326e: ffb865 call8 40002df4 +40003271: 0715a7 beq a5, a10, 4000327c <_XX_unk3240+0x3c> +40003274: 120c movi.n a2, 1 +40003276: 7f0c movi.n a15, 7 +40003278: 03f9 s32i.n a15, a3, 0 +4000327a: f01d retw.n +4000327c: 20b660 or a11, a6, a6 +4000327f: 0524a2 l32i a10, a4, 20 +40003282: 0222c2 l32i a12, a2, 8 +40003285: feb3e5 call8 40001dc4 <_X_ets_memcpy> +40003288: 4498 l32i.n a9, a4, 16 +4000328a: 2288 l32i.n a8, a2, 8 +4000328c: 5428 l32i.n a2, a4, 20 +4000328e: 998a add.n a9, a9, a8 +40003290: 228a add.n a2, a2, a8 +40003292: 5429 s32i.n a2, a4, 20 +40003294: 4499 s32i.n a9, a4, 16 +40003296: 020c movi.n a2, 0 +40003298: f01d retw.n + ... + +4000329c <_X_MemDwnLdStopReqMsgProc>: +4000329c: 004136 entry a1, 32 +4000329f: 011272 l16ui a7, a2, 2 +400032a2: 860c movi.n a6, 8 +400032a4: 08b677 bgeu a6, a7, 400032b0 <_X_MemDwnLdStopReqMsgProc+0x14> +400032a7: 120c movi.n a2, 1 +400032a9: 580c movi.n a8, 5 +400032ab: 0389 s32i.n a8, a3, 0 +400032ad: f01d retw.n +400032af: 2b0c00 depbits a0, a12, 18, 1 +400032b2: 0a0c movi.n a10, 0 +400032b4: 3298 l32i.n a9, a2, 12 +400032b6: 0802c2 l8ui a12, a2, 8 +400032b9: 020c movi.n a2, 0 +400032bb: 83abc0 moveqz a10, a11, a12 +400032be: 64a9 s32i.n a10, a4, 24 +400032c0: 0599 s32i.n a9, a5, 0 +400032c2: f01d retw.n + +400032c4 <_X_UartConnectProc>: +400032c4: 004136 entry a1, 32 +400032c7: 2228 l32i.n a2, a2, 8 +400032c9: 0429 s32i.n a2, a4, 0 +400032cb: 020c movi.n a2, 0 +400032cd: 0329 s32i.n a2, a3, 0 +400032cf: f01d retw.n +400032d1: 000000 ill + +400032d4 <_X_UartRegWriteProc>: +400032d4: 004136 entry a1, 32 +400032d7: 011242 l16ui a4, a2, 2 +400032da: 228b addi.n a2, a2, 8 +400032dc: 414440 srli a4, a4, 4 +400032df: e4ac beqz.n a4, 40003311 <_X_UartRegWriteProc+0x3d> +400032e1: ffaf52 movi a5, -1 +400032e4: 0612a2 l16ui a10, a2, 12 +400032e7: fface5 call8 40002db4 +400032ea: 002292 l32i a9, a2, 0 +400032ed: 0020c0 memw +400032f0: 012282 l32i a8, a2, 4 +400032f3: 0222b2 l32i a11, a2, 8 +400032f6: 09a8 l32i.n a10, a9, 0 +400032f8: 30cb50 xor a12, a11, a5 +400032fb: 10aac0 and a10, a10, a12 +400032fe: 1088b0 and a8, a8, a11 +40003301: 2088a0 or a8, a8, a10 +40003304: 0020c0 memw +40003307: 0989 s32i.n a8, a9, 0 +40003309: 10c222 addi a2, a2, 16 +4000330c: 440b addi.n a4, a4, -1 +4000330e: d214e6 bgei a4, 1, 400032e4 <_X_UartRegWriteProc+0x10> +40003311: 020c movi.n a2, 0 +40003313: 0329 s32i.n a2, a3, 0 +40003315: f01d retw.n + ... + +40003318 <_X_UartRegReadProc>: +40003318: 004136 entry a1, 32 +4000331b: 2228 l32i.n a2, a2, 8 +4000331d: 0020c0 memw +40003320: 0228 l32i.n a2, a2, 0 +40003322: 0429 s32i.n a2, a4, 0 +40003324: 020c movi.n a2, 0 +40003326: 0329 s32i.n a2, a3, 0 +40003328: f01d retw.n + ... + +4000332c <_c_115200>: +4000332c: 01c200 slli a12, a2, 32 + ... + +40003330 <_c_0x3feffe00>: +40003330: effe00 excw +40003333: 3f .byte 0x3f + +40003334 <_c_0xffff83ff>: +40003334: ff .byte 0xff +40003335: ffff83 excw + +40003338 <_c_0x00001400>: +40003338: 001400 movsp a0, a4 + ... + +4000333c <_c_0x40003728_uart_int_handler>: +4000333c: 3728 l32i.n a2, a7, 12 +4000333e: 2c4000 excw + +40003340 <_c_0x3fffdb2c_uart_int_handler_arg>: +40003340: db2c movi.n a11, 45 +40003342: ff .byte 0xff +40003343: 3f .byte 0x3f + +40003344 <_X_uart_attach>: +40003344: 004136 entry a1, 32 +40003347: 03a0a2 movi a10, 3 +4000334a: 01a082 movi a8, 1 +4000334d: fff7b1 l32r a11, 4000332c <_c_115200> +40003350: febf91 l32r a9, 40002e4c <_st_0x3fffdb10_uartdev> +40003353: 0d0c movi.n a13, 0 +40003355: 59d9 s32i.n a13, a9, 20 +40003357: 29d9 s32i.n a13, a9, 8 +40003359: 39d9 s32i.n a13, a9, 12 +4000335b: 49d9 s32i.n a13, a9, 16 +4000335d: b9d9 s32i.n a13, a9, 44 +4000335f: c9d9 s32i.n a13, a9, 48 +40003361: d9d9 s32i.n a13, a9, 52 +40003363: 1849d2 s8i a13, a9, 24 +40003366: 1949d2 s8i a13, a9, 25 +40003369: 09b9 s32i.n a11, a9, 0 +4000336b: 284982 s8i a8, a9, 40 +4000336e: 19a9 s32i.n a10, a9, 4 +40003370: 40c9c2 addi a12, a9, 64 +40003373: 79c9 s32i.n a12, a9, 28 +40003375: 89c9 s32i.n a12, a9, 32 +40003377: 99c9 s32i.n a12, a9, 36 +40003379: 20a0a2 movi a10, 32 +4000337c: fdd7a5 call8 400010f8 <_X_ets_isr_mask> +4000337f: 1facc2 movi a12, 0xfffffc1f +40003382: a0a0b2 movi a11, 160 +40003385: ffeaf1 l32r a15, 40003330 <_c_0x3feffe00> +40003388: 0020c0 memw +4000338b: 9e2fa2 l32i a10, a15, 0x278 +4000338e: 10aac0 and a10, a10, a12 +40003391: 20aab0 or a10, a10, a11 +40003394: 0020c0 memw +40003397: 9e6fa2 s32i a10, a15, 0x278 +4000339a: ffe691 l32r a9, 40003334 <_c_0xffff83ff> +4000339d: ffe681 l32r a8, 40003338 <_c_0x00001400> +400033a0: 0020c0 memw +400033a3: 9e2fe2 l32i a14, a15, 0x278 +400033a6: 10ee90 and a14, a14, a9 +400033a9: 20ee80 or a14, a14, a8 +400033ac: 0020c0 memw +400033af: 9e6fe2 s32i a14, a15, 0x278 +400033b2: 5a0c movi.n a10, 5 +400033b4: ffe2b1 l32r a11, 4000333c <_c_0x40003728_uart_int_handler> +400033b7: ffe2c1 l32r a12, 40003340 <_c_0x3fffdb2c_uart_int_handler_arg> +400033ba: fdd2e5 call8 400010e8 <_X_ets_isr_attach> +400033bd: f01d retw.n + ... + +400033c0 <_XX_uart_set_unk33c0>: +400033c0: 004136 entry a1, 32 +400033c3: fea231 l32r a3, 40002e4c <_st_0x3fffdb10_uartdev> +400033c6: 194322 s8i a2, a3, 25 +400033c9: f01d retw.n + ... + +400033cc <_c_0x5ffffe00>: +400033cc: fffe00 excw +400033cf: 5f .byte 0x5f + +400033d0 <_c_0x0000ffff>: +400033d0: ff .byte 0xff +400033d1: ff .byte 0xff +400033d2: 360000 excw +400033d5: 510061 l32r a6, 3ffd77d8 <_start-0x28828> +400033d8: fe .byte 0xfe +400033d9: ff .byte 0xff +400033da: 190c movi.n a9, 1 +400033dc: 080c movi.n a8, 0 +400033de: fffb41 l32r a4, 400033cc <_c_0x5ffffe00> +400033e1: 838920 moveqz a8, a9, a2 +400033e4: 118800 slli a8, a8, 16 +400033e7: 884a add.n a8, a8, a4 +400033e9: 0020c0 memw +400033ec: 846852 s32i a5, a8, 0x210 +400033ef: e97c movi.n a9, -2 +400033f1: 0020c0 memw +400033f4: 832832 l32i a3, a8, 0x20c +400033f7: 103390 and a3, a3, a9 +400033fa: 0020c0 memw +400033fd: 836832 s32i a3, a8, 0x20c +40003400: ffd031 l32r a3, 40003340 <_c_0x3fffdb2c_uart_int_handler_arg> +40003403: 01bd mov.n a11, a1 +40003405: 03ad mov.n a10, a3 +40003407: 0059a5 call8 400039a0 <_X_uart_rx_readbuff> +4000340a: 7aac beqz.n a10, 40003435 <_c_0x0000ffff+0x65> +4000340c: fe90a1 l32r a10, 40002e4c <_st_0x3fffdb10_uartdev> +4000340f: 0d0c movi.n a13, 0 +40003411: bad9 s32i.n a13, a10, 44 +40003413: 11c200 slli a12, a2, 16 +40003416: cc4a add.n a12, a12, a4 +40003418: 0020c0 memw +4000341b: 846c52 s32i a5, a12, 0x210 +4000341e: 1d0c movi.n a13, 1 +40003420: 0020c0 memw +40003423: 832cb2 l32i a11, a12, 0x20c +40003426: 20bbd0 or a11, a11, a13 +40003429: 0020c0 memw +4000342c: 836cb2 s32i a11, a12, 0x20c +4000342f: 184a22 s8i a2, a10, 24 +40003432: f01d retw.n +40003434: 03ad00 excw +40003437: 01bd mov.n a11, a1 +40003439: 005665 call8 400039a0 <_X_uart_rx_readbuff> +4000343c: ff5a16 beqz a10, 40003435 <_c_0x0000ffff+0x65> +4000343f: fff246 j 4000340c <_c_0x0000ffff+0x3c> +40003442: 010000 slli a0, a0, 32 +40003445: 0008 l32i.n a0, a0, 0 + ... + +40003448 <_c_0x000fffff>: +40003448: ff .byte 0xff +40003449: ff .byte 0xff +4000344a: 0f .byte 0xf +4000344b: 413600 srli a3, a0, 6 +4000344e: 7fc100 excw +40003451: fe .byte 0xfe +40003452: e47c movi.n a4, -2 +40003454: ccb8 l32i.n a11, a12, 48 +40003456: 01a0d2 movi a13, 1 +40003459: 83bdb0 moveqz a11, a13, a11 +4000345c: 0c6cb2 s32i a11, a12, 48 +4000345f: ffdba1 l32r a10, 400033cc <_c_0x5ffffe00> +40003462: 119200 slli a9, a2, 16 +40003465: 29aa add.n a2, a9, a10 +40003467: 0020c0 memw +4000346a: 862282 l32i a8, a2, 0x218 +4000346d: 17e807 bbsi a8, 0, 40003488 <_c_0x000fffff+0x40> +40003470: 0020c0 memw +40003473: 8622f2 l32i a15, a2, 0x218 +40003476: 10ff40 and a15, a15, a4 +40003479: 0020c0 memw +4000347c: 8662f2 s32i a15, a2, 0x218 +4000347f: fff1e1 l32r a14, 40003444 <_c_0x0000ffff+0x74> +40003482: 0020c0 memw +40003485: 8662e2 s32i a14, a2, 0x218 +40003488: 0020c0 memw +4000348b: 8c2282 l32i a8, a2, 0x230 +4000348e: 13e8f6 bgeui a8, 128, 400034a5 <_c_0x000fffff+0x5d> +40003491: 045356 bnez a3, 400034da <_c_0x000fffff+0x92> +40003494: e8a3a2 movi a10, 0x3e8 +40003497: ff91e5 call8 40002db4 +4000349a: 0020c0 memw +4000349d: 8c2292 l32i a9, a2, 0x230 +400034a0: f03d nop.n +400034a2: ebe9b6 bltui a9, 128, 40003491 <_c_0x000fffff+0x49> +400034a5: ffe8a1 l32r a10, 40003448 <_c_0x000fffff> +400034a8: 0020c0 memw +400034ab: 8a2282 l32i a8, a2, 0x228 +400034ae: 1088a0 and a8, a8, a10 +400034b1: 0020c0 memw +400034b4: 8b2292 l32i a9, a2, 0x22c +400034b7: 1099a0 and a9, a9, a10 +400034ba: 0020c0 memw +400034bd: 8622a2 l32i a10, a2, 0x218 +400034c0: 10aa40 and a10, a10, a4 +400034c3: 0020c0 memw +400034c6: 8662a2 s32i a10, a2, 0x218 +400034c9: 289a add.n a2, a8, a9 +400034cb: 080c movi.n a8, 0 +400034cd: 212120 srai a2, a2, 1 +400034d0: 532280 max a2, a2, a8 +400034d3: 223b addi.n a2, a2, 3 +400034d5: f01d retw.n +400034d7: 000000 ill +400034da: 020c movi.n a2, 0 +400034dc: f01d retw.n + ... + +400034e0 <_c_0x00060000>: +400034e0: 060000 excw + ... + +400034e4 <_c_0xfff9ffff>: +400034e4: ff .byte 0xff +400034e5: ff .byte 0xff +400034e6: fff9 s32i.n a15, a15, 60 + +400034e8 : +400034e8: 004136 entry a1, 32 +400034eb: ffb861 l32r a6, 400033cc <_c_0x5ffffe00> +400034ee: 115200 slli a5, a2, 16 +400034f1: 556a add.n a5, a5, a6 +400034f3: 0020c0 memw +400034f6: 856532 s32i a3, a5, 0x214 +400034f9: fff991 l32r a9, 400034e0 <_c_0x00060000> +400034fc: 0020c0 memw +400034ff: 882582 l32i a8, a5, 0x220 +40003502: 208890 or a8, a8, a9 +40003505: 0020c0 memw +40003508: 886582 s32i a8, a5, 0x220 +4000350b: fff661 l32r a6, 400034e4 <_c_0xfff9ffff> +4000350e: 0020c0 memw +40003511: 882542 l32i a4, a5, 0x220 +40003514: 104460 and a4, a4, a6 +40003517: 0020c0 memw +4000351a: 886542 s32i a4, a5, 0x220 +4000351d: f01d retw.n + ... + +40003520 <_c_0xfffc0fff>: +40003520: ff .byte 0xff +40003521: 0f .byte 0xf +40003522: fffc bnez.n a15, 40003565 <_X_uart_init+0x31> + +40003524 <_c_0x00009000>: +40003524: 009000 all4 b0, b0:b1:b2:b3 + ... + +40003528 <_c_0x00020000>: +40003528: 020000 andb b0, b0, b0 + ... + +4000352c <_c_13000000>: +4000352c: c65d40 excw + ... + +40003530 <_c_0x08000000>: +40003530: 000000 ill +40003533: 3608 l32i.n a0, a6, 12 + +40003534 <_X_uart_init>: +40003534: 004136 entry a1, 32 +40003537: 0b9216 beqz a2, 400035f4 <_l_35f4> +4000353a: fff9d1 l32r a13, 40003520 <_c_0xfffc0fff> +4000353d: fff9c1 l32r a12, 40003524 <_c_0x00009000> +40003540: f6fc91 l32r a9, 40001130 <_c_0x60003e00> +40003543: 0020c0 memw +40003546: cf29b2 l32i a11, a9, 0x33c +40003549: f03d nop.n +4000354b: 10bbd0 and a11, a11, a13 +4000354e: 20bbc0 or a11, a11, a12 +40003551: 0020c0 memw +40003554: cf69b2 s32i a11, a9, 0x33c +40003557: fff4a1 l32r a10, 40003528 <_c_0x00020000> +4000355a: 0020c0 memw +4000355d: f02982 l32i a8, a9, 0x3c0 +40003560: 2088a0 or a8, a8, a10 +40003563: 0020c0 memw +40003566: f06982 s32i a8, a9, 0x3c0 +40003569: fe3831 l32r a3, 40002e4c <_st_0x3fffdb10_uartdev> +4000356c: fff0b1 l32r a11, 4000352c <_c_13000000> +4000356f: 03c8 l32i.n a12, a3, 0 +40003571: 02ad mov.n a10, a2 +40003573: c2bbc0 quou a11, a11, a12 +40003576: f4b0b0 extui a11, a11, 0, 16 +40003579: fff6e5 call8 400034e8 +4000357c: 3398 l32i.n a9, a3, 12 +4000357e: ff93f1 l32r a15, 400033cc <_c_0x5ffffe00> +40003581: 11e200 slli a14, a2, 16 +40003584: 4388 l32i.n a8, a3, 16 +40003586: eefa add.n a14, a14, a15 +40003588: 23f8 l32i.n a15, a3, 8 +4000358a: 1188c0 slli a8, a8, 4 +4000358d: 20ff90 or a15, a15, a9 +40003590: 1398 l32i.n a9, a3, 4 +40003592: 20ff80 or a15, a15, a8 +40003595: 1199e0 slli a9, a9, 2 +40003598: ffe681 l32r a8, 40003530 <_c_0x08000000> +4000359b: 20ff90 or a15, a15, a9 +4000359e: 20ff80 or a15, a15, a8 +400035a1: 0020c0 memw +400035a4: 886ef2 s32i a15, a14, 0x220 +400035a7: ffced1 l32r a13, 400034e0 <_c_0x00060000> +400035aa: 0020c0 memw +400035ad: 882ec2 l32i a12, a14, 0x220 +400035b0: 20ccd0 or a12, a12, a13 +400035b3: 0020c0 memw +400035b6: 886ec2 s32i a12, a14, 0x220 +400035b9: ffcab1 l32r a11, 400034e4 <_c_0xfff9ffff> +400035bc: 0020c0 memw +400035bf: 882ea2 l32i a10, a14, 0x220 +400035c2: 10aab0 and a10, a10, a11 +400035c5: 0020c0 memw +400035c8: 886ea2 s32i a10, a14, 0x220 +400035cb: 280392 l8ui a9, a3, 40 +400035ce: 649090 extui a9, a9, 0, 7 +400035d1: 0020c0 memw +400035d4: 896e92 s32i a9, a14, 0x224 +400035d7: ff7e81 l32r a8, 400033d0 <_c_0x0000ffff> +400035da: 0020c0 memw +400035dd: 846e82 s32i a8, a14, 0x210 +400035e0: 1f0c movi.n a15, 1 +400035e2: 0020c0 memw +400035e5: 832ed2 l32i a13, a14, 0x20c +400035e8: 20ddf0 or a13, a13, a15 +400035eb: 0020c0 memw +400035ee: 836ed2 s32i a13, a14, 0x20c +400035f1: f01d retw.n + ... + +400035f4 <_l_35f4>: +400035f4: 7d7c movi.n a13, -9 +400035f6: f7bf91 l32r a9, 400014f4 <_c_0x60008e00> +400035f9: 0020c0 memw +400035fc: a229c2 l32i a12, a9, 0x288 +400035ff: 10ccd0 and a12, a12, a13 +40003602: 0020c0 memw +40003605: a269c2 s32i a12, a9, 0x288 +40003608: f7bab1 l32r a11, 400014f0 <_c_0xffff8fff> +4000360b: 0020c0 memw +4000360e: a229a2 l32i a10, a9, 0x288 +40003611: 10aab0 and a10, a10, a11 +40003614: 0020c0 memw +40003617: a269a2 s32i a10, a9, 0x288 +4000361a: 0020c0 memw +4000361d: a22982 l32i a8, a9, 0x288 +40003620: 0020c0 memw +40003623: a26982 s32i a8, a9, 0x288 +40003626: ffcfc6 j 40003569 <_X_uart_init+0x35> +40003629: 000000 ill + +4000362c : +4000362c: 004136 entry a1, 32 +4000362f: fe0741 l32r a4, 40002e4c <_st_0x3fffdb10_uartdev> +40003632: 0020f0 nop +40003635: 190442 l8ui a4, a4, 25 +40003638: ff6581 l32r a8, 400033cc <_c_0x5ffffe00> +4000363b: 114400 slli a4, a4, 16 +4000363e: 448a add.n a4, a4, a8 +40003640: 0020c0 memw +40003643: 872432 l32i a3, a4, 0x21c +40003646: 0e7377 bbci a3, 23, 40003658 +40003649: 088076 loop a0, 40003655 +4000364c: 0020c0 memw +4000364f: 872482 l32i a8, a4, 0x21c +40003652: 027877 bbci a8, 23, 40003658 +40003655: fffc06 j 40003649 +40003658: 0020c0 memw +4000365b: 806422 s32i a2, a4, 0x200 +4000365e: 020c movi.n a2, 0 +40003660: f01d retw.n + ... + +40003664 <_X_uart_tx_one_char2>: +40003664: 004136 entry a1, 32 +40003667: fdf941 l32r a4, 40002e4c <_st_0x3fffdb10_uartdev> +4000366a: 0020f0 nop +4000366d: 180442 l8ui a4, a4, 24 +40003670: ff5781 l32r a8, 400033cc <_c_0x5ffffe00> +40003673: 114400 slli a4, a4, 16 +40003676: 448a add.n a4, a4, a8 +40003678: 0020c0 memw +4000367b: 872432 l32i a3, a4, 0x21c +4000367e: 0e7377 bbci a3, 23, 40003690 <_X_uart_tx_one_char2+0x2c> +40003681: 088076 loop a0, 4000368d <_X_uart_tx_one_char2+0x29> +40003684: 0020c0 memw +40003687: 872482 l32i a8, a4, 0x21c +4000368a: 027877 bbci a8, 23, 40003690 <_X_uart_tx_one_char2+0x2c> +4000368d: fffc06 j 40003681 <_X_uart_tx_one_char2+0x1d> +40003690: 0020c0 memw +40003693: 806422 s32i a2, a4, 0x200 +40003696: 020c movi.n a2, 0 +40003698: f01d retw.n + ... + +4000369c <_X_uart_wait_tx_empty>: +4000369c: 004136 entry a1, 32 +4000369f: 115200 slli a5, a2, 16 +400036a2: ff4a81 l32r a8, 400033cc <_c_0x5ffffe00> +400036a5: f43c21 l32r a2, 40000798 <_c_0x00ff0000> +400036a8: 805580 add a5, a5, a8 +400036ab: 0020c0 memw +400036ae: 872532 l32i a3, a5, 0x21c +400036b1: 100237 bnone a2, a3, 400036c5 <_X_uart_wait_tx_empty+0x29> +400036b4: f03d nop.n +400036b6: 088076 loop a0, 400036c2 <_X_uart_wait_tx_empty+0x26> +400036b9: 0020c0 memw +400036bc: 872592 l32i a9, a5, 0x21c +400036bf: 020927 bnone a9, a2, 400036c5 <_X_uart_wait_tx_empty+0x29> +400036c2: fffb86 j 400036b4 <_X_uart_wait_tx_empty+0x18> +400036c5: f01d retw.n +400036c7: 413600 srli a3, a0, 6 +400036ca: e02100 subx4 a2, a1, a0 +400036cd: 52fd excw +400036cf: 22ffa0 orb b15, b15, b10 +400036d2: 311802 l16ui a0, a8, 98 +400036d5: 3e .byte 0x3e +400036d6: ff .byte 0xff +400036d7: 112200 slli a2, a2, 16 +400036da: 802230 add a2, a2, a3 +400036dd: 088076 loop a0, 400036e9 <_X_uart_wait_tx_empty+0x4d> +400036e0: 0020c0 memw +400036e3: 872232 l32i a3, a2, 0x21c +400036e6: 028537 bany a5, a3, 400036ec <_X_uart_wait_tx_empty+0x50> +400036e9: fffc06 j 400036dd <_X_uart_wait_tx_empty+0x41> +400036ec: 0020c0 memw +400036ef: 802222 l32i a2, a2, 0x200 +400036f2: 742020 extui a2, a2, 0, 8 +400036f5: f01d retw.n +400036f7: 413600 srli a3, a0, 6 +400036fa: d44100 extui a4, a0, 1, 14 +400036fd: 42fd excw +400036ff: 811804 excw +40003702: 00ff32 s32ri a3, a15, 0 +40003705: 8a1144 excw +40003708: 20c044 excw +4000370b: 243200 extui a3, a0, 2, 3 +4000370e: 303087 bltu a0, a8, 40003742 <_X_uart_int_handler+0x1a> +40003711: b38c74 excw +40003714: 0020c0 memw +40003717: 802482 l32i a8, a4, 0x200 +4000371a: 004282 s8i a8, a2, 0 +4000371d: 020c movi.n a2, 0 +4000371f: f01d retw.n +40003721: 120c movi.n a2, 1 +40003723: f01d retw.n +40003725: 000000 ill + +40003728 <_X_uart_int_handler>: +40003728: 004136 entry a1, 32 +4000372b: fdc861 l32r a6, 40002e4c <_st_0x3fffdb10_uartdev> +4000372e: 180c movi.n a8, 1 +40003730: 180662 l8ui a6, a6, 24 +40003733: ff2671 l32r a7, 400033cc <_c_0x5ffffe00> +40003736: 115600 slli a5, a6, 16 +40003739: 557a add.n a5, a5, a7 +4000373b: 0020c0 memw +4000373e: 822532 l32i a3, a5, 0x208 +40003741: 546307 bbci a3, 0, 40003799 <_X_uart_int_handler+0x71> +40003744: ffa092 movi a9, 255 +40003747: 0020c0 memw +4000374a: 846582 s32i a8, a5, 0x210 +4000374d: 116600 slli a6, a6, 16 +40003750: 667a add.n a6, a6, a7 +40003752: 0020c0 memw +40003755: 8726a2 l32i a10, a6, 0x21c +40003758: 3d09a7 bnone a9, a10, 40003799 <_X_uart_int_handler+0x71> +4000375b: 1258 l32i.n a5, a2, 4 +4000375d: 290c movi.n a9, 2 +4000375f: d80c movi.n a8, 13 +40003761: 2b8076 loop a0, 40003790 <_X_uart_int_handler+0x68> +40003764: 0020c0 memw +40003767: 8026a2 l32i a10, a6, 0x200 +4000376a: 74a0a0 extui a10, a10, 0, 8 +4000376d: 0045a2 s8i a10, a5, 0 +40003770: 0278 l32i.n a7, a2, 0 +40003772: 1258 l32i.n a5, a2, 4 +40003774: 01d7b2 addmi a11, a7, 0x100 +40003777: 551b addi.n a5, a5, 1 +40003779: 171a87 beq a10, a8, 40003794 <_X_uart_int_handler+0x6c> +4000377c: 1259 s32i.n a5, a2, 4 +4000377e: 0395b7 bne a5, a11, 40003785 <_X_uart_int_handler+0x5d> +40003781: 075d mov.n a5, a7 +40003783: 1279 s32i.n a7, a2, 4 +40003785: 0020c0 memw +40003788: 8726a2 l32i a10, a6, 0x21c +4000378b: 74a0a0 extui a10, a10, 0, 8 +4000378e: 7a8c beqz.n a10, 40003799 <_X_uart_int_handler+0x71> +40003790: fff346 j 40003761 <_X_uart_int_handler+0x39> +40003793: 429900 xorb b9, b9, b0 +40003796: fff886 j 4000377c <_X_uart_int_handler+0x54> +40003799: f01d retw.n +4000379b: 413600 srli a3, a0, 6 +4000379e: 23b600 sext a11, a6, 7 +400037a1: 140c26 beqi a12, -1, 400037b9 <_X_uart_int_handler+0x91> +400037a4: d50c movi.n a5, 13 +400037a6: 02ad mov.n a10, a2 +400037a8: fff4e5 call8 400036f8 <_X_uart_wait_tx_empty+0x5c> +400037ab: 071a66 bnei a10, 1, 400037b6 <_X_uart_int_handler+0x8e> +400037ae: 02ad mov.n a10, a2 +400037b0: fff465 call8 400036f8 <_X_uart_wait_tx_empty+0x5c> +400037b3: f71a26 beqi a10, 1, 400037ae <_X_uart_int_handler+0x86> +400037b6: 0002a2 l8ui a10, a2, 0 +400037b9: 0c9a26 beqi a10, 10, 400037c9 <_X_uart_int_handler+0xa1> +400037bc: 091a57 beq a10, a5, 400037c9 <_X_uart_int_handler+0xa1> +400037bf: 221b addi.n a2, a2, 1 +400037c1: 441b addi.n a4, a4, 1 +400037c3: 744040 extui a4, a4, 0, 8 +400037c6: dc9347 bne a3, a4, 400037a6 <_X_uart_int_handler+0x7e> +400037c9: 0d0c movi.n a13, 0 +400037cb: 0142d2 s8i a13, a2, 1 +400037ce: 0d2d mov.n a2, a13 +400037d0: f01d retw.n + ... + +400037d4 <_X_send_packet>: +400037d4: 004136 entry a1, 32 +400037d7: c0a0a2 movi a10, 192 +400037da: ffe8a5 call8 40003664 <_X_uart_tx_one_char2> +400037dd: 330b addi.n a3, a3, -1 +400037df: 3d0326 beqi a3, -1, 40003820 <_X_send_packet+0x4c> +400037e2: dba052 movi a5, 219 +400037e5: c0a042 movi a4, 192 +400037e8: 000306 j 400037f8 <_X_send_packet+0x24> +400037eb: 650000 extui a0, a0, 16, 7 +400037ee: 1bffe7 bbsi a15, 30, 4000380d <_X_send_packet+0x39> +400037f1: c33222 excw +400037f4: ff .byte 0xff +400037f5: 027396 bltz a3, 40003820 <_X_send_packet+0x4c> +400037f8: 0002a2 l8ui a10, a2, 0 +400037fb: 111a47 beq a10, a4, 40003810 <_X_send_packet+0x3c> +400037fe: eb9a57 bne a10, a5, 400037ed <_X_send_packet+0x19> +40003801: dba0a2 movi a10, 219 +40003804: ffe5e5 call8 40003664 <_X_uart_tx_one_char2> +40003807: dda0a2 movi a10, 221 +4000380a: ffe5a5 call8 40003664 <_X_uart_tx_one_char2> +4000380d: fff7c6 j 400037f0 <_X_send_packet+0x1c> +40003810: dba0a2 movi a10, 219 +40003813: ffe525 call8 40003664 <_X_uart_tx_one_char2> +40003816: dca0a2 movi a10, 220 +40003819: ffe4a5 call8 40003664 <_X_uart_tx_one_char2> +4000381c: fff406 j 400037f0 <_X_send_packet+0x1c> +4000381f: a0a200 addx4 a10, a2, a0 +40003822: e425c0 extui a2, a12, 5, 15 +40003825: ff .byte 0xff +40003826: f01d retw.n + +40003828 <_X_SendMsg>: +40003828: 004136 entry a1, 32 +4000382b: 03bd mov.n a11, a3 +4000382d: 02ad mov.n a10, a2 +4000382f: fffa65 call8 400037d4 <_X_send_packet> +40003832: 020c movi.n a2, 0 +40003834: f01d retw.n +40003836: c80000 excw +40003839: ffc8 l32i.n a12, a15, 60 +4000383b: 3f .byte 0x3f + +4000383c <_X_recv_packet>: +4000383c: 006136 entry a1, 48 +4000383f: 180c movi.n a8, 1 +40003841: 047d mov.n a7, a4 +40003843: fd8261 l32r a6, 40002e4c <_st_0x3fffdb10_uartdev> +40003846: 029d mov.n a9, a2 +40003848: 1199 s32i.n a9, a1, 4 +4000384a: 020c movi.n a2, 0 +4000384c: 004122 s8i a2, a1, 0 +4000384f: d658 l32i.n a5, a6, 52 +40003851: 937840 movnez a7, a8, a4 +40003854: 2179 s32i.n a7, a1, 8 +40003856: 932570 movnez a2, a5, a7 +40003859: c0a072 movi a7, 192 +4000385c: 1cc652 addi a5, a6, 28 +4000385f: 0d3416 beqz a4, 40003936 <_X_recv_packet+0xfa> +40003862: c698 l32i.n a9, a6, 48 +40003864: 1e2966 bnei a9, 2, 40003886 <_X_recv_packet+0x4a> +40003867: 000f86 j 400038a9 <_X_recv_packet+0x6d> +4000386a: 648c beqz.n a4, 40003874 <_X_recv_packet+0x38> +4000386c: c6e8 l32i.n a14, a6, 48 +4000386e: 024e66 bnei a14, 4, 40003874 <_X_recv_packet+0x38> +40003871: 002406 j 40003905 <_X_recv_packet+0xc9> +40003874: 0001a2 l8ui a10, a1, 0 +40003877: 1d1a77 beq a10, a7, 40003898 <_X_recv_packet+0x5c> +4000387a: dba0f2 movi a15, 219 +4000387d: 511af7 beq a10, a15, 400038d2 <_X_recv_packet+0x96> +40003880: 02a237 bge a2, a3, 40003886 <_X_recv_packet+0x4a> +40003883: 002746 j 40003924 <_X_recv_packet+0xe8> +40003886: 05ad mov.n a10, a5 +40003888: 01bd mov.n a11, a1 +4000388a: 001165 call8 400039a0 <_X_uart_rx_readbuff> +4000388d: fd9a16 beqz a10, 4000386a <_X_recv_packet+0x2e> +40003890: ff2416 beqz a4, 40003886 <_X_recv_packet+0x4a> +40003893: 020c movi.n a2, 0 +40003895: f01d retw.n +40003897: a21600 muluh a1, a6, a0 +4000389a: fe .byte 0xfe +4000389b: 0e7416 beqz a4, 40003986 <_X_recv_packet+0x14a> +4000389e: 280c movi.n a8, 2 +400038a0: 090c movi.n a9, 0 +400038a2: d699 s32i.n a9, a6, 52 +400038a4: c689 s32i.n a8, a6, 48 +400038a6: f01d retw.n +400038a8: 05ad00 extui a10, a0, 29, 1 +400038ab: 01bd mov.n a11, a1 +400038ad: 000f25 call8 400039a0 <_X_uart_rx_readbuff> +400038b0: 4adc bnez.n a10, 400038c8 <_X_recv_packet+0x8c> +400038b2: 0001a2 l8ui a10, a1, 0 +400038b5: 0020f0 nop +400038b8: c0ba70 sub a11, a10, a7 +400038bb: 09db16 beqz a11, 4000395c <_X_recv_packet+0x120> +400038be: 05ad mov.n a10, a5 +400038c0: 01bd mov.n a11, a1 +400038c2: 000de5 call8 400039a0 <_X_uart_rx_readbuff> +400038c5: fe9a16 beqz a10, 400038b2 <_X_recv_packet+0x76> +400038c8: 0001c2 l8ui a12, a1, 0 +400038cb: c49c77 bne a12, a7, 40003893 <_X_recv_packet+0x57> +400038ce: ffed06 j 40003886 <_X_recv_packet+0x4a> +400038d1: e4bc00 extui a11, a0, 12, 15 +400038d4: c698 l32i.n a9, a6, 48 +400038d6: 0101d2 l8ui a13, a1, 1 +400038d9: 023966 bnei a9, 3, 400038df <_X_recv_packet+0xa3> +400038dc: 002186 j 40003966 <_X_recv_packet+0x12a> +400038df: 0041d2 s8i a13, a1, 0 +400038e2: 24afe2 movi a14, -220 +400038e5: 0001a2 l8ui a10, a1, 0 +400038e8: 21d8 l32i.n a13, a1, 8 +400038ea: 3c0c movi.n a12, 3 +400038ec: 939cd0 movnez a9, a12, a13 +400038ef: c699 s32i.n a9, a6, 48 +400038f1: eaea add.n a14, a10, a14 +400038f3: 0a2ef6 bgeui a14, 2, 40003901 <_X_recv_packet+0xc5> +400038f6: ffd0b1 l32r a11, 40003838 <_X_SendMsg+0x10> +400038f9: aaba add.n a10, a10, a11 +400038fb: 7c0aa2 l8ui a10, a10, 124 +400038fe: 0041a2 s8i a10, a1, 0 +40003901: ffdec6 j 40003880 <_X_recv_packet+0x44> +40003904: a0e200 addx4 a14, a2, a0 +40003907: f2db addi.n a15, a2, 13 +40003909: f20001 l32r a0, 4000010c <_WindowOverflowHandler+0xc> +4000390c: e20141 l32r a4, 3fffc110 <_start-0x3ef0> +4000390f: 560041 l32r a4, 3ffd9110 <_start-0x26ef0> +40003912: adfbf4 excw +40003915: b11005 call0 3ffb4a18 <_start-0x4b5e8> +40003918: 086520 excw +4000391b: 1a2600 depbits a0, a6, 1, 3 +4000391e: 9859 s32i.n a5, a8, 36 +40003920: ef46c6 j 3ffff63f <_start-0x9c1> +40003923: ff .byte 0xff +40003924: 1198 l32i.n a9, a1, 4 +40003926: 929a add.n a9, a2, a9 +40003928: 0049a2 s8i a10, a9, 0 +4000392b: d688 l32i.n a8, a6, 52 +4000392d: 221b addi.n a2, a2, 1 +4000392f: 881b addi.n a8, a8, 1 +40003931: d689 s32i.n a8, a6, 52 +40003933: ffd3c6 j 40003886 <_X_recv_packet+0x4a> +40003936: 05ad mov.n a10, a5 +40003938: 01bd mov.n a11, a1 +4000393a: 000665 call8 400039a0 <_X_uart_rx_readbuff> +4000393d: 081a26 beqi a10, 1, 40003949 <_X_recv_packet+0x10d> +40003940: 0001a2 l8ui a10, a1, 0 +40003943: c0aa70 sub a10, a10, a7 +40003946: f3ca16 beqz a10, 40003886 <_X_recv_packet+0x4a> +40003949: 05ad mov.n a10, a5 +4000394b: 01bd mov.n a11, a1 +4000394d: 000525 call8 400039a0 <_X_uart_rx_readbuff> +40003950: f51a26 beqi a10, 1, 40003949 <_X_recv_packet+0x10d> +40003953: 0001b2 l8ui a11, a1, 0 +40003956: ef9b77 bne a11, a7, 40003949 <_X_recv_packet+0x10d> +40003959: ffca46 j 40003886 <_X_recv_packet+0x4a> +4000395c: 3d0c movi.n a13, 3 +4000395e: c6d9 s32i.n a13, a6, 48 +40003960: f2fb56 bnez a11, 40003893 <_X_recv_packet+0x57> +40003963: ffc7c6 j 40003886 <_X_recv_packet+0x4a> +40003966: 05ad mov.n a10, a5 +40003968: 01bd mov.n a11, a1 +4000396a: 4e0c movi.n a14, 4 +4000396c: c6e9 s32i.n a14, a6, 48 +4000396e: 000325 call8 400039a0 <_X_uart_rx_readbuff> +40003971: aa1a66 bnei a10, 1, 4000391f <_X_recv_packet+0xe3> +40003974: ffc6c6 j 40003893 <_X_recv_packet+0x57> +40003977: ad0000 excw +4000397a: 01bd05 call0 4000554c <_X_slc_init_attach+0x64> +4000397d: 000225 call8 400039a0 <_X_uart_rx_readbuff> +40003980: f51a26 beqi a10, 1, 40003979 <_X_recv_packet+0x13d> +40003983: ffe606 j 4000391f <_X_recv_packet+0xe3> +40003986: f01d retw.n + +40003988 <_X_RcvMsg>: +40003988: 004136 entry a1, 32 +4000398b: 04cd mov.n a12, a4 +4000398d: 03bd mov.n a11, a3 +4000398f: 02ad mov.n a10, a2 +40003991: ffeaa5 call8 4000383c <_X_recv_packet> +40003994: 2a8c beqz.n a10, 4000399a <_X_RcvMsg+0x12> +40003996: 020c movi.n a2, 0 +40003998: f01d retw.n +4000399a: 120c movi.n a2, 1 +4000399c: f01d retw.n + ... + +400039a0 <_X_uart_rx_readbuff>: +400039a0: 004136 entry a1, 32 +400039a3: 2268 l32i.n a6, a2, 8 +400039a5: 1248 l32i.n a4, a2, 4 +400039a7: 059467 bne a4, a6, 400039b0 <_X_uart_rx_readbuff+0x10> +400039aa: 120c movi.n a2, 1 +400039ac: f01d retw.n +400039ae: 820000 mull a0, a0, a0 +400039b1: 820006 j 3ffe41b5 <_start-0x1be4b> +400039b4: 380043 excw +400039b7: 225802 s16i a0, a8, 68 +400039ba: 01d382 addmi a8, a3, 0x100 +400039bd: 551b addi.n a5, a5, 1 +400039bf: 2259 s32i.n a5, a2, 8 +400039c1: 059587 bne a5, a8, 400039ca <_X_uart_rx_readbuff+0x2a> +400039c4: 2239 s32i.n a3, a2, 8 +400039c6: 020c movi.n a2, 0 +400039c8: f01d retw.n +400039ca: 020c movi.n a2, 0 +400039cc: f01d retw.n +400039ce: 360000 excw +400039d1: 410061 l32r a6, 3ffd3dd4 <_start-0x2c22c> +400039d4: 1e .byte 0x1e +400039d5: 88fd excw +400039d7: 2826b4 excw +400039da: 120c04 excw +400039dd: f01d retw.n +400039df: 01bd00 slli a11, a13, 32 +400039e2: 1cc432 addi a3, a4, 28 +400039e5: 03ad mov.n a10, a3 +400039e7: fffba5 call8 400039a0 <_X_uart_rx_readbuff> +400039ea: ba8c beqz.n a10, 400039f9 <_X_uart_rx_readbuff+0x59> +400039ec: 090c movi.n a9, 0 +400039ee: 004292 s8i a9, a2, 0 +400039f1: 092d mov.n a2, a9 +400039f3: b429 s32i.n a2, a4, 44 +400039f5: f01d retw.n +400039f7: ad0000 excw +400039fa: 019203 excw +400039fd: 01bd00 slli a11, a13, 32 +40003a00: 004292 s8i a9, a2, 0 +40003a03: 221b addi.n a2, a2, 1 +40003a05: fff9a5 call8 400039a0 <_X_uart_rx_readbuff> +40003a08: feda16 beqz a10, 400039f9 <_X_uart_rx_readbuff+0x59> +40003a0b: fff746 j 400039ec <_X_uart_rx_readbuff+0x4c> +40003a0e: 360000 excw +40003a11: 210041 l32r a4, 3ffcbe14 <_start-0x341ec> +40003a14: 0e .byte 0xe +40003a15: 1dfd excw +40003a17: 4e00f0 excw + +40003a18 <_c_0x60004e00>: +40003a18: 004e00 break 14, 0 +40003a1b: 413660 srli a3, a6, 6 + +40003a1c <_X_SelectSpiFunction>: +40003a1c: 004136 entry a1, 32 +40003a1f: 787c movi.n a8, -9 +40003a21: f6b371 l32r a7, 400014f0 <_c_0xffff8fff> +40003a24: f5c641 l32r a4, 4000113c <_c_0x1000> +40003a27: fffc51 l32r a5, 40003a18 <_c_0x60004e00> +40003a2a: f6b261 l32r a6, 400014f4 <_c_0x60008e00> +40003a2d: 123216 beqz a2, 40003b54 <_X_SelectSpiFunction+0x138> +40003a30: 0020c0 memw +40003a33: 8c2632 l32i a3, a6, 0x230 +40003a36: 103380 and a3, a3, a8 +40003a39: 0020c0 memw +40003a3c: 8c6632 s32i a3, a6, 0x230 +40003a3f: 0020c0 memw +40003a42: 8c2622 l32i a2, a6, 0x230 +40003a45: 102270 and a2, a2, a7 +40003a48: 0020c0 memw +40003a4b: 8c6622 s32i a2, a6, 0x230 +40003a4e: 0020c0 memw +40003a51: 8c26f2 l32i a15, a6, 0x230 +40003a54: 20ff40 or a15, a15, a4 +40003a57: 0020c0 memw +40003a5a: 8c66f2 s32i a15, a6, 0x230 +40003a5d: 0020c0 memw +40003a60: 8d26e2 l32i a14, a6, 0x234 +40003a63: 10ee80 and a14, a14, a8 +40003a66: 0020c0 memw +40003a69: 8d66e2 s32i a14, a6, 0x234 +40003a6c: 0020c0 memw +40003a6f: 8d26d2 l32i a13, a6, 0x234 +40003a72: 10dd70 and a13, a13, a7 +40003a75: 0020c0 memw +40003a78: 8d66d2 s32i a13, a6, 0x234 +40003a7b: 0020c0 memw +40003a7e: 8d26c2 l32i a12, a6, 0x234 +40003a81: 20cc40 or a12, a12, a4 +40003a84: 0020c0 memw +40003a87: 8d66c2 s32i a12, a6, 0x234 +40003a8a: 0020c0 memw +40003a8d: 8e26b2 l32i a11, a6, 0x238 +40003a90: 10bb80 and a11, a11, a8 +40003a93: 0020c0 memw +40003a96: 8e66b2 s32i a11, a6, 0x238 +40003a99: 0020c0 memw +40003a9c: 8e26a2 l32i a10, a6, 0x238 +40003a9f: 10aa70 and a10, a10, a7 +40003aa2: 0020c0 memw +40003aa5: 8e66a2 s32i a10, a6, 0x238 +40003aa8: 0020c0 memw +40003aab: 8e2692 l32i a9, a6, 0x238 +40003aae: 209940 or a9, a9, a4 +40003ab1: 0020c0 memw +40003ab4: 8e6692 s32i a9, a6, 0x238 +40003ab7: 0020c0 memw +40003aba: 8f2632 l32i a3, a6, 0x23c +40003abd: 103380 and a3, a3, a8 +40003ac0: 0020c0 memw +40003ac3: 8f6632 s32i a3, a6, 0x23c +40003ac6: 0020c0 memw +40003ac9: 8f2622 l32i a2, a6, 0x23c +40003acc: 102270 and a2, a2, a7 +40003acf: 0020c0 memw +40003ad2: 8f6622 s32i a2, a6, 0x23c +40003ad5: 0020c0 memw +40003ad8: 8f26f2 l32i a15, a6, 0x23c +40003adb: 20ff40 or a15, a15, a4 +40003ade: 0020c0 memw +40003ae1: 8f66f2 s32i a15, a6, 0x23c +40003ae4: 0020c0 memw +40003ae7: 9026e2 l32i a14, a6, 0x240 +40003aea: 10ee80 and a14, a14, a8 +40003aed: 0020c0 memw +40003af0: 9066e2 s32i a14, a6, 0x240 +40003af3: 0020c0 memw +40003af6: 9026d2 l32i a13, a6, 0x240 +40003af9: 10dd70 and a13, a13, a7 +40003afc: 0020c0 memw +40003aff: 9066d2 s32i a13, a6, 0x240 +40003b02: 0020c0 memw +40003b05: 9026c2 l32i a12, a6, 0x240 +40003b08: 20cc40 or a12, a12, a4 +40003b0b: 0020c0 memw +40003b0e: 9066c2 s32i a12, a6, 0x240 +40003b11: 0020c0 memw +40003b14: 9226b2 l32i a11, a6, 0x248 +40003b17: 10bb80 and a11, a11, a8 +40003b1a: 0020c0 memw +40003b1d: 9266b2 s32i a11, a6, 0x248 +40003b20: 0020c0 memw +40003b23: 9226a2 l32i a10, a6, 0x248 +40003b26: 10aa70 and a10, a10, a7 +40003b29: 0020c0 memw +40003b2c: 9266a2 s32i a10, a6, 0x248 +40003b2f: 0020c0 memw +40003b32: 922692 l32i a9, a6, 0x248 +40003b35: 209940 or a9, a9, a4 +40003b38: 0020c0 memw +40003b3b: 926692 s32i a9, a6, 0x248 +40003b3e: f2fe31 l32r a3, 40000738 <_c_0x80000000> +40003b41: 0020c0 memw +40003b44: 492522 l32i a2, a5, 0x124 +40003b47: 202230 or a2, a2, a3 +40003b4a: 0020c0 memw +40003b4d: 496522 s32i a2, a5, 0x124 +40003b50: f01d retw.n +40003b52: c00000 sub a0, a0, a0 +40003b55: a20020 muluh a0, a0, a2 +40003b58: 809826 beqi a8, 10, 40003adc <_X_SelectSpiFunction+0xc0> +40003b5b: 10aa add.n a1, a0, a10 +40003b5d: 0020c0 memw +40003b60: 9866a2 s32i a10, a6, 0x260 +40003b63: 0020c0 memw +40003b66: 982692 l32i a9, a6, 0x260 +40003b69: 109970 and a9, a9, a7 +40003b6c: 0020c0 memw +40003b6f: 986692 s32i a9, a6, 0x260 +40003b72: 0020c0 memw +40003b75: 982632 l32i a3, a6, 0x260 +40003b78: 203340 or a3, a3, a4 +40003b7b: 0020c0 memw +40003b7e: 986632 s32i a3, a6, 0x260 +40003b81: 0020c0 memw +40003b84: 992622 l32i a2, a6, 0x264 +40003b87: 102280 and a2, a2, a8 +40003b8a: 0020c0 memw +40003b8d: 996622 s32i a2, a6, 0x264 +40003b90: 0020c0 memw +40003b93: 9926f2 l32i a15, a6, 0x264 +40003b96: 10ff70 and a15, a15, a7 +40003b99: 0020c0 memw +40003b9c: 9966f2 s32i a15, a6, 0x264 +40003b9f: 0020c0 memw +40003ba2: 9926e2 l32i a14, a6, 0x264 +40003ba5: 20ee40 or a14, a14, a4 +40003ba8: 0020c0 memw +40003bab: 9966e2 s32i a14, a6, 0x264 +40003bae: 0020c0 memw +40003bb1: 9a26d2 l32i a13, a6, 0x268 +40003bb4: 10dd80 and a13, a13, a8 +40003bb7: 0020c0 memw +40003bba: 9a66d2 s32i a13, a6, 0x268 +40003bbd: 0020c0 memw +40003bc0: 9a26c2 l32i a12, a6, 0x268 +40003bc3: 10cc70 and a12, a12, a7 +40003bc6: 0020c0 memw +40003bc9: 9a66c2 s32i a12, a6, 0x268 +40003bcc: 0020c0 memw +40003bcf: 9a26b2 l32i a11, a6, 0x268 +40003bd2: 20bb40 or a11, a11, a4 +40003bd5: 0020c0 memw +40003bd8: 9a66b2 s32i a11, a6, 0x268 +40003bdb: 0020c0 memw +40003bde: 9526a2 l32i a10, a6, 0x254 +40003be1: 10aa80 and a10, a10, a8 +40003be4: 0020c0 memw +40003be7: 9566a2 s32i a10, a6, 0x254 +40003bea: 0020c0 memw +40003bed: 952692 l32i a9, a6, 0x254 +40003bf0: 109970 and a9, a9, a7 +40003bf3: 0020c0 memw +40003bf6: 956692 s32i a9, a6, 0x254 +40003bf9: 0020c0 memw +40003bfc: 952632 l32i a3, a6, 0x254 +40003bff: 203340 or a3, a3, a4 +40003c02: 0020c0 memw +40003c05: 956632 s32i a3, a6, 0x254 +40003c08: 0020c0 memw +40003c0b: 962622 l32i a2, a6, 0x258 +40003c0e: 102280 and a2, a2, a8 +40003c11: 0020c0 memw +40003c14: 966622 s32i a2, a6, 0x258 +40003c17: 0020c0 memw +40003c1a: 9626f2 l32i a15, a6, 0x258 +40003c1d: 10ff70 and a15, a15, a7 +40003c20: 0020c0 memw +40003c23: 9666f2 s32i a15, a6, 0x258 +40003c26: 0020c0 memw +40003c29: 9626e2 l32i a14, a6, 0x258 +40003c2c: 20ee40 or a14, a14, a4 +40003c2f: 0020c0 memw +40003c32: 9666e2 s32i a14, a6, 0x258 +40003c35: 0020c0 memw +40003c38: 9726d2 l32i a13, a6, 0x25c +40003c3b: 10dd80 and a13, a13, a8 +40003c3e: 0020c0 memw +40003c41: 9766d2 s32i a13, a6, 0x25c +40003c44: 0020c0 memw +40003c47: 9726c2 l32i a12, a6, 0x25c +40003c4a: 10cc70 and a12, a12, a7 +40003c4d: 0020c0 memw +40003c50: 9766c2 s32i a12, a6, 0x25c +40003c53: 0020c0 memw +40003c56: 9726b2 l32i a11, a6, 0x25c +40003c59: 20bb40 or a11, a11, a4 +40003c5c: 0020c0 memw +40003c5f: 9766b2 s32i a11, a6, 0x25c +40003c62: f2c7a1 l32r a10, 40000780 <_c_0x7fffffff> +40003c65: 0020c0 memw +40003c68: 492592 l32i a9, a5, 0x124 +40003c6b: 1099a0 and a9, a9, a10 +40003c6e: 0020c0 memw +40003c71: 496592 s32i a9, a5, 0x124 +40003c74: f01d retw.n + ... + +40003c78 <_c_0x60002e00>: +40003c78: 002e00 excw +40003c7b: 413660 srli a3, a6, 6 + +40003c7c <_X_SPI_chip_erase>: +40003c7c: 004136 entry a1, 32 +40003c7f: 20a220 or a10, a2, a2 +40003c82: 005865 call8 40004208 <_X_Wait_SPI_Idle> +40003c85: fb4ba1 l32r a10, 400029b4 <_c_0x00400000> +40003c88: fffc91 l32r a9, 40003c78 <_c_0x60002e00> +40003c8b: 0020c0 memw +40003c8e: 8069a2 s32i a10, a9, 0x200 +40003c91: 0020c0 memw +40003c94: 802982 l32i a8, a9, 0x200 +40003c97: d88c beqz.n a8, 40003ca8 <_X_SPI_chip_erase+0x2c> +40003c99: 078076 loop a0, 40003ca4 <_X_SPI_chip_erase+0x28> +40003c9c: 0020c0 memw +40003c9f: 8029b2 l32i a11, a9, 0x200 +40003ca2: 2b8c beqz.n a11, 40003ca8 <_X_SPI_chip_erase+0x2c> +40003ca4: fffc46 j 40003c99 <_X_SPI_chip_erase+0x1d> +40003ca7: 02ad00 andb b10, b13, b0 +40003caa: 0055e5 call8 40004208 <_X_Wait_SPI_Idle> +40003cad: 020c movi.n a2, 0 +40003caf: f01d retw.n +40003cb1: 000000 ill + +40003cb4 <_c_0x00ffffff>: +40003cb4: ff .byte 0xff +40003cb5: ff .byte 0xff +40003cb6: ff .byte 0xff + ... + +40003cb8 <_c_0x01000000>: +40003cb8: 000000 ill +40003cbb: 413601 l32r a0, 3ffd4194 <_start-0x2be6c> + +40003cbc <_XX_unk3cbc>: +40003cbc: 004136 entry a1, 32 +40003cbf: b48030 extui a8, a3, 0, 12 +40003cc2: 288c beqz.n a8, 40003cc8 <_XX_unk3cbc+0xc> +40003cc4: 120c movi.n a2, 1 +40003cc6: f01d retw.n +40003cc8: 02ad mov.n a10, a2 +40003cca: 0053e5 call8 40004208 <_X_Wait_SPI_Idle> +40003ccd: fff9c1 l32r a12, 40003cb4 <_c_0x00ffffff> +40003cd0: ffea91 l32r a9, 40003c78 <_c_0x60002e00> +40003cd3: 10c3c0 and a12, a3, a12 +40003cd6: 0020c0 memw +40003cd9: 8169c2 s32i a12, a9, 0x204 +40003cdc: fff7b1 l32r a11, 40003cb8 <_c_0x01000000> +40003cdf: 0020c0 memw +40003ce2: 8069b2 s32i a11, a9, 0x200 +40003ce5: 0020c0 memw +40003ce8: 8029a2 l32i a10, a9, 0x200 +40003ceb: 7a8c beqz.n a10, 40003cf6 <_XX_unk3cbc+0x3a> +40003ced: 0020c0 memw +40003cf0: 8029d2 l32i a13, a9, 0x200 +40003cf3: ff6d56 bnez a13, 40003ced <_XX_unk3cbc+0x31> +40003cf6: 02ad mov.n a10, a2 +40003cf8: 0050e5 call8 40004208 <_X_Wait_SPI_Idle> +40003cfb: 020c movi.n a2, 0 +40003cfd: f01d retw.n + ... + +40003d00 <_c_0x00800000>: +40003d00: 800000 add a0, a0, a0 +40003d03: 413600 srli a3, a0, 6 +40003d06: a22000 muluh a2, a0, a0 +40003d09: 4fe520 excw +40003d0c: e9b100 excw +40003d0f: ff .byte 0xff +40003d10: ffda91 l32r a9, 40003c78 <_c_0x60002e00> +40003d13: 10b3b0 and a11, a3, a11 +40003d16: 0020c0 memw +40003d19: 8169b2 s32i a11, a9, 0x204 +40003d1c: fff9a1 l32r a10, 40003d00 <_c_0x00800000> +40003d1f: 0020c0 memw +40003d22: 8069a2 s32i a10, a9, 0x200 +40003d25: 0020c0 memw +40003d28: 802982 l32i a8, a9, 0x200 +40003d2b: d88c beqz.n a8, 40003d3c <_c_0x00800000+0x3c> +40003d2d: 078076 loop a0, 40003d38 <_c_0x00800000+0x38> +40003d30: 0020c0 memw +40003d33: 8029c2 l32i a12, a9, 0x200 +40003d36: 2c8c beqz.n a12, 40003d3c <_c_0x00800000+0x3c> +40003d38: fffc46 j 40003d2d <_c_0x00800000+0x2d> +40003d3b: 02ad00 andb b10, b13, b0 +40003d3e: 004ca5 call8 40004208 <_X_Wait_SPI_Idle> +40003d41: 020c movi.n a2, 0 +40003d43: f01d retw.n +40003d45: 000000 ill +40003d48: 000000 ill +40003d4b: 000020 excw + +40003d4c <_c_0x02000000>: +40003d4c: 000000 ill +40003d4f: 413602 excw +40003d52: 380c00 excw +40003d55: 1f8857 bany a8, a5, 40003d78 <_c_0x02000000+0x2c> +40003d58: 4298 l32i.n a9, a2, 16 +40003d5a: e2a390 remu a10, a3, a9 +40003d5d: a5aa add.n a10, a5, a10 +40003d5f: 1539a7 bltu a9, a10, 40003d78 <_c_0x02000000+0x2c> +40003d62: 20a220 or a10, a2, a2 +40003d65: 004a25 call8 40004208 <_X_Wait_SPI_Idle> +40003d68: 0215e6 bgei a5, 1, 40003d6e <_c_0x02000000+0x22> +40003d6b: 002b86 j 40003e1d <_c_0x02000000+0xd1> +40003d6e: ffc261 l32r a6, 40003c78 <_c_0x60002e00> +40003d71: ffd071 l32r a7, 40003cb4 <_c_0x00ffffff> +40003d74: 0003c6 j 40003d87 <_c_0x02000000+0x3b> +40003d77: 120c00 andbc b0, b12, b0 +40003d7a: f01d retw.n +40003d7c: 02ad mov.n a10, a2 +40003d7e: 0048a5 call8 40004208 <_X_Wait_SPI_Idle> +40003d81: 0215e6 bgei a5, 1, 40003d87 <_c_0x02000000+0x3b> +40003d84: 002546 j 40003e1d <_c_0x02000000+0xd1> +40003d87: 10a370 and a10, a3, a7 +40003d8a: 56c5a6 blti a5, 32, 40003de4 <_c_0x02000000+0x98> +40003d8d: ffeed1 l32r a13, 40003d48 <_c_0x00800000+0x48> +40003d90: 20dad0 or a13, a10, a13 +40003d93: 0020c0 memw +40003d96: 8166d2 s32i a13, a6, 0x204 +40003d99: 080c movi.n a8, 0 +40003d9b: 890c movi.n a9, 8 +40003d9d: 11a976 loopgtz a9, 40003db2 <_c_0x02000000+0x66> +40003da0: a09860 addx4 a9, a8, a6 +40003da3: 04b8 l32i.n a11, a4, 0 +40003da5: 0020c0 memw +40003da8: 9069b2 s32i a11, a9, 0x240 +40003dab: 444b addi.n a4, a4, 4 +40003dad: 981b addi.n a9, a8, 1 +40003daf: 748090 extui a8, a9, 0, 8 +40003db2: e0c552 addi a5, a5, -32 +40003db5: 20c332 addi a3, a3, 32 +40003db8: 02ad mov.n a10, a2 +40003dba: 004025 call8 400041bc <_X_SPI_write_enable> +40003dbd: fb7a56 bnez a10, 40003d78 <_c_0x02000000+0x2c> +40003dc0: 0020c0 memw +40003dc3: ffe2f1 l32r a15, 40003d4c <_c_0x02000000> +40003dc6: 8066f2 s32i a15, a6, 0x200 +40003dc9: 0020c0 memw +40003dcc: 8026e2 l32i a14, a6, 0x200 +40003dcf: 0020f0 nop +40003dd2: fa6e16 beqz a14, 40003d7c <_c_0x02000000+0x30> +40003dd5: 088076 loop a0, 40003de1 <_c_0x02000000+0x95> +40003dd8: 0020c0 memw +40003ddb: 802682 l32i a8, a6, 0x200 +40003dde: f9a816 beqz a8, 40003d7c <_c_0x02000000+0x30> +40003de1: fffc06 j 40003dd5 <_c_0x02000000+0x89> +40003de4: 03bd mov.n a11, a3 +40003de6: 8b75b0 depbits a11, a5, 24, 8 +40003de9: 0020c0 memw +40003dec: 8166b2 s32i a11, a6, 0x204 +40003def: 219250 srai a9, a5, 2 +40003df2: 14a050 extui a10, a5, 0, 2 +40003df5: fa9c beqz.n a10, 40003e18 <_c_0x02000000+0xcc> +40003df7: a91b addi.n a10, a9, 1 +40003df9: 080c movi.n a8, 0 +40003dfb: 7490a0 extui a9, a10, 0, 8 +40003dfe: 119976 loopnez a9, 40003e13 <_c_0x02000000+0xc7> +40003e01: a09860 addx4 a9, a8, a6 +40003e04: 04b8 l32i.n a11, a4, 0 +40003e06: 0020c0 memw +40003e09: 9069b2 s32i a11, a9, 0x240 +40003e0c: 444b addi.n a4, a4, 4 +40003e0e: 981b addi.n a9, a8, 1 +40003e10: 748090 extui a8, a9, 0, 8 +40003e13: 050c movi.n a5, 0 +40003e15: ffe7c6 j 40003db8 <_c_0x02000000+0x6c> +40003e18: 09ad mov.n a10, a9 +40003e1a: fff6c6 j 40003df9 <_c_0x02000000+0xad> +40003e1d: 020c movi.n a2, 0 +40003e1f: f01d retw.n +40003e21: 000000 ill + +40003e24 <_XX_unk3e24>: +40003e24: 004136 entry a1, 32 +40003e27: 1288 l32i.n a8, a2, 4 +40003e29: 953a add.n a9, a5, a3 +40003e2b: 03b897 bgeu a8, a9, 40003e32 <_XX_unk3e24+0xe> +40003e2e: 120c movi.n a2, 1 +40003e30: f01d retw.n +40003e32: 02ad mov.n a10, a2 +40003e34: 003d25 call8 40004208 <_X_Wait_SPI_Idle> +40003e37: 0215e6 bgei a5, 1, 40003e3d <_XX_unk3e24+0x19> +40003e3a: 002e06 j 40003ef6 <_XX_unk3e24+0xd2> +40003e3d: ffc2f1 l32r a15, 40003d48 <_c_0x00800000+0x48> +40003e40: f23ee1 l32r a14, 40000738 <_c_0x80000000> +40003e43: ff8dd1 l32r a13, 40003c78 <_c_0x60002e00> +40003e46: 000b46 j 40003e77 <_XX_unk3e24+0x53> +40003e49: 219250 srai a9, a5, 2 +40003e4c: 14a050 extui a10, a5, 0, 2 +40003e4f: 09ea16 beqz a10, 40003ef1 <_XX_unk3e24+0xcd> +40003e52: a91b addi.n a10, a9, 1 +40003e54: 080c movi.n a8, 0 +40003e56: 7490a0 extui a9, a10, 0, 8 +40003e59: 119976 loopnez a9, 40003e6e <_XX_unk3e24+0x4a> +40003e5c: a098d0 addx4 a9, a8, a13 +40003e5f: 0020c0 memw +40003e62: 902992 l32i a9, a9, 0x240 +40003e65: 881b addi.n a8, a8, 1 +40003e67: 0499 s32i.n a9, a4, 0 +40003e69: 748080 extui a8, a8, 0, 8 +40003e6c: 444b addi.n a4, a4, 4 +40003e6e: 00a052 movi a5, 0 +40003e71: 0215e6 bgei a5, 1, 40003e77 <_XX_unk3e24+0x53> +40003e74: 001f86 j 40003ef6 <_XX_unk3e24+0xd2> +40003e77: 4ac5a6 blti a5, 32, 40003ec5 <_XX_unk3e24+0xa1> +40003e7a: 20c3f0 or a12, a3, a15 +40003e7d: 0020c0 memw +40003e80: 816dc2 s32i a12, a13, 0x204 +40003e83: 0020c0 memw +40003e86: 806de2 s32i a14, a13, 0x200 +40003e89: 0020c0 memw +40003e8c: 802db2 l32i a11, a13, 0x200 +40003e8f: eb8c beqz.n a11, 40003ea1 <_XX_unk3e24+0x7d> +40003e91: 078076 loop a0, 40003e9c <_XX_unk3e24+0x78> +40003e94: 0020c0 memw +40003e97: 802d82 l32i a8, a13, 0x200 +40003e9a: 388c beqz.n a8, 40003ea1 <_XX_unk3e24+0x7d> +40003e9c: fffc46 j 40003e91 <_XX_unk3e24+0x6d> +40003e9f: 0c0000 excw +40003ea2: 0c08 l32i.n a0, a12, 0 +40003ea4: 7689 s32i.n a8, a6, 28 +40003ea6: 11a9 s32i.n a10, a1, 4 +40003ea8: a098d0 addx4 a9, a8, a13 +40003eab: 0020c0 memw +40003eae: 902992 l32i a9, a9, 0x240 +40003eb1: 881b addi.n a8, a8, 1 +40003eb3: 0499 s32i.n a9, a4, 0 +40003eb5: 748080 extui a8, a8, 0, 8 +40003eb8: 444b addi.n a4, a4, 4 +40003eba: e0c552 addi a5, a5, -32 +40003ebd: 20c332 addi a3, a3, 32 +40003ec0: ffeb46 j 40003e71 <_XX_unk3e24+0x4d> +40003ec3: 800000 add a0, a0, a0 +40003ec6: a001a5 call8 3ffa3ee0 <_start-0x5c120> +40003ec9: c020a3 excw +40003ecc: a20020 muluh a0, a0, a2 +40003ecf: 816d excw +40003ed1: 0020c0 memw +40003ed4: 806de2 s32i a14, a13, 0x200 +40003ed7: 0020c0 memw +40003eda: 802d92 l32i a9, a13, 0x200 +40003edd: f68916 beqz a9, 40003e49 <_XX_unk3e24+0x25> +40003ee0: f03d nop.n +40003ee2: 088076 loop a0, 40003eee <_XX_unk3e24+0xca> +40003ee5: 0020c0 memw +40003ee8: 802db2 l32i a11, a13, 0x200 +40003eeb: f5ab16 beqz a11, 40003e49 <_XX_unk3e24+0x25> +40003eee: fffb86 j 40003ee0 <_XX_unk3e24+0xbc> +40003ef1: 09ad mov.n a10, a9 +40003ef3: ffd746 j 40003e54 <_XX_unk3e24+0x30> +40003ef6: 020c movi.n a2, 0 +40003ef8: f01d retw.n + ... + +40003efc <_X_SPI_read_status>: +40003efc: 004136 entry a1, 32 +40003eff: fd8c61 l32r a6, 40003530 <_c_0x08000000> +40003f02: ff5d51 l32r a5, 40003c78 <_c_0x60002e00> +40003f05: 080c movi.n a8, 0 +40003f07: 000346 j 40003f18 <_X_SPI_read_status+0x1c> +40003f0a: 5298 l32i.n a9, a2, 20 +40003f0c: 0020c0 memw +40003f0f: 842572 l32i a7, a5, 0x210 +40003f12: 107790 and a7, a7, a9 +40003f15: 236707 bbci a7, 0, 40003f3c <_X_SPI_read_status+0x40> +40003f18: 0020c0 memw +40003f1b: 846582 s32i a8, a5, 0x210 +40003f1e: 0020c0 memw +40003f21: 806562 s32i a6, a5, 0x200 +40003f24: 0020c0 memw +40003f27: 8025a2 l32i a10, a5, 0x200 +40003f2a: fdca16 beqz a10, 40003f0a <_X_SPI_read_status+0xe> +40003f2d: 088076 loop a0, 40003f39 <_X_SPI_read_status+0x3d> +40003f30: 0020c0 memw +40003f33: 8025b2 l32i a11, a5, 0x200 +40003f36: fd0b16 beqz a11, 40003f0a <_X_SPI_read_status+0xe> +40003f39: fffc06 j 40003f2d <_X_SPI_read_status+0x31> +40003f3c: 0379 s32i.n a7, a3, 0 +40003f3e: 020c movi.n a2, 0 +40003f40: f01d retw.n +40003f42: 480000 excw +40003f45: ffc8 l32i.n a12, a15, 60 +40003f47: 3f .byte 0x3f + +40003f48 <_c_0x90000000>: +40003f48: 000000 ill +40003f4b: 003590 excw + +40003f4c <_c_0x70000035>: +40003f4c: 000035 call12 40003f50 <_c_0x00040000> +40003f4f: 000070 excw + +40003f50 <_c_0x00040000>: +40003f50: 040000 extui a0, a0, 0, 1 + ... + +40003f54 <_XX_unk3f54>: +40003f54: 004136 entry a1, 32 +40003f57: fffba1 l32r a10, 40003f44 <_X_SPI_read_status+0x48> +40003f5a: 0020f0 nop +40003f5d: 0aa8 l32i.n a10, a10, 0 +40003f5f: 002aa5 call8 40004208 <_X_Wait_SPI_Idle> +40003f62: 0d0c movi.n a13, 0 +40003f64: ff4591 l32r a9, 40003c78 <_c_0x60002e00> +40003f67: 0020c0 memw +40003f6a: 8229a2 l32i a10, a9, 0x208 +40003f6d: 0020c0 memw +40003f70: 8269d2 s32i a13, a9, 0x208 +40003f73: 00a782 movi a8, 0x700 +40003f76: 0020c0 memw +40003f79: 886982 s32i a8, a9, 0x220 +40003f7c: fff3f1 l32r a15, 40003f48 <_c_0x90000000> +40003f7f: 0020c0 memw +40003f82: 8769f2 s32i a15, a9, 0x21c +40003f85: fff1e1 l32r a14, 40003f4c <_c_0x70000035> +40003f88: 0020c0 memw +40003f8b: 8969e2 s32i a14, a9, 0x224 +40003f8e: 0020c0 memw +40003f91: 9069d2 s32i a13, a9, 0x240 +40003f94: ffefc1 l32r a12, 40003f50 <_c_0x00040000> +40003f97: 0020c0 memw +40003f9a: 8069c2 s32i a12, a9, 0x200 +40003f9d: 0020c0 memw +40003fa0: 8029b2 l32i a11, a9, 0x200 +40003fa3: db8c beqz.n a11, 40003fb4 <_XX_unk3f54+0x60> +40003fa5: 078076 loop a0, 40003fb0 <_XX_unk3f54+0x5c> +40003fa8: 0020c0 memw +40003fab: 8029b2 l32i a11, a9, 0x200 +40003fae: 2b8c beqz.n a11, 40003fb4 <_XX_unk3f54+0x60> +40003fb0: fffc46 j 40003fa5 <_XX_unk3f54+0x51> +40003fb3: 20c000 or a12, a0, a0 +40003fb6: 29c200 excw +40003fb9: c0c090 sub a12, a0, a9 +40003fbc: cc8074 excw +40003fbf: 03c911 l32r a1, 3ffc4ee4 <_start-0x3b11c> +40003fc2: 0020c0 memw +40003fc5: 8269a2 s32i a10, a9, 0x208 +40003fc8: 020c movi.n a2, 0 +40003fca: f01d retw.n + +40003fcc <_c_0x04000000>: +40003fcc: 000000 ill +40003fcf: 413604 excw +40003fd2: 02ad00 andb b10, b13, b0 +40003fd5: 002325 call8 40004208 <_X_Wait_SPI_Idle> +40003fd8: ff2891 l32r a9, 40003c78 <_c_0x60002e00> +40003fdb: 0020c0 memw +40003fde: 846932 s32i a3, a9, 0x210 +40003fe1: fffaa1 l32r a10, 40003fcc <_c_0x04000000> +40003fe4: 0020c0 memw +40003fe7: 8069a2 s32i a10, a9, 0x200 +40003fea: 0020c0 memw +40003fed: 802982 l32i a8, a9, 0x200 +40003ff0: 0020f0 nop +40003ff3: d88c beqz.n a8, 40004004 <_c_0x04000000+0x38> +40003ff5: 078076 loop a0, 40004000 <_c_0x04000000+0x34> +40003ff8: 0020c0 memw +40003ffb: 8029b2 l32i a11, a9, 0x200 +40003ffe: 2b8c beqz.n a11, 40004004 <_c_0x04000000+0x38> +40004000: fffc46 j 40003ff5 <_c_0x04000000+0x29> +40004003: 020c00 andb b0, b12, b0 +40004006: f01d retw.n +40004008: 000000 ill +4000400b: 000070 excw +4000400e: 368800 excw + +40004010 <_XX_unk4010>: +40004010: 004136 entry a1, 32 +40004013: 000232 l8ui a3, a2, 0 +40004016: 0020f0 nop +40004019: 144230 extui a4, a3, 2, 2 +4000401c: 145430 extui a5, a3, 4, 2 +4000401f: 143030 extui a3, a3, 0, 2 +40004022: 09a316 beqz a3, 400040c0 <_XX_unk4010+0xb0> +40004025: 0233b6 bltui a3, 3, 4000402b <_XX_unk4010+0x1b> +40004028: 002506 j 400040c0 <_XX_unk4010+0xb0> +4000402b: 022366 bnei a3, 2, 40004031 <_XX_unk4010+0x21> +4000402e: 002186 j 400040b8 <_XX_unk4010+0xa8> +40004031: 0b1366 bnei a3, 1, 40004040 <_XX_unk4010+0x30> +40004034: 0224b6 bltui a4, 2, 4000403a <_XX_unk4010+0x2a> +40004037: 002146 j 400040c0 <_XX_unk4010+0xb0> +4000403a: 808340 add a8, a3, a4 +4000403d: 7f9587 bne a5, a8, 400040c0 <_XX_unk4010+0xb0> +40004040: ffc1a1 l32r a10, 40003f44 <_X_SPI_read_status+0x48> +40004043: f03d nop.n +40004045: 0aa8 l32i.n a10, a10, 0 +40004047: 001c25 call8 40004208 <_X_Wait_SPI_Idle> +4000404a: 0f0c movi.n a15, 0 +4000404c: ffefc1 l32r a12, 40004008 <_c_0x04000000+0x3c> +4000404f: ffc0b1 l32r a11, 40003f50 <_c_0x00040000> +40004052: ff09a1 l32r a10, 40003c78 <_c_0x60002e00> +40004055: 0020c0 memw +40004058: 822ad2 l32i a13, a10, 0x208 +4000405b: 0020c0 memw +4000405e: 0212e2 l16ui a14, a2, 4 +40004061: 826af2 s32i a15, a10, 0x208 +40004064: 10ce16 beqz a14, 40004174 <_XX_unk4010+0x164> +40004067: f87c movi.n a8, -1 +40004069: b09580 addx8 a9, a5, a8 +4000406c: ffb751 l32r a5, 40003f48 <_c_0x90000000> +4000406f: b08380 addx8 a8, a3, a8 +40004072: 0199f0 slli a9, a9, 17 +40004075: 118880 slli a8, a8, 8 +40004078: 208890 or a8, a8, a9 +4000407b: 0c9d mov.n a9, a12 +4000407d: 0020c0 memw +40004080: 886a82 s32i a8, a10, 0x220 +40004083: 0020c0 memw +40004086: 876a52 s32i a5, a10, 0x21c +40004089: 0102e2 l8ui a14, a2, 1 +4000408c: 0a7e90 depbits a9, a14, 0, 8 +4000408f: 0020c0 memw +40004092: 896a92 s32i a9, a10, 0x224 +40004095: 0020c0 memw +40004098: 906af2 s32i a15, a10, 0x240 +4000409b: 0020c0 memw +4000409e: 806ab2 s32i a11, a10, 0x200 +400040a1: 0020c0 memw +400040a4: 802a82 l32i a8, a10, 0x200 +400040a7: a89c beqz.n a8, 400040c5 <_XX_unk4010+0xb5> +400040a9: 078076 loop a0, 400040b4 <_XX_unk4010+0xa4> +400040ac: 0020c0 memw +400040af: 802a92 l32i a9, a10, 0x200 +400040b2: f98c beqz.n a9, 400040c5 <_XX_unk4010+0xb5> +400040b4: fffc46 j 400040a9 <_XX_unk4010+0x99> +400040b7: 44cc00 extui a12, a0, 12, 5 +400040ba: 022566 bnei a5, 2, 400040c0 <_XX_unk4010+0xb0> +400040bd: ffdc06 j 40004031 <_XX_unk4010+0x21> +400040c0: f01d retw.n +400040c2: 000000 ill +400040c5: 0020c0 memw +400040c8: 902ae2 l32i a14, a10, 0x240 +400040cb: 930b addi.n a9, a3, -1 +400040cd: 7480e0 extui a8, a14, 0, 8 +400040d0: f4e0e0 extui a14, a14, 0, 16 +400040d3: 83e890 moveqz a14, a8, a9 +400040d6: 0ca416 beqz a4, 400041a4 <_XX_unk4010+0x194> +400040d9: 0c9d mov.n a9, a12 +400040db: 0020c0 memw +400040de: 876a52 s32i a5, a10, 0x21c +400040e1: 020282 l8ui a8, a2, 2 +400040e4: 0a7890 depbits a9, a8, 0, 8 +400040e7: 0020c0 memw +400040ea: 896a92 s32i a9, a10, 0x224 +400040ed: 0020c0 memw +400040f0: 906af2 s32i a15, a10, 0x240 +400040f3: 0020c0 memw +400040f6: 806ab2 s32i a11, a10, 0x200 +400040f9: 0020c0 memw +400040fc: 802a82 l32i a8, a10, 0x200 +400040ff: d88c beqz.n a8, 40004110 <_XX_unk4010+0x100> +40004101: 078076 loop a0, 4000410c <_XX_unk4010+0xfc> +40004104: 0020c0 memw +40004107: 802a92 l32i a9, a10, 0x200 +4000410a: 298c beqz.n a9, 40004110 <_XX_unk4010+0x100> +4000410c: fffc46 j 40004101 <_XX_unk4010+0xf1> +4000410f: 20c000 or a12, a0, a0 +40004112: 128200 andbc b8, b2, b0 +40004115: 2a9202 l16si a0, a2, 84 +40004118: 12f290 andbc b15, b2, b9 +4000411b: 909003 excw +4000411e: ff8074 excw +40004121: 998010 excw +40004124: 9e9011 l32r a1, 3ffebb64 <_start-0x1449c> +40004127: 909020 addx2 a9, a0, a2 +4000412a: 8890f4 excw +4000412d: 1f8710 excw +40004130: 913a add.n a9, a1, a3 +40004132: c0ffb6 bltui a15, 0x100, 400040f6 <_XX_unk4010+0xe6> +40004135: 920020 excw +40004138: 876a add.n a8, a7, a6 +4000413a: 030282 l8ui a8, a2, 3 +4000413d: 0a78c0 depbits a12, a8, 0, 8 +40004140: 0020c0 memw +40004143: 896ac2 s32i a12, a10, 0x224 +40004146: 0312f2 l16ui a15, a2, 6 +40004149: 0020c0 memw +4000414c: 906af2 s32i a15, a10, 0x240 +4000414f: 0020c0 memw +40004152: 806ab2 s32i a11, a10, 0x200 +40004155: 0020c0 memw +40004158: 802ae2 l32i a14, a10, 0x200 +4000415b: de8c beqz.n a14, 4000416c <_XX_unk4010+0x15c> +4000415d: 078076 loop a0, 40004168 <_XX_unk4010+0x158> +40004160: 0020c0 memw +40004163: 802ab2 l32i a11, a10, 0x200 +40004166: 2b8c beqz.n a11, 4000416c <_XX_unk4010+0x15c> +40004168: fffc46 j 4000415d <_XX_unk4010+0x14d> +4000416b: 20c000 or a12, a0, a0 +4000416e: 6ad200 depbits a0, a2, 6, 14 +40004171: f01d82 l16ui a8, a13, 0x1e0 +40004174: f17181 l32r a8, 40000738 <_c_0x80000000> +40004177: 0020c0 memw +4000417a: 876a82 s32i a8, a10, 0x21c +4000417d: 0102f2 l8ui a15, a2, 1 +40004180: 0a7fc0 depbits a12, a15, 0, 8 +40004183: 0020c0 memw +40004186: 896ac2 s32i a12, a10, 0x224 +40004189: 0020c0 memw +4000418c: 806ab2 s32i a11, a10, 0x200 +4000418f: 0020c0 memw +40004192: 802ae2 l32i a14, a10, 0x200 +40004195: fd3e16 beqz a14, 4000416c <_XX_unk4010+0x15c> +40004198: 0020c0 memw +4000419b: 802a92 l32i a9, a10, 0x200 +4000419e: ff6956 bnez a9, 40004198 <_XX_unk4010+0x188> +400041a1: fff1c6 j 4000416c <_XX_unk4010+0x15c> +400041a4: 021292 l16ui a9, a2, 4 +400041a7: 0312f2 l16ui a15, a2, 6 +400041aa: 108e90 and a8, a14, a9 +400041ad: 10ff90 and a15, a15, a9 +400041b0: 021f87 beq a15, a8, 400041b6 <_XX_unk4010+0x1a6> +400041b3: ffde86 j 40004131 <_XX_unk4010+0x121> +400041b6: ffec86 j 4000416c <_XX_unk4010+0x15c> +400041b9: 000000 ill + +400041bc <_X_SPI_write_enable>: +400041bc: 006136 entry a1, 48 +400041bf: 02ad mov.n a10, a2 +400041c1: 080c movi.n a8, 0 +400041c3: 006182 s32i a8, a1, 0 +400041c6: 000425 call8 40004208 <_X_Wait_SPI_Idle> +400041c9: f165b1 l32r a11, 40000760 <_c_0x40000000> +400041cc: feab91 l32r a9, 40003c78 <_c_0x60002e00> +400041cf: 0020c0 memw +400041d2: 8069b2 s32i a11, a9, 0x200 +400041d5: 0020c0 memw +400041d8: 8029a2 l32i a10, a9, 0x200 +400041db: fa8c beqz.n a10, 400041ee <_X_SPI_write_enable+0x32> +400041dd: 078076 loop a0, 400041e8 <_X_SPI_write_enable+0x2c> +400041e0: 0020c0 memw +400041e3: 8029c2 l32i a12, a9, 0x200 +400041e6: 4c8c beqz.n a12, 400041ee <_X_SPI_write_enable+0x32> +400041e8: fffc46 j 400041dd <_X_SPI_write_enable+0x21> +400041eb: 000000 ill +400041ee: 01d8 l32i.n a13, a1, 0 +400041f0: 230c movi.n a3, 2 +400041f2: 0bed17 bbsi a13, 1, 40004201 <_X_SPI_write_enable+0x45> +400041f5: 02ad mov.n a10, a2 +400041f7: 01bd mov.n a11, a1 +400041f9: ffd025 call8 40003efc <_X_SPI_read_status> +400041fc: 01e8 l32i.n a14, a1, 0 +400041fe: f30e37 bnone a14, a3, 400041f5 <_X_SPI_write_enable+0x39> +40004201: 020c movi.n a2, 0 +40004203: f01d retw.n +40004205: 000000 ill + +40004208 <_X_Wait_SPI_Idle>: +40004208: 006136 entry a1, 48 +4000420b: 20a220 or a10, a2, a2 +4000420e: fe9a91 l32r a9, 40003c78 <_c_0x60002e00> +40004211: 0020c0 memw +40004214: be2982 l32i a8, a9, 0x2f8 +40004217: d88c beqz.n a8, 40004228 <_l_4228> +40004219: 078076 loop a0, 40004224 <_X_Wait_SPI_Idle+0x1c> +4000421c: 0020c0 memw +4000421f: be29b2 l32i a11, a9, 0x2f8 +40004222: 2b8c beqz.n a11, 40004228 <_l_4228> +40004224: fffc46 j 40004219 <_X_Wait_SPI_Idle+0x11> + ... + +40004228 <_l_4228>: +40004228: 01bd mov.n a11, a1 +4000422a: ffcd25 call8 40003efc <_X_SPI_read_status> +4000422d: 3a8c beqz.n a10, 40004234 <_l_4234> +4000422f: 120c movi.n a2, 1 +40004231: f01d retw.n + ... + +40004234 <_l_4234>: +40004234: 020c movi.n a2, 0 +40004236: f01d retw.n + +40004238 <_XX_unk4238>: +40004238: 006136 entry a1, 48 +4000423b: 02ad mov.n a10, a2 +4000423d: 01bd mov.n a11, a1 +4000423f: ffd165 call8 40003f54 <_XX_unk3f54> +40004242: 2a8c beqz.n a10, 40004248 <_XX_unk4238+0x10> +40004244: 120c movi.n a2, 1 +40004246: f01d retw.n +40004248: 0188 l32i.n a8, a1, 0 +4000424a: 036897 bbci a8, 9, 40004251 <_XX_unk4238+0x19> +4000424d: 020c movi.n a2, 0 +4000424f: f01d retw.n +40004251: f9d8b1 l32r a11, 400029b4 <_c_0x00400000> +40004254: fe89a1 l32r a10, 40003c78 <_c_0x60002e00> +40004257: 0020c0 memw +4000425a: 822a92 l32i a9, a10, 0x208 +4000425d: f03d nop.n +4000425f: 2099b0 or a9, a9, a11 +40004262: 0020c0 memw +40004265: 826a92 s32i a9, a10, 0x208 +40004268: 02ad mov.n a10, a2 +4000426a: fff525 call8 400041bc <_X_SPI_write_enable> +4000426d: fd3a56 bnez a10, 40004244 <_XX_unk4238+0xc> +40004270: 02ad mov.n a10, a2 +40004272: 00a2b2 movi a11, 0x200 +40004275: ffd5a5 call8 40003fd0 <_c_0x04000000+0x4> +40004278: fc8a56 bnez a10, 40004244 <_XX_unk4238+0xc> +4000427b: fff386 j 4000424d <_XX_unk4238+0x15> +4000427e: 360000 excw +40004281: ad0061 l32r a6, 3ffef684 <_start-0x1097c> +40004284: 01bd02 l32ai a0, a13, 4 +40004287: ffcce5 call8 40003f54 <_XX_unk3f54> +4000428a: 2a8c beqz.n a10, 40004290 <_XX_unk4238+0x58> +4000428c: 120c movi.n a2, 1 +4000428e: f01d retw.n +40004290: 0188 l32i.n a8, a1, 0 +40004292: 03e897 bbsi a8, 9, 40004299 <_XX_unk4238+0x61> +40004295: 020c movi.n a2, 0 +40004297: f01d retw.n +40004299: f9c6b1 l32r a11, 400029b4 <_c_0x00400000> +4000429c: fe77a1 l32r a10, 40003c78 <_c_0x60002e00> +4000429f: 0020c0 memw +400042a2: 822a92 l32i a9, a10, 0x208 +400042a5: f03d nop.n +400042a7: 2099b0 or a9, a9, a11 +400042aa: 0020c0 memw +400042ad: 826a92 s32i a9, a10, 0x208 +400042b0: 02ad mov.n a10, a2 +400042b2: fff0a5 call8 400041bc <_X_SPI_write_enable> +400042b5: fd3a56 bnez a10, 4000428c <_XX_unk4238+0x54> +400042b8: 02ad mov.n a10, a2 +400042ba: 04c1b2 addi a11, a1, 4 +400042bd: ffc3e5 call8 40003efc <_X_SPI_read_status> +400042c0: 02ad mov.n a10, a2 +400042c2: 0401b2 l8ui a11, a1, 4 +400042c5: ffd0a5 call8 40003fd0 <_c_0x04000000+0x4> +400042c8: fc0a56 bnez a10, 4000428c <_XX_unk4238+0x54> +400042cb: fff186 j 40004295 <_XX_unk4238+0x5d> +400042ce: 000000 ill +400042d1: 002880 excw +400042d4: 100000 and a0, a0, a0 + ... + +400042d8 <_X_SPIFlashModeConfig>: +400042d8: 004136 entry a1, 32 +400042db: 069216 beqz a2, 40004348 <_X_SPIFlashModeConfig+0x70> +400042de: 6e1226 beqi a2, 1, 40004350 <_X_SPIFlashModeConfig+0x78> +400042e1: 732226 beqi a2, 2, 40004358 <_X_SPIFlashModeConfig+0x80> +400042e4: 783226 beqi a2, 3, 40004360 <_X_SPIFlashModeConfig+0x88> +400042e7: 7d4226 beqi a2, 4, 40004368 <_X_SPIFlashModeConfig+0x90> +400042ea: 050c movi.n a5, 0 +400042ec: fe6321 l32r a2, 40003c78 <_c_0x60002e00> +400042ef: 0b23f6 bgeui a3, 2, 400042fe <_X_SPIFlashModeConfig+0x26> +400042f2: f11141 l32r a4, 40000738 <_c_0x80000000> +400042f5: 0020c0 memw +400042f8: 866242 s32i a4, a2, 0x218 +400042fb: 000646 j 40004318 <_X_SPIFlashModeConfig+0x40> +400042fe: 630b addi.n a6, a3, -1 +40004300: 118640 slli a8, a6, 12 +40004303: 206680 or a6, a6, a8 +40004306: 418130 srli a8, a3, 1 +40004309: ffc882 addi a8, a8, -1 +4000430c: 1188a0 slli a8, a8, 6 +4000430f: 206680 or a6, a6, a8 +40004312: 0020c0 memw +40004315: 866262 s32i a6, a2, 0x218 +40004318: ffeeb1 l32r a11, 400042d0 <_XX_unk4238+0x98> +4000431b: 20b5b0 or a11, a5, a11 +4000431e: 0020c0 memw +40004321: 8262b2 s32i a11, a2, 0x208 +40004324: ffeca1 l32r a10, 400042d4 <_XX_unk4238+0x9c> +40004327: 0020c0 memw +4000432a: 8062a2 s32i a10, a2, 0x200 +4000432d: 0020c0 memw +40004330: 802292 l32i a9, a2, 0x200 +40004333: d98c beqz.n a9, 40004344 <_X_SPIFlashModeConfig+0x6c> +40004335: 078076 loop a0, 40004340 <_X_SPIFlashModeConfig+0x68> +40004338: 0020c0 memw +4000433b: 8022c2 l32i a12, a2, 0x200 +4000433e: 2c8c beqz.n a12, 40004344 <_X_SPIFlashModeConfig+0x6c> +40004340: fffc46 j 40004335 <_X_SPIFlashModeConfig+0x5d> +40004343: f01d00 subx8 a1, a13, a0 +40004346: 510000 excw +40004349: fe5c movi.n a14, 95 +4000434b: ffe746 j 400042ec <_X_SPIFlashModeConfig+0x14> +4000434e: 510000 excw +40004351: 46ffe1 l32r a14, 3ffd5f50 <_start-0x2a0b0> +40004354: 00ffe5 call8 40005354 <_XX_unk51ac+0x1a8> +40004357: 6a5100 depbits a0, a1, 6, 6 +4000435a: fe .byte 0xfe +4000435b: ffe346 j 400042ec <_X_SPIFlashModeConfig+0x14> +4000435e: 510000 excw +40004361: fabd excw +40004363: ffe146 j 400042ec <_X_SPIFlashModeConfig+0x14> +40004366: 510000 excw +40004369: 46f473 excw +4000436c: df .byte 0xdf +4000436d: ff .byte 0xff + ... + +40004370 <_X_spi_flash_attach>: +40004370: 004136 entry a1, 32 +40004373: 20a220 or a10, a2, a2 +40004376: ff6a65 call8 40003a1c <_X_SelectSpiFunction> +40004379: 5a0c movi.n a10, 5 +4000437b: 04a0b2 movi a11, 4 +4000437e: fff5a5 call8 400042d8 <_X_SPIFlashModeConfig> +40004381: 03bd mov.n a11, a3 +40004383: 5a0c movi.n a10, 5 +40004385: 001b25 call8 40004538 <_X_SPIReadModeConfig> +40004388: f01d retw.n + ... + +4000438c : +4000438c: 004136 entry a1, 32 +4000438f: feeda1 l32r a10, 40003f44 <_X_SPI_read_status+0x48> +40004392: 002aa2 l32i a10, a10, 0 +40004395: ffe725 call8 40004208 <_X_Wait_SPI_Idle> +40004398: 0b2c movi.n a11, 32 +4000439a: fd6c movi.n a13, -17 +4000439c: fe37c1 l32r a12, 40003c78 <_c_0x60002e00> +4000439f: f7af92 movi a9, -9 +400043a2: fbe3a1 l32r a10, 40003330 <_c_0x3feffe00> +400043a5: 0020c0 memw +400043a8: 822a82 l32i a8, a10, 0x208 +400043ab: 108890 and a8, a8, a9 +400043ae: 0020c0 memw +400043b1: 826a82 s32i a8, a10, 0x208 +400043b4: f35ef1 l32r a15, 4000112c <_c_0xfffdffff> +400043b7: 0020c0 memw +400043ba: 822ce2 l32i a14, a12, 0x208 +400043bd: 10eef0 and a14, a14, a15 +400043c0: 0020c0 memw +400043c3: 826ce2 s32i a14, a12, 0x208 +400043c6: 0020c0 memw +400043c9: 822a92 l32i a9, a10, 0x208 +400043cc: 1099d0 and a9, a9, a13 +400043cf: 0020c0 memw +400043d2: 826a92 s32i a9, a10, 0x208 +400043d5: 10a082 movi a8, 16 +400043d8: 0020c0 memw +400043db: 822af2 l32i a15, a10, 0x208 +400043de: f03d nop.n +400043e0: 20ff80 or a15, a15, a8 +400043e3: 0020c0 memw +400043e6: 826af2 s32i a15, a10, 0x208 +400043e9: 0020c0 memw +400043ec: 822ae2 l32i a14, a10, 0x208 +400043ef: 0020f0 nop +400043f2: 0eee57 bbsi a14, 5, 40004404 +400043f5: 088076 loop a0, 40004401 +400043f8: 0020c0 memw +400043fb: 822ae2 l32i a14, a10, 0x208 +400043fe: 028eb7 bany a14, a11, 40004404 +40004401: fffc06 j 400043f5 +40004404: 0020c0 memw +40004407: 822a82 l32i a8, a10, 0x208 +4000440a: 1088d0 and a8, a8, a13 +4000440d: 0020c0 memw +40004410: 826a82 s32i a8, a10, 0x208 +40004413: 7f0c movi.n a15, 7 +40004415: 0020c0 memw +40004418: 822ae2 l32i a14, a10, 0x208 +4000441b: 20eef0 or a14, a14, a15 +4000441e: 0020c0 memw +40004421: 826ae2 s32i a14, a10, 0x208 +40004424: fc41b1 l32r a11, 40003528 <_c_0x00020000> +40004427: 0020c0 memw +4000442a: 822c92 l32i a9, a12, 0x208 +4000442d: 2099b0 or a9, a9, a11 +40004430: 0020c0 memw +40004433: 826c92 s32i a9, a12, 0x208 +40004436: 880c movi.n a8, 8 +40004438: 0020c0 memw +4000443b: 822af2 l32i a15, a10, 0x208 +4000443e: 20ff80 or a15, a15, a8 +40004441: 0020c0 memw +40004444: 826af2 s32i a15, a10, 0x208 +40004447: f01d retw.n +40004449: 000000 ill + +4000444c : +4000444c: 004136 entry a1, 32 +4000444f: 052c movi.n a5, 32 +40004451: f76c movi.n a7, -17 +40004453: fe09a1 l32r a10, 40003c78 <_c_0x60002e00> +40004456: 7d7c movi.n a13, -9 +40004458: fbb641 l32r a4, 40003330 <_c_0x3feffe00> +4000445b: 0020c0 memw +4000445e: 8224c2 l32i a12, a4, 0x208 +40004461: 10ccd0 and a12, a12, a13 +40004464: 0020c0 memw +40004467: 8264c2 s32i a12, a4, 0x208 +4000446a: f330b1 l32r a11, 4000112c <_c_0xfffdffff> +4000446d: 0020c0 memw +40004470: 822a92 l32i a9, a10, 0x208 +40004473: 1099b0 and a9, a9, a11 +40004476: 0020c0 memw +40004479: 826a92 s32i a9, a10, 0x208 +4000447c: 0020c0 memw +4000447f: 822482 l32i a8, a4, 0x208 +40004482: 108870 and a8, a8, a7 +40004485: 0020c0 memw +40004488: 826482 s32i a8, a4, 0x208 +4000448b: 061c movi.n a6, 16 +4000448d: 0020c0 memw +40004490: 822432 l32i a3, a4, 0x208 +40004493: 203360 or a3, a3, a6 +40004496: 0020c0 memw +40004499: 826432 s32i a3, a4, 0x208 +4000449c: 0020c0 memw +4000449f: 822422 l32i a2, a4, 0x208 +400044a2: 0ee257 bbsi a2, 5, 400044b4 +400044a5: 088076 loop a0, 400044b1 +400044a8: 0020c0 memw +400044ab: 8224e2 l32i a14, a4, 0x208 +400044ae: 028e57 bany a14, a5, 400044b4 +400044b1: fffc06 j 400044a5 +400044b4: 0020c0 memw +400044b7: 8224f2 l32i a15, a4, 0x208 +400044ba: 10ff70 and a15, a15, a7 +400044bd: 0020c0 memw +400044c0: 8264f2 s32i a15, a4, 0x208 +400044c3: f01d retw.n +400044c5: 000000 ill +400044c8: 006136 entry a1, 48 +400044cb: fe9e21 l32r a2, 40003f44 <_X_SPI_read_status+0x48> +400044ce: 01bd mov.n a11, a1 +400044d0: 0022a2 l32i a10, a2, 0 +400044d3: ffa825 call8 40003f54 <_XX_unk3f54> +400044d6: 2a8c beqz.n a10, 400044dc +400044d8: 120c movi.n a2, 1 +400044da: f01d retw.n +400044dc: 02a8 l32i.n a10, a2, 0 +400044de: ffcde5 call8 400041bc <_X_SPI_write_enable> +400044e1: ff3a56 bnez a10, 400044d8 +400044e4: 02a8 l32i.n a10, a2, 0 +400044e6: 0021b2 l32i a11, a1, 0 +400044e9: ffae65 call8 40003fd0 <_c_0x04000000+0x4> +400044ec: fe8a56 bnez a10, 400044d8 +400044ef: 020c movi.n a2, 0 +400044f1: f01d retw.n +400044f3: 613600 excw +400044f6: 932100 movnez a2, a1, a0 +400044f9: fe .byte 0xfe +400044fa: 01bd mov.n a11, a1 +400044fc: 0022a2 l32i a10, a2, 0 +400044ff: ffa565 call8 40003f54 <_XX_unk3f54> +40004502: 2a8c beqz.n a10, 40004508 +40004504: 120c movi.n a2, 1 +40004506: f01d retw.n +40004508: 02a8 l32i.n a10, a2, 0 +4000450a: ffcb25 call8 400041bc <_X_SPI_write_enable> +4000450d: ff3a56 bnez a10, 40004504 +40004510: 02a8 l32i.n a10, a2, 0 +40004512: 01b8 l32i.n a11, a1, 0 +40004514: cc1c movi.n a12, 28 +40004516: 20bbc0 or a11, a11, a12 +40004519: ffab65 call8 40003fd0 <_c_0x04000000+0x4> +4000451c: fe4a56 bnez a10, 40004504 +4000451f: 020c movi.n a2, 0 +40004521: f01d retw.n +40004523: 9fff00 excw +40004526: 6f .byte 0x6f +40004527: fe .byte 0xfe +40004528: 002000 isync +4000452b: 200001 l32r a0, 3ffcc52c <_start-0x33ad4> +4000452e: 000010 excw +40004531: 008020 any4 b2, b0:b1:b2:b3 +40004534: 006000 rsil a0, 0 + ... + +40004538 <_X_SPIReadModeConfig>: +40004538: 004136 entry a1, 32 +4000453b: fffa91 l32r a9, 40004524 +4000453e: fe81a1 l32r a10, 40003f44 <_X_SPI_read_status+0x48> +40004541: fdcd51 l32r a5, 40003c78 <_c_0x60002e00> +40004544: 0020c0 memw +40004547: 822582 l32i a8, a5, 0x208 +4000454a: f03d nop.n +4000454c: 108890 and a8, a8, a9 +4000454f: 0020c0 memw +40004552: 826582 s32i a8, a5, 0x208 +40004555: c2ac beqz.n a2, 40004585 <_X_SPIReadModeConfig+0x4d> +40004557: 451226 beqi a2, 1, 400045a0 <_X_SPIReadModeConfig+0x68> +4000455a: 4a2226 beqi a2, 2, 400045a8 <_X_SPIReadModeConfig+0x70> +4000455d: 4f3226 beqi a2, 3, 400045b0 <_X_SPIReadModeConfig+0x78> +40004560: 544226 beqi a2, 4, 400045b8 <_X_SPIReadModeConfig+0x80> +40004563: 040c movi.n a4, 0 +40004565: 1f1226 beqi a2, 1, 40004588 <_X_SPIReadModeConfig+0x50> +40004568: 438c beqz.n a3, 40004570 <_X_SPIReadModeConfig+0x38> +4000456a: 002aa2 l32i a10, a10, 0 +4000456d: ffd125 call8 40004280 <_XX_unk4238+0x48> +40004570: 0020c0 memw +40004573: 8225a2 l32i a10, a5, 0x208 +40004576: 20aa40 or a10, a10, a4 +40004579: 0020c0 memw +4000457c: 8265a2 s32i a10, a5, 0x208 +4000457f: 020c movi.n a2, 0 +40004581: f01d retw.n +40004583: 410000 srli a0, a0, 0 +40004586: ffe8 l32i.n a14, a15, 60 +40004588: 0aa8 l32i.n a10, a10, 0 +4000458a: ffcae5 call8 40004238 <_XX_unk4238> +4000458d: 0020c0 memw +40004590: 8225b2 l32i a11, a5, 0x208 +40004593: 20bb40 or a11, a11, a4 +40004596: 0020c0 memw +40004599: 8265b2 s32i a11, a5, 0x208 +4000459c: 020c movi.n a2, 0 +4000459e: f01d retw.n +400045a0: ffe341 l32r a4, 4000452c +400045a3: ffef86 j 40004565 <_X_SPIReadModeConfig+0x2d> +400045a6: 410000 srli a0, a0, 0 +400045a9: 86ffe2 s32ri a14, a15, 0x218 +400045ac: ffed excw +400045ae: 410000 srli a0, a0, 0 +400045b1: 86ffe1 l32r a14, 3ffe61b0 <_start-0x19e50> +400045b4: ffeb addi.n a15, a15, 14 +400045b6: 410000 srli a0, a0, 0 +400045b9: df .byte 0xdf +400045ba: e986f3 excw +400045bd: ff .byte 0xff +400045be: 360000 excw +400045c1: a10041 l32r a4, 3ffec9c4 <_start-0x1363c> +400045c4: a2fe60 muluh a15, a14, a6 +400045c7: 002a add.n a0, a0, a2 +400045c9: ffc3e5 call8 40004208 <_X_Wait_SPI_Idle> +400045cc: ffd6c1 l32r a12, 40004524 +400045cf: fdaaa1 l32r a10, 40003c78 <_c_0x60002e00> +400045d2: 0020c0 memw +400045d5: 822ab2 l32i a11, a10, 0x208 +400045d8: 10bbc0 and a11, a11, a12 +400045db: 0020c0 memw +400045de: 826ab2 s32i a11, a10, 0x208 +400045e1: 32bc beqz.n a2, 40004618 <_X_SPIReadModeConfig+0xe0> +400045e3: 491226 beqi a2, 1, 40004630 <_X_SPIReadModeConfig+0xf8> +400045e6: 5e2226 beqi a2, 2, 40004648 <_X_SPIReadModeConfig+0x110> +400045e9: 733226 beqi a2, 3, 40004660 <_X_SPIReadModeConfig+0x128> +400045ec: 104226 beqi a2, 4, 40004600 <_X_SPIReadModeConfig+0xc8> +400045ef: 0020c0 memw +400045f2: 822ad2 l32i a13, a10, 0x208 +400045f5: 0020c0 memw +400045f8: 826ad2 s32i a13, a10, 0x208 +400045fb: 020c movi.n a2, 0 +400045fd: f01d retw.n +400045ff: cdf100 excw +40004602: 20c0f3 excw +40004605: 2ae200 depbits a0, a2, 2, 15 +40004608: eef082 s32ri a8, a0, 0x3b8 +4000460b: 20c020 or a12, a0, a2 +4000460e: 6ae200 depbits a0, a2, 6, 15 +40004611: 020c82 l8ui a8, a12, 2 +40004614: f01d retw.n +40004616: 910000 srl a0, a0 +40004619: c0ffc4 excw +4000461c: 220020 orb b0, b0, b2 +4000461f: 822a add.n a8, a2, a2 +40004621: 202290 or a2, a2, a9 +40004624: 0020c0 memw +40004627: 826a22 s32i a2, a10, 0x208 +4000462a: 020c movi.n a2, 0 +4000462c: f01d retw.n +4000462e: 910000 srl a0, a0 +40004631: bf .byte 0xbf +40004632: ff .byte 0xff +40004633: 0020c0 memw +40004636: 822a82 l32i a8, a10, 0x208 +40004639: 208890 or a8, a8, a9 +4000463c: 0020c0 memw +4000463f: 826a82 s32i a8, a10, 0x208 +40004642: 020c movi.n a2, 0 +40004644: f01d retw.n +40004646: c10000 mul16u a0, a0, a0 +40004649: ffba add.n a15, a15, a11 +4000464b: 0020c0 memw +4000464e: 822ab2 l32i a11, a10, 0x208 +40004651: 20bbc0 or a11, a11, a12 +40004654: 0020c0 memw +40004657: 826ab2 s32i a11, a10, 0x208 +4000465a: 020c movi.n a2, 0 +4000465c: f01d retw.n +4000465e: 910000 srl a0, a0 +40004661: c0ffb5 call12 3ffc565c <_start-0x3a9a4> +40004664: 220020 orb b0, b0, b2 +40004667: 822a add.n a8, a2, a2 +40004669: 202290 or a2, a2, a9 +4000466c: 0020c0 memw +4000466f: 826a22 s32i a2, a10, 0x208 +40004672: 020c movi.n a2, 0 +40004674: f01d retw.n +40004676: 360000 excw +40004679: a10041 l32r a4, 3ffeca7c <_start-0x13584> +4000467c: a2fe32 s32ri a3, a14, 0x288 +4000467f: 002a add.n a0, a0, a2 +40004681: ffb865 call8 40004208 <_X_Wait_SPI_Idle> +40004684: fd7d91 l32r a9, 40003c78 <_c_0x60002e00> +40004687: 0d22f6 bgeui a2, 2, 40004698 <_X_SPIReadModeConfig+0x160> +4000468a: f02bb1 l32r a11, 40000738 <_c_0x80000000> +4000468d: 0020c0 memw +40004690: 8669b2 s32i a11, a9, 0x218 +40004693: 020c movi.n a2, 0 +40004695: f01d retw.n +40004697: c20b00 quou a0, a11, a0 +4000469a: 11dc40 slli a13, a12, 12 +4000469d: 20ccd0 or a12, a12, a13 +400046a0: 41d120 srli a13, a2, 1 +400046a3: dd0b addi.n a13, a13, -1 +400046a5: 11dda0 slli a13, a13, 6 +400046a8: 20ccd0 or a12, a12, a13 +400046ab: 0020c0 memw +400046ae: 8669c2 s32i a12, a9, 0x218 +400046b1: 020c movi.n a2, 0 +400046b3: f01d retw.n +400046b5: 000000 ill +400046b8: 004136 entry a1, 32 +400046bb: fe2221 l32r a2, 40003f44 <_X_SPI_read_status+0x48> +400046be: f03d nop.n +400046c0: 0022a2 l32i a10, a2, 0 +400046c3: ffafa5 call8 400041bc <_X_SPI_write_enable> +400046c6: 2a8c beqz.n a10, 400046cc <_X_SPIReadModeConfig+0x194> +400046c8: 120c movi.n a2, 1 +400046ca: f01d retw.n +400046cc: 02a8 l32i.n a10, a2, 0 +400046ce: ff5ae5 call8 40003c7c <_X_SPI_chip_erase> +400046d1: ff3a56 bnez a10, 400046c8 <_X_SPIReadModeConfig+0x190> +400046d4: 020c movi.n a2, 0 +400046d6: f01d retw.n +400046d8: 004136 entry a1, 32 +400046db: fe1a31 l32r a3, 40003f44 <_X_SPI_read_status+0x48> +400046de: 03a8 l32i.n a10, a3, 0 +400046e0: 2a98 l32i.n a9, a10, 8 +400046e2: 1a88 l32i.n a8, a10, 4 +400046e4: c28890 quou a8, a8, a9 +400046e7: 063287 bltu a2, a8, 400046f1 <_X_SPIReadModeConfig+0x1b9> +400046ea: 120c movi.n a2, 1 +400046ec: f01d retw.n +400046ee: 000000 ill +400046f1: ffaca5 call8 400041bc <_X_SPI_write_enable> +400046f4: ff2a56 bnez a10, 400046ea <_X_SPIReadModeConfig+0x1b2> +400046f7: 03a8 l32i.n a10, a3, 0 +400046f9: 2ab8 l32i.n a11, a10, 8 +400046fb: 82bb20 mull a11, a11, a2 +400046fe: ff6065 call8 40003d04 <_c_0x00800000+0x4> +40004701: fe5a56 bnez a10, 400046ea <_X_SPIReadModeConfig+0x1b2> +40004704: 020c movi.n a2, 0 +40004706: f01d retw.n + +40004708 : +40004708: 004136 entry a1, 32 +4000470b: fe0e31 l32r a3, 40003f44 <_X_SPI_read_status+0x48> +4000470e: 03a8 l32i.n a10, a3, 0 +40004710: 3a98 l32i.n a9, a10, 12 +40004712: 1a88 l32i.n a8, a10, 4 +40004714: c28890 quou a8, a8, a9 +40004717: 063287 bltu a2, a8, 40004721 +4000471a: 120c movi.n a2, 1 +4000471c: f01d retw.n +4000471e: 000000 ill +40004721: ffa9a5 call8 400041bc <_X_SPI_write_enable> +40004724: ff2a56 bnez a10, 4000471a +40004727: 03a8 l32i.n a10, a3, 0 +40004729: 3ab8 l32i.n a11, a10, 12 +4000472b: 82bb20 mull a11, a11, a2 +4000472e: ff58e5 call8 40003cbc <_XX_unk3cbc> +40004731: fe5a56 bnez a10, 4000471a +40004734: 020c movi.n a2, 0 +40004736: f01d retw.n + +40004738 : +40004738: 006136 entry a1, 48 +4000473b: fe02c1 l32r a12, 40003f44 <_X_SPI_read_status+0x48> +4000473e: 046d mov.n a6, a4 +40004740: 0ca8 l32i.n a10, a12, 0 +40004742: 942a add.n a9, a4, a2 +40004744: 1a88 l32i.n a8, a10, 4 +40004746: 4a78 l32i.n a7, a10, 16 +40004748: 143897 bltu a8, a9, 40004760 +4000474b: e25270 remu a5, a2, a7 +4000474e: c05750 sub a5, a7, a5 +40004751: 0fb457 bgeu a4, a5, 40004764 +40004754: 04dd mov.n a13, a4 +40004756: 03cd mov.n a12, a3 +40004758: 02bd mov.n a11, a2 +4000475a: ff5f65 call8 40003d50 <_c_0x02000000+0x4> +4000475d: 05fa16 beqz a10, 400047c0 +40004760: 120c movi.n a2, 1 +40004762: f01d retw.n +40004764: 02bd mov.n a11, a2 +40004766: 03cd mov.n a12, a3 +40004768: 05dd mov.n a13, a5 +4000476a: ff5e65 call8 40003d50 <_c_0x02000000+0x4> +4000476d: fefa56 bnez a10, 40004760 +40004770: 0129 s32i.n a2, a1, 0 +40004772: 1169 s32i.n a6, a1, 4 +40004774: c04650 sub a4, a6, a5 +40004777: c24470 quou a4, a4, a7 +4000477a: 74ac beqz.n a4, 400047a5 +4000477c: 806520 add a6, a5, a2 +4000477f: 00a022 movi a2, 0 +40004782: 06bd mov.n a11, a6 +40004784: 07dd mov.n a13, a7 +40004786: fdefa1 l32r a10, 40003f44 <_X_SPI_read_status+0x48> +40004789: 41c250 srli a12, a5, 2 +4000478c: a0cc30 addx4 a12, a12, a3 +4000478f: 002aa2 l32i a10, a10, 0 +40004792: ff5be5 call8 40003d50 <_c_0x02000000+0x4> +40004795: fc7a56 bnez a10, 40004760 +40004798: 667a add.n a6, a6, a7 +4000479a: 575a add.n a5, a7, a5 +4000479c: 01c222 addi a2, a2, 1 +4000479f: 742020 extui a2, a2, 0, 8 +400047a2: dc9427 bne a4, a2, 40004782 +400047a5: fde7a1 l32r a10, 40003f44 <_X_SPI_read_status+0x48> +400047a8: 11d8 l32i.n a13, a1, 4 +400047aa: 01b8 l32i.n a11, a1, 0 +400047ac: 41c250 srli a12, a5, 2 +400047af: a0cc30 addx4 a12, a12, a3 +400047b2: bb5a add.n a11, a11, a5 +400047b4: c0dd50 sub a13, a13, a5 +400047b7: 002aa2 l32i a10, a10, 0 +400047ba: ff5965 call8 40003d50 <_c_0x02000000+0x4> +400047bd: f9fa56 bnez a10, 40004760 +400047c0: 020c movi.n a2, 0 +400047c2: f01d retw.n +400047c4: 011000 slli a1, a0, 32 +400047c7: 413600 srli a3, a0, 6 +400047ca: fe4100 excw +400047cd: ff .byte 0xff +400047ce: fad831 l32r a3, 40003330 <_c_0x3feffe00> +400047d1: 0020c0 memw +400047d4: 9a2322 l32i a2, a3, 0x268 +400047d7: 202240 or a2, a2, a4 +400047da: 0020c0 memw +400047dd: 9a6322 s32i a2, a3, 0x268 +400047e0: f01d retw.n +400047e2: 000000 ill +400047e5: ae .byte 0xae +400047e6: 366001 l32r a0, 3ffd2168 <_start-0x2de98> +400047e9: 4d0041 l32r a4, 3ffd7bec <_start-0x28414> +400047ec: f51c03 excw +400047ef: 458527 bany a5, a2, 40004838 +400047f2: fffc61 l32r a6, 400047e4 +400047f5: 0020c0 memw +400047f8: 896622 s32i a2, a6, 0x224 +400047fb: 08a032 movi a3, 8 +400047fe: 00a022 movi a2, 0 +40004801: 0ea376 loopgtz a3, 40004813 +40004804: a03260 addx4 a3, a2, a6 +40004807: 0458 l32i.n a5, a4, 0 +40004809: 0020c0 memw +4000480c: 806352 s32i a5, a3, 0x200 +4000480f: 444b addi.n a4, a4, 4 +40004811: 221b addi.n a2, a2, 1 +40004813: 01a0a2 movi a10, 1 +40004816: 0020c0 memw +40004819: 8866a2 s32i a10, a6, 0x220 +4000481c: 0020c0 memw +4000481f: 882692 l32i a9, a6, 0x220 +40004822: 0ee907 bbsi a9, 0, 40004834 +40004825: 088076 loop a0, 40004831 +40004828: 0020c0 memw +4000482b: 8826b2 l32i a11, a6, 0x220 +4000482e: 02eb07 bbsi a11, 0, 40004834 +40004831: fffc06 j 40004825 +40004834: 020c movi.n a2, 0 +40004836: f01d retw.n +40004838: 120c movi.n a2, 1 +4000483a: f01d retw.n +4000483c: ff .byte 0xff +4000483d: ef .byte 0xef +4000483e: fe .byte 0xfe +4000483f: ff .byte 0xff +40004840: 004136 entry a1, 32 +40004843: fffe41 l32r a4, 4000483c +40004846: faba31 l32r a3, 40003330 <_c_0x3feffe00> +40004849: 0020c0 memw +4000484c: 9a2322 l32i a2, a3, 0x268 +4000484f: 102240 and a2, a2, a4 +40004852: 0020c0 memw +40004855: 9a6322 s32i a2, a3, 0x268 +40004858: f01d retw.n +4000485a: 360000 excw +4000485d: 1c0041 l32r a4, 3ffcb860 <_start-0x347a0> +40004860: 20fa add.n a2, a0, a15 +40004862: cc4480 excw +40004865: 4718 l32i.n a1, a7, 16 +40004867: 030a add.n a0, a3, a0 +40004869: 120c movi.n a2, 1 +4000486b: f01d retw.n +4000486d: fff5a5 call8 400047c8 +40004870: 415540 srli a5, a4, 5 +40004873: a59c beqz.n a5, 40004891 +40004875: 00a042 movi a4, 0 +40004878: 02ad mov.n a10, a2 +4000487a: 11b4d0 slli a11, a4, 3 +4000487d: a0bb30 addx4 a11, a11, a3 +40004880: fff665 call8 400047e8 +40004883: 02ad mov.n a10, a2 +40004885: 03bd mov.n a11, a3 +40004887: 0c2c movi.n a12, 32 +40004889: ffeae5 call8 40004738 +4000488c: 441b addi.n a4, a4, 1 +4000488e: e69457 bne a4, a5, 40004878 +40004891: fffae5 call8 40004840 +40004894: f01d retw.n + ... + +40004898 : +40004898: 004136 entry a1, 32 +4000489b: 04dd mov.n a13, a4 +4000489d: 03cd mov.n a12, a3 +4000489f: fda9a1 l32r a10, 40003f44 <_X_SPI_read_status+0x48> +400048a2: 02bd mov.n a11, a2 +400048a4: 0aa8 l32i.n a10, a10, 0 +400048a6: ff57e5 call8 40003e24 <_XX_unk3e24> +400048a9: 3a8c beqz.n a10, 400048b0 +400048ab: 120c movi.n a2, 1 +400048ad: f01d retw.n +400048af: 020c00 andb b0, b12, b0 +400048b2: f01d retw.n + +400048b4 <_X_SPIEraseArea>: +400048b4: 004136 entry a1, 32 +400048b7: 05a0a2 movi a10, 5 +400048ba: 01a0b2 movi a11, 1 +400048bd: ffc7a5 call8 40004538 <_X_SPIReadModeConfig> +400048c0: fda141 l32r a4, 40003f44 <_X_SPI_read_status+0x48> +400048c3: 04a8 l32i.n a10, a4, 0 +400048c5: 923a add.n a9, a2, a3 +400048c7: 1a88 l32i.n a8, a10, 4 +400048c9: 3ab8 l32i.n a11, a10, 12 +400048cb: 053897 bltu a8, a9, 400048d4 <_X_SPIEraseArea+0x20> +400048ce: e2b2b0 remu a11, a2, a11 +400048d1: 004b16 beqz a11, 400048d9 <_X_SPIEraseArea+0x25> +400048d4: 120c movi.n a2, 1 +400048d6: f01d retw.n +400048d8: bee500 excw +400048db: ff .byte 0xff +400048dc: ff4a56 bnez a10, 400048d4 <_X_SPIEraseArea+0x20> +400048df: 0448 l32i.n a4, a4, 0 +400048e1: 3488 l32i.n a8, a4, 12 +400048e3: 2448 l32i.n a4, a4, 8 +400048e5: c29380 quou a9, a3, a8 +400048e8: e2c380 remu a12, a3, a8 +400048eb: c22280 quou a2, a2, a8 +400048ee: c24480 quou a4, a4, a8 +400048f1: e25240 remu a5, a2, a4 +400048f4: 1c8c beqz.n a12, 400048f9 <_X_SPIEraseArea+0x45> +400048f6: 01c992 addi a9, a9, 1 +400048f9: c05450 sub a5, a4, a5 +400048fc: 435950 min a5, a9, a5 +400048ff: c03950 sub a3, a9, a5 +40004902: e58c beqz.n a5, 40004914 <_X_SPIEraseArea+0x60> +40004904: 02ad mov.n a10, a2 +40004906: ffe025 call8 40004708 +40004909: fc7a56 bnez a10, 400048d4 <_X_SPIEraseArea+0x20> +4000490c: 221b addi.n a2, a2, 1 +4000490e: ffc552 addi a5, a5, -1 +40004911: fef556 bnez a5, 40004904 <_X_SPIEraseArea+0x50> +40004914: 11b437 bgeu a4, a3, 40004929 <_X_SPIEraseArea+0x75> +40004917: c2a240 quou a10, a2, a4 +4000491a: ffdbe5 call8 400046d8 <_X_SPIReadModeConfig+0x1a0> +4000491d: fb3a56 bnez a10, 400048d4 <_X_SPIEraseArea+0x20> +40004920: 802240 add a2, a2, a4 +40004923: c03340 sub a3, a3, a4 +40004926: ed3437 bltu a4, a3, 40004917 <_X_SPIEraseArea+0x63> +40004929: 0f13a6 blti a3, 1, 4000493c <_X_SPIEraseArea+0x88> +4000492c: 02ad mov.n a10, a2 +4000492e: ffdda5 call8 40004708 +40004931: f9fa56 bnez a10, 400048d4 <_X_SPIEraseArea+0x20> +40004934: 221b addi.n a2, a2, 1 +40004936: ffc332 addi a3, a3, -1 +40004939: fef356 bnez a3, 4000492c <_X_SPIEraseArea+0x78> +4000493c: 020c movi.n a2, 0 +4000493e: f01d retw.n + +40004940 <_XX_unk4940>: +40004940: 004136 entry a1, 32 +40004943: fd8081 l32r a8, 40003f44 <_X_SPI_read_status+0x48> +40004946: 0888 l32i.n a8, a8, 0 +40004948: 1839 s32i.n a3, a8, 4 +4000494a: 2849 s32i.n a4, a8, 8 +4000494c: 3859 s32i.n a5, a8, 12 +4000494e: 4869 s32i.n a6, a8, 16 +40004950: 0829 s32i.n a2, a8, 0 +40004952: 5879 s32i.n a7, a8, 20 +40004954: 020c movi.n a2, 0 +40004956: f01d retw.n +40004958: 4bac beqz.n a11, 40004980 <_XX_unk4940+0x40> +4000495a: 884000 excw +4000495d: 600040 neg a0, a4 +40004960: 4128 l32i.n a2, a1, 16 +40004962: 506000 witlb a0, a0 +40004965: ffdc bnez.n a15, 40004988 <_XX_unk4940+0x48> +40004967: 3f .byte 0x3f +40004968: 004136 entry a1, 32 +4000496b: 04a0a2 movi a10, 4 +4000496e: fffab1 l32r a11, 40004958 <_XX_unk4940+0x18> +40004971: 00a0c2 movi a12, 0 +40004974: fc7725 call8 400010e8 <_X_ets_isr_attach> +40004977: 10a0a2 movi a10, 16 +4000497a: fc77e5 call8 400010f8 <_X_ets_isr_mask> +4000497d: 0a0c movi.n a10, 0 +4000497f: 0b0c movi.n a11, 0 +40004981: 0c0c movi.n a12, 0 +40004983: fd7c movi.n a13, -1 +40004985: 000525 call8 400049d8 +40004988: 0a0c movi.n a10, 0 +4000498a: 0b0c movi.n a11, 0 +4000498c: 0c0c movi.n a12, 0 +4000498e: ffa0d2 movi a13, 255 +40004991: 000665 call8 400049f8 +40004994: fff221 l32r a2, 4000495c <_XX_unk4940+0x1c> +40004997: f1e631 l32r a3, 40001130 <_c_0x60003e00> +4000499a: fff151 l32r a5, 40004960 <_XX_unk4940+0x20> +4000499d: fff141 l32r a4, 40004964 <_XX_unk4940+0x24> +400049a0: 080c movi.n a8, 0 +400049a2: 0489 s32i.n a8, a4, 0 +400049a4: 1489 s32i.n a8, a4, 4 +400049a6: 02ad mov.n a10, a2 +400049a8: 0b0c movi.n a11, 0 +400049aa: 000925 call8 40004a3c +400049ad: 0020c0 memw +400049b0: 224b addi.n a2, a2, 4 +400049b2: 441b addi.n a4, a4, 1 +400049b4: a22392 l32i a9, a3, 0x288 +400049b7: 334b addi.n a3, a3, 4 +400049b9: 249790 extui a9, a9, 7, 3 +400049bc: 0f4492 s8i a9, a4, 15 +400049bf: e39257 bne a2, a5, 400049a6 <_XX_unk4940+0x66> +400049c2: fc7c movi.n a12, -1 +400049c4: f1dbb1 l32r a11, 40001130 <_c_0x60003e00> +400049c7: 0020c0 memw +400049ca: 936bc2 s32i a12, a11, 0x24c +400049cd: ffa0a2 movi a10, 255 +400049d0: 0020c0 memw +400049d3: 966ba2 s32i a10, a11, 0x258 +400049d6: f01d retw.n + +400049d8 : +400049d8: 004136 entry a1, 32 +400049db: f1d561 l32r a6, 40001130 <_c_0x60003e00> +400049de: 0020c0 memw +400049e1: 826622 s32i a2, a6, 0x208 +400049e4: 0020c0 memw +400049e7: 836632 s32i a3, a6, 0x20c +400049ea: 0020c0 memw +400049ed: 896642 s32i a4, a6, 0x224 +400049f0: 0020c0 memw +400049f3: 8a6652 s32i a5, a6, 0x228 +400049f6: f01d retw.n + +400049f8 : +400049f8: 004136 entry a1, 32 +400049fb: f1cd61 l32r a6, 40001130 <_c_0x60003e00> +400049fe: 0020c0 memw +40004a01: 856622 s32i a2, a6, 0x214 +40004a04: 0020c0 memw +40004a07: 866632 s32i a3, a6, 0x218 +40004a0a: 0020c0 memw +40004a0d: 8c6642 s32i a4, a6, 0x230 +40004a10: 0020c0 memw +40004a13: 8d6652 s32i a5, a6, 0x234 +40004a16: f01d retw.n +40004a18: 004136 entry a1, 32 +40004a1b: f1c521 l32r a2, 40001130 <_c_0x60003e00> +40004a1e: 0020c0 memw +40004a21: 8f2222 l32i a2, a2, 0x23c +40004a24: f01d retw.n +40004a26: 360000 excw +40004a29: 210041 l32r a4, 3ffcce2c <_start-0x331d4> +40004a2c: c0f1c1 l32r a12, 3fff4df0 <_start-0xb210> +40004a2f: 220020 orb b0, b0, b2 +40004a32: 1d9022 l16si a2, a0, 58 +40004a35: 0000f0 callx12 a0 +40004a38: ffdc60 excw +40004a3b: 3f .byte 0x3f +40004a3c: 004136 entry a1, 32 +40004a3f: fc66a5 call8 400010a8 <_X_ets_enter_critical> +40004a42: ffc6a1 l32r a10, 4000495c <_XX_unk4940+0x1c> +40004a45: 1192e0 slli a9, a2, 2 +40004a48: 8099a0 add a9, a9, a10 +40004a4b: 0020c0 memw +40004a4e: 006932 s32i a3, a9, 0 +40004a51: 27a082 movi a8, 39 +40004a54: 3e3827 bltu a8, a2, 40004a96 +40004a57: fff8a1 l32r a10, 40004a38 +40004a5a: 24b730 extui a11, a3, 7, 3 +40004a5d: c2aa add.n a12, a2, a10 +40004a5f: 004cb2 s8i a11, a12, 0 +40004a62: 0bfc bnez.n a11, 40004a96 +40004a64: 1c0c movi.n a12, 1 +40004a66: fb7c movi.n a11, -1 +40004a68: f0caa2 addi a10, a10, -16 +40004a6b: 12c2f6 bgeui a2, 32, 40004a81 +40004a6e: 0ad8 l32i.n a13, a10, 0 +40004a70: 401200 ssl a2 +40004a73: a1ec00 sll a14, a12 +40004a76: 30eeb0 xor a14, a14, a11 +40004a79: 10dde0 and a13, a13, a14 +40004a7c: 0ad9 s32i.n a13, a10, 0 +40004a7e: 000506 j 40004a96 +40004a81: 012af2 l32i a15, a10, 4 +40004a84: e0c292 addi a9, a2, -32 +40004a87: 401900 ssl a9 +40004a8a: a18c00 sll a8, a12 +40004a8d: 3088b0 xor a8, a8, a11 +40004a90: 10ff80 and a15, a15, a8 +40004a93: 016af2 s32i a15, a10, 4 +40004a96: fc6265 call8 400010bc <_X_ets_exit_critical> +40004a99: 000090 retw +40004a9c: 004136 entry a1, 32 +40004a9f: ffaf81 l32r a8, 4000495c <_XX_unk4940+0x1c> +40004aa2: 1152e0 slli a5, a2, 2 +40004aa5: 558a add.n a5, a5, a8 +40004aa7: 0020c0 memw +40004aaa: 0558 l32i.n a5, a5, 0 +40004aac: 732c movi.n a3, 39 +40004aae: 163327 bltu a3, a2, 40004ac8 +40004ab1: ffe131 l32r a3, 40004a38 +40004ab4: 223a add.n a2, a2, a3 +40004ab6: 000222 l8ui a2, a2, 0 +40004ab9: 7fac32 movi a3, 0xfffffc7f +40004abc: 103530 and a3, a5, a3 +40004abf: 112290 slli a2, a2, 7 +40004ac2: 202230 or a2, a2, a3 +40004ac5: f01d retw.n +40004ac7: 052d00 extui a2, a0, 29, 1 +40004aca: f01d retw.n +40004acc: 004136 entry a1, 32 +40004acf: ffa521 l32r a2, 40004964 <_XX_unk4940+0x24> +40004ad2: 0228 l32i.n a2, a2, 0 +40004ad4: f01d retw.n +40004ad6: 360000 excw +40004ad9: 210041 l32r a4, 3ffccedc <_start-0x33124> +40004adc: 28ffa2 s32ri a10, a15, 160 +40004adf: f01d12 l16ui a1, a13, 0x1e0 +40004ae2: 360000 excw +40004ae5: 250041 l32r a4, 3ffcdee8 <_start-0x32118> +40004ae8: fc5c movi.n a12, 95 +40004aea: f19191 l32r a9, 40001130 <_c_0x60003e00> +40004aed: a09290 addx4 a9, a2, a9 +40004af0: 0020c0 memw +40004af3: 7facb2 movi a11, 0xfffffc7f +40004af6: a22982 l32i a8, a9, 0x288 +40004af9: 11a390 slli a10, a3, 7 +40004afc: 1088b0 and a8, a8, a11 +40004aff: 2088a0 or a8, a8, a10 +40004b02: 0020c0 memw +40004b05: a26982 s32i a8, a9, 0x288 +40004b08: fc5b25 call8 400010bc <_X_ets_exit_critical> +40004b0b: f01d retw.n +40004b0d: 000000 ill +40004b10: 004136 entry a1, 32 +40004b13: fc5965 call8 400010a8 <_X_ets_enter_critical> +40004b16: ff93b1 l32r a11, 40004964 <_XX_unk4940+0x24> +40004b19: ffaf92 movi a9, -1 +40004b1c: 002b82 l32i a8, a11, 0 +40004b1f: 309290 xor a9, a2, a9 +40004b22: 10a890 and a10, a8, a9 +40004b25: 006ba2 s32i a10, a11, 0 +40004b28: 0c0897 bnone a8, a9, 40004b38 +40004b2b: f181d1 l32r a13, 40001130 <_c_0x60003e00> +40004b2e: 0020c0 memw +40004b31: 926da2 s32i a10, a13, 0x248 +40004b34: 0c0c movi.n a12, 0 +40004b36: 0bc9 s32i.n a12, a11, 0 +40004b38: ffc041 l32r a4, 40004a38 +40004b3b: 030c movi.n a3, 0 +40004b3d: 075237 bbc a2, a3, 40004b48 +40004b40: 03ad mov.n a10, a3 +40004b42: 0004b2 l8ui a11, a4, 0 +40004b45: fff9e5 call8 40004ae4 +40004b48: 01c442 addi a4, a4, 1 +40004b4b: 01c332 addi a3, a3, 1 +40004b4e: ebc366 bnei a3, 32, 40004b3d +40004b51: fc56a5 call8 400010bc <_X_ets_exit_critical> +40004b54: 000090 retw +40004b57: 413600 srli a3, a0, 6 +40004b5a: 54e500 extui a14, a0, 5, 6 +40004b5d: b1fc bnez.n a1, 40004b9c +40004b5f: 92ff81 l32r a8, 3ffe975c <_start-0x168a4> +40004b62: af .byte 0xaf +40004b63: ff .byte 0xff +40004b64: 012b82 l32i a8, a11, 4 +40004b67: 309290 xor a9, a2, a9 +40004b6a: 10a890 and a10, a8, a9 +40004b6d: 016ba2 s32i a10, a11, 4 +40004b70: 0c0897 bnone a8, a9, 40004b80 +40004b73: f16fd1 l32r a13, 40001130 <_c_0x60003e00> +40004b76: 0020c0 memw +40004b79: 956da2 s32i a10, a13, 0x254 +40004b7c: 0c0c movi.n a12, 0 +40004b7e: 1bc9 s32i.n a12, a11, 4 +40004b80: ffae41 l32r a4, 40004a38 +40004b83: 030c movi.n a3, 0 +40004b85: 085237 bbc a2, a3, 40004b91 +40004b88: 20c3a2 addi a10, a3, 32 +40004b8b: 2004b2 l8ui a11, a4, 32 +40004b8e: fff565 call8 40004ae4 +40004b91: 01c442 addi a4, a4, 1 +40004b94: 01c332 addi a3, a3, 1 +40004b97: ea8366 bnei a3, 8, 40004b85 +40004b9a: fc5225 call8 400010bc <_X_ets_exit_critical> +40004b9d: 000090 retw +40004ba0: 004136 entry a1, 32 +40004ba3: ff7041 l32r a4, 40004964 <_XX_unk4940+0x24> +40004ba6: 3439 s32i.n a3, a4, 12 +40004ba8: 2429 s32i.n a2, a4, 8 +40004baa: f01d retw.n +40004bac: 004136 entry a1, 32 +40004baf: f16051 l32r a5, 40001130 <_c_0x60003e00> +40004bb2: 0020c0 memw +40004bb5: 912532 l32i a3, a5, 0x244 +40004bb8: 0020c0 memw +40004bbb: 942522 l32i a2, a5, 0x250 +40004bbe: 040c movi.n a4, 0 +40004bc0: 065347 bbc a3, a4, 40004bca +40004bc3: 04ad mov.n a10, a4 +40004bc5: 0b0c movi.n a11, 0 +40004bc7: fff1e5 call8 40004ae4 +40004bca: 441b addi.n a4, a4, 1 +40004bcc: f0c466 bnei a4, 32, 40004bc0 +40004bcf: ff6561 l32r a6, 40004964 <_XX_unk4940+0x24> +40004bd2: 0020c0 memw +40004bd5: 936532 s32i a3, a5, 0x24c +40004bd8: 0698 l32i.n a9, a6, 0 +40004bda: 89ac beqz.n a9, 40004c06 +40004bdc: 208390 or a8, a3, a9 +40004bdf: 006682 s32i a8, a6, 0 +40004be2: 040c movi.n a4, 0 +40004be4: 085247 bbc a2, a4, 40004bf0 +40004be7: 20c4a2 addi a10, a4, 32 +40004bea: 00a0b2 movi a11, 0 +40004bed: ffef65 call8 40004ae4 +40004bf0: 441b addi.n a4, a4, 1 +40004bf2: ee8466 bnei a4, 8, 40004be4 +40004bf5: 0020c0 memw +40004bf8: 966522 s32i a2, a5, 0x258 +40004bfb: 1698 l32i.n a9, a6, 4 +40004bfd: 499c beqz.n a9, 40004c15 +40004bff: 20a290 or a10, a2, a9 +40004c02: 16a9 s32i.n a10, a6, 4 +40004c04: f01d retw.n +40004c06: 03ad mov.n a10, a3 +40004c08: 0639 s32i.n a3, a6, 0 +40004c0a: 2688 l32i.n a8, a6, 8 +40004c0c: 36b8 l32i.n a11, a6, 12 +40004c0e: 0008e0 callx8 a8 +40004c11: fff346 j 40004be2 +40004c14: 02ad00 andb b10, b13, b0 +40004c17: 1629 s32i.n a2, a6, 4 +40004c19: 2688 l32i.n a8, a6, 8 +40004c1b: 36b8 l32i.n a11, a6, 12 +40004c1d: 0008e0 callx8 a8 +40004c20: f01d retw.n +40004c22: 360000 excw +40004c25: 250041 l32r a4, 3ffce028 <_start-0x31fd8> +40004c28: fc48 l32i.n a4, a12, 60 +40004c2a: f14191 l32r a9, 40001130 <_c_0x60003e00> +40004c2d: a09290 addx4 a9, a2, a9 +40004c30: 0020c0 memw +40004c33: 7facb2 movi a11, 0xfffffc7f +40004c36: a22982 l32i a8, a9, 0x288 +40004c39: 11a390 slli a10, a3, 7 +40004c3c: 1088b0 and a8, a8, a11 +40004c3f: 2088a0 or a8, a8, a10 +40004c42: 00a4a2 movi a10, 0x400 +40004c45: 2088a0 or a8, a8, a10 +40004c48: 0020c0 memw +40004c4b: a26982 s32i a8, a9, 0x288 +40004c4e: fc46e5 call8 400010bc <_X_ets_exit_critical> +40004c51: f01d retw.n +40004c53: 413600 srli a3, a0, 6 +40004c56: 030c00 rsr.scompare1 a0 +40004c59: 7fa872 movi a7, 0xfffff87f +40004c5c: f13541 l32r a4, 40001130 <_c_0x60003e00> +40004c5f: 822c movi.n a2, 40 +40004c61: 19a276 loopgtz a2, 40004c7e +40004c64: a06340 addx4 a6, a3, a4 +40004c67: 0020c0 memw +40004c6a: a22652 l32i a5, a6, 0x288 +40004c6d: 0865a7 bbci a5, 10, 40004c79 +40004c70: 108570 and a8, a5, a7 +40004c73: 0020c0 memw +40004c76: a26682 s32i a8, a6, 0x288 +40004c79: 331b addi.n a3, a3, 1 +40004c7b: 743030 extui a3, a3, 0, 8 +40004c7e: f01d retw.n + +40004c80 <_c_0xbfffffff>: +40004c80: ff .byte 0xff +40004c81: ff .byte 0xff +40004c82: ff .byte 0xff +40004c83: bf .byte 0xbf +40004c84: fff000 excw +40004c87: ff .byte 0xff + +40004c88 <_c_0xff000fff>: +40004c88: ff .byte 0xff +40004c89: 0f .byte 0xf +40004c8a: 36ff00 excw + +40004c8c : +40004c8c: 006136 entry a1, 48 +40004c8f: b4f060 extui a15, a6, 0, 12 +40004c92: 00a092 movi a9, 0 +40004c95: fffae1 l32r a14, 40004c80 <_c_0xbfffffff> +40004c98: eebad1 l32r a13, 40000780 <_c_0x7fffffff> +40004c9b: eea7c1 l32r a12, 40000738 <_c_0x80000000> +40004c9e: f70db1 l32r a11, 400028d4 <_c_0xdfffffff> +40004ca1: 036172 s32i a7, a1, 12 +40004ca4: fff8a1 l32r a10, 40004c84 <_c_0xbfffffff+0x4> +40004ca7: c27560 quou a7, a5, a6 +40004caa: 205110 or a5, a1, a1 +40004cad: 628076 loop a0, 40004d13 +40004cb0: 1249 s32i.n a4, a2, 4 +40004cb2: 0020c0 memw +40004cb5: 0238 l32i.n a3, a2, 0 +40004cb7: 1033a0 and a3, a3, a10 +40004cba: 2033f0 or a3, a3, a15 +40004cbd: 0020c0 memw +40004cc0: 0239 s32i.n a3, a2, 0 +40004cc2: 0020c0 memw +40004cc5: 0288 l32i.n a8, a2, 0 +40004cc7: 1088d0 and a8, a8, a13 +40004cca: 2088c0 or a8, a8, a12 +40004ccd: 0020c0 memw +40004cd0: 3138 l32i.n a3, a1, 12 +40004cd2: 0289 s32i.n a8, a2, 0 +40004cd4: 040316 beqz a3, 40004d18 +40004cd7: 0020c0 memw +40004cda: 0288 l32i.n a8, a2, 0 +40004cdc: 0020c0 memw +40004cdf: 0238 l32i.n a3, a2, 0 +40004ce1: b43030 extui a3, a3, 0, 12 +40004ce4: cab380 depbits a8, a3, 12, 12 +40004ce7: 0020c0 memw +40004cea: 0289 s32i.n a8, a2, 0 +40004cec: 0020c0 memw +40004cef: 0288 l32i.n a8, a2, 0 +40004cf1: 1088e0 and a8, a8, a14 +40004cf4: 0020c0 memw +40004cf7: 0289 s32i.n a8, a2, 0 +40004cf9: 0020c0 memw +40004cfc: 0288 l32i.n a8, a2, 0 +40004cfe: 1088b0 and a8, a8, a11 +40004d01: 0020c0 memw +40004d04: 464a add.n a4, a6, a4 +40004d06: 991b addi.n a9, a9, 1 +40004d08: 0289 s32i.n a8, a2, 0 +40004d0a: 2529 s32i.n a2, a5, 8 +40004d0c: 2558 l32i.n a5, a5, 8 +40004d0e: 22cb addi.n a2, a2, 12 +40004d10: 18b977 bgeu a9, a7, 40004d2c +40004d13: ffe586 j 40004cad +40004d16: c00000 sub a0, a0, a0 +40004d19: 810020 src a0, a0, a2 +40004d1c: ffdb addi.n a15, a15, 13 +40004d1e: 0238 l32i.n a3, a2, 0 +40004d20: 103380 and a3, a3, a8 +40004d23: 0020c0 memw +40004d26: 0239 s32i.n a3, a2, 0 +40004d28: fff006 j 40004cec +40004d2b: d19800 mul16s a9, a8, a0 +40004d2e: c1b8 l32i.n a11, a1, 48 +40004d30: 0a0c movi.n a10, 0 +40004d32: 25a9 s32i.n a10, a5, 8 +40004d34: 21a8 l32i.n a10, a1, 8 +40004d36: 0ba9 s32i.n a10, a11, 0 +40004d38: 0959 s32i.n a5, a9, 0 +40004d3a: f01d retw.n +40004d3c: 004136 entry a1, 32 +40004d3f: ffc332 addi a3, a3, -1 +40004d42: f43030 extui a3, a3, 0, 16 +40004d45: 019376 loopnez a3, 40004d4a +40004d48: 2228 l32i.n a2, a2, 8 +40004d4a: f01d retw.n +40004d4c: 004136 entry a1, 32 +40004d4f: 015410 slli a5, a4, 31 +40004d52: ee8b81 l32r a8, 40000780 <_c_0x7fffffff> +40004d55: 0020c0 memw +40004d58: 0268 l32i.n a6, a2, 0 +40004d5a: fb0460 depbits a6, a4, 31, 1 +40004d5d: 084d mov.n a4, a8 +40004d5f: 0020c0 memw +40004d62: 0269 s32i.n a6, a2, 0 +40004d64: 330b addi.n a3, a3, -1 +40004d66: f43030 extui a3, a3, 0, 16 +40004d69: 119376 loopnez a3, 40004d7e +40004d6c: 2228 l32i.n a2, a2, 8 +40004d6e: 0020c0 memw +40004d71: 0238 l32i.n a3, a2, 0 +40004d73: 103340 and a3, a3, a4 +40004d76: 203350 or a3, a3, a5 +40004d79: 0020c0 memw +40004d7c: 0239 s32i.n a3, a2, 0 +40004d7e: f01d retw.n + +40004d80 <_st_0x3fffdc90>: +40004d80: ffdc90 excw +40004d83: 3f .byte 0x3f +40004d84: ffdd50 excw +40004d87: 3f .byte 0x3f + +40004d88 <_XX_unk4d88>: +40004d88: 008136 entry a1, 64 +40004d8b: fffec1 l32r a12, 40004d84 <_st_0x3fffdc90+0x4> +40004d8e: 00a2d2 movi a13, 0x200 +40004d91: 14c182 addi a8, a1, 20 +40004d94: 10c192 addi a9, a1, 16 +40004d97: fffa21 l32r a2, 40004d80 <_st_0x3fffdc90> +40004d9a: 00a0f2 movi a15, 0 +40004d9d: 40a0b2 movi a11, 64 +40004da0: 20ebb0 or a14, a11, a11 +40004da3: 22f9 s32i.n a15, a2, 8 +40004da5: 0052b2 s16i a11, a2, 0 +40004da8: a28b addi.n a10, a2, 8 +40004daa: 32a9 s32i.n a10, a2, 12 +40004dac: 60a0b2 movi a11, 96 +40004daf: 60c2a2 addi a10, a2, 96 +40004db2: 0199 s32i.n a9, a1, 0 +40004db4: 1189 s32i.n a8, a1, 4 +40004db6: ffed65 call8 40004c8c +40004db9: 51c8 l32i.n a12, a1, 20 +40004dbb: 41d8 l32i.n a13, a1, 16 +40004dbd: 22d9 s32i.n a13, a2, 8 +40004dbf: cc8b addi.n a12, a12, 8 +40004dc1: 32c9 s32i.n a12, a2, 12 +40004dc3: f01d retw.n +40004dc5: 000000 ill +40004dc8: dc98 l32i.n a9, a12, 52 +40004dca: ff .byte 0xff +40004dcb: 3f .byte 0x3f +40004dcc: 004136 entry a1, 32 +40004dcf: ffec51 l32r a5, 40004d80 <_st_0x3fffdc90> +40004dd2: 2588 l32i.n a8, a5, 8 +40004dd4: 058816 beqz a8, 40004e30 <_XX_unk4d88+0xa8> +40004dd7: fc2d25 call8 400010a8 <_X_ets_enter_critical> +40004dda: 2548 l32i.n a4, a5, 8 +40004ddc: 2498 l32i.n a9, a4, 8 +40004dde: 026592 s32i a9, a5, 8 +40004de1: 04f916 beqz a9, 40004e34 <_XX_unk4d88+0xac> +40004de4: fc2d65 call8 400010bc <_X_ets_exit_critical> +40004de7: 00a0a2 movi a10, 0 +40004dea: 0ca0b2 movi a11, 12 +40004ded: 24a9 s32i.n a10, a4, 8 +40004def: 14a8 l32i.n a10, a4, 4 +40004df1: fdc625 call8 40002a54 +40004df4: f0a0f2 movi a15, 240 +40004df7: 14d8 l32i.n a13, a4, 4 +40004df9: 22cb addi.n a2, a2, 12 +40004dfb: 418820 srli a8, a2, 8 +40004dfe: 034d82 s8i a8, a13, 3 +40004e01: 024d22 s8i a2, a13, 2 +40004e04: 000de2 l8ui a14, a13, 0 +40004e07: 014d32 s8i a3, a13, 1 +40004e0a: 10eef0 and a14, a14, a15 +40004e0d: 004de2 s8i a14, a13, 0 +40004e10: 0020c0 memw +40004e13: dd2b addi.n a13, a13, 2 +40004e15: 04b8 l32i.n a11, a4, 0 +40004e17: 010dc2 l8ui a12, a13, 1 +40004e1a: 000dd2 l8ui a13, a13, 0 +40004e1d: 11cc80 slli a12, a12, 8 +40004e20: 20ccd0 or a12, a12, a13 +40004e23: cabcb0 depbits a11, a12, 12, 12 +40004e26: 0020c0 memw +40004e29: 04b9 s32i.n a11, a4, 0 +40004e2b: 042d mov.n a2, a4 +40004e2d: f01d retw.n +40004e2f: 020c00 andb b0, b12, b0 +40004e32: f01d retw.n +40004e34: ffe591 l32r a9, 40004dc8 <_XX_unk4d88+0x40> +40004e37: 3599 s32i.n a9, a5, 12 +40004e39: ffe9c6 j 40004de4 <_XX_unk4d88+0x5c> +40004e3c: 004136 entry a1, 32 +40004e3f: ee5081 l32r a8, 40000780 <_c_0x7fffffff> +40004e42: ee3df1 l32r a15, 40000738 <_c_0x80000000> +40004e45: 00a032 movi a3, 0 +40004e48: 0020c0 memw +40004e4b: 0022e2 l32i a14, a2, 0 +40004e4e: 10ee80 and a14, a14, a8 +40004e51: 20eef0 or a14, a14, a15 +40004e54: 0020c0 memw +40004e57: 02e9 s32i.n a14, a2, 0 +40004e59: ff89d1 l32r a13, 40004c80 <_c_0xbfffffff> +40004e5c: 0020c0 memw +40004e5f: 02c8 l32i.n a12, a2, 0 +40004e61: 10ccd0 and a12, a12, a13 +40004e64: 0020c0 memw +40004e67: 02c9 s32i.n a12, a2, 0 +40004e69: f69ab1 l32r a11, 400028d4 <_c_0xdfffffff> +40004e6c: 0020c0 memw +40004e6f: 02a8 l32i.n a10, a2, 0 +40004e71: 10aab0 and a10, a10, a11 +40004e74: 0020c0 memw +40004e77: 02a9 s32i.n a10, a2, 0 +40004e79: ff8391 l32r a9, 40004c88 <_c_0xff000fff> +40004e7c: 0020c0 memw +40004e7f: 0288 l32i.n a8, a2, 0 +40004e81: 108890 and a8, a8, a9 +40004e84: 0020c0 memw +40004e87: 0289 s32i.n a8, a2, 0 +40004e89: fc21e5 call8 400010a8 <_X_ets_enter_critical> +40004e8c: ffbda1 l32r a10, 40004d80 <_st_0x3fffdc90> +40004e8f: 2239 s32i.n a3, a2, 8 +40004e91: 3ab8 l32i.n a11, a10, 12 +40004e93: 928b addi.n a9, a2, 8 +40004e95: 0b29 s32i.n a2, a11, 0 +40004e97: 3a99 s32i.n a9, a10, 12 +40004e99: fc2225 call8 400010bc <_X_ets_exit_critical> +40004e9c: f01d retw.n +40004e9e: 360000 excw +40004ea1: cd0041 l32r a4, 3fff82a4 <_start-0x7d5c> +40004ea4: 03bd04 excw +40004ea7: 02ad mov.n a10, a2 +40004ea9: 209220 or a9, a2, a2 +40004eac: 05c216 beqz a2, 40004f0c <_XX_unk4d88+0x184> +40004eaf: ff7631 l32r a3, 40004c88 <_c_0xff000fff> +40004eb2: ff7321 l32r a2, 40004c80 <_c_0xbfffffff> +40004eb5: ee32f1 l32r a15, 40000780 <_c_0x7fffffff> +40004eb8: ee20e1 l32r a14, 40000738 <_c_0x80000000> +40004ebb: f686d1 l32r a13, 400028d4 <_c_0xdfffffff> +40004ebe: 00a042 movi a4, 0 +40004ec1: 438076 loop a0, 40004f08 <_XX_unk4d88+0x180> +40004ec4: 0020c0 memw +40004ec7: 0988 l32i.n a8, a9, 0 +40004ec9: 1088f0 and a8, a8, a15 +40004ecc: 2088e0 or a8, a8, a14 +40004ecf: 0020c0 memw +40004ed2: 0989 s32i.n a8, a9, 0 +40004ed4: 0020c0 memw +40004ed7: 0988 l32i.n a8, a9, 0 +40004ed9: 108820 and a8, a8, a2 +40004edc: 208840 or a8, a8, a4 +40004edf: 0020c0 memw +40004ee2: 0989 s32i.n a8, a9, 0 +40004ee4: 0020c0 memw +40004ee7: 0988 l32i.n a8, a9, 0 +40004ee9: 1088d0 and a8, a8, a13 +40004eec: 208840 or a8, a8, a4 +40004eef: 0020c0 memw +40004ef2: 0989 s32i.n a8, a9, 0 +40004ef4: 0020c0 memw +40004ef7: 0988 l32i.n a8, a9, 0 +40004ef9: 108830 and a8, a8, a3 +40004efc: 208840 or a8, a8, a4 +40004eff: 0020c0 memw +40004f02: 0989 s32i.n a8, a9, 0 +40004f04: 2998 l32i.n a9, a9, 8 +40004f06: 298c beqz.n a9, 40004f0c <_XX_unk4d88+0x184> +40004f08: ffed46 j 40004ec1 <_XX_unk4d88+0x139> +40004f0b: 8aa500 depbits a0, a5, 8, 11 +40004f0e: f01d00 subx8 a1, a13, a0 +40004f11: 000000 ill + +40004f14 <_XX_unk4f14>: +40004f14: 004136 entry a1, 32 +40004f17: 20b220 or a11, a2, a2 +40004f1a: ee1991 l32r a9, 40000780 <_c_0x7fffffff> +40004f1d: ee0681 l32r a8, 40000738 <_c_0x80000000> +40004f20: 0020c0 memw +40004f23: 0022f2 l32i a15, a2, 0 +40004f26: 10ff90 and a15, a15, a9 +40004f29: 20ff80 or a15, a15, a8 +40004f2c: 0020c0 memw +40004f2f: 0062f2 s32i a15, a2, 0 +40004f32: ff53e1 l32r a14, 40004c80 <_c_0xbfffffff> +40004f35: 0020c0 memw +40004f38: 02d8 l32i.n a13, a2, 0 +40004f3a: 10dde0 and a13, a13, a14 +40004f3d: 0020c0 memw +40004f40: 02d9 s32i.n a13, a2, 0 +40004f42: f664c1 l32r a12, 400028d4 <_c_0xdfffffff> +40004f45: 0020c0 memw +40004f48: 02a8 l32i.n a10, a2, 0 +40004f4a: 10aac0 and a10, a10, a12 +40004f4d: 0020c0 memw +40004f50: 02a9 s32i.n a10, a2, 0 +40004f52: ff4d91 l32r a9, 40004c88 <_c_0xff000fff> +40004f55: 02ad mov.n a10, a2 +40004f57: 0020c0 memw +40004f5a: 0288 l32i.n a8, a2, 0 +40004f5c: 108890 and a8, a8, a9 +40004f5f: 0020c0 memw +40004f62: 1c0c movi.n a12, 1 +40004f64: 0289 s32i.n a8, a2, 0 +40004f66: 008525 call8 400057b8 <_XX_unk57b8> +40004f69: f01d retw.n + ... + +40004f6c <_XX_unk4f6c>: +40004f6c: 006136 entry a1, 48 +40004f6f: 20a110 or a10, a1, a1 +40004f72: 04c1b2 addi a11, a1, 4 +40004f75: 0087e5 call8 400057f4 <_XX_unk57f4> +40004f78: 0021c2 l32i a12, a1, 0 +40004f7b: ff81a1 l32r a10, 40004d80 <_st_0x3fffdc90> +40004f7e: ac9c beqz.n a12, 40004f9c <_XX_unk4f6c+0x30> +40004f80: 6a98 l32i.n a9, a10, 24 +40004f82: 698c beqz.n a9, 40004f8c <_XX_unk4f6c+0x20> +40004f84: 29c9 s32i.n a12, a9, 8 +40004f86: 1188 l32i.n a8, a1, 4 +40004f88: 6a89 s32i.n a8, a10, 24 +40004f8a: f01d retw.n +40004f8c: 00a0b2 movi a11, 0 +40004f8f: 012192 l32i a9, a1, 4 +40004f92: 056ac2 s32i a12, a10, 20 +40004f95: 6a99 s32i.n a9, a10, 24 +40004f97: da1c movi.n a10, 29 +40004f99: fbffe5 call8 40000f98 <_XX_unk0f96> +40004f9c: f01d retw.n +40004f9e: 360000 excw +40004fa1: 410041 l32r a4, 3ffd53a4 <_start-0x2ac5c> +40004fa4: 32ff77 bbsi a15, 23, 40004fda <_XX_unk4fc8+0x12> +40004fa7: 201424 excw +40004faa: 329332 l16si a3, a3, 100 +40004fad: 1d1464 excw +40004fb0: 0000f0 callx12 a0 +40004fb3: 413600 srli a3, a0, 6 +40004fb6: 724100 excw +40004fb9: ff .byte 0xff +40004fba: 152432 l32i a3, a4, 84 +40004fbd: 933220 movnez a3, a2, a2 +40004fc0: 156432 s32i a3, a4, 84 +40004fc3: f01d retw.n +40004fc5: 000000 ill + +40004fc8 <_XX_unk4fc8>: +40004fc8: 006136 entry a1, 48 +40004fcb: 01ad mov.n a10, a1 +40004fcd: b14b addi.n a11, a1, 4 +40004fcf: 0087a5 call8 40005848 <_XX_unk5848> +40004fd2: 2e0a26 beqi a10, -1, 40005004 <_XX_unk4fc8+0x3c> +40004fd5: ff6aa1 l32r a10, 40004d80 <_st_0x3fffdc90> +40004fd8: 01c8 l32i.n a12, a1, 0 +40004fda: 162ad2 l32i a13, a10, 88 +40004fdd: 8a98 l32i.n a9, a10, 32 +40004fdf: 7d8c beqz.n a13, 40004fea <_XX_unk4fc8+0x22> +40004fe1: 0cad mov.n a10, a12 +40004fe3: 11b8 l32i.n a11, a1, 4 +40004fe5: 000de0 callx8 a13 +40004fe8: f01d retw.n +40004fea: 698c beqz.n a9, 40004ff4 <_XX_unk4fc8+0x2c> +40004fec: 29c9 s32i.n a12, a9, 8 +40004fee: 1188 l32i.n a8, a1, 4 +40004ff0: 8a89 s32i.n a8, a10, 32 +40004ff2: f01d retw.n +40004ff4: 02a0b2 movi a11, 2 +40004ff7: 012192 l32i a9, a1, 4 +40004ffa: 076ac2 s32i a12, a10, 28 +40004ffd: 8a99 s32i.n a9, a10, 32 +40004fff: da1c movi.n a10, 29 +40005001: fbf965 call8 40000f98 <_XX_unk0f96> +40005004: f01d retw.n + ... + +40005008 <_s_no_rds>: +40005008: ffcce4 excw +4000500b: 3f .byte 0x3f + +4000500c <_XX_unk500c>: +4000500c: 004136 entry a1, 32 +4000500f: 12a8 l32i.n a10, a2, 4 +40005011: 070a92 l8ui a9, a10, 7 +40005014: 060ab2 l8ui a11, a10, 6 +40005017: 119980 slli a9, a9, 8 +4000501a: 2099b0 or a9, a9, a11 +4000501d: 050ab2 l8ui a11, a10, 5 +40005020: 119980 slli a9, a9, 8 +40005023: 2099b0 or a9, a9, a11 +40005026: 040ab2 l8ui a11, a10, 4 +40005029: 119980 slli a9, a9, 8 +4000502c: 2099b0 or a9, a9, a11 +4000502f: 890b addi.n a8, a9, -1 +40005031: 0a8816 beqz a8, 400050dd <_XX_unk500c+0xd1> +40005034: 603926 beqi a9, 3, 40005098 <_XX_unk500c+0x8c> +40005037: fbc9b2 addi a11, a9, -5 +4000503a: 0e6b16 beqz a11, 40005124 <_XX_unk500c+0x118> +4000503d: 07a926 beqi a9, 12, 40005048 <_XX_unk500c+0x3c> +40005040: 02ad mov.n a10, a2 +40005042: ffed25 call8 40004f14 <_XX_unk4f14> +40005045: f01d retw.n +40005047: ca3200 depbits a0, a2, 12, 4 +4000504a: 420c movi.n a2, 4 +4000504c: 18ca add.n a1, a8, a12 +4000504e: 07a0b2 movi a11, 7 +40005051: 0ca0a2 movi a10, 12 +40005054: ffd765 call8 40004dcc <_XX_unk4d88+0x44> +40005057: 0a5d mov.n a5, a10 +40005059: 7acc bnez.n a10, 40005064 <_XX_unk500c+0x58> +4000505b: ffeba1 l32r a10, 40005008 <_s_no_rds> +4000505e: fd7a65 call8 40002804 +40005061: fff6c6 j 40005040 <_XX_unk500c+0x34> +40005064: 20b440 or a11, a4, a4 +40005067: 0603d2 l8ui a13, a3, 6 +4000506a: 0703c2 l8ui a12, a3, 7 +4000506d: 012aa2 l32i a10, a10, 4 +40005070: 11cc80 slli a12, a12, 8 +40005073: 0ccaa2 addi a10, a10, 12 +40005076: 20ccd0 or a12, a12, a13 +40005079: 0503d2 l8ui a13, a3, 5 +4000507c: 11cc80 slli a12, a12, 8 +4000507f: 20ccd0 or a12, a12, a13 +40005082: 0403d2 l8ui a13, a3, 4 +40005085: 11cc80 slli a12, a12, 8 +40005088: 20ccd0 or a12, a12, a13 +4000508b: fcd3a5 call8 40001dc4 <_X_ets_memcpy> +4000508e: 05bd mov.n a11, a5 +40005090: 05ad mov.n a10, a5 +40005092: 0013a5 call8 400051cc <_XX_unk51ac+0x20> +40005095: ffe9c6 j 40005040 <_XX_unk500c+0x34> +40005098: 0d0a82 l8ui a8, a10, 13 +4000509b: 0e0af2 l8ui a15, a10, 14 +4000509e: 0f0ae2 l8ui a14, a10, 15 +400050a1: 130ad2 l8ui a13, a10, 19 +400050a4: 11ee80 slli a14, a14, 8 +400050a7: 11dd80 slli a13, a13, 8 +400050aa: 20eef0 or a14, a14, a15 +400050ad: 120af2 l8ui a15, a10, 18 +400050b0: 11ee80 slli a14, a14, 8 +400050b3: 20ee80 or a14, a14, a8 +400050b6: 20ddf0 or a13, a13, a15 +400050b9: 110af2 l8ui a15, a10, 17 +400050bc: 11dd80 slli a13, a13, 8 +400050bf: 20ddf0 or a13, a13, a15 +400050c2: 100af2 l8ui a15, a10, 16 +400050c5: 11dd80 slli a13, a13, 8 +400050c8: 20ddf0 or a13, a13, a15 +400050cb: 0c0af2 l8ui a15, a10, 12 +400050ce: 11ee80 slli a14, a14, 8 +400050d1: 20eef0 or a14, a14, a15 +400050d4: 0020c0 memw +400050d7: 0ed9 s32i.n a13, a14, 0 +400050d9: ffd8c6 j 40005040 <_XX_unk500c+0x34> +400050dc: cab200 depbits a0, a2, 12, 12 +400050df: 0ae214 excw +400050e2: 0e .byte 0xe +400050e3: 110af2 l8ui a15, a10, 17 +400050e6: 130ac2 l8ui a12, a10, 19 +400050e9: 120ad2 l8ui a13, a10, 18 +400050ec: 11cc80 slli a12, a12, 8 +400050ef: 20ccd0 or a12, a12, a13 +400050f2: 11cc80 slli a12, a12, 8 +400050f5: 0f0ad2 l8ui a13, a10, 15 +400050f8: 20ccf0 or a12, a12, a15 +400050fb: 100af2 l8ui a15, a10, 16 +400050fe: 11dd80 slli a13, a13, 8 +40005101: 11cc80 slli a12, a12, 8 +40005104: 20dde0 or a13, a13, a14 +40005107: 0d0ae2 l8ui a14, a10, 13 +4000510a: 11dd80 slli a13, a13, 8 +4000510d: 20ccf0 or a12, a12, a15 +40005110: 20dde0 or a13, a13, a14 +40005113: 0c0ae2 l8ui a14, a10, 12 +40005116: 11dd80 slli a13, a13, 8 +40005119: 20dde0 or a13, a13, a14 +4000511c: 0dad mov.n a10, a13 +4000511e: fcca65 call8 40001dc4 <_X_ets_memcpy> +40005121: ffc6c6 j 40005040 <_XX_unk500c+0x34> +40005124: 0c0ab2 l8ui a11, a10, 12 +40005127: 0f0ac2 l8ui a12, a10, 15 +4000512a: 0e0ad2 l8ui a13, a10, 14 +4000512d: 11cc80 slli a12, a12, 8 +40005130: 20ccd0 or a12, a12, a13 +40005133: 0d0ad2 l8ui a13, a10, 13 +40005136: 11cc80 slli a12, a12, 8 +40005139: 20ccd0 or a12, a12, a13 +4000513c: 11ac80 slli a10, a12, 8 +4000513f: 20aab0 or a10, a10, a11 +40005142: fbfd25 call8 40001114 <_XX_set_0x3fffda30_0> +40005145: 02a0d2 movi a13, 2 +40005148: ff0ee1 l32r a14, 40004d80 <_st_0x3fffdc90> +4000514b: 00a0f2 movi a15, 0 +4000514e: 166ef2 s32i a15, a14, 88 +40005151: 0b6ed2 s32i a13, a14, 44 +40005154: ffba06 j 40005040 <_XX_unk500c+0x34> +40005157: 413600 srli a3, a0, 6 +4000515a: 092100 l32e a0, a1, -56 +4000515d: ff .byte 0xff +4000515e: b228 l32i.n a2, a2, 44 +40005160: f01d retw.n + ... + +40005164 <_fp_0x40004f6c>: +40005164: 4f6c movi.n a15, -28 +40005166: c84000 excw + +40005168 <_fp_0x40004fc8>: +40005168: 4fc8 l32i.n a12, a15, 16 +4000516a: 0c4000 excw +4000516d: 400050 excw + +40005170 <_X_sip_init_attach>: +40005170: 004136 entry a1, 32 +40005173: 02dd mov.n a13, a2 +40005175: fffba1 l32r a10, 40005164 <_fp_0x40004f6c> +40005178: fffcb1 l32r a11, 40005168 <_fp_0x40004fc8> +4000517b: ff0131 l32r a3, 40004d80 <_st_0x3fffdc90> +4000517e: 080c movi.n a8, 0 +40005180: 03cd mov.n a12, a3 +40005182: b389 s32i.n a8, a3, 44 +40005184: 003625 call8 400054e8 <_X_slc_init_attach> +40005187: c3a9 s32i.n a10, a3, 48 +40005189: ffbfe5 call8 40004d88 <_XX_unk4d88> +4000518c: c398 l32i.n a9, a3, 48 +4000518e: fff7a1 l32r a10, 4000516c <_fp_0x40004fc8+0x4> +40005191: 1663a2 s32i a10, a3, 88 +40005194: 001992 l16ui a9, a9, 0 +40005197: 015392 s16i a9, a3, 2 +4000519a: 004de5 call8 40005678 <_X_slc_enable> +4000519d: 1b0c movi.n a11, 1 +4000519f: b3b9 s32i.n a11, a3, 44 +400051a1: f01d retw.n +400051a3: 544400 extui a4, a0, 4, 6 +400051a6: 504000 iitlb a0 +400051a9: df .byte 0xdf +400051aa: ff .byte 0xff +400051ab: 3f .byte 0x3f + +400051ac <_XX_unk51ac>: +400051ac: 004136 entry a1, 32 +400051af: fffda1 l32r a10, 400051a4 <_X_sip_init_attach+0x34> +400051b2: 1da0b2 movi a11, 29 +400051b5: fffcc1 l32r a12, 400051a8 <_X_sip_init_attach+0x38> +400051b8: 04a0d2 movi a13, 4 +400051bb: fef191 l32r a9, 40004d80 <_st_0x3fffdc90> +400051be: 00a082 movi a8, 0 +400051c1: a989 s32i.n a8, a9, 40 +400051c3: 9989 s32i.n a8, a9, 36 +400051c5: fbd8e5 call8 40000f54 <_X_ets_task> +400051c8: f01d retw.n +400051ca: 360000 excw +400051cd: ad0041 l32r a4, 3fff05d0 <_start-0xfa30> +400051d0: eb8102 excw +400051d3: fe .byte 0xfe +400051d4: 1298 l32i.n a9, a2, 4 +400051d6: a8f8 l32i.n a15, a8, 40 +400051d8: 0849f2 s8i a15, a9, 8 +400051db: 41c8f0 srli a12, a15, 8 +400051de: 0949c2 s8i a12, a9, 9 +400051e1: ff1b addi.n a15, a15, 1 +400051e3: 41c8c0 srli a12, a12, 8 +400051e6: 0a49c2 s8i a12, a9, 10 +400051e9: 41c8c0 srli a12, a12, 8 +400051ec: 0b49c2 s8i a12, a9, 11 +400051ef: a8f9 s32i.n a15, a8, 40 +400051f1: 03bd mov.n a11, a3 +400051f3: fea3e1 l32r a14, 40004c80 <_c_0xbfffffff> +400051f6: ed5ad1 l32r a13, 40000760 <_c_0x40000000> +400051f9: 0020c0 memw +400051fc: 03c8 l32i.n a12, a3, 0 +400051fe: 10cce0 and a12, a12, a14 +40005201: 20ccd0 or a12, a12, a13 +40005204: 0020c0 memw +40005207: 03c9 s32i.n a12, a3, 0 +40005209: 994b addi.n a9, a9, 4 +4000520b: 0d0c movi.n a13, 0 +4000520d: 0020c0 memw +40005210: 0049d2 s8i a13, a9, 0 +40005213: 0020c0 memw +40005216: 0149d2 s8i a13, a9, 1 +40005219: 0c0c movi.n a12, 0 +4000521b: 0020c0 memw +4000521e: 0249c2 s8i a12, a9, 2 +40005221: 080c movi.n a8, 0 +40005223: 0020c0 memw +40005226: 034982 s8i a8, a9, 3 +40005229: 0050a5 call8 40005734 <_XX_unk5734> +4000522c: 020c movi.n a2, 0 +4000522e: f01d retw.n +40005230: 004136 entry a1, 32 +40005233: fed321 l32r a2, 40004d80 <_st_0x3fffdc90> +40005236: f01d retw.n +40005238: 004136 entry a1, 32 +4000523b: fed141 l32r a4, 40004d80 <_st_0x3fffdc90> +4000523e: 5488 l32i.n a8, a4, 20 +40005240: 010816 beqz a8, 40005254 <_XX_unk51ac+0xa8> +40005243: fbe665 call8 400010a8 <_X_ets_enter_critical> +40005246: 5428 l32i.n a2, a4, 20 +40005248: 050c movi.n a5, 0 +4000524a: 6459 s32i.n a5, a4, 24 +4000524c: 5459 s32i.n a5, a4, 20 +4000524e: fbe6e5 call8 400010bc <_X_ets_exit_critical> +40005251: 057256 bnez a2, 400052ac <_XX_unk51ac+0x100> +40005254: f01d retw.n +40005256: ffc9b2 addi a11, a9, -1 +40005259: 083b16 beqz a11, 400052e0 <_XX_unk51ac+0x134> +4000525c: fdc9c2 addi a12, a9, -3 +4000525f: 07dc16 beqz a12, 400052e0 <_XX_unk51ac+0x134> +40005262: 432966 bnei a9, 2, 400052a9 <_XX_unk51ac+0xfd> +40005265: 030ac2 l8ui a12, a10, 3 +40005268: 020ab2 l8ui a11, a10, 2 +4000526b: 11ac80 slli a10, a12, 8 +4000526e: 20aab0 or a10, a10, a11 +40005271: 0314b2 l16ui a11, a4, 6 +40005274: 0162a5 call8 400068a0 +40005277: ffca62 addi a6, a10, -1 +4000527a: 02ad mov.n a10, a2 +4000527c: f46060 extui a6, a6, 0, 16 +4000527f: 06bd mov.n a11, a6 +40005281: ffaba5 call8 40004d3c +40005284: 2a28 l32i.n a2, a10, 8 +40005286: 2a59 s32i.n a5, a10, 8 +40005288: 1224d2 l32i a13, a4, 72 +4000528b: 0abd mov.n a11, a10 +4000528d: 5d8c beqz.n a13, 40005296 <_XX_unk51ac+0xea> +4000528f: 06cd mov.n a12, a6 +40005291: 03ad mov.n a10, a3 +40005293: 000de0 callx8 a13 +40005296: 02bd mov.n a11, a2 +40005298: 2228 l32i.n a2, a2, 8 +4000529a: 2b59 s32i.n a5, a11, 8 +4000529c: 1324d2 l32i a13, a4, 76 +4000529f: 6d8c beqz.n a13, 400052a9 <_XX_unk51ac+0xfd> +400052a1: 0bad mov.n a10, a11 +400052a3: 01a0c2 movi a12, 1 +400052a6: 000de0 callx8 a13 +400052a9: fa7216 beqz a2, 40005254 <_XX_unk51ac+0xa8> +400052ac: 12a8 l32i.n a10, a2, 4 +400052ae: 000a92 l8ui a9, a10, 0 +400052b1: 023d mov.n a3, a2 +400052b3: 349090 extui a9, a9, 0, 4 +400052b6: f9c956 bnez a9, 40005256 <_XX_unk51ac+0xaa> +400052b9: 2228 l32i.n a2, a2, 8 +400052bb: 2359 s32i.n a5, a3, 8 +400052bd: 010ab2 l8ui a11, a10, 1 +400052c0: 1324d2 l32i a13, a4, 76 +400052c3: 118b66 bnei a11, 8, 400052d8 <_XX_unk51ac+0x12c> +400052c6: fdfd16 beqz a13, 400052a9 <_XX_unk51ac+0xfd> +400052c9: 03bd mov.n a11, a3 +400052cb: 20a330 or a10, a3, a3 +400052ce: 01a0c2 movi a12, 1 +400052d1: 000de0 callx8 a13 +400052d4: fff446 j 400052a9 <_XX_unk51ac+0xfd> +400052d7: 03ad00 excw +400052da: ffb625 call8 40004e3c <_XX_unk4d88+0xb4> +400052dd: fff206 j 400052a9 <_XX_unk51ac+0xfd> +400052e0: 030ac2 l8ui a12, a10, 3 +400052e3: 020ab2 l8ui a11, a10, 2 +400052e6: 11ac80 slli a10, a12, 8 +400052e9: 20aab0 or a10, a10, a11 +400052ec: 0314b2 l16ui a11, a4, 6 +400052ef: 015b25 call8 400068a0 +400052f2: f460a0 extui a6, a10, 0, 16 +400052f5: 20a330 or a10, a3, a3 +400052f8: 20b660 or a11, a6, a6 +400052fb: ffa425 call8 40004d3c +400052fe: 2a28 l32i.n a2, a10, 8 +40005300: 2a59 s32i.n a5, a10, 8 +40005302: 1224d2 l32i a13, a4, 72 +40005305: 0abd mov.n a11, a10 +40005307: f9ed16 beqz a13, 400052a9 <_XX_unk51ac+0xfd> +4000530a: 06cd mov.n a12, a6 +4000530c: 03ad mov.n a10, a3 +4000530e: 000de0 callx8 a13 +40005311: ffe506 j 400052a9 <_XX_unk51ac+0xfd> +40005314: ccec bnez.n a12, 40005344 <_XX_unk51ac+0x198> +40005316: ff .byte 0xff +40005317: 3f .byte 0x3f +40005318: 004136 entry a1, 32 +4000531b: fe9961 l32r a6, 40004d80 <_st_0x3fffdc90> +4000531e: 072682 l32i a8, a6, 28 +40005321: 014816 beqz a8, 40005339 <_XX_unk51ac+0x18d> +40005324: fbd825 call8 400010a8 <_X_ets_enter_critical> +40005327: 7628 l32i.n a2, a6, 28 +40005329: 030c movi.n a3, 0 +4000532b: 8639 s32i.n a3, a6, 32 +4000532d: 7639 s32i.n a3, a6, 28 +4000532f: fbd8e5 call8 400010bc <_X_ets_exit_critical> +40005332: 328c beqz.n a2, 40005339 <_XX_unk51ac+0x18d> +40005334: 050c movi.n a5, 0 +40005336: 001246 j 40005383 <_XX_unk51ac+0x1d7> +40005339: f01d retw.n +4000533b: 370000 excw +4000533e: 1c69 s32i.n a6, a12, 4 +40005340: 0b03a2 l8ui a10, a3, 11 +40005343: 11aa80 slli a10, a10, 8 +40005346: 20aab0 or a10, a10, a11 +40005349: 0903b2 l8ui a11, a3, 9 +4000534c: 11aa80 slli a10, a10, 8 +4000534f: 20aab0 or a10, a10, a11 +40005352: 0803b2 l8ui a11, a3, 8 +40005355: 11aa80 slli a10, a10, 8 +40005358: 20aab0 or a10, a10, a11 +4000535b: 96a9 s32i.n a10, a6, 36 +4000535d: 010556 bnez a5, 40005371 <_XX_unk51ac+0x1c5> +40005360: 0103b2 l8ui a11, a3, 1 +40005363: 0a6b17 bbci a11, 1, 40005371 <_XX_unk51ac+0x1c5> +40005366: 001165 call8 4000547c <_c_0x60017e00+0x4> +40005369: 4acc bnez.n a10, 40005371 <_XX_unk51ac+0x1c5> +4000536b: 000f65 call8 40005460 <_XX_unk51ac+0x2b4> +4000536e: 01a052 movi a5, 1 +40005371: 000392 l8ui a9, a3, 0 +40005374: 349090 extui a9, a9, 0, 4 +40005377: 04d916 beqz a9, 400053c8 <_XX_unk51ac+0x21c> +4000537a: 621926 beqi a9, 1, 400053e0 <_XX_unk51ac+0x234> +4000537d: 5f2926 beqi a9, 2, 400053e0 <_XX_unk51ac+0x234> +40005380: fb5216 beqz a2, 40005339 <_XX_unk51ac+0x18d> +40005383: 1238 l32i.n a3, a2, 4 +40005385: 024d mov.n a4, a2 +40005387: 010392 l8ui a9, a3, 1 +4000538a: 0a03b2 l8ui a11, a3, 10 +4000538d: 14a290 extui a10, a9, 2, 2 +40005390: fa9a56 bnez a10, 4000533d <_XX_unk51ac+0x191> +40005393: 96c8 l32i.n a12, a6, 36 +40005395: bc1b addi.n a11, a12, 1 +40005397: 96b9 s32i.n a11, a6, 36 +40005399: 0b03a2 l8ui a10, a3, 11 +4000539c: 0a03b2 l8ui a11, a3, 10 +4000539f: 11aa80 slli a10, a10, 8 +400053a2: 20aab0 or a10, a10, a11 +400053a5: 0903b2 l8ui a11, a3, 9 +400053a8: 11aa80 slli a10, a10, 8 +400053ab: 20aab0 or a10, a10, a11 +400053ae: 0803b2 l8ui a11, a3, 8 +400053b1: 11aa80 slli a10, a10, 8 +400053b4: 20aab0 or a10, a10, a11 +400053b7: a21ac7 beq a10, a12, 4000535d <_XX_unk51ac+0x1b1> +400053ba: 0abd mov.n a11, a10 +400053bc: 02dd mov.n a13, a2 +400053be: ffd5a1 l32r a10, 40005314 <_XX_unk51ac+0x168> +400053c1: fd4425 call8 40002804 +400053c4: ffe546 j 4000535d <_XX_unk51ac+0x1b1> +400053c7: 04ad00 extui a10, a0, 13, 1 +400053ca: 04bd mov.n a11, a4 +400053cc: 2428 l32i.n a2, a4, 8 +400053ce: 090c movi.n a9, 0 +400053d0: 142682 l32i a8, a6, 80 +400053d3: 2499 s32i.n a9, a4, 8 +400053d5: 0008e0 callx8 a8 +400053d8: 04ad mov.n a10, a4 +400053da: ffb3a5 call8 40004f14 <_XX_unk4f14> +400053dd: ffe7c6 j 40005380 <_XX_unk51ac+0x1d4> +400053e0: 0303a2 l8ui a10, a3, 3 +400053e3: 0203b2 l8ui a11, a3, 2 +400053e6: 11aa80 slli a10, a10, 8 +400053e9: 20aab0 or a10, a10, a11 +400053ec: 0216b2 l16ui a11, a6, 4 +400053ef: 014b25 call8 400068a0 +400053f2: 0a7d mov.n a7, a10 +400053f4: 04ad mov.n a10, a4 +400053f6: f4b070 extui a11, a7, 0, 16 +400053f9: ff9425 call8 40004d3c +400053fc: 2a28 l32i.n a2, a10, 8 +400053fe: 0d0c movi.n a13, 0 +40005400: 2ad9 s32i.n a13, a10, 8 +40005402: 0abd mov.n a11, a10 +40005404: 0020c0 memw +40005407: 0a98 l32i.n a9, a10, 0 +40005409: c32b addi.n a12, a3, 2 +4000540b: 0216d2 l16ui a13, a6, 4 +4000540e: 000ce2 l8ui a14, a12, 0 +40005411: 010cc2 l8ui a12, a12, 1 +40005414: 82d7d0 mull a13, a7, a13 +40005417: 11cc80 slli a12, a12, 8 +4000541a: 20cce0 or a12, a12, a14 +4000541d: c0ccd0 sub a12, a12, a13 +40005420: 0020c0 memw +40005423: fa24d1 l32r a13, 40003cb4 <_c_0x00ffffff> +40005426: 0aa8 l32i.n a10, a10, 0 +40005428: 10aad0 and a10, a10, a13 +4000542b: 41aca0 srli a10, a10, 12 +4000542e: aaca add.n a10, a10, a12 +40005430: caba90 depbits a9, a10, 12, 12 +40005433: 04ad mov.n a10, a4 +40005435: 0020c0 memw +40005438: 152682 l32i a8, a6, 84 +4000543b: 0b99 s32i.n a9, a11, 0 +4000543d: 0008e0 callx8 a8 +40005440: ffcf06 j 40005380 <_XX_unk51ac+0x1d4> +40005443: 413600 srli a3, a0, 6 +40005446: 029800 andb b9, b8, b0 +40005449: 398c beqz.n a9, 40005450 <_XX_unk51ac+0x2a4> +4000544b: 092926 beqi a9, 2, 40005458 <_XX_unk51ac+0x2ac> +4000544e: f01d retw.n +40005450: 12a8 l32i.n a10, a2, 4 +40005452: ffde65 call8 40005238 <_XX_unk51ac+0x8c> +40005455: f01d retw.n +40005457: 12a800 andbc b10, b8, b0 +4000545a: ffebe5 call8 40005318 <_XX_unk51ac+0x16c> +4000545d: f01d retw.n +4000545f: 413600 srli a3, a0, 6 +40005462: a0a200 addx4 a10, a2, a0 +40005465: a0b200 addx4 a11, a2, a0 +40005468: 962505 call0 3ff9b6bc <_start-0x64944> +4000546b: ff .byte 0xff +4000546c: 0abd mov.n a11, a10 +4000546e: ffd5e5 call8 400051cc <_XX_unk51ac+0x20> +40005471: f01d retw.n +40005473: 4e0000 excw +40005476: 006001 l32r a0, 3ffc55f8 <_start-0x3aa08> + +40005478 <_c_0x60017e00>: +40005478: 017e00 slli a7, a14, 32 +4000547b: 413660 srli a3, a6, 6 +4000547e: fd3100 excw +40005481: ff .byte 0xff +40005482: 0020c0 memw +40005485: 942332 l32i a3, a3, 0x250 +40005488: 181c movi.n a8, 17 +4000548a: 0ff377 bbsi a3, 23, 4000549d <_c_0x60017e00+0x25> +4000548d: fffa41 l32r a4, 40005478 <_c_0x60017e00> +40005490: 0020c0 memw +40005493: a52442 l32i a4, a4, 0x294 +40005496: 020c movi.n a2, 0 +40005498: 01c487 bnall a4, a8, 4000549d <_c_0x60017e00+0x25> +4000549b: f01d retw.n +4000549d: 120c movi.n a2, 1 +4000549f: f01d retw.n +400054a1: 000000 ill + +400054a4 <_st_0x3fffdf70>: +400054a4: ffdf70 excw +400054a7: 3f .byte 0x3f +400054a8: 004136 entry a1, 32 +400054ab: fffe51 l32r a5, 400054a4 <_st_0x3fffdf70> +400054ae: b549 s32i.n a4, a5, 44 +400054b0: 6539 s32i.n a3, a5, 24 +400054b2: 5529 s32i.n a2, a5, 20 +400054b4: f01d retw.n + ... + +400054b8 <_c_0x6000ae00>: +400054b8: 00ae00 any8 b0, b8:b9:b10:b11:b12:b13:b14:b15 +400054bb: 000060 excw + +400054bc <_c_0xf0000000>: +400054bc: 000000 ill +400054bf: 0017f0 movsp a15, a7 + +400054c0 <_c_0x02320017>: +400054c0: 320017 bnone a0, a1, 400054f6 <_X_slc_init_attach+0xe> +400054c3: 666602 s32i a0, a6, 0x198 + +400054c4 <_c_0x11116666>: +400054c4: 116666 bnei a6, 6, 400054d9 <_c_0x11116666+0x15> +400054c7: bfff11 l32r a1, 3fff54c4 <_start-0xab3c> +400054ca: ff .byte 0xff +400054cb: ff .byte 0xff +400054cc: ff .byte 0xff +400054cd: fffc01 l32r a0, 400054c0 <_c_0x02320017> +400054d0: 00c800 excw +400054d3: dfb000 excw +400054d6: ff .byte 0xff +400054d7: 3f .byte 0x3f +400054d8: 030003 excw +400054db: fffc00 excw +400054de: fffc bnez.n a15, 40005521 <_X_slc_init_attach+0x39> +400054e0: 800040 add a0, a0, a4 + ... + +400054e4 <_c_0x01110013>: +400054e4: 110013 excw +400054e7: 813601 l32r a0, 3ffe59c0 <_start-0x1a640> + +400054e8 <_X_slc_init_attach>: +400054e8: 008136 entry a1, 64 +400054eb: 029d mov.n a9, a2 +400054ed: 0e2c movi.n a14, 32 +400054ef: ffed21 l32r a2, 400054a4 <_st_0x3fffdf70> +400054f2: 0f0c movi.n a15, 0 +400054f4: 92f9 s32i.n a15, a2, 36 +400054f6: 82f9 s32i.n a15, a2, 32 +400054f8: 72f9 s32i.n a15, a2, 28 +400054fa: b249 s32i.n a4, a2, 44 +400054fc: 6239 s32i.n a3, a2, 24 +400054fe: 056292 s32i a9, a2, 20 +40005501: ffeda1 l32r a10, 400054b8 <_c_0x6000ae00> +40005504: f78bb1 l32r a11, 40003330 <_c_0x3feffe00> +40005507: ffed91 l32r a9, 400054bc <_c_0xf0000000> +4000550a: 0020c0 memw +4000550d: 952bd2 l32i a13, a11, 0x254 +40005510: 20dde0 or a13, a13, a14 +40005513: 0020c0 memw +40005516: 956bd2 s32i a13, a11, 0x254 +40005519: dfafc2 movi a12, -33 +4000551c: 0020c0 memw +4000551f: 952b82 l32i a8, a11, 0x254 +40005522: 1088c0 and a8, a8, a12 +40005525: 0020c0 memw +40005528: 956b82 s32i a8, a11, 0x254 +4000552b: 0545b6 bltui a5, 4, 40005534 <_X_slc_init_attach+0x4c> +4000552e: fcc582 addi a8, a5, -4 +40005531: 11f816 beqz a8, 40005654 <_l_slc_boot_mode_4> +40005534: 0020c0 memw +40005537: 812ab2 l32i a11, a10, 0x204 +4000553a: ffe1c1 l32r a12, 400054c0 <_c_0x02320017> +4000553d: 10bb90 and a11, a11, a9 +40005540: 20bbc0 or a11, a11, a12 +40005543: 0020c0 memw +40005546: 816ab2 s32i a11, a10, 0x204 +40005549: ffdec1 l32r a12, 400054c4 <_c_0x11116666> +4000554c: 0020c0 memw +4000554f: 806ac2 s32i a12, a10, 0x200 +40005552: 0043a5 call8 4000598c <_X_slc_set_host_io_max_window> +40005555: 3c0c movi.n a12, 3 +40005557: ffc741 l32r a4, 40005474 <_XX_unk51ac+0x2c8> +4000555a: ffc731 l32r a3, 40005478 <_c_0x60017e00> +4000555d: 0020c0 memw +40005560: 8023b2 l32i a11, a3, 0x200 +40005563: 20bbc0 or a11, a11, a12 +40005566: 0020c0 memw +40005569: 8063b2 s32i a11, a3, 0x200 +4000556c: ca7c movi.n a10, -4 +4000556e: 0020c0 memw +40005571: 802392 l32i a9, a3, 0x200 +40005574: 1099a0 and a9, a9, a10 +40005577: 0020c0 memw +4000557a: 806392 s32i a9, a3, 0x200 +4000557d: 10a382 movi a8, 0x310 +40005580: 0020c0 memw +40005583: 8023f2 l32i a15, a3, 0x200 +40005586: 20ff80 or a15, a15, a8 +40005589: 0020c0 memw +4000558c: 8063f2 s32i a15, a3, 0x200 +4000558f: ffcee1 l32r a14, 400054c8 <_c_0x11116666+0x4> +40005592: 0020c0 memw +40005595: 8023d2 l32i a13, a3, 0x200 +40005598: 10dde0 and a13, a13, a14 +4000559b: 0020c0 memw +4000559e: 8063d2 s32i a13, a3, 0x200 +400055a1: 00a1c2 movi a12, 0x100 +400055a4: 0020c0 memw +400055a7: 9e24b2 l32i a11, a4, 0x278 +400055aa: 20bbc0 or a11, a11, a12 +400055ad: 0020c0 memw +400055b0: 9e64b2 s32i a11, a4, 0x278 +400055b3: ffc6a1 l32r a10, 400054cc <_c_0x11116666+0x8> +400055b6: ffc691 l32r a9, 400054d0 <_c_0x11116666+0xc> +400055b9: 0020c0 memw +400055bc: c02482 l32i a8, a4, 0x300 +400055bf: 1088a0 and a8, a8, a10 +400055c2: 208890 or a8, a8, a9 +400055c5: 0020c0 memw +400055c8: cb0c movi.n a11, 12 +400055ca: 10c1f2 addi a15, a1, 16 +400055cd: 14c1e2 addi a14, a1, 20 +400055d0: c06482 s32i a8, a4, 0x300 +400055d3: ffc0a1 l32r a10, 400054d4 <_c_0x11116666+0x10> +400055d6: 10a2d2 movi a13, 0x210 +400055d9: 10cac2 addi a12, a10, 16 +400055dc: 0052d2 s16i a13, a2, 0 +400055df: 11e9 s32i.n a14, a1, 4 +400055e1: 01f9 s32i.n a15, a1, 0 +400055e3: 10a2e2 movi a14, 0x210 +400055e6: 01a0f2 movi a15, 1 +400055e9: ff6a25 call8 40004c8c +400055ec: ffbb81 l32r a8, 400054d8 <_c_0x11116666+0x14> +400055ef: 0020c0 memw +400055f2: 8023f2 l32i a15, a3, 0x200 +400055f5: 20ff80 or a15, a15, a8 +400055f8: 0020c0 memw +400055fb: 8063f2 s32i a15, a3, 0x200 +400055fe: ffb7e1 l32r a14, 400054dc <_c_0x11116666+0x18> +40005601: 0020c0 memw +40005604: 8023d2 l32i a13, a3, 0x200 +40005607: 10dde0 and a13, a13, a14 +4000560a: 0020c0 memw +4000560d: 8063d2 s32i a13, a3, 0x200 +40005610: 41a8 l32i.n a10, a1, 16 +40005612: 51b8 l32i.n a11, a1, 20 +40005614: 1c0c movi.n a12, 1 +40005616: 001a25 call8 400057b8 <_XX_unk57b8> +40005619: f9b9e1 l32r a14, 40003d00 <_c_0x00800000> +4000561c: fc7c movi.n a12, -1 +4000561e: 0020c0 memw +40005621: b864e2 s32i a14, a4, 0x2e0 +40005624: ffafd1 l32r a13, 400054e0 <_c_0x11116666+0x1c> +40005627: 0020c0 memw +4000562a: b464d2 s32i a13, a4, 0x2d0 +4000562d: 0020c0 memw +40005630: b264c2 s32i a12, a4, 0x2c8 +40005633: 0020c0 memw +40005636: 8463c2 s32i a12, a3, 0x210 +40005639: 00acb2 movi a11, 0xfffffc00 +4000563c: 0a1c movi.n a10, 16 +4000563e: 0020c0 memw +40005641: c62392 l32i a9, a3, 0x318 +40005644: 1099b0 and a9, a9, a11 +40005647: 2099a0 or a9, a9, a10 +4000564a: 0020c0 memw +4000564d: c66392 s32i a9, a3, 0x318 +40005650: f01d retw.n + ... + +40005654 <_l_slc_boot_mode_4>: +40005654: 0020c0 memw +40005657: 812af2 l32i a15, a10, 0x204 +4000565a: ffa281 l32r a8, 400054e4 <_c_0x01110013> +4000565d: 10ff90 and a15, a15, a9 +40005660: 20ff80 or a15, a15, a8 +40005663: 0020c0 memw +40005666: 816af2 s32i a15, a10, 0x204 +40005669: ffb706 j 40005549 <_X_slc_init_attach+0x61> +4000566c: ff .byte 0xff +4000566d: ff .byte 0xff +4000566e: cf .byte 0xcf +4000566f: ff .byte 0xff +40005670: 0058e0 excw +40005673: 840040 extui a0, a4, 0, 9 +40005676: 040a add.n a0, a4, a0 + +40005678 <_X_slc_enable>: +40005678: 004136 entry a1, 32 +4000567b: fffcc1 l32r a12, 4000566c <_l_slc_boot_mode_4+0x18> +4000567e: f72c91 l32r a9, 40003330 <_c_0x3feffe00> +40005681: ff7d21 l32r a2, 40005478 <_c_0x60017e00> +40005684: 0020c0 memw +40005687: 9822b2 l32i a11, a2, 0x260 +4000568a: 10bbc0 and a11, a11, a12 +4000568d: 0020c0 memw +40005690: 9862b2 s32i a11, a2, 0x260 +40005693: 20a0a2 movi a10, 32 +40005696: 0020c0 memw +40005699: 982282 l32i a8, a2, 0x260 +4000569c: 2088a0 or a8, a8, a10 +4000569f: 0020c0 memw +400056a2: 986282 s32i a8, a2, 0x260 +400056a5: 40a0f2 movi a15, 64 +400056a8: 0020c0 memw +400056ab: 9822e2 l32i a14, a2, 0x260 +400056ae: 20eef0 or a14, a14, a15 +400056b1: 0020c0 memw +400056b4: 9862e2 s32i a14, a2, 0x260 +400056b7: fd6c movi.n a13, -17 +400056b9: 0020c0 memw +400056bc: 9822c2 l32i a12, a2, 0x260 +400056bf: 10ccd0 and a12, a12, a13 +400056c2: 0020c0 memw +400056c5: 9862c2 s32i a12, a2, 0x260 +400056c8: f71bb1 l32r a11, 40003334 <_c_0xffff83ff> +400056cb: 00a4a2 movi a10, 0x400 +400056ce: 0020c0 memw +400056d1: 9c2982 l32i a8, a9, 0x270 +400056d4: 1088b0 and a8, a8, a11 +400056d7: 2088a0 or a8, a8, a10 +400056da: 0020c0 memw +400056dd: 1a0c movi.n a10, 1 +400056df: ffe4b1 l32r a11, 40005670 <_l_slc_boot_mode_4+0x1c> +400056e2: 00a0c2 movi a12, 0 +400056e5: 9c6982 s32i a8, a9, 0x270 +400056e8: fb9fe5 call8 400010e8 <_X_ets_isr_attach> +400056eb: ffe2d1 l32r a13, 40005674 <_l_slc_boot_mode_4+0x20> +400056ee: 0020c0 memw +400056f1: 8362d2 s32i a13, a2, 0x20c +400056f4: 2a0c movi.n a10, 2 +400056f6: fba0e5 call8 40001104 <_X_ets_isr_unmask> +400056f9: f01d retw.n + ... + +400056fc <_X_slc_select_tohost_gpio_mode>: +400056fc: 004136 entry a1, 32 +400056ff: ff6931 l32r a3, 400054a4 <_st_0x3fffdf70> +40005702: d329 s32i.n a2, a3, 52 +40005704: f01d retw.n + ... + +40005708 <_X_slc_select_tohost_gpio>: +40005708: 004136 entry a1, 32 +4000570b: 11b2f6 bgeui a2, 16, 40005720 <_X_slc_select_tohost_gpio+0x18> +4000570e: ff6541 l32r a4, 400054a4 <_st_0x3fffdf70> +40005711: 401200 ssl a2 +40005714: 130c movi.n a3, 1 +40005716: a13300 sll a3, a3 +40005719: 1c5432 s16i a3, a4, 56 +4000571c: f01d retw.n +4000571e: 910000 srl a0, a0 +40005721: 0cff61 l32r a6, 3ffc8b20 <_start-0x374e0> +40005724: 8208 l32i.n a0, a2, 32 +40005726: 1c59 s32i.n a5, a12, 4 +40005728: f01d retw.n +4000572a: 000000 ill +4000572d: 00c000 excw + +40005730 <_c_0xff300000>: +40005730: 300000 xor a0, a0, a0 +40005733: ff .byte 0xff + +40005734 <_XX_unk5734>: +40005734: 006136 entry a1, 48 +40005737: 026d mov.n a6, a2 +40005739: 040c movi.n a4, 0 +4000573b: 0020c0 memw +4000573e: 0149 s32i.n a4, a1, 0 +40005740: 06c216 beqz a2, 400057b0 <_XX_unk5734+0x7c> +40005743: 00a052 movi a5, 0 +40005746: f95b71 l32r a7, 40003cb4 <_c_0x00ffffff> +40005749: 108076 loop a0, 4000575d <_XX_unk5734+0x29> +4000574c: 0020c0 memw +4000574f: 0688 l32i.n a8, a6, 0 +40005751: 2668 l32i.n a6, a6, 8 +40005753: 108870 and a8, a8, a7 +40005756: 418c80 srli a8, a8, 12 +40005759: 585a add.n a5, a8, a5 +4000575b: 268c beqz.n a6, 40005761 <_XX_unk5734+0x2d> +4000575d: fffa06 j 40005749 <_XX_unk5734+0x15> +40005760: 20c000 or a12, a0, a0 +40005763: 615200 excw +40005766: 446100 extui a6, a0, 1, 5 +40005769: ff .byte 0xff +4000576a: f96571 l32r a7, 40003d00 <_c_0x00800000> +4000576d: 088076 loop a0, 40005779 <_XX_unk5734+0x45> +40005770: 0020c0 memw +40005773: b92692 l32i a9, a6, 0x2e4 +40005776: 020977 bnone a9, a7, 4000577c <_XX_unk5734+0x48> +40005779: fffc06 j 4000576d <_XX_unk5734+0x39> +4000577c: 0020c0 memw +4000577f: bd6622 s32i a2, a6, 0x2f4 +40005782: 0020c0 memw +40005785: be6632 s32i a3, a6, 0x2f8 +40005788: ffe9b1 l32r a11, 4000572c <_X_slc_select_tohost_gpio+0x24> +4000578b: f72fd1 l32r a13, 40003448 <_c_0x000fffff> +4000578e: 0020c0 memw +40005791: 01c8 l32i.n a12, a1, 0 +40005793: 10ccd0 and a12, a12, a13 +40005796: ffe6d1 l32r a13, 40005730 <_c_0xff300000> +40005799: 0020c0 memw +4000579c: b926a2 l32i a10, a6, 0x2e4 +4000579f: 10aad0 and a10, a10, a13 +400057a2: 20aac0 or a10, a10, a12 +400057a5: 20aab0 or a10, a10, a11 +400057a8: 0020c0 memw +400057ab: b966a2 s32i a10, a6, 0x2e4 +400057ae: f01d retw.n +400057b0: 050c movi.n a5, 0 +400057b2: ffeac6 j 40005761 <_XX_unk5734+0x2d> +400057b5: 000000 ill + +400057b8 <_XX_unk57b8>: +400057b8: 004136 entry a1, 32 +400057bb: ff2fb1 l32r a11, 40005478 <_c_0x60017e00> +400057be: f93ea1 l32r a10, 40003cb8 <_c_0x01000000> +400057c1: 088076 loop a0, 400057cd <_XX_unk57b8+0x15> +400057c4: 0020c0 memw +400057c7: b92b82 l32i a8, a11, 0x2e4 +400057ca: 0208a7 bnone a8, a10, 400057d0 <_XX_unk57b8+0x18> +400057cd: fffc06 j 400057c1 <_XX_unk57b8+0x9> +400057d0: 0020c0 memw +400057d3: bb6b22 s32i a2, a11, 0x2ec +400057d6: 0020c0 memw +400057d9: bc6b32 s32i a3, a11, 0x2f0 +400057dc: 0020c0 memw +400057df: b92b92 l32i a9, a11, 0x2e4 +400057e2: 2099a0 or a9, a9, a10 +400057e5: 0020c0 memw +400057e8: b96b92 s32i a9, a11, 0x2e4 +400057eb: f4a040 extui a10, a4, 0, 16 +400057ee: 001d65 call8 400059c4 <_X_slc_add_credits> +400057f1: f01d retw.n + ... + +400057f4 <_XX_unk57f4>: +400057f4: 004136 entry a1, 32 +400057f7: 00a082 movi a8, 0 +400057fa: ff1f61 l32r a6, 40005478 <_c_0x60017e00> +400057fd: 0020c0 memw +40005800: b92642 l32i a4, a6, 0x2e4 +40005803: 3974b7 bbci a4, 27, 40005840 <_XX_unk57f4+0x4c> +40005806: f951a1 l32r a10, 40003d4c <_c_0x02000000> +40005809: 0020c0 memw +4000580c: b92692 l32i a9, a6, 0x2e4 +4000580f: 2099a0 or a9, a9, a10 +40005812: 0020c0 memw +40005815: b96692 s32i a9, a6, 0x2e4 +40005818: 0020c0 memw +4000581b: c12672 l32i a7, a6, 0x304 +4000581e: 006272 s32i a7, a2, 0 +40005821: 0020c0 memw +40005824: f93721 l32r a2, 40003d00 <_c_0x00800000> +40005827: c22672 l32i a7, a6, 0x308 +4000582a: 006372 s32i a7, a3, 0 +4000582d: 088076 loop a0, 40005839 <_XX_unk57f4+0x45> +40005830: 0020c0 memw +40005833: b926b2 l32i a11, a6, 0x2e4 +40005836: 020b27 bnone a11, a2, 4000583c <_XX_unk57f4+0x48> +40005839: fffc06 j 4000582d <_XX_unk57f4+0x39> +4000583c: 2789 s32i.n a8, a7, 8 +4000583e: f01d retw.n +40005840: 0289 s32i.n a8, a2, 0 +40005842: 0389 s32i.n a8, a3, 0 +40005844: f01d retw.n + ... + +40005848 <_XX_unk5848>: +40005848: 004136 entry a1, 32 +4000584b: ff0b61 l32r a6, 40005478 <_c_0x60017e00> +4000584e: 0020c0 memw +40005851: b92642 l32i a4, a6, 0x2e4 +40005854: 7474c7 bbci a4, 28, 400058cc <_XX_unk5848+0x84> +40005857: f9dd91 l32r a9, 40003fcc <_c_0x04000000> +4000585a: 0020c0 memw +4000585d: b92682 l32i a8, a6, 0x2e4 +40005860: 208890 or a8, a8, a9 +40005863: 0020c0 memw +40005866: b96682 s32i a8, a6, 0x2e4 +40005869: 0020c0 memw +4000586c: bf2672 l32i a7, a6, 0x2fc +4000586f: 0279 s32i.n a7, a2, 0 +40005871: 0020c0 memw +40005874: f91171 l32r a7, 40003cb8 <_c_0x01000000> +40005877: c02682 l32i a8, a6, 0x300 +4000587a: 006382 s32i a8, a3, 0 +4000587d: 088076 loop a0, 40005889 <_XX_unk5848+0x41> +40005880: 0020c0 memw +40005883: b926a2 l32i a10, a6, 0x2e4 +40005886: 040a77 bnone a10, a7, 4000588e <_XX_unk5848+0x46> +40005889: fffc06 j 4000587d <_XX_unk5848+0x35> +4000588c: 0c0000 excw +4000588f: 286906 j 4000fa37 <__umoddi3+0x8bd3> +40005892: 002222 l32i a2, a2, 0 +40005895: f2ac beqz.n a2, 400058c8 <_XX_unk5848+0x80> +40005897: fcfa51 l32r a5, 40004c80 <_c_0xbfffffff> +4000589a: ebb931 l32r a3, 40000780 <_c_0x7fffffff> +4000589d: 118076 loop a0, 400058b2 <_XX_unk5848+0x6a> +400058a0: 0020c0 memw +400058a3: 02b8 l32i.n a11, a2, 0 +400058a5: 10bb30 and a11, a11, a3 +400058a8: 15beb0 extui a11, a11, 30, 2 +400058ab: 061b26 beqi a11, 1, 400058b5 <_XX_unk5848+0x6d> +400058ae: 2228 l32i.n a2, a2, 8 +400058b0: 429c beqz.n a2, 400058c8 <_XX_unk5848+0x80> +400058b2: fff9c6 j 4000589d <_XX_unk5848+0x55> +400058b5: 0020c0 memw +400058b8: 02c8 l32i.n a12, a2, 0 +400058ba: 10cc50 and a12, a12, a5 +400058bd: 20cc60 or a12, a12, a6 +400058c0: 0020c0 memw +400058c3: 02c9 s32i.n a12, a2, 0 +400058c5: fff946 j 400058ae <_XX_unk5848+0x66> +400058c8: 020c movi.n a2, 0 +400058ca: f01d retw.n +400058cc: f27c movi.n a2, -1 +400058ce: f01d retw.n +400058d0: ff .byte 0xff +400058d1: ff .byte 0xff +400058d2: fe .byte 0xfe +400058d3: ff .byte 0xff +400058d4: 020000 andb b0, b0, b0 +400058d7: 000004 excw +400058da: 000001 l32r a0, 3ffc58dc <_start-0x3a724> +400058dd: 000080 ret +400058e0: 004136 entry a1, 32 +400058e3: fffb71 l32r a7, 400058d0 <_XX_unk5848+0x88> +400058e6: fffb51 l32r a5, 400058d4 <_XX_unk5848+0x8c> +400058e9: fffb61 l32r a6, 400058d8 <_XX_unk5848+0x90> +400058ec: fee331 l32r a3, 40005478 <_c_0x60017e00> +400058ef: feed41 l32r a4, 400054a4 <_st_0x3fffdf70> +400058f2: 0020c0 memw +400058f5: 822322 l32i a2, a3, 0x208 +400058f8: 74b8 l32i.n a11, a4, 28 +400058fa: 07e216 beqz a2, 4000597c <_XX_unk5848+0x134> +400058fd: 4b8c beqz.n a11, 40005905 <_XX_unk5848+0xbd> +400058ff: 20a220 or a10, a2, a2 +40005902: 000be0 callx8 a11 +40005905: 0c0527 bnone a5, a2, 40005915 <_XX_unk5848+0xcd> +40005908: 0020c0 memw +4000590b: 846352 s32i a5, a3, 0x210 +4000590e: 5488 l32i.n a8, a4, 20 +40005910: b4a8 l32i.n a10, a4, 44 +40005912: 0008e0 callx8 a8 +40005915: 1062f7 bbci a2, 15, 40005929 <_XX_unk5848+0xe1> +40005918: 0020c0 memw +4000591b: fff091 l32r a9, 400058dc <_XX_unk5848+0x94> +4000591e: 846392 s32i a9, a3, 0x210 +40005921: 6488 l32i.n a8, a4, 24 +40005923: 0b24a2 l32i a10, a4, 44 +40005926: 0008e0 callx8 a8 +40005929: 1c7207 bbci a2, 16, 40005949 <_XX_unk5848+0x101> +4000592c: d498 l32i.n a9, a4, 52 +4000592e: 2a2926 beqi a9, 2, 4000595c <_XX_unk5848+0x114> +40005931: 373926 beqi a9, 3, 4000596c <_XX_unk5848+0x124> +40005934: 0020c0 memw +40005937: 8323a2 l32i a10, a3, 0x20c +4000593a: 10aa70 and a10, a10, a7 +4000593d: 0020c0 memw +40005940: 8363a2 s32i a10, a3, 0x20c +40005943: 0020c0 memw +40005946: 846362 s32i a6, a3, 0x210 +40005949: 2ff237 bbsi a2, 19, 4000597c <_XX_unk5848+0x134> +4000594c: 2ce2a7 bbsi a2, 10, 4000597c <_XX_unk5848+0x134> +4000594f: 84b8 l32i.n a11, a4, 32 +40005951: f9db16 beqz a11, 400058f2 <_XX_unk5848+0xaa> +40005954: 02ad mov.n a10, a2 +40005956: 000be0 callx8 a11 +40005959: ffe546 j 400058f2 <_XX_unk5848+0xaa> +4000595c: 1c14b2 l16ui a11, a4, 56 +4000595f: 0020c0 memw +40005962: edf3c1 l32r a12, 40001130 <_c_0x60003e00> +40005965: 836cb2 s32i a11, a12, 0x20c +40005968: fff206 j 40005934 <_XX_unk5848+0xec> +4000596b: 14d200 extui a13, a0, 2, 2 +4000596e: c01c movi.n a0, 28 +40005970: e10020 excw +40005973: ef .byte 0xef +40005974: d2ed excw +40005976: 6e .byte 0x6e +40005977: ee0682 l8ui a8, a6, 238 +4000597a: ff .byte 0xff +4000597b: 94b800 extui a11, a0, 8, 10 +4000597e: 3b8c beqz.n a11, 40005985 <_XX_unk5848+0x13d> +40005980: 0a0c movi.n a10, 0 +40005982: 000be0 callx8 a11 +40005985: f01d retw.n + ... + +40005988 <_c_0xfffff0c0>: +40005988: fff0c0 excw +4000598b: ff .byte 0xff + +4000598c <_X_slc_set_host_io_max_window>: +4000598c: 004136 entry a1, 32 +4000598f: feba31 l32r a3, 40005478 <_c_0x60017e00> +40005992: 0020c0 memw +40005995: fffc41 l32r a4, 40005988 <_c_0xfffff0c0> +40005998: 9d2322 l32i a2, a3, 0x274 +4000599b: 102240 and a2, a2, a4 +4000599e: 20a742 movi a4, 0x720 +400059a1: 202240 or a2, a2, a4 +400059a4: 0020c0 memw +400059a7: 9d6322 s32i a2, a3, 0x274 +400059aa: f01d retw.n + +400059ac <_X_slc_init_credit>: +400059ac: 004136 entry a1, 32 +400059af: ede381 l32r a8, 4000113c <_c_0x1000> +400059b2: feb191 l32r a9, 40005478 <_c_0x60017e00> +400059b5: 0020c0 memw +400059b8: 1a0c movi.n a10, 1 +400059ba: 956982 s32i a8, a9, 0x254 +400059bd: 000065 call8 400059c4 <_X_slc_add_credits> +400059c0: f01d retw.n + ... + +400059c4 <_X_slc_add_credits>: +400059c4: 004136 entry a1, 32 +400059c7: f52331 l32r a3, 40002e54 <_c_0x3fffdb00+0x4> +400059ca: feab41 l32r a4, 40005478 <_c_0x60017e00> +400059cd: 0ab230 depbits a3, a2, 0, 12 +400059d0: 0020c0 memw +400059d3: 956432 s32i a3, a4, 0x254 +400059d6: f01d retw.n + +400059d8 <_X_xtos_set_interrupt_handler_arg>: +400059d8: 004136 entry a1, 32 +400059db: 02e296 bltz a2, 40005a0d <_X_xtos_set_interrupt_handler_arg+0x35> +400059de: 2bc2e6 bgei a2, 32, 40005a0d <_X_xtos_set_interrupt_handler_arg+0x35> +400059e1: eb6851 l32r a5, 40000784 <_c_0x7fffffff+0x4> +400059e4: 1182d0 slli a8, a2, 3 +400059e7: 525a add.n a5, a2, a5 +400059e9: 000552 l8ui a5, a5, 0 +400059ec: eb5471 l32r a7, 4000073c <_c_0x80000000+0x4> +400059ef: 1a75f6 bgeui a5, 7, 40005a0d <_X_xtos_set_interrupt_handler_arg+0x35> +400059f2: c07780 sub a7, a7, a8 +400059f5: eb6481 l32r a8, 40000788 <_c_0x7fffffff+0x8> +400059f8: 3e2762 l32i a6, a7, 248 +400059fb: 23dc bnez.n a3, 40005a11 <_X_xtos_set_interrupt_handler_arg+0x39> +400059fd: 3e6782 s32i a8, a7, 248 +40005a00: 3f6722 s32i a2, a7, 252 +40005a03: c09680 sub a9, a6, a8 +40005a06: 020c movi.n a2, 0 +40005a08: 932690 movnez a2, a6, a9 +40005a0b: f01d retw.n +40005a0d: 020c movi.n a2, 0 +40005a0f: f01d retw.n +40005a11: 3e6732 s32i a3, a7, 248 +40005a14: 3f6742 s32i a4, a7, 252 +40005a17: c0a680 sub a10, a6, a8 +40005a1a: 020c movi.n a2, 0 +40005a1c: 9326a0 movnez a2, a6, a10 +40005a1f: f01d retw.n +40005a21: 000000 ill + +40005a24 <_X_xtos_set_interrupt_handler>: +40005a24: 004136 entry a1, 32 +40005a27: 03bd mov.n a11, a3 +40005a29: 02ad mov.n a10, a2 +40005a2b: 02cd mov.n a12, a2 +40005a2d: fffaa5 call8 400059d8 <_X_xtos_set_interrupt_handler_arg> +40005a30: 0a2d mov.n a2, a10 +40005a32: f01d retw.n + +40005a34 <_X_xtos_ints_on>: +40005a34: 002136 entry a1, 16 +40005a37: eb3f41 l32r a4, 40000734 <_c_0x3fffc210> +40005a3a: 006670 rsil a7, 6 +40005a3d: 0438 l32i.n a3, a4, 0 +40005a3f: 1468 l32i.n a6, a4, 4 +40005a41: 205320 or a5, a3, a2 +40005a44: 0459 s32i.n a5, a4, 0 +40005a46: 105560 and a5, a5, a6 +40005a49: 13e450 wsr.intenable a5 +40005a4c: 13e670 wsr.ps a7 +40005a4f: 002010 rsync +40005a52: 032d mov.n a2, a3 +40005a54: f01d retw.n + ... + +40005a58 <_X_xtos_ints_off>: +40005a58: 002136 entry a1, 16 +40005a5b: eb3641 l32r a4, 40000734 <_c_0x3fffc210> +40005a5e: 006670 rsil a7, 6 +40005a61: 0438 l32i.n a3, a4, 0 +40005a63: 1468 l32i.n a6, a4, 4 +40005a65: 205320 or a5, a3, a2 +40005a68: 305520 xor a5, a5, a2 +40005a6b: 0459 s32i.n a5, a4, 0 +40005a6d: 105560 and a5, a5, a6 +40005a70: 13e450 wsr.intenable a5 +40005a73: 13e670 wsr.ps a7 +40005a76: 002010 rsync +40005a79: 032d mov.n a2, a3 +40005a7b: f01d retw.n +40005a7d: 000000 ill + +40005a80 <_XX_xtos_exc_unk5a80>: +40005a80: 034820 rsr.windowbase a2 +40005a83: 221b addi.n a2, a2, 1 +40005a85: 400200 ssr a2 +40005a88: 034930 rsr.windowstart a3 +40005a8b: 912030 srl a2, a3 +40005a8e: a13300 sll a3, a3 +40005a91: 0bf3d6 bgez a3, 40005b54 <_XX_xtos_exc_unk5a80+0xd4> +40005a94: f53030 extui a3, a3, 16, 16 +40005a97: 202230 or a2, a2, a3 +40005a9a: 603020 neg a3, a2 +40005a9d: 103320 and a3, a3, a2 +40005aa0: 40f330 nsau a3, a3 +40005aa3: 401300 ssl a3 +40005aa6: 912020 srl a2, a2 +40005aa9: 134920 wsr.windowstart a2 +40005aac: 034820 rsr.windowbase a2 +40005aaf: 1fc222 addi a2, a2, 31 +40005ab2: c03230 sub a3, a2, a3 +40005ab5: 134830 wsr.windowbase a3 +40005ab8: 002010 rsync +40005abb: 034920 rsr.windowstart a2 +40005abe: 07a216 beqz a2, 40005b3c <_XX_xtos_exc_unk5a80+0xbc> +40005ac1: 08e207 bbsi a2, 0, 40005acd <_XX_xtos_exc_unk5a80+0x4d> +40005ac4: 19e217 bbsi a2, 1, 40005ae1 <_XX_xtos_exc_unk5a80+0x61> +40005ac7: 3ae227 bbsi a2, 2, 40005b05 <_XX_xtos_exc_unk5a80+0x85> +40005aca: 0022c6 j 40005b59 <_XX_xtos_exc_unk5a80+0xd9> +40005acd: f0c932 addi a3, a9, -16 +40005ad0: 0349 s32i.n a4, a3, 0 +40005ad2: 1359 s32i.n a5, a3, 4 +40005ad4: 2369 s32i.n a6, a3, 8 +40005ad6: 3379 s32i.n a7, a3, 12 +40005ad8: 416120 srli a6, a2, 1 +40005adb: 408010 rotw 1 +40005ade: fff706 j 40005abe <_XX_xtos_exc_unk5a80+0x3e> +40005ae1: f0cd32 addi a3, a13, -16 +40005ae4: 0349 s32i.n a4, a3, 0 +40005ae6: 1359 s32i.n a5, a3, 4 +40005ae8: 2369 s32i.n a6, a3, 8 +40005aea: 3379 s32i.n a7, a3, 12 +40005aec: f4c532 addi a3, a5, -12 +40005aef: 0338 l32i.n a3, a3, 0 +40005af1: e0c332 addi a3, a3, -32 +40005af4: 0389 s32i.n a8, a3, 0 +40005af6: 1399 s32i.n a9, a3, 4 +40005af8: 23a9 s32i.n a10, a3, 8 +40005afa: 33b9 s32i.n a11, a3, 12 +40005afc: 41a220 srli a10, a2, 2 +40005aff: 408020 rotw 2 +40005b02: ffee06 j 40005abe <_XX_xtos_exc_unk5a80+0x3e> +40005b05: 408010 rotw 1 +40005b08: f0cdd2 addi a13, a13, -16 +40005b0b: 0d09 s32i.n a0, a13, 0 +40005b0d: 1d19 s32i.n a1, a13, 4 +40005b0f: 2d29 s32i.n a2, a13, 8 +40005b11: 3d39 s32i.n a3, a13, 12 +40005b13: f4c132 addi a3, a1, -12 +40005b16: 0338 l32i.n a3, a3, 0 +40005b18: 10cdd2 addi a13, a13, 16 +40005b1b: d0c332 addi a3, a3, -48 +40005b1e: 0349 s32i.n a4, a3, 0 +40005b20: 1359 s32i.n a5, a3, 4 +40005b22: 2369 s32i.n a6, a3, 8 +40005b24: 3379 s32i.n a7, a3, 12 +40005b26: 4389 s32i.n a8, a3, 16 +40005b28: 5399 s32i.n a9, a3, 20 +40005b2a: 63a9 s32i.n a10, a3, 24 +40005b2c: 73b9 s32i.n a11, a3, 28 +40005b2e: 4080f0 rotw -1 +40005b31: 41e320 srli a14, a2, 3 +40005b34: 408030 rotw 3 +40005b37: ffe0c6 j 40005abe <_XX_xtos_exc_unk5a80+0x3e> +40005b3a: 100000 and a0, a0, a0 +40005b3d: 204080 or a4, a0, a8 +40005b40: 0348 l32i.n a4, a3, 0 +40005b42: 401200 ssl a2 +40005b45: 120c movi.n a2, 1 +40005b47: a12200 sll a2, a2 +40005b4a: 134920 wsr.windowstart a2 +40005b4d: 002010 rsync +40005b50: 020c movi.n a2, 0 +40005b52: f00d ret.n +40005b54: 120c movi.n a2, 1 +40005b56: f00d ret.n +40005b58: 22f000 orb b15, b0, b0 +40005b5b: c22211 l32r a1, 3fff63e4 <_start-0x9c1c> +40005b5e: 483001 l32r a0, 3ffd7c20 <_start-0x283e0> +40005b61: e2f703 excw +40005b64: 0b0a add.n a0, a11, a0 +40005b66: 22f033 excw +40005b69: 62f711 l32r a1, 3ffde748 <_start-0x218b8> +40005b6c: 3030f7 bltu a0, a15, 40005ba0 <_XX_xtos_exc_unk5b94+0xc> +40005b6f: 130034 excw +40005b72: 320040 orbc b0, b0, b4 +40005b75: 223011 l32r a1, 3ffce438 <_start-0x31bc8> +40005b78: 202081 l32r a8, 3ffcdbf8 <_start-0x32408> +40005b7b: 4920f4 mula.da.hl.ldinc m2, a0, m0, a15 +40005b7e: 033013 excw +40005b81: 022c03 excw +40005b84: c03230 sub a3, a2, a3 +40005b87: 134830 wsr.windowbase a3 +40005b8a: 002010 rsync +40005b8d: 220c movi.n a2, 2 +40005b8f: f00d ret.n +40005b91: 000000 ill + +40005b94 <_XX_xtos_exc_unk5b94>: +40005b94: 002136 entry a1, 16 +40005b97: eafd61 l32r a6, 4000078c <_c_0x7fffffff+0xc> +40005b9a: 03e650 rsr.ps a5 +40005b9d: 004d mov.n a4, a0 +40005b9f: 102560 and a2, a5, a6 +40005ba2: 223b addi.n a2, a2, 3 +40005ba4: 13e620 wsr.ps a2 +40005ba7: 002010 rsync +40005baa: ffed45 call0 40005a80 <_XX_xtos_exc_unk5a80> +40005bad: 040d mov.n a0, a4 +40005baf: 13e650 wsr.ps a5 +40005bb2: 002010 rsync +40005bb5: f01d retw.n + ... + +40005bb8 : +40005bb8: 002136 entry a1, 16 +40005bbb: 000282 l8ui a8, a2, 0 +40005bbe: 000392 l8ui a9, a3, 0 +40005bc1: 3a0c movi.n a10, 3 +40005bc3: 529897 bne a8, a9, 40005c19 +40005bc6: 20b230 or a11, a2, a3 +40005bc9: 530ba7 bnone a11, a10, 40005c20 +40005bcc: 30b230 xor a11, a2, a3 +40005bcf: 318ba7 bany a11, a10, 40005c04 +40005bd2: 221b addi.n a2, a2, 1 +40005bd4: 0b2816 beqz a8, 40005c8a +40005bd7: 331b addi.n a3, a3, 1 +40005bd9: 4302a7 bnone a2, a10, 40005c20 +40005bdc: 000282 l8ui a8, a2, 0 +40005bdf: 000392 l8ui a9, a3, 0 +40005be2: 221b addi.n a2, a2, 1 +40005be4: 319897 bne a8, a9, 40005c19 +40005be7: 09f816 beqz a8, 40005c8a +40005bea: 331b addi.n a3, a3, 1 +40005bec: 3002a7 bnone a2, a10, 40005c20 +40005bef: 000282 l8ui a8, a2, 0 +40005bf2: 000392 l8ui a9, a3, 0 +40005bf5: 221b addi.n a2, a2, 1 +40005bf7: 1e9897 bne a8, a9, 40005c19 +40005bfa: 08c816 beqz a8, 40005c8a +40005bfd: 331b addi.n a3, a3, 1 +40005bff: 000746 j 40005c20 +40005c02: 0c0000 excw +40005c05: 7608 l32i.n a0, a6, 28 +40005c07: 0f88 l32i.n a8, a15, 0 +40005c09: 000282 l8ui a8, a2, 0 +40005c0c: 000392 l8ui a9, a3, 0 +40005c0f: 221b addi.n a2, a2, 1 +40005c11: 049897 bne a8, a9, 40005c19 +40005c14: 331b addi.n a3, a3, 1 +40005c16: fff816 beqz a8, 40005c19 +40005c19: c02890 sub a2, a8, a9 +40005c1c: f01d retw.n +40005c1e: 410000 srli a0, a0, 0 +40005c21: eadc bnez.n a10, 40005c43 +40005c23: eadf71 l32r a7, 400007a0 <_c_0x00ff0000+0x8> +40005c26: 238076 loop a0, 40005c4d +40005c29: 0288 l32i.n a8, a2, 0 +40005c2b: 0398 l32i.n a9, a3, 0 +40005c2d: 1158f0 slli a5, a8, 1 +40005c30: 5c9897 bne a8, a9, 40005c90 +40005c33: 209850 or a9, a8, a5 +40005c36: 1ac977 bnall a9, a7, 40005c54 +40005c39: 1288 l32i.n a8, a2, 4 +40005c3b: 1398 l32i.n a9, a3, 4 +40005c3d: 1158f0 slli a5, a8, 1 +40005c40: 4c9897 bne a8, a9, 40005c90 +40005c43: 209850 or a9, a8, a5 +40005c46: 06c977 bnall a9, a7, 40005c50 +40005c49: 228b addi.n a2, a2, 8 +40005c4b: 338b addi.n a3, a3, 8 +40005c4d: fff546 j 40005c26 +40005c50: 224b addi.n a2, a2, 4 +40005c52: 334b addi.n a3, a3, 4 +40005c54: 320847 bnone a8, a4, 40005c8a +40005c57: eacf51 l32r a5, 40000794 <_c_0x7fffffff+0x14> +40005c5a: eacf61 l32r a6, 40000798 <_c_0x00ff0000> +40005c5d: 290857 bnone a8, a5, 40005c8a +40005c60: eacf71 l32r a7, 4000079c <_c_0x00ff0000+0x4> +40005c63: 230867 bnone a8, a6, 40005c8a +40005c66: 200877 bnone a8, a7, 40005c8a +40005c69: 224b addi.n a2, a2, 4 +40005c6b: 334b addi.n a3, a3, 4 +40005c6d: 198076 loop a0, 40005c8a +40005c70: 0288 l32i.n a8, a2, 0 +40005c72: 0398 l32i.n a9, a3, 0 +40005c74: 224b addi.n a2, a2, 4 +40005c76: 169897 bne a8, a9, 40005c90 +40005c79: 0d0847 bnone a8, a4, 40005c8a +40005c7c: 0a0857 bnone a8, a5, 40005c8a +40005c7f: 070867 bnone a8, a6, 40005c8a +40005c82: 040877 bnone a8, a7, 40005c8a +40005c85: 334b addi.n a3, a3, 4 +40005c87: fff886 j 40005c6d +40005c8a: 020c movi.n a2, 0 +40005c8c: f01d retw.n +40005c8e: 900000 addx2 a0, a0, a0 +40005c91: 3028 l32i.n a2, a0, 12 +40005c93: 218247 bany a2, a4, 40005cb8 +40005c96: eabf51 l32r a5, 40000794 <_c_0x7fffffff+0x14> +40005c99: ed0847 bnone a8, a4, 40005c8a +40005c9c: 248257 bany a2, a5, 40005cc4 +40005c9f: eabe61 l32r a6, 40000798 <_c_0x00ff0000> +40005ca2: e40857 bnone a8, a5, 40005c8a +40005ca5: 278267 bany a2, a6, 40005cd0 +40005ca8: de0867 bnone a8, a6, 40005c8a +40005cab: 75a880 extui a10, a8, 24, 8 +40005cae: 75b890 extui a11, a9, 24, 8 +40005cb1: c02ab0 sub a2, a10, a11 +40005cb4: f01d retw.n +40005cb6: 800000 add a0, a0, a0 +40005cb9: 9074a0 addx2 a7, a4, a10 +40005cbc: b074b0 addx8 a7, a4, a11 +40005cbf: c02a add.n a12, a0, a2 +40005cc1: f01d retw.n +40005cc3: a88000 excw +40005cc6: b89074 excw +40005cc9: 2ab074 excw +40005ccc: f01dc0 subx8 a1, a13, a12 +40005ccf: a08000 addx4 a8, a0, a0 +40005cd2: b09075 call12 3ffb65d8 <_start-0x49a28> +40005cd5: 2ab075 call12 400307dc <__bss_start+0x207dc> +40005cd8: f01dc0 subx8 a1, a13, a12 + ... + +40005cdc : +40005cdc: 002136 entry a1, 16 +40005cdf: 20a220 or a10, a2, a2 +40005ce2: ffa042 movi a4, 255 +40005ce5: eaab51 l32r a5, 40000794 <_c_0x7fffffff+0x14> +40005ce8: eaac61 l32r a6, 40000798 <_c_0x00ff0000> +40005ceb: eaac71 l32r a7, 4000079c <_c_0x00ff0000+0x4> +40005cee: 0be307 bbsi a3, 0, 40005cfd +40005cf1: 17e317 bbsi a3, 1, 40005d0c +40005cf4: 380c movi.n a8, 3 +40005cf6: 2a0a87 bnone a10, a8, 40005d24 +40005cf9: 0016c6 j 40005d58 +40005cfc: 038200 excw +40005cff: 331b00 clamps a1, a11, 7 +40005d02: 004a82 s8i a8, a10, 0 +40005d05: 889c beqz.n a8, 40005d21 +40005d07: aa1b addi.n a10, a10, 1 +40005d09: e76317 bbci a3, 1, 40005cf4 +40005d0c: 000382 l8ui a8, a3, 0 +40005d0f: 004a82 s8i a8, a10, 0 +40005d12: b88c beqz.n a8, 40005d21 +40005d14: 010382 l8ui a8, a3, 1 +40005d17: 332b addi.n a3, a3, 2 +40005d19: 014a82 s8i a8, a10, 1 +40005d1c: aa2b addi.n a10, a10, 2 +40005d1e: fd2856 bnez a8, 40005cf4 +40005d21: f01d retw.n +40005d23: 080c00 excw +40005d26: 138876 loop a8, 40005d3d +40005d29: 0388 l32i.n a8, a3, 0 +40005d2b: 334b addi.n a3, a3, 4 +40005d2d: 0f0847 bnone a8, a4, 40005d40 +40005d30: 110857 bnone a8, a5, 40005d45 +40005d33: 150867 bnone a8, a6, 40005d4c +40005d36: 0a89 s32i.n a8, a10, 0 +40005d38: 010877 bnone a8, a7, 40005d3d +40005d3b: aa4b addi.n a10, a10, 4 +40005d3d: f01d retw.n +40005d3f: 4a8200 depbits a0, a2, 4, 9 +40005d42: f01d00 subx8 a1, a13, a0 +40005d45: 005a82 s16i a8, a10, 0 +40005d48: f01d retw.n +40005d4a: 820000 mull a0, a0, a0 +40005d4d: 005a add.n a0, a0, a5 +40005d4f: 080c movi.n a8, 0 +40005d51: 024a82 s8i a8, a10, 2 +40005d54: f01d retw.n +40005d56: 0c0000 excw +40005d59: 7608 l32i.n a0, a6, 28 +40005d5b: 0c88 l32i.n a8, a12, 0 +40005d5d: 000382 l8ui a8, a3, 0 +40005d60: 331b addi.n a3, a3, 1 +40005d62: 004a82 s8i a8, a10, 0 +40005d65: aa1b addi.n a10, a10, 1 +40005d67: fff816 beqz a8, 40005d6a +40005d6a: f01d retw.n + +40005d6c : +40005d6c: 002136 entry a1, 16 +40005d6f: fcc232 addi a3, a2, -4 +40005d72: ffa042 movi a4, 255 +40005d75: ea8751 l32r a5, 40000794 <_c_0x7fffffff+0x14> +40005d78: ea8861 l32r a6, 40000798 <_c_0x00ff0000> +40005d7b: ea8871 l32r a7, 4000079c <_c_0x00ff0000+0x4> +40005d7e: 06e207 bbsi a2, 0, 40005d88 +40005d81: 0de217 bbsi a2, 1, 40005d92 +40005d84: 000706 j 40005da4 +40005d87: 038200 excw +40005d8a: 331b04 excw +40005d8d: 88ac beqz.n a8, 40005db9 +40005d8f: 116317 bbci a3, 1, 40005da4 +40005d92: 332b addi.n a3, a3, 2 +40005d94: 0388 l32i.n a8, a3, 0 +40005d96: 2e0867 bnone a8, a6, 40005dc8 +40005d99: 078877 bany a8, a7, 40005da4 +40005d9c: 333b addi.n a3, a3, 3 +40005d9e: c02320 sub a2, a3, a2 +40005da1: f01d retw.n +40005da3: 080c00 excw +40005da6: 0f8876 loop a8, 40005db9 +40005da9: 1388 l32i.n a8, a3, 4 +40005dab: 334b addi.n a3, a3, 4 +40005dad: 0a0847 bnone a8, a4, 40005dbb +40005db0: 0c0857 bnone a8, a5, 40005dc0 +40005db3: 110867 bnone a8, a6, 40005dc8 +40005db6: ff0877 bnone a8, a7, 40005db9 +40005db9: 333b addi.n a3, a3, 3 +40005dbb: c02320 sub a2, a3, a2 +40005dbe: f01d retw.n +40005dc0: 331b addi.n a3, a3, 1 +40005dc2: c02320 sub a2, a3, a2 +40005dc5: f01d retw.n +40005dc7: 332b00 clamps a2, a11, 7 +40005dca: c02320 sub a2, a3, a2 +40005dcd: f01d retw.n + ... + +40005dd0 : +40005dd0: 004136 entry a1, 32 +40005dd3: a4bc beqz.n a4, 40005e11 +40005dd5: 205320 or a5, a3, a2 +40005dd8: 145050 extui a5, a5, 0, 2 +40005ddb: 041516 beqz a5, 40005e20 +40005dde: 000272 l8ui a7, a2, 0 +40005de1: 000382 l8ui a8, a3, 0 +40005de4: 049d mov.n a9, a4 +40005de6: 440b addi.n a4, a4, -1 +40005de8: 09ac beqz.n a9, 40005e0c +40005dea: 1e9787 bne a7, a8, 40005e0c +40005ded: 168076 loop a0, 40005e07 +40005df0: 046d mov.n a6, a4 +40005df2: b49c beqz.n a4, 40005e11 +40005df4: 979c beqz.n a7, 40005e11 +40005df6: 440b addi.n a4, a4, -1 +40005df8: 331b addi.n a3, a3, 1 +40005dfa: 221b addi.n a2, a2, 1 +40005dfc: 569c beqz.n a6, 40005e15 +40005dfe: 000272 l8ui a7, a2, 0 +40005e01: 000382 l8ui a8, a3, 0 +40005e04: 049877 bne a8, a7, 40005e0c +40005e07: fff886 j 40005ded +40005e0a: 800000 add a0, a0, a0 +40005e0d: 1dc027 bnall a0, a2, 40005e2e +40005e10: 020cf0 andb b0, b12, b15 +40005e13: f01d retw.n +40005e15: 000352 l8ui a5, a3, 0 +40005e18: 000222 l8ui a2, a2, 0 +40005e1b: c02250 sub a2, a2, a5 +40005e1e: f01d retw.n +40005e20: ba44b6 bltui a4, 4, 40005dde +40005e23: 0278 l32i.n a7, a2, 0 +40005e25: 002362 l32i a6, a3, 0 +40005e28: b29677 bne a6, a7, 40005dde +40005e2b: ea5e91 l32r a9, 400007a4 <_c_0x00ff0000+0xc> +40005e2e: ffafa2 movi a10, -1 +40005e31: ea5d81 l32r a8, 400007a8 <_c_0x00ff0000+0x10> +40005e34: fcc442 addi a4, a4, -4 +40005e37: fd6416 beqz a4, 40005e11 +40005e3a: 30c7a0 xor a12, a7, a10 +40005e3d: b78a add.n a11, a7, a8 +40005e3f: 10bbc0 and a11, a11, a12 +40005e42: cb89b7 bany a9, a11, 40005e11 +40005e45: 334b addi.n a3, a3, 4 +40005e47: 224b addi.n a2, a2, 4 +40005e49: 9144b6 bltui a4, 4, 40005dde +40005e4c: 0278 l32i.n a7, a2, 0 +40005e4e: 03d8 l32i.n a13, a3, 0 +40005e50: e01d77 beq a13, a7, 40005e34 +40005e53: ffe1c6 j 40005dde +40005e56: 820000 mull a0, a0, a0 +40005e59: 1b0003 excw +40005e5c: 4a8233 excw +40005e5f: 440b00 extui a0, a0, 11, 5 +40005e62: 74ac beqz.n a4, 40005e8d +40005e64: aa1b addi.n a10, a10, 1 +40005e66: 048816 beqz a8, 40005eb2 +40005e69: 3d6317 bbci a3, 1, 40005eaa +40005e6c: 000382 l8ui a8, a3, 0 +40005e6f: 440b addi.n a4, a4, -1 +40005e71: 004a82 s8i a8, a10, 0 +40005e74: 549c beqz.n a4, 40005e8d +40005e76: aa1b addi.n a10, a10, 1 +40005e78: 68bc beqz.n a8, 40005eb2 +40005e7a: 010382 l8ui a8, a3, 1 +40005e7d: 332b addi.n a3, a3, 2 +40005e7f: 004a82 s8i a8, a10, 0 +40005e82: 440b addi.n a4, a4, -1 +40005e84: 548c beqz.n a4, 40005e8d +40005e86: aa1b addi.n a10, a10, 1 +40005e88: e8dc bnez.n a8, 40005eaa +40005e8a: 000906 j 40005eb2 +40005e8d: f01d retw.n + ... + +40005e90 : +40005e90: 002136 entry a1, 16 +40005e93: 02ad mov.n a10, a2 +40005e95: ff4416 beqz a4, 40005e8d +40005e98: ffa0b2 movi a11, 255 +40005e9b: ea3e51 l32r a5, 40000794 <_c_0x7fffffff+0x14> +40005e9e: ea3e61 l32r a6, 40000798 <_c_0x00ff0000> +40005ea1: ea3e71 l32r a7, 4000079c <_c_0x00ff0000+0x4> +40005ea4: b0e307 bbsi a3, 0, 40005e58 +40005ea7: c1e317 bbsi a3, 1, 40005e6c +40005eaa: 380c movi.n a8, 3 +40005eac: 540a87 bnone a10, a8, 40005f04 +40005eaf: 002746 j 40005f50 +40005eb2: 090c movi.n a9, 0 +40005eb4: 28ea07 bbsi a10, 0, 40005ee0 +40005eb7: 32ea17 bbsi a10, 1, 40005eed +40005eba: 1344a6 blti a4, 4, 40005ed1 +40005ebd: 218240 srai a8, a4, 2 +40005ec0: f03d nop.n +40005ec2: 038876 loop a8, 40005ec9 +40005ec5: 0a99 s32i.n a9, a10, 0 +40005ec7: aa4b addi.n a10, a10, 4 +40005ec9: 1188e0 slli a8, a8, 2 +40005ecc: c04480 sub a4, a4, a8 +40005ecf: 948c beqz.n a4, 40005edc +40005ed1: 004a92 s8i a9, a10, 0 +40005ed4: 440b addi.n a4, a4, -1 +40005ed6: 01caa2 addi a10, a10, 1 +40005ed9: ff4456 bnez a4, 40005ed1 +40005edc: f01d retw.n +40005ede: 920000 excw +40005ee1: 004a add.n a0, a0, a4 +40005ee3: 440b addi.n a4, a4, -1 +40005ee5: ff3416 beqz a4, 40005edc +40005ee8: aa1b addi.n a10, a10, 1 +40005eea: cc6a17 bbci a10, 1, 40005eba +40005eed: 004a92 s8i a9, a10, 0 +40005ef0: 440b addi.n a4, a4, -1 +40005ef2: fe6416 beqz a4, 40005edc +40005ef5: 014a92 s8i a9, a10, 1 +40005ef8: 440b addi.n a4, a4, -1 +40005efa: fde416 beqz a4, 40005edc +40005efd: aa2b addi.n a10, a10, 2 +40005eff: ffedc6 j 40005eba +40005f02: 0c0000 excw +40005f05: 7608 l32i.n a0, a6, 28 +40005f07: 1988 l32i.n a8, a9, 4 +40005f09: 4354a6 blti a4, 5, 40005f50 +40005f0c: 0388 l32i.n a8, a3, 0 +40005f0e: 334b addi.n a3, a3, 4 +40005f10: 1408b7 bnone a8, a11, 40005f28 +40005f13: 1d0857 bnone a8, a5, 40005f34 +40005f16: 260867 bnone a8, a6, 40005f40 +40005f19: 0a89 s32i.n a8, a10, 0 +40005f1b: fcc442 addi a4, a4, -4 +40005f1e: aa4b addi.n a10, a10, 4 +40005f20: 8e0877 bnone a8, a7, 40005eb2 +40005f23: ffe2c6 j 40005eb2 +40005f26: 820000 mull a0, a0, a0 +40005f29: 004a add.n a0, a0, a4 +40005f2b: 440b addi.n a4, a4, -1 +40005f2d: aa1b addi.n a10, a10, 1 +40005f2f: ffdfc6 j 40005eb2 +40005f32: 820000 mull a0, a0, a0 +40005f35: 005a add.n a0, a0, a5 +40005f37: fec442 addi a4, a4, -2 +40005f3a: aa2b addi.n a10, a10, 2 +40005f3c: ffdc86 j 40005eb2 +40005f3f: 5a8200 depbits a0, a2, 5, 9 +40005f42: 080c00 excw +40005f45: 024a82 s8i a8, a10, 2 +40005f48: fdc442 addi a4, a4, -3 +40005f4b: aa3b addi.n a10, a10, 3 +40005f4d: ffd846 j 40005eb2 +40005f50: 080c movi.n a8, 0 +40005f52: 108876 loop a8, 40005f66 +40005f55: 000382 l8ui a8, a3, 0 +40005f58: 331b addi.n a3, a3, 1 +40005f5a: 004a82 s8i a8, a10, 0 +40005f5d: 440b addi.n a4, a4, -1 +40005f5f: 648c beqz.n a4, 40005f69 +40005f61: aa1b addi.n a10, a10, 1 +40005f63: fff816 beqz a8, 40005f66 +40005f66: ffd206 j 40005eb2 +40005f69: f01d retw.n + ... + +40005f6c : +40005f6c: 090136 entry a1, 0x480 +40005f6f: 50c172 addi a7, a1, 80 +40005f72: 025d mov.n a5, a2 +40005f74: 038d mov.n a8, a3 +40005f76: 20c220 or a12, a2, a2 +40005f79: 0003b2 l8ui a11, a3, 0 +40005f7c: 0002e2 l8ui a14, a2, 0 +40005f7f: 209bb0 or a9, a11, a11 +40005f82: eebc beqz.n a14, 40005fc4 +40005f84: cbac beqz.n a11, 40005fb4 +40005f86: 01a0a2 movi a10, 1 +40005f89: 228076 loop a0, 40005faf +40005f8c: cc1b addi.n a12, a12, 1 +40005f8e: 1d0c movi.n a13, 1 +40005f90: 74f0e0 extui a15, a14, 0, 8 +40005f93: 040c movi.n a4, 0 +40005f95: 74e090 extui a14, a9, 0, 8 +40005f98: c0eef0 sub a14, a14, a15 +40005f9b: 834de0 moveqz a4, a13, a14 +40005f9e: 10aa40 and a10, a10, a4 +40005fa1: 000ce2 l8ui a14, a12, 0 +40005fa4: 481b addi.n a4, a8, 1 +40005fa6: 048d mov.n a8, a4 +40005fa8: ce8c beqz.n a14, 40005fb8 +40005faa: 000492 l8ui a9, a4, 0 +40005fad: 198c beqz.n a9, 40005fb2 +40005faf: fff586 j 40005f89 +40005fb2: 4a9c beqz.n a10, 40005fca +40005fb4: 052d mov.n a2, a5 +40005fb6: f01d retw.n +40005fb8: 000482 l8ui a8, a4, 0 +40005fbb: ff3816 beqz a8, 40005fb2 +40005fbe: 020c movi.n a2, 0 +40005fc0: f01d retw.n +40005fc2: 560000 excw +40005fc5: ff6b addi.n a15, a15, 6 +40005fc7: fffa46 j 40005fb4 +40005fca: 01c5a2 addi a10, a5, 1 +40005fcd: 005aa5 call8 40006578 +40005fd0: 202aa0 or a2, a10, a10 +40005fd3: 271a16 beqz a10, 40006248 +40005fd6: c04430 sub a4, a4, a3 +40005fd9: ffc492 addi a9, a4, -1 +40005fdc: 268916 beqz a9, 40006248 +40005fdf: 845a add.n a8, a4, a5 +40005fe1: 0538a7 bltu a8, a10, 40005fea +40005fe4: c088a0 sub a8, a8, a10 +40005fe7: 000046 j 40005fec +40005fea: 180c movi.n a8, 1 +40005fec: 18c4f6 bgeui a4, 32, 40006008 +40005fef: 085d mov.n a5, a8 +40005ff1: 0224f6 bgeui a4, 2, 40005ff7 +40005ff4: 002f06 j 400060b4 +40005ff7: 190c movi.n a9, 1 +40005ff9: 180c movi.n a8, 1 +40005ffb: 1f0c movi.n a15, 1 +40005ffd: 0a0c movi.n a10, 0 +40005fff: f67c movi.n a6, -1 +40006001: 5159 s32i.n a5, a1, 20 +40006003: 0008c6 j 4000602a +40006006: 820000 mull a0, a0, a0 +40006009: f61061 l32r a6, 4000384c <_X_recv_packet+0x10> +4000600c: 860224 excw +4000600f: 0c0067 bnone a0, a6, 4000601f +40006012: 0c19 s32i.n a1, a12, 0 +40006014: 0c18 l32i.n a1, a12, 0 +40006016: 1f .byte 0x1f +40006017: 0a0c movi.n a10, 0 +40006019: f67c movi.n a6, -1 +4000601b: 0042c6 j 4000612a +4000601e: 09ad mov.n a10, a9 +40006020: c0f960 sub a15, a9, a6 +40006023: 180c movi.n a8, 1 +40006025: 98aa add.n a9, a8, a10 +40006027: 2db947 bgeu a9, a4, 40006058 +4000602a: b86a add.n a11, a8, a6 +4000602c: c93a add.n a12, a9, a3 +4000602e: 000cc2 l8ui a12, a12, 0 +40006031: b3ba add.n a11, a3, a11 +40006033: 000bb2 l8ui a11, a11, 0 +40006036: e43cb7 bltu a12, a11, 4000601e +40006039: 0f9cb7 bne a12, a11, 4000604c +4000603c: 0418f7 beq a8, a15, 40006044 +4000603f: 881b addi.n a8, a8, 1 +40006041: fff806 j 40006025 +40006044: afaa add.n a10, a15, a10 +40006046: 180c movi.n a8, 1 +40006048: fff646 j 40006025 +4000604b: 180c00 excw +4000604e: 0a6d mov.n a6, a10 +40006050: 1f0c movi.n a15, 1 +40006052: aa1b addi.n a10, a10, 1 +40006054: fff346 j 40006025 +40006057: 190c00 excw +4000605a: f57c movi.n a5, -1 +4000605c: 180c movi.n a8, 1 +4000605e: 11f9 s32i.n a15, a1, 4 +40006060: 0a0c movi.n a10, 0 +40006062: 1f0c movi.n a15, 1 +40006064: 000346 j 40006075 +40006067: ad0000 excw +4000606a: 5009 s32i.n a0, a0, 20 +4000606c: c0f9 s32i.n a15, a0, 48 +4000606e: 180c movi.n a8, 1 +40006070: 98aa add.n a9, a8, a10 +40006072: 2eb947 bgeu a9, a4, 400060a4 +40006075: b58a add.n a11, a5, a8 +40006077: c93a add.n a12, a9, a3 +40006079: 000cc2 l8ui a12, a12, 0 +4000607c: b3ba add.n a11, a3, a11 +4000607e: 000bb2 l8ui a11, a11, 0 +40006081: e43bc7 bltu a11, a12, 40006069 +40006084: 109cb7 bne a12, a11, 40006098 +40006087: 0518f7 beq a8, a15, 40006090 +4000608a: 881b addi.n a8, a8, 1 +4000608c: fff806 j 40006070 +4000608f: afaa00 excw +40006092: 180c movi.n a8, 1 +40006094: fff606 j 40006070 +40006097: 180c00 excw +4000609a: 0a5d mov.n a5, a10 +4000609c: 1f0c movi.n a15, 1 +4000609e: aa1b addi.n a10, a10, 1 +400060a0: fff306 j 40006070 +400060a3: 961b00 excw +400060a6: c51b addi.n a12, a5, 1 +400060a8: 5158 l32i.n a5, a1, 20 +400060aa: 0abc97 bgeu a12, a9, 400060b8 +400060ad: 1168 l32i.n a6, a1, 4 +400060af: 09cd mov.n a12, a9 +400060b1: 000146 j 400060ba +400060b4: 0c0c movi.n a12, 0 +400060b6: 1f0c movi.n a15, 1 +400060b8: 0f6d mov.n a6, a15 +400060ba: 81c9 s32i.n a12, a1, 32 +400060bc: 03ad mov.n a10, a3 +400060be: b36a add.n a11, a3, a6 +400060c0: 0082a5 call8 400068ec +400060c3: 5159 s32i.n a5, a1, 20 +400060c5: 213a16 beqz a10, 400062dc +400060c8: 81e8 l32i.n a14, a1, 32 +400060ca: 6e0b addi.n a6, a14, -1 +400060cc: c0f4e0 sub a15, a4, a14 +400060cf: 6169 s32i.n a6, a1, 24 +400060d1: 00a062 movi a6, 0 +400060d4: 73eef0 maxu a14, a14, a15 +400060d7: 21e9 s32i.n a14, a1, 8 +400060d9: a25a add.n a10, a2, a5 +400060db: c46a add.n a12, a4, a6 +400060dd: 00a0b2 movi a11, 0 +400060e0: 0761c2 s32i a12, a1, 28 +400060e3: c0cc50 sub a12, a12, a5 +400060e6: 0041a5 call8 40006500 +400060e9: 15da56 bnez a10, 4000624a +400060ec: 71d8 l32i.n a13, a1, 28 +400060ee: 81e8 l32i.n a14, a1, 32 +400060f0: 0d5d mov.n a5, a13 +400060f2: 154d16 beqz a13, 4000624a +400060f5: 0e8d mov.n a8, a14 +400060f7: 11be47 bgeu a14, a4, 4000610c +400060fa: fe6a add.n a15, a14, a6 +400060fc: 9e3a add.n a9, a14, a3 +400060fe: 000992 l8ui a9, a9, 0 +40006101: f2fa add.n a15, a2, a15 +40006103: 000ff2 l8ui a15, a15, 0 +40006106: c0ff90 sub a15, a15, a9 +40006109: 154f16 beqz a15, 40006261 +4000610c: 023847 bltu a8, a4, 40006112 +4000610f: 0054c6 j 40006266 +40006112: 81a8 l32i.n a10, a1, 32 +40006114: c0a8a0 sub a10, a8, a10 +40006117: aa1b addi.n a10, a10, 1 +40006119: 66aa add.n a6, a6, a10 +4000611b: ffee86 j 400060d9 +4000611e: 09ad mov.n a10, a9 +40006120: c0f960 sub a15, a9, a6 +40006123: 180c movi.n a8, 1 +40006125: 98aa add.n a9, a8, a10 +40006127: 2db947 bgeu a9, a4, 40006158 +4000612a: b86a add.n a11, a8, a6 +4000612c: c93a add.n a12, a9, a3 +4000612e: 000cc2 l8ui a12, a12, 0 +40006131: b3ba add.n a11, a3, a11 +40006133: 000bb2 l8ui a11, a11, 0 +40006136: e43cb7 bltu a12, a11, 4000611e +40006139: 0f9cb7 bne a12, a11, 4000614c +4000613c: 0418f7 beq a8, a15, 40006144 +4000613f: 881b addi.n a8, a8, 1 +40006141: fff806 j 40006125 +40006144: afaa add.n a10, a15, a10 +40006146: 180c movi.n a8, 1 +40006148: fff646 j 40006125 +4000614b: 180c00 excw +4000614e: 0a6d mov.n a6, a10 +40006150: 1f0c movi.n a15, 1 +40006152: aa1b addi.n a10, a10, 1 +40006154: fff346 j 40006125 +40006157: 190c00 excw +4000615a: f57c movi.n a5, -1 +4000615c: 180c movi.n a8, 1 +4000615e: f1f9 s32i.n a15, a1, 60 +40006160: 0a0c movi.n a10, 0 +40006162: 1f0c movi.n a15, 1 +40006164: 000346 j 40006175 +40006167: ad0000 excw +4000616a: 5009 s32i.n a0, a0, 20 +4000616c: c0f9 s32i.n a15, a0, 48 +4000616e: 180c movi.n a8, 1 +40006170: 98aa add.n a9, a8, a10 +40006172: 2eb947 bgeu a9, a4, 400061a4 +40006175: b58a add.n a11, a5, a8 +40006177: c93a add.n a12, a9, a3 +40006179: 000cc2 l8ui a12, a12, 0 +4000617c: b3ba add.n a11, a3, a11 +4000617e: 000bb2 l8ui a11, a11, 0 +40006181: e43bc7 bltu a11, a12, 40006169 +40006184: 109cb7 bne a12, a11, 40006198 +40006187: 0518f7 beq a8, a15, 40006190 +4000618a: 881b addi.n a8, a8, 1 +4000618c: fff806 j 40006170 +4000618f: afaa00 excw +40006192: 180c movi.n a8, 1 +40006194: fff606 j 40006170 +40006197: 180c00 excw +4000619a: 0a5d mov.n a5, a10 +4000619c: 1f0c movi.n a15, 1 +4000619e: aa1b addi.n a10, a10, 1 +400061a0: fff306 j 40006170 +400061a3: 961b00 excw +400061a6: c51b addi.n a12, a5, 1 +400061a8: 08bc97 bgeu a12, a9, 400061b4 +400061ab: 09ed mov.n a14, a9 +400061ad: 0001c6 j 400061b8 +400061b0: 0c0c movi.n a12, 0 +400061b2: 1f0c movi.n a15, 1 +400061b4: 0ced mov.n a14, a12 +400061b6: f1f9 s32i.n a15, a1, 60 +400061b8: 0a2c movi.n a10, 32 +400061ba: 10c762 addi a6, a7, 16 +400061bd: 068d mov.n a8, a6 +400061bf: 12aa76 loopgtz a10, 400061d5 +400061c2: 0849 s32i.n a4, a8, 0 +400061c4: 1849 s32i.n a4, a8, 4 +400061c6: 2849 s32i.n a4, a8, 8 +400061c8: 3849 s32i.n a4, a8, 12 +400061ca: 4849 s32i.n a4, a8, 16 +400061cc: 5849 s32i.n a4, a8, 20 +400061ce: 6849 s32i.n a4, a8, 24 +400061d0: 7849 s32i.n a4, a8, 28 +400061d2: 20c882 addi a8, a8, 32 +400061d5: 080c movi.n a8, 0 +400061d7: a40b addi.n a10, a4, -1 +400061d9: e1a9 s32i.n a10, a1, 56 +400061db: 10a476 loopgtz a4, 400061ef +400061de: 938a add.n a9, a3, a8 +400061e0: 000992 l8ui a9, a9, 0 +400061e3: 01c882 addi a8, a8, 1 +400061e6: a09960 addx4 a9, a9, a6 +400061e9: 0069a2 s32i a10, a9, 0 +400061ec: ffcaa2 addi a10, a10, -1 +400061ef: 0ecd mov.n a12, a14 +400061f1: d1e9 s32i.n a14, a1, 52 +400061f3: f1b8 l32i.n a11, a1, 60 +400061f5: 03ad mov.n a10, a3 +400061f7: b3ba add.n a11, a3, a11 +400061f9: 006f25 call8 400068ec +400061fc: 174a16 beqz a10, 40006374 +400061ff: d1d8 l32i.n a13, a1, 52 +40006201: 050c movi.n a5, 0 +40006203: c0e4d0 sub a14, a4, a13 +40006206: 73ede0 maxu a14, a13, a14 +40006209: a1e9 s32i.n a14, a1, 40 +4000620b: dd0b addi.n a13, a13, -1 +4000620d: b1d9 s32i.n a13, a1, 44 +4000620f: 000686 j 4000622d +40006212: c1f8 l32i.n a15, a1, 48 +40006214: 1061f2 s32i a15, a1, 64 +40006217: 9fac beqz.n a15, 40006244 +40006219: 8092f0 add a9, a2, a15 +4000621c: ffd992 addmi a9, a9, 0xffffff00 +4000621f: ff0992 l8ui a9, a9, 255 +40006222: a09960 addx4 a9, a9, a6 +40006225: 0998 l32i.n a9, a9, 0 +40006227: 07a916 beqz a9, 400062a5 +4000622a: 805950 add a5, a9, a5 +4000622d: 00a0b2 movi a11, 0 +40006230: 1021d2 l32i a13, a1, 64 +40006233: 80c450 add a12, a4, a5 +40006236: 0c61c2 s32i a12, a1, 48 +40006239: a2da add.n a10, a2, a13 +4000623b: c0ccd0 sub a12, a12, a13 +4000623e: 002c25 call8 40006500 +40006241: fcda16 beqz a10, 40006212 +40006244: 020c movi.n a2, 0 +40006246: f01d retw.n +40006248: f01d retw.n +4000624a: 020c movi.n a2, 0 +4000624c: f01d retw.n +4000624e: e86a add.n a14, a8, a6 +40006250: f38a add.n a15, a3, a8 +40006252: 000ff2 l8ui a15, a15, 0 +40006255: 80e2e0 add a14, a2, a14 +40006258: 000ee2 l8ui a14, a14, 0 +4000625b: c0eef0 sub a14, a14, a15 +4000625e: eaae56 bnez a14, 4000610c +40006261: 881b addi.n a8, a8, 1 +40006263: e73847 bltu a8, a4, 4000624e +40006266: 8198 l32i.n a9, a1, 32 +40006268: 6188 l32i.n a8, a1, 24 +4000626a: 69ac beqz.n a9, 40006294 +4000626c: a86a add.n a10, a8, a6 +4000626e: b83a add.n a11, a8, a3 +40006270: 000bb2 l8ui a11, a11, 0 +40006273: 80a2a0 add a10, a2, a10 +40006276: 000aa2 l8ui a10, a10, 0 +40006279: 179ab7 bne a10, a11, 40006294 +4000627c: 126162 s32i a6, a1, 72 +4000627f: 880b addi.n a8, a8, -1 +40006281: 4f0826 beqi a8, -1, 400062d4 +40006284: c86a add.n a12, a8, a6 +40006286: d38a add.n a13, a3, a8 +40006288: 000dd2 l8ui a13, a13, 0 +4000628b: 80c2c0 add a12, a2, a12 +4000628e: 000cc2 l8ui a12, a12, 0 +40006291: e71cd7 beq a12, a13, 4000627c +40006294: 126162 s32i a6, a1, 72 +40006297: 390826 beqi a8, -1, 400062d4 +4000629a: 21e8 l32i.n a14, a1, 8 +4000629c: ee1b addi.n a14, a14, 1 +4000629e: 66ea add.n a6, a6, a14 +400062a0: ff8d46 j 400060d9 +400062a3: f80000 excw +400062a6: e198d1 l32r a13, 3fffe908 <_start-0x16f8> +400062a9: 0f8d mov.n a8, a15 +400062ab: 11bf97 bgeu a15, a9, 400062c0 +400062ae: af5a add.n a10, a15, a5 +400062b0: bf3a add.n a11, a15, a3 +400062b2: 000bb2 l8ui a11, a11, 0 +400062b5: a2aa add.n a10, a2, a10 +400062b7: 000aa2 l8ui a10, a10, 0 +400062ba: c0aab0 sub a10, a10, a11 +400062bd: 145a16 beqz a10, 40006406 +400062c0: e1c8 l32i.n a12, a1, 56 +400062c2: 0238c7 bltu a8, a12, 400062c8 +400062c5: 005106 j 4000640d +400062c8: d1d8 l32i.n a13, a1, 52 +400062ca: c0d8d0 sub a13, a8, a13 +400062cd: dd1b addi.n a13, a13, 1 +400062cf: 55da add.n a5, a5, a13 +400062d1: ffd606 j 4000622d +400062d4: 1221e2 l32i a14, a1, 72 +400062d7: 22ea add.n a2, a2, a14 +400062d9: f01d retw.n +400062db: 714900 excw +400062de: 090c movi.n a9, 0 +400062e0: 8158 l32i.n a5, a1, 32 +400062e2: c0f460 sub a15, a4, a6 +400062e5: 31f9 s32i.n a15, a1, 12 +400062e7: 550b addi.n a5, a5, -1 +400062e9: 6159 s32i.n a5, a1, 24 +400062eb: 050c movi.n a5, 0 +400062ed: 00a0b2 movi a11, 0 +400062f0: 116192 s32i a9, a1, 68 +400062f3: 0521d2 l32i a13, a1, 20 +400062f6: 0721c2 l32i a12, a1, 28 +400062f9: a2da add.n a10, a2, a13 +400062fb: c0ccd0 sub a12, a12, a13 +400062fe: 002025 call8 40006500 +40006301: 112192 l32i a9, a1, 68 +40006304: f42a56 bnez a10, 4000624a +40006307: 71e8 l32i.n a14, a1, 28 +40006309: 51e9 s32i.n a14, a1, 20 +4000630b: f3be16 beqz a14, 4000624a +4000630e: 8188 l32i.n a8, a1, 32 +40006310: 738890 maxu a8, a8, a9 +40006313: 2ab847 bgeu a8, a4, 40006341 +40006316: a85a add.n a10, a8, a5 +40006318: b83a add.n a11, a8, a3 +4000631a: 000bb2 l8ui a11, a11, 0 +4000631d: a2aa add.n a10, a2, a10 +4000631f: 000aa2 l8ui a10, a10, 0 +40006322: c0aab0 sub a10, a10, a11 +40006325: 3a9c beqz.n a10, 4000633c +40006327: 004746 j 40006448 +4000632a: c85a add.n a12, a8, a5 +4000632c: d38a add.n a13, a3, a8 +4000632e: 000dd2 l8ui a13, a13, 0 +40006331: c2ca add.n a12, a2, a12 +40006333: 000cc2 l8ui a12, a12, 0 +40006336: c0ccd0 sub a12, a12, a13 +40006339: 10bc56 bnez a12, 40006448 +4000633c: 881b addi.n a8, a8, 1 +4000633e: e83847 bltu a8, a4, 4000632a +40006341: 81e8 l32i.n a14, a1, 32 +40006343: 6188 l32i.n a8, a1, 24 +40006345: 11b9e7 bgeu a9, a14, 4000635a +40006348: f85a add.n a15, a8, a5 +4000634a: a83a add.n a10, a8, a3 +4000634c: 000aa2 l8ui a10, a10, 0 +4000634f: f2fa add.n a15, a2, a15 +40006351: 000ff2 l8ui a15, a15, 0 +40006354: c0ffa0 sub a15, a15, a10 +40006357: 117f16 beqz a15, 40006472 +4000635a: 81a8 l32i.n a10, a1, 32 +4000635c: 126152 s32i a5, a1, 72 +4000635f: b91b addi.n a11, a9, 1 +40006361: 02bab7 bgeu a10, a11, 40006367 +40006364: ffdb06 j 400062d4 +40006367: 556a add.n a5, a5, a6 +40006369: 71c8 l32i.n a12, a1, 28 +4000636b: 3198 l32i.n a9, a1, 12 +4000636d: cc6a add.n a12, a12, a6 +4000636f: 71c9 s32i.n a12, a1, 28 +40006371: ffde06 j 400062ed +40006374: c149 s32i.n a4, a1, 48 +40006376: 050c movi.n a5, 0 +40006378: d1f8 l32i.n a15, a1, 52 +4000637a: f1e8 l32i.n a14, a1, 60 +4000637c: 0d0c movi.n a13, 0 +4000637e: 91d9 s32i.n a13, a1, 36 +40006380: c0e4e0 sub a14, a4, a14 +40006383: ff0b addi.n a15, a15, -1 +40006385: b1f9 s32i.n a15, a1, 44 +40006387: 41e9 s32i.n a14, a1, 16 +40006389: 000546 j 400063a2 +4000638c: 919800 excw +4000638f: 598c beqz.n a9, 40006398 +40006391: f1a8 l32i.n a10, a1, 60 +40006393: 01b8a7 bgeu a8, a10, 40006398 +40006396: 4188 l32i.n a8, a1, 16 +40006398: 585a add.n a5, a8, a5 +4000639a: 0c0c movi.n a12, 0 +4000639c: 91c9 s32i.n a12, a1, 36 +4000639e: b45a add.n a11, a4, a5 +400063a0: c1b9 s32i.n a11, a1, 48 +400063a2: 00a0b2 movi a11, 0 +400063a5: 1021d2 l32i a13, a1, 64 +400063a8: 0c21c2 l32i a12, a1, 48 +400063ab: 80a2d0 add a10, a2, a13 +400063ae: c0ccd0 sub a12, a12, a13 +400063b1: 0014e5 call8 40006500 +400063b4: e8ca56 bnez a10, 40006244 +400063b7: c1e8 l32i.n a14, a1, 48 +400063b9: 1061e2 s32i a14, a1, 64 +400063bc: e84e16 beqz a14, 40006244 +400063bf: 82ea add.n a8, a2, a14 +400063c1: ffd882 addmi a8, a8, 0xffffff00 +400063c4: ff0882 l8ui a8, a8, 255 +400063c7: a08860 addx4 a8, a8, a6 +400063ca: 0888 l32i.n a8, a8, 0 +400063cc: fbd856 bnez a8, 4000638d +400063cf: 9198 l32i.n a9, a1, 36 +400063d1: d188 l32i.n a8, a1, 52 +400063d3: 738890 maxu a8, a8, a9 +400063d6: e198 l32i.n a9, a1, 56 +400063d8: 023897 bltu a8, a9, 400063de +400063db: 002d46 j 40006494 +400063de: a85a add.n a10, a8, a5 +400063e0: b83a add.n a11, a8, a3 +400063e2: 000bb2 l8ui a11, a11, 0 +400063e5: a2aa add.n a10, a2, a10 +400063e7: 000aa2 l8ui a10, a10, 0 +400063ea: c0aab0 sub a10, a10, a11 +400063ed: 09ca16 beqz a10, 4000648d +400063f0: 003586 j 400064ca +400063f3: c58a00 extui a8, a0, 26, 13 +400063f6: d38a add.n a13, a3, a8 +400063f8: 000dd2 l8ui a13, a13, 0 +400063fb: c2ca add.n a12, a2, a12 +400063fd: 000cc2 l8ui a12, a12, 0 +40006400: c0ccd0 sub a12, a12, a13 +40006403: eb9c56 bnez a12, 400062c0 +40006406: e1e8 l32i.n a14, a1, 56 +40006408: 881b addi.n a8, a8, 1 +4000640a: e638e7 bltu a8, a14, 400063f4 +4000640d: d1f8 l32i.n a15, a1, 52 +4000640f: b188 l32i.n a8, a1, 44 +40006411: 3fac beqz.n a15, 40006438 +40006413: 985a add.n a9, a8, a5 +40006415: a83a add.n a10, a8, a3 +40006417: 000aa2 l8ui a10, a10, 0 +4000641a: 929a add.n a9, a2, a9 +4000641c: 000992 l8ui a9, a9, 0 +4000641f: 1599a7 bne a9, a10, 40006438 +40006422: 880b addi.n a8, a8, -1 +40006424: b81b addi.n a11, a8, 1 +40006426: 09cb16 beqz a11, 400064c6 +40006429: c58a add.n a12, a5, a8 +4000642b: d38a add.n a13, a3, a8 +4000642d: 000dd2 l8ui a13, a13, 0 +40006430: c2ca add.n a12, a2, a12 +40006432: 000cc2 l8ui a12, a12, 0 +40006435: e91cd7 beq a12, a13, 40006422 +40006438: 020866 bnei a8, -1, 4000643e +4000643b: 0021c6 j 400064c6 +4000643e: a1e8 l32i.n a14, a1, 40 +40006440: ee1b addi.n a14, a14, 1 +40006442: 55ea add.n a5, a5, a14 +40006444: ff7946 j 4000622d +40006447: 384700 excw +4000644a: bc8602 excw +4000644d: ff .byte 0xff +4000644e: 8198 l32i.n a9, a1, 32 +40006450: c09890 sub a9, a8, a9 +40006453: 991b addi.n a9, a9, 1 +40006455: 559a add.n a5, a5, a9 +40006457: 090c movi.n a9, 0 +40006459: f45a add.n a15, a4, a5 +4000645b: 71f9 s32i.n a15, a1, 28 +4000645d: ffa306 j 400062ed +40006460: b85a add.n a11, a8, a5 +40006462: c38a add.n a12, a3, a8 +40006464: 000cc2 l8ui a12, a12, 0 +40006467: b2ba add.n a11, a2, a11 +40006469: 000bb2 l8ui a11, a11, 0 +4000646c: 021bc7 beq a11, a12, 40006472 +4000646f: ffba46 j 4000635c +40006472: 08ad mov.n a10, a8 +40006474: 880b addi.n a8, a8, -1 +40006476: e639a7 bltu a9, a10, 40006460 +40006479: ffb7c6 j 4000635c +4000647c: 8a0000 depbits a0, a0, 8, 1 +4000647f: e38ad5 call4 3ffe9d2c <_start-0x162d4> +40006482: 000ee2 l8ui a14, a14, 0 +40006485: d2da add.n a13, a2, a13 +40006487: 000dd2 l8ui a13, a13, 0 +4000648a: 3c9de7 bne a13, a14, 400064ca +4000648d: e1f8 l32i.n a15, a1, 56 +4000648f: 881b addi.n a8, a8, 1 +40006491: e938f7 bltu a8, a15, 4000647e +40006494: d1a8 l32i.n a10, a1, 52 +40006496: 9198 l32i.n a9, a1, 36 +40006498: b188 l32i.n a8, a1, 44 +4000649a: 0eb9a7 bgeu a9, a10, 400064ac +4000649d: b85a add.n a11, a8, a5 +4000649f: c83a add.n a12, a8, a3 +400064a1: 000cc2 l8ui a12, a12, 0 +400064a4: b2ba add.n a11, a2, a11 +400064a6: 000bb2 l8ui a11, a11, 0 +400064a9: 471bc7 beq a11, a12, 400064f4 +400064ac: d198 l32i.n a9, a1, 52 +400064ae: 91d8 l32i.n a13, a1, 36 +400064b0: dd1b addi.n a13, a13, 1 +400064b2: 1039d7 bltu a9, a13, 400064c6 +400064b5: c1f8 l32i.n a15, a1, 48 +400064b7: f188 l32i.n a8, a1, 60 +400064b9: 41e8 l32i.n a14, a1, 16 +400064bb: 91e9 s32i.n a14, a1, 36 +400064bd: 558a add.n a5, a5, a8 +400064bf: ff8a add.n a15, a15, a8 +400064c1: c1f9 s32i.n a15, a1, 48 +400064c3: ffb6c6 j 400063a2 +400064c6: 225a add.n a2, a2, a5 +400064c8: f01d retw.n +400064ca: e198 l32i.n a9, a1, 56 +400064cc: c4b897 bgeu a8, a9, 40006494 +400064cf: d1c8 l32i.n a12, a1, 52 +400064d1: 0b0c movi.n a11, 0 +400064d3: 91b9 s32i.n a11, a1, 36 +400064d5: c0c8c0 sub a12, a8, a12 +400064d8: cc1b addi.n a12, a12, 1 +400064da: 55ca add.n a5, a5, a12 +400064dc: a45a add.n a10, a4, a5 +400064de: c1a9 s32i.n a10, a1, 48 +400064e0: ffaf86 j 400063a2 +400064e3: 8a0000 depbits a0, a0, 8, 1 +400064e6: e38ad5 call4 3ffe9d94 <_start-0x1626c> +400064e9: 000ee2 l8ui a14, a14, 0 +400064ec: d2da add.n a13, a2, a13 +400064ee: 000dd2 l8ui a13, a13, 0 +400064f1: b99de7 bne a13, a14, 400064ae +400064f4: 089d mov.n a9, a8 +400064f6: 91f8 l32i.n a15, a1, 36 +400064f8: 880b addi.n a8, a8, -1 +400064fa: e73f97 bltu a15, a9, 400064e5 +400064fd: ffeb46 j 400064ae +40006500: 004136 entry a1, 32 +40006503: 03a052 movi a5, 3 +40006506: 1e0527 bnone a5, a2, 40006528 +40006509: 747030 extui a7, a3, 0, 8 +4000650c: f03d nop.n +4000650e: 118076 loop a0, 40006523 +40006511: 440b addi.n a4, a4, -1 +40006513: 5d0426 beqi a4, -1, 40006574 +40006516: 000262 l8ui a6, a2, 0 +40006519: 551677 beq a6, a7, 40006572 +4000651c: 221b addi.n a2, a2, 1 +4000651e: 148020 extui a8, a2, 0, 2 +40006521: 388c beqz.n a8, 40006528 +40006523: fff946 j 4000650c +40006526: b60000 excw +40006529: 812b44 excw +4000652c: 9e .byte 0x9e +4000652d: 7ce8 l32i.n a14, a12, 28 +4000652f: 71fa add.n a7, a1, a15 +40006531: 9e .byte 0x9e +40006532: 40e8 l32i.n a14, a0, 16 +40006534: 3041b2 s8i a11, a1, 48 +40006537: 907490 addx2 a7, a4, a9 +4000653a: 908a73 excw +4000653d: 0bf9 s32i.n a15, a11, 0 +4000653f: 149b76 loopnez a11, 40006557 +40006542: 02c8 l32i.n a12, a2, 0 +40006544: 30cc90 xor a12, a12, a9 +40006547: 30dca0 xor a13, a12, a10 +4000654a: cc7a add.n a12, a12, a7 +4000654c: 10ccd0 and a12, a12, a13 +4000654f: 048c87 bany a12, a8, 40006557 +40006552: fcc442 addi a4, a4, -4 +40006555: 224b addi.n a2, a2, 4 +40006557: 046d mov.n a6, a4 +40006559: 440b addi.n a4, a4, -1 +4000655b: 150426 beqi a4, -1, 40006574 +4000655e: 747030 extui a7, a3, 0, 8 +40006561: 099676 loopnez a6, 4000656e +40006564: 0002d2 l8ui a13, a2, 0 +40006567: 440b addi.n a4, a4, -1 +40006569: 051d77 beq a13, a7, 40006572 +4000656c: 221b addi.n a2, a2, 1 +4000656e: 020c movi.n a2, 0 +40006570: f01d retw.n +40006572: f01d retw.n +40006574: 020c movi.n a2, 0 +40006576: f01d retw.n +40006578: 004136 entry a1, 32 +4000657b: e88b81 l32r a8, 400007a8 <_c_0x00ff0000+0x10> +4000657e: ffaf92 movi a9, -1 +40006581: e88871 l32r a7, 400007a4 <_c_0x00ff0000+0xc> +40006584: 746030 extui a6, a3, 0, 8 +40006587: 03a032 movi a3, 3 +4000658a: 08f616 beqz a6, 4000661d +4000658d: 180327 bnone a3, a2, 400065a9 +40006590: f03d nop.n +40006592: 0f8076 loop a0, 400065a5 +40006595: 000232 l8ui a3, a2, 0 +40006598: 0c8316 beqz a3, 40006664 +4000659b: 791637 beq a6, a3, 40006618 +4000659e: 221b addi.n a2, a2, 1 +400065a0: 144020 extui a4, a2, 0, 2 +400065a3: 248c beqz.n a4, 400065a9 +400065a5: fff9c6 j 40006590 +400065a8: 063d00 excw +400065ab: 850c movi.n a5, 8 +400065ad: 0e8076 loop a0, 400065bf +400065b0: 401500 ssl a5 +400065b3: 1155f0 slli a5, a5, 1 +400065b6: a1a300 sll a10, a3 +400065b9: 2033a0 or a3, a3, a10 +400065bc: 02c5f6 bgeui a5, 32, 400065c2 +400065bf: fffa86 j 400065ad +400065c2: 02a8 l32i.n a10, a2, 0 +400065c4: 30ca90 xor a12, a10, a9 +400065c7: ba8a add.n a11, a10, a8 +400065c9: 10bbc0 and a11, a11, a12 +400065cc: 2d8b77 bany a11, a7, 400065fd +400065cf: 30da30 xor a13, a10, a3 +400065d2: 30ed90 xor a14, a13, a9 +400065d5: dd8a add.n a13, a13, a8 +400065d7: 10dde0 and a13, a13, a14 +400065da: 100d77 bnone a13, a7, 400065ee +400065dd: 000706 j 400065fd +400065e0: 30ea30 xor a14, a10, a3 +400065e3: 30fe90 xor a15, a14, a9 +400065e6: ee8a add.n a14, a14, a8 +400065e8: 10eef0 and a14, a14, a15 +400065eb: 0e8e77 bany a14, a7, 400065fd +400065ee: 12a8 l32i.n a10, a2, 4 +400065f0: 224b addi.n a2, a2, 4 +400065f2: 304a90 xor a4, a10, a9 +400065f5: fa8a add.n a15, a10, a8 +400065f7: 10ff40 and a15, a15, a4 +400065fa: e20f77 bnone a15, a7, 400065e0 +400065fd: 000232 l8ui a3, a2, 0 +40006600: 139c beqz.n a3, 40006615 +40006602: 121637 beq a6, a3, 40006618 +40006605: 098076 loop a0, 40006612 +40006608: 010232 l8ui a3, a2, 1 +4000660b: 221b addi.n a2, a2, 1 +4000660d: 438c beqz.n a3, 40006615 +4000660f: 051637 beq a6, a3, 40006618 +40006612: fffbc6 j 40006605 +40006615: 4b9637 bne a6, a3, 40006664 +40006618: f01d retw.n +4000661a: 000000 ill +4000661d: 0d0327 bnone a3, a2, 4000662e +40006620: 0002a2 l8ui a10, a2, 0 +40006623: ff1a16 beqz a10, 40006618 +40006626: 221b addi.n a2, a2, 1 +40006628: 14b020 extui a11, a2, 0, 2 +4000662b: ff1b56 bnez a11, 40006620 +4000662e: 02c8 l32i.n a12, a2, 0 +40006630: f03d nop.n +40006632: 30dc90 xor a13, a12, a9 +40006635: cc8a add.n a12, a12, a8 +40006637: 10ccd0 and a12, a12, a13 +4000663a: 0e8c77 bany a12, a7, 4000664c +4000663d: 12d8 l32i.n a13, a2, 4 +4000663f: 224b addi.n a2, a2, 4 +40006641: 30ed90 xor a14, a13, a9 +40006644: dd8a add.n a13, a13, a8 +40006646: 10dde0 and a13, a13, a14 +40006649: f00d77 bnone a13, a7, 4000663d +4000664c: 0002e2 l8ui a14, a2, 0 +4000664f: 0020f0 nop +40006652: fc2e16 beqz a14, 40006618 +40006655: 078076 loop a0, 40006660 +40006658: 0102f2 l8ui a15, a2, 1 +4000665b: 221b addi.n a2, a2, 1 +4000665d: fb7f16 beqz a15, 40006618 +40006660: fffc46 j 40006655 +40006663: 020c00 andb b0, b12, b0 +40006666: f01d retw.n +40006668: 002136 entry a1, 16 +4000666b: f01d retw.n +4000666d: 000000 ill + +40006670 <_xtos_set_intlevel>: +40006670: 002136 entry a1, 16 +40006673: 03e630 rsr.ps a3 +40006676: 047c movi.n a4, -16 +40006678: 342020 extui a2, a2, 0, 4 +4000667b: 104430 and a4, a4, a3 +4000667e: 204420 or a4, a4, a2 +40006681: 13e640 wsr.ps a4 +40006684: 342030 extui a2, a3, 0, 4 +40006687: 002010 rsync +4000668a: f01d retw.n +4000668c: 002136 entry a1, 16 +4000668f: 343020 extui a3, a2, 0, 4 +40006692: 03e620 rsr.ps a2 +40006695: 047c movi.n a4, -16 +40006697: 345020 extui a5, a2, 0, 4 +4000669a: c05350 sub a5, a3, a5 +4000669d: 104420 and a4, a4, a2 +400066a0: 204430 or a4, a4, a3 +400066a3: a34250 movltz a4, a2, a5 +400066a6: 13e640 wsr.ps a4 +400066a9: 002010 rsync +400066ac: f01d retw.n +400066ae: 360000 excw +400066b1: f00021 l32r a2, 400026b4 <_X_ets_unk225c+0x458> +400066b4: 1d0041 l32r a4, 3ffcdab4 <_start-0x3254c> +400066b7: 4128f0 srli a2, a15, 8 +400066ba: 5138 l32i.n a3, a1, 20 +400066bc: 01d112 addmi a1, a1, 0x100 +400066bf: 004110 break 1, 1 +400066c2: 003000 rfe +400066c5: 000000 ill +400066c8: 3b6522 s32i a2, a5, 236 +400066cb: 1528 l32i.n a2, a5, 4 +400066cd: 0538 l32i.n a3, a5, 0 +400066cf: 13e620 wsr.ps a2 +400066d2: 4528 l32i.n a2, a5, 16 +400066d4: 6548 l32i.n a4, a5, 24 +400066d6: 002010 rsync +400066d9: 13b130 wsr.epc1 a3 +400066dc: 5538 l32i.n a3, a5, 20 +400066de: 7558 l32i.n a5, a5, 28 +400066e0: 003000 rfe +400066e3: 213600 srai a3, a0, 6 +400066e6: ea2000 depbits a0, a0, 14, 3 +400066e9: f01d03 excw +400066ec: 002136 entry a1, 16 +400066ef: 52cc bnez.n a2, 400066f8 <_xtos_set_intlevel+0x88> +400066f1: 13f030 wsr.ccompare0 a3 +400066f4: 0003c6 j 40006707 <_xtos_set_intlevel+0x97> +400066f7: 22f600 orb b15, b6, b0 +400066fa: f13005 call0 3fff79fc <_start-0x8604> +400066fd: 014613 excw +40006700: 32f600 orbc b15, b6, b0 +40006703: f23005 call0 3fff8a04 <_start-0x75fc> +40006706: 200013 excw +40006709: f01d00 subx8 a1, a13, a0 +4000670c: 002136 entry a1, 16 +4000670f: 52cc bnez.n a2, 40006718 <_xtos_set_intlevel+0xa8> +40006711: 03f020 rsr.ccompare0 a2 +40006714: f01d retw.n +40006716: f60000 excw +40006719: 200422 l8ui a2, a4, 32 +4000671c: 1d03f1 l32r a15, 3ffcdb28 <_start-0x324d8> +4000671f: 32f6f0 orbc b15, b6, b15 +40006722: f22004 excw +40006725: f01d03 excw +40006728: 020c movi.n a2, 0 +4000672a: f01d retw.n +4000672c: 004136 entry a1, 32 +4000672f: 035d mov.n a5, a3 +40006731: 023d mov.n a3, a2 +40006733: 052d mov.n a2, a5 +40006735: 000706 j 40006755 <_xtos_set_intlevel+0xe5> +40006738: 05ad mov.n a10, a5 +4000673a: 03bd mov.n a11, a3 +4000673c: 4c0c movi.n a12, 4 +4000673e: c06c60 sub a6, a12, a6 +40006741: 06cd mov.n a12, a6 +40006743: 000765 call8 400067b8 <_xtos_set_intlevel+0x148> +40006746: 556a add.n a5, a5, a6 +40006748: 336a add.n a3, a3, a6 +4000674a: c04460 sub a4, a4, a6 +4000674d: 000346 j 4000675e <_xtos_set_intlevel+0xee> +40006750: 004136 entry a1, 32 +40006753: 025d mov.n a5, a2 +40006755: 6244b6 bltui a4, 4, 400067bb <_xtos_set_intlevel+0x14b> +40006758: 146020 extui a6, a2, 0, 2 +4000675b: fd9656 bnez a6, 40006738 <_xtos_set_intlevel+0xc8> +4000675e: 417440 srli a7, a4, 4 +40006761: 14b030 extui a11, a3, 0, 2 +40006764: 09cb56 bnez a11, 40006804 <_xtos_set_intlevel+0x194> +40006767: 159776 loopnez a7, 40006780 <_xtos_set_intlevel+0x110> +4000676a: 0368 l32i.n a6, a3, 0 +4000676c: 1378 l32i.n a7, a3, 4 +4000676e: 0569 s32i.n a6, a5, 0 +40006770: 2368 l32i.n a6, a3, 8 +40006772: 1579 s32i.n a7, a5, 4 +40006774: 3378 l32i.n a7, a3, 12 +40006776: 2569 s32i.n a6, a5, 8 +40006778: 10c332 addi a3, a3, 16 +4000677b: 3579 s32i.n a7, a5, 12 +4000677d: 10c552 addi a5, a5, 16 +40006780: 0d6437 bbci a4, 3, 40006791 <_xtos_set_intlevel+0x121> +40006783: 0368 l32i.n a6, a3, 0 +40006785: 1378 l32i.n a7, a3, 4 +40006787: 338b addi.n a3, a3, 8 +40006789: 0569 s32i.n a6, a5, 0 +4000678b: 016572 s32i a7, a5, 4 +4000678e: 08c552 addi a5, a5, 8 +40006791: 076427 bbci a4, 2, 4000679c <_xtos_set_intlevel+0x12c> +40006794: 0368 l32i.n a6, a3, 0 +40006796: 334b addi.n a3, a3, 4 +40006798: 0569 s32i.n a6, a5, 0 +4000679a: 554b addi.n a5, a5, 4 +4000679c: 144040 extui a4, a4, 0, 2 +4000679f: 349c beqz.n a4, 400067b6 <_xtos_set_intlevel+0x146> +400067a1: 0368 l32i.n a6, a3, 0 +400067a3: 0578 l32i.n a7, a5, 0 +400067a5: 402400 ssa8l a4 +400067a8: a16600 sll a6, a6 +400067ab: 917070 srl a7, a7 +400067ae: 403400 ssa8b a4 +400067b1: 817760 src a7, a7, a6 +400067b4: 0579 s32i.n a7, a5, 0 +400067b6: f01d retw.n +400067b8: 002136 entry a1, 16 +400067bb: 041416 beqz a4, 40006800 <_xtos_set_intlevel+0x190> +400067be: c57c movi.n a5, -4 +400067c0: 105350 and a5, a3, a5 +400067c3: 0568 l32i.n a6, a5, 0 +400067c5: 1578 l32i.n a7, a5, 4 +400067c7: 402300 ssa8l a3 +400067ca: 813760 src a3, a7, a6 +400067cd: 14a020 extui a10, a2, 0, 2 +400067d0: c052a0 sub a5, a2, a10 +400067d3: 0588 l32i.n a8, a5, 0 +400067d5: 6a4a add.n a6, a10, a4 +400067d7: 402200 ssa8l a2 +400067da: a1a800 sll a10, a8 +400067dd: 0b46b6 bltui a6, 4, 400067ec <_xtos_set_intlevel+0x17c> +400067e0: 1588 l32i.n a8, a5, 4 +400067e2: 403200 ssa8b a2 +400067e5: 81a3a0 src a10, a3, a10 +400067e8: 05a9 s32i.n a10, a5, 0 +400067ea: 554b addi.n a5, a5, 4 +400067ec: 402400 ssa8l a4 +400067ef: 8133a0 src a3, a3, a10 +400067f2: 402600 ssa8l a6 +400067f5: 918080 srl a8, a8 +400067f8: 403600 ssa8b a6 +400067fb: 818830 src a8, a8, a3 +400067fe: 0589 s32i.n a8, a5, 0 +40006800: f01d retw.n +40006802: 000000 ill +40006805: 304023 excw +40006808: b014b0 addx8 a1, a4, a11 +4000680b: 68c033 excw +4000680e: 977603 excw +40006811: 137821 l32r a2, 3ffcb5f4 <_start-0x34a0c> +40006814: 2388 l32i.n a8, a3, 8 +40006816: 816760 src a6, a7, a6 +40006819: 0569 s32i.n a6, a5, 0 +4000681b: 3398 l32i.n a9, a3, 12 +4000681d: 817870 src a7, a8, a7 +40006820: 1579 s32i.n a7, a5, 4 +40006822: 4368 l32i.n a6, a3, 16 +40006824: 818980 src a8, a9, a8 +40006827: 2589 s32i.n a8, a5, 8 +40006829: 10c332 addi a3, a3, 16 +4000682c: 819690 src a9, a6, a9 +4000682f: 3599 s32i.n a9, a5, 12 +40006831: 10c552 addi a5, a5, 16 +40006834: 146437 bbci a4, 3, 4000684c <_xtos_set_intlevel+0x1dc> +40006837: 1378 l32i.n a7, a3, 4 +40006839: 2388 l32i.n a8, a3, 8 +4000683b: 816760 src a6, a7, a6 +4000683e: 0569 s32i.n a6, a5, 0 +40006840: 338b addi.n a3, a3, 8 +40006842: 817870 src a7, a8, a7 +40006845: 1579 s32i.n a7, a5, 4 +40006847: 558b addi.n a5, a5, 8 +40006849: 206880 or a6, a8, a8 +4000684c: 0c6427 bbci a4, 2, 4000685c <_xtos_set_intlevel+0x1ec> +4000684f: 1378 l32i.n a7, a3, 4 +40006851: 334b addi.n a3, a3, 4 +40006853: 816760 src a6, a7, a6 +40006856: 0569 s32i.n a6, a5, 0 +40006858: 554b addi.n a5, a5, 4 +4000685a: 076d mov.n a6, a7 +4000685c: 1378 l32i.n a7, a3, 4 +4000685e: 0538 l32i.n a3, a5, 0 +40006860: 816760 src a6, a7, a6 +40006863: 402400 ssa8l a4 +40006866: a16600 sll a6, a6 +40006869: 913030 srl a3, a3 +4000686c: 403400 ssa8b a4 +4000686f: 813360 src a3, a3, a6 +40006872: 0539 s32i.n a3, a5, 0 +40006874: f01d retw.n +40006876: 360000 excw +40006879: 200021 l32r a2, 3ffce87c <_start-0x31784> +4000687c: 1d03e2 l8ui a14, a3, 29 +4000687f: 2136f0 srai a3, a15, 6 +40006882: e32000 excw +40006885: f01d13 excw + +40006888 <__divsi3>: +40006888: 002136 entry a1, 16 +4000688b: d22230 quos a2, a2, a3 +4000688e: f01d retw.n + +40006890 : +40006890: 004136 entry a1, 32 +40006893: 604030 neg a4, a3 +40006896: 223a add.n a2, a2, a3 +40006898: 220b addi.n a2, a2, -1 +4000689a: 102240 and a2, a2, a4 +4000689d: f01d retw.n + ... + +400068a0 : +400068a0: 004136 entry a1, 32 +400068a3: 223a add.n a2, a2, a3 +400068a5: 220b addi.n a2, a2, -1 +400068a7: c22230 quou a2, a2, a3 +400068aa: f01d retw.n +400068ac: 004136 entry a1, 32 +400068af: 430b addi.n a4, a3, -1 +400068b1: 320426 beqi a4, -1, 400068e7 +400068b4: 206330 or a6, a3, a3 +400068b7: 248030 extui a8, a3, 0, 3 +400068ba: 00a032 movi a3, 0 +400068bd: 049876 loopnez a8, 400068c5 +400068c0: 004232 s8i a3, a2, 0 +400068c3: 221b addi.n a2, a2, 1 +400068c5: 414360 srli a4, a6, 3 +400068c8: f03d nop.n +400068ca: 199476 loopnez a4, 400068e7 +400068cd: 004232 s8i a3, a2, 0 +400068d0: 014232 s8i a3, a2, 1 +400068d3: 024232 s8i a3, a2, 2 +400068d6: 034232 s8i a3, a2, 3 +400068d9: 044232 s8i a3, a2, 4 +400068dc: 054232 s8i a3, a2, 5 +400068df: 064232 s8i a3, a2, 6 +400068e2: 074232 s8i a3, a2, 7 +400068e5: 228b addi.n a2, a2, 8 +400068e7: f01d retw.n +400068e9: 000000 ill + +400068ec : +400068ec: 004136 entry a1, 32 +400068ef: 1b44b6 bltui a4, 4, 4000690e +400068f2: 205320 or a5, a3, a2 +400068f5: 145050 extui a5, a5, 0, 2 +400068f8: 25dc bnez.n a5, 4000690e +400068fa: 418240 srli a8, a4, 2 +400068fd: 0d9876 loopnez a8, 4000690e +40006900: 03a8 l32i.n a10, a3, 0 +40006902: 0298 l32i.n a9, a2, 0 +40006904: 0699a7 bne a9, a10, 4000690e +40006907: 224b addi.n a2, a2, 4 +40006909: 334b addi.n a3, a3, 4 +4000690b: fcc442 addi a4, a4, -4 +4000690e: 046d mov.n a6, a4 +40006910: 440b addi.n a4, a4, -1 +40006912: 110426 beqi a4, -1, 40006927 +40006915: 0e9676 loopnez a6, 40006927 +40006918: 000372 l8ui a7, a3, 0 +4000691b: 000282 l8ui a8, a2, 0 +4000691e: 221b addi.n a2, a2, 1 +40006920: 089877 bne a8, a7, 4000692c +40006923: 440b addi.n a4, a4, -1 +40006925: 331b addi.n a3, a3, 1 +40006927: 020c movi.n a2, 0 +40006929: f01d retw.n +4000692b: 287000 excw +4000692e: f01dc0 subx8 a1, a13, a12 +40006931: 000000 ill +40006934: 947600 extui a7, a0, 6, 10 +40006937: 6209 s32i.n a0, a2, 24 +40006939: 1b0003 excw +4000693c: 456233 excw +4000693f: 551b00 extui a1, a0, 27, 6 +40006942: f01d retw.n +40006944: ed74b6 bltui a4, 7, 40006935 +40006947: 000362 l8ui a6, a3, 0 +4000694a: 331b addi.n a3, a3, 1 +4000694c: ffc442 addi a4, a4, -1 +4000694f: 004562 s8i a6, a5, 0 +40006952: 01c552 addi a5, a5, 1 +40006955: 276517 bbci a5, 1, 40006980 +40006958: d964b6 bltui a4, 6, 40006935 +4000695b: 000362 l8ui a6, a3, 0 +4000695e: 010372 l8ui a7, a3, 1 +40006961: 332b addi.n a3, a3, 2 +40006963: fec442 addi a4, a4, -2 +40006966: 004562 s8i a6, a5, 0 +40006969: 014572 s8i a7, a5, 1 +4000696c: 552b addi.n a5, a5, 2 +4000696e: 000386 j 40006980 +40006971: 000000 ill + +40006974 : +40006974: 002136 entry a1, 16 +40006977: 205220 or a5, a2, a2 +4000697a: c6e207 bbsi a2, 0, 40006944 +4000697d: d7e217 bbsi a2, 1, 40006958 +40006980: 417440 srli a7, a4, 4 +40006983: 018320 slli a8, a3, 30 +40006986: 05a856 bnez a8, 400069e4 +40006989: 159776 loopnez a7, 400069a2 +4000698c: 0368 l32i.n a6, a3, 0 +4000698e: 1378 l32i.n a7, a3, 4 +40006990: 0569 s32i.n a6, a5, 0 +40006992: 2368 l32i.n a6, a3, 8 +40006994: 1579 s32i.n a7, a5, 4 +40006996: 3378 l32i.n a7, a3, 12 +40006998: 2569 s32i.n a6, a5, 8 +4000699a: 10c332 addi a3, a3, 16 +4000699d: 3579 s32i.n a7, a5, 12 +4000699f: 10c552 addi a5, a5, 16 +400069a2: 0b6437 bbci a4, 3, 400069b1 +400069a5: 0368 l32i.n a6, a3, 0 +400069a7: 1378 l32i.n a7, a3, 4 +400069a9: 338b addi.n a3, a3, 8 +400069ab: 0569 s32i.n a6, a5, 0 +400069ad: 1579 s32i.n a7, a5, 4 +400069af: 558b addi.n a5, a5, 8 +400069b1: 07e427 bbsi a4, 2, 400069bc +400069b4: 14e417 bbsi a4, 1, 400069cc +400069b7: 21e407 bbsi a4, 0, 400069dc +400069ba: f01d retw.n +400069bc: 0368 l32i.n a6, a3, 0 +400069be: 334b addi.n a3, a3, 4 +400069c0: 0569 s32i.n a6, a5, 0 +400069c2: 554b addi.n a5, a5, 4 +400069c4: 04e417 bbsi a4, 1, 400069cc +400069c7: 11e407 bbsi a4, 0, 400069dc +400069ca: f01d retw.n +400069cc: 001362 l16ui a6, a3, 0 +400069cf: 332b addi.n a3, a3, 2 +400069d1: 005562 s16i a6, a5, 0 +400069d4: 552b addi.n a5, a5, 2 +400069d6: 02e407 bbsi a4, 0, 400069dc +400069d9: f01d retw.n +400069db: 036200 excw +400069de: 456200 extui a6, a0, 18, 5 +400069e1: f01d00 subx8 a1, a13, a0 +400069e4: ffa416 beqz a4, 400069e2 +400069e7: 402300 ssa8l a3 +400069ea: 15be80 extui a11, a8, 30, 2 +400069ed: c033b0 sub a3, a3, a11 +400069f0: 0368 l32i.n a6, a3, 0 +400069f2: 219776 loopnez a7, 40006a17 +400069f5: 1378 l32i.n a7, a3, 4 +400069f7: 2388 l32i.n a8, a3, 8 +400069f9: 816760 src a6, a7, a6 +400069fc: 0569 s32i.n a6, a5, 0 +400069fe: 3398 l32i.n a9, a3, 12 +40006a00: 817870 src a7, a8, a7 +40006a03: 1579 s32i.n a7, a5, 4 +40006a05: 4368 l32i.n a6, a3, 16 +40006a07: 818980 src a8, a9, a8 +40006a0a: 2589 s32i.n a8, a5, 8 +40006a0c: 10c332 addi a3, a3, 16 +40006a0f: 819690 src a9, a6, a9 +40006a12: 3599 s32i.n a9, a5, 12 +40006a14: 10c552 addi a5, a5, 16 +40006a17: 156437 bbci a4, 3, 40006a30 +40006a1a: 1378 l32i.n a7, a3, 4 +40006a1c: 2388 l32i.n a8, a3, 8 +40006a1e: 816760 src a6, a7, a6 +40006a21: 0569 s32i.n a6, a5, 0 +40006a23: 338b addi.n a3, a3, 8 +40006a25: 817870 src a7, a8, a7 +40006a28: 1579 s32i.n a7, a5, 4 +40006a2a: 08c552 addi a5, a5, 8 +40006a2d: 206880 or a6, a8, a8 +40006a30: 0c6427 bbci a4, 2, 40006a40 +40006a33: 1378 l32i.n a7, a3, 4 +40006a35: 334b addi.n a3, a3, 4 +40006a37: 816760 src a6, a7, a6 +40006a3a: 0569 s32i.n a6, a5, 0 +40006a3c: 554b addi.n a5, a5, 4 +40006a3e: 076d mov.n a6, a7 +40006a40: 33ba add.n a3, a3, a11 +40006a42: 06e417 bbsi a4, 1, 40006a4c +40006a45: 18e407 bbsi a4, 0, 40006a61 +40006a48: f01d retw.n +40006a4a: 620000 excw +40006a4d: 720003 excw +40006a50: 2b0103 excw +40006a53: 456233 excw +40006a56: 457200 extui a7, a0, 18, 5 +40006a59: 552b01 l32r a0, 3ffdbf08 <_start-0x240f8> +40006a5c: 01e407 bbsi a4, 0, 40006a61 +40006a5f: f01d retw.n +40006a61: 000362 l8ui a6, a3, 0 +40006a64: 004562 s8i a6, a5, 0 +40006a67: f01d retw.n +40006a69: 000000 ill + +40006a6c : +40006a6c: 004136 entry a1, 32 +40006a6f: 049d mov.n a9, a4 +40006a71: 028d mov.n a8, a2 +40006a73: 04ad mov.n a10, a4 +40006a75: 032d mov.n a2, a3 +40006a77: 087d mov.n a7, a8 +40006a79: 1bb387 bgeu a3, a8, 40006a98 +40006a7c: 343a add.n a3, a4, a3 +40006a7e: 16b837 bgeu a8, a3, 40006a98 +40006a81: 748a add.n a7, a4, a8 +40006a83: 131416 beqz a4, 40006bb8 +40006a86: 099476 loopnez a4, 40006a93 +40006a89: 770b addi.n a7, a7, -1 +40006a8b: 330b addi.n a3, a3, -1 +40006a8d: 000342 l8ui a4, a3, 0 +40006a90: 004742 s8i a4, a7, 0 +40006a93: 082d mov.n a2, a8 +40006a95: f01d retw.n +40006a97: b9f600 excw +40006a9a: 320602 l8ui a0, a6, 50 +40006a9d: b28000 mulsh a8, a0, a0 +40006aa0: b0b020 addx8 a11, a0, a2 +40006aa3: fb5614 excw +40006aa6: 7d0b addi.n a7, a13, -1 +40006aa8: 9008 l32i.n a0, a0, 36 +40006aaa: 9014c4 excw +40006aad: 4d4164 excw +40006ab0: 9c7606 j 3ffedc8c <_start-0x12374> +40006ab3: 025815 call4 40009034 <__umoddi3+0x21d0> +40006ab6: 0759 s32i.n a5, a7, 0 +40006ab8: 12f8 l32i.n a15, a2, 4 +40006aba: 17f9 s32i.n a15, a7, 4 +40006abc: 22e8 l32i.n a14, a2, 8 +40006abe: 27e9 s32i.n a14, a7, 8 +40006ac0: 32d8 l32i.n a13, a2, 12 +40006ac2: 37d9 s32i.n a13, a7, 12 +40006ac4: 10c222 addi a2, a2, 16 +40006ac7: 10c772 addi a7, a7, 16 +40006aca: 413240 srli a3, a4, 2 +40006acd: 459376 loopnez a3, 40006b16 +40006ad0: 0238 l32i.n a3, a2, 0 +40006ad2: 0739 s32i.n a3, a7, 0 +40006ad4: 1238 l32i.n a3, a2, 4 +40006ad6: 1739 s32i.n a3, a7, 4 +40006ad8: 2238 l32i.n a3, a2, 8 +40006ada: 2739 s32i.n a3, a7, 8 +40006adc: 3238 l32i.n a3, a2, 12 +40006ade: 3739 s32i.n a3, a7, 12 +40006ae0: 4238 l32i.n a3, a2, 16 +40006ae2: 4739 s32i.n a3, a7, 16 +40006ae4: 5238 l32i.n a3, a2, 20 +40006ae6: 5739 s32i.n a3, a7, 20 +40006ae8: 6238 l32i.n a3, a2, 24 +40006aea: 6739 s32i.n a3, a7, 24 +40006aec: 7238 l32i.n a3, a2, 28 +40006aee: 7739 s32i.n a3, a7, 28 +40006af0: 8238 l32i.n a3, a2, 32 +40006af2: 8739 s32i.n a3, a7, 32 +40006af4: 9238 l32i.n a3, a2, 36 +40006af6: 9739 s32i.n a3, a7, 36 +40006af8: a238 l32i.n a3, a2, 40 +40006afa: a739 s32i.n a3, a7, 40 +40006afc: b238 l32i.n a3, a2, 44 +40006afe: b739 s32i.n a3, a7, 44 +40006b00: c238 l32i.n a3, a2, 48 +40006b02: c739 s32i.n a3, a7, 48 +40006b04: d238 l32i.n a3, a2, 52 +40006b06: d739 s32i.n a3, a7, 52 +40006b08: e238 l32i.n a3, a2, 56 +40006b0a: e739 s32i.n a3, a7, 56 +40006b0c: f238 l32i.n a3, a2, 60 +40006b0e: f739 s32i.n a3, a7, 60 +40006b10: 40c222 addi a2, a2, 64 +40006b13: 40c772 addi a7, a7, 64 +40006b16: 1166c0 slli a6, a6, 4 +40006b19: c06960 sub a6, a9, a6 +40006b1c: 06ad mov.n a10, a6 +40006b1e: 4546b6 bltui a6, 4, 40006b67 +40006b21: 24b260 extui a11, a6, 2, 3 +40006b24: 419260 srli a9, a6, 2 +40006b27: 204990 or a4, a9, a9 +40006b2a: 413340 srli a3, a4, 3 +40006b2d: 079b76 loopnez a11, 40006b38 +40006b30: 02c8 l32i.n a12, a2, 0 +40006b32: 07c9 s32i.n a12, a7, 0 +40006b34: 224b addi.n a2, a2, 4 +40006b36: 774b addi.n a7, a7, 4 +40006b38: 11a9e0 slli a10, a9, 2 +40006b3b: 259376 loopnez a3, 40006b64 +40006b3e: 0238 l32i.n a3, a2, 0 +40006b40: 0739 s32i.n a3, a7, 0 +40006b42: 1238 l32i.n a3, a2, 4 +40006b44: 1739 s32i.n a3, a7, 4 +40006b46: 2238 l32i.n a3, a2, 8 +40006b48: 2739 s32i.n a3, a7, 8 +40006b4a: 3238 l32i.n a3, a2, 12 +40006b4c: 3739 s32i.n a3, a7, 12 +40006b4e: 4238 l32i.n a3, a2, 16 +40006b50: 4739 s32i.n a3, a7, 16 +40006b52: 5238 l32i.n a3, a2, 20 +40006b54: 5739 s32i.n a3, a7, 20 +40006b56: 6238 l32i.n a3, a2, 24 +40006b58: 6739 s32i.n a3, a7, 24 +40006b5a: 7238 l32i.n a3, a2, 28 +40006b5c: 7739 s32i.n a3, a7, 28 +40006b5e: 20c222 addi a2, a2, 32 +40006b61: 20c772 addi a7, a7, 32 +40006b64: c0a6a0 sub a10, a6, a10 +40006b67: ba0b addi.n a11, a10, -1 +40006b69: 4b0b26 beqi a11, -1, 40006bb8 +40006b6c: 4133a0 srli a3, a10, 3 +40006b6f: 24c0a0 extui a12, a10, 0, 3 +40006b72: 099c76 loopnez a12, 40006b7f +40006b75: 0002d2 l8ui a13, a2, 0 +40006b78: 0047d2 s8i a13, a7, 0 +40006b7b: 221b addi.n a2, a2, 1 +40006b7d: 771b addi.n a7, a7, 1 +40006b7f: f03d nop.n +40006b81: 339376 loopnez a3, 40006bb8 +40006b84: 000232 l8ui a3, a2, 0 +40006b87: 004732 s8i a3, a7, 0 +40006b8a: 010232 l8ui a3, a2, 1 +40006b8d: 014732 s8i a3, a7, 1 +40006b90: 020232 l8ui a3, a2, 2 +40006b93: 024732 s8i a3, a7, 2 +40006b96: 030232 l8ui a3, a2, 3 +40006b99: 034732 s8i a3, a7, 3 +40006b9c: 040232 l8ui a3, a2, 4 +40006b9f: 044732 s8i a3, a7, 4 +40006ba2: 050232 l8ui a3, a2, 5 +40006ba5: 054732 s8i a3, a7, 5 +40006ba8: 060232 l8ui a3, a2, 6 +40006bab: 064732 s8i a3, a7, 6 +40006bae: 070232 l8ui a3, a2, 7 +40006bb1: 074732 s8i a3, a7, 7 +40006bb4: 228b addi.n a2, a2, 8 +40006bb6: 778b addi.n a7, a7, 8 +40006bb8: 082d mov.n a2, a8 +40006bba: f01d retw.n +40006bbc: 947600 extui a7, a0, 6, 10 +40006bbf: 453204 excw +40006bc2: 551b00 extui a1, a0, 27, 6 +40006bc5: f01d retw.n +40006bc7: 84b600 extui a11, a0, 6, 9 +40006bca: 4532f1 l32r a15, 3ffd8094 <_start-0x27f6c> +40006bcd: 551b00 extui a1, a0, 27, 6 +40006bd0: 440b addi.n a4, a4, -1 +40006bd2: 286517 bbci a5, 1, 40006bfe +40006bd5: e484b6 bltui a4, 8, 40006bbd +40006bd8: 005532 s16i a3, a5, 0 +40006bdb: 552b addi.n a5, a5, 2 +40006bdd: fec442 addi a4, a4, -2 +40006be0: 000686 j 40006bfe + ... + +40006be4 : +40006be4: 002136 entry a1, 16 +40006be7: 743030 extui a3, a3, 0, 8 +40006bea: 117380 slli a7, a3, 8 +40006bed: 203370 or a3, a3, a7 +40006bf0: 117300 slli a7, a3, 16 +40006bf3: 203370 or a3, a3, a7 +40006bf6: 025d mov.n a5, a2 +40006bf8: cce207 bbsi a2, 0, 40006bc8 +40006bfb: d6e217 bbsi a2, 1, 40006bd5 +40006bfe: 417440 srli a7, a4, 4 +40006c01: 0a9776 loopnez a7, 40006c0f +40006c04: 0539 s32i.n a3, a5, 0 +40006c06: 1539 s32i.n a3, a5, 4 +40006c08: 2539 s32i.n a3, a5, 8 +40006c0a: 3539 s32i.n a3, a5, 12 +40006c0c: 10c552 addi a5, a5, 16 +40006c0f: 066437 bbci a4, 3, 40006c19 +40006c12: 0539 s32i.n a3, a5, 0 +40006c14: 1539 s32i.n a3, a5, 4 +40006c16: 08c552 addi a5, a5, 8 +40006c19: 036427 bbci a4, 2, 40006c20 +40006c1c: 0539 s32i.n a3, a5, 0 +40006c1e: 554b addi.n a5, a5, 4 +40006c20: 046417 bbci a4, 1, 40006c28 +40006c23: 005532 s16i a3, a5, 0 +40006c26: 552b addi.n a5, a5, 2 +40006c28: 026407 bbci a4, 0, 40006c2e +40006c2b: 004532 s8i a3, a5, 0 +40006c2e: f01d retw.n + +40006c30 <__udivdi3>: +40006c30: 008136 entry a1, 64 +40006c33: 02ad mov.n a10, a2 +40006c35: 038d mov.n a8, a3 +40006c37: 0b2c movi.n a11, 32 +40006c39: 049d mov.n a9, a4 +40006c3b: 0a2516 beqz a5, 40006ce1 <__udivdi3+0xb1> +40006c3e: 07b357 bgeu a3, a5, 40006c49 <__udivdi3+0x19> +40006c41: 020c movi.n a2, 0 +40006c43: 030c movi.n a3, 0 +40006c45: f01d retw.n +40006c47: 900000 addx2 a0, a0, a0 +40006c4a: 1640f5 call12 4001d058 <__bss_start+0xd058> +40006c4d: 1e89 s32i.n a8, a14, 4 +40006c4f: c02b90 sub a2, a11, a9 +40006c52: 400200 ssr a2 +40006c55: 91f030 srl a15, a3 +40006c58: 401900 ssl a9 +40006c5b: 81c3a0 src a12, a3, a10 +40006c5e: 81b540 src a11, a5, a4 +40006c61: f480b0 extui a8, a11, 0, 16 +40006c64: f5d0b0 extui a13, a11, 16, 16 +40006c67: 404010 ssai 16 +40006c6a: c2efd0 quou a14, a15, a13 +40006c6d: 0e5d mov.n a5, a14 +40006c6f: 8238e0 mull a3, a8, a14 +40006c72: e2ffd0 remu a15, a15, a13 +40006c75: 81ffc0 src a15, a15, a12 +40006c78: 0f2d mov.n a2, a15 +40006c7a: 0ebf37 bgeu a15, a3, 40006c8c <__udivdi3+0x5c> +40006c7d: 5e0b addi.n a5, a14, -1 +40006c7f: 2bfa add.n a2, a11, a15 +40006c81: 0732b7 bltu a2, a11, 40006c8c <__udivdi3+0x5c> +40006c84: 04b237 bgeu a2, a3, 40006c8c <__udivdi3+0x5c> +40006c87: fece52 addi a5, a14, -2 +40006c8a: 2b2a add.n a2, a11, a2 +40006c8c: 115500 slli a5, a5, 16 +40006c8f: c03230 sub a3, a2, a3 +40006c92: 401900 ssl a9 +40006c95: a14400 sll a4, a4 +40006c98: c2e3d0 quou a14, a3, a13 +40006c9b: 0e2d mov.n a2, a14 +40006c9d: 8288e0 mull a8, a8, a14 +40006ca0: e233d0 remu a3, a3, a13 +40006ca3: 0bf3c0 depbits a12, a3, 16, 16 +40006ca6: 0c3d mov.n a3, a12 +40006ca8: 11bc87 bgeu a12, a8, 40006cbd <__udivdi3+0x8d> +40006cab: ffce22 addi a2, a14, -1 +40006cae: 803bc0 add a3, a11, a12 +40006cb1: 0833b7 bltu a3, a11, 40006cbd <__udivdi3+0x8d> +40006cb4: 05b387 bgeu a3, a8, 40006cbd <__udivdi3+0x8d> +40006cb7: 803b30 add a3, a11, a3 +40006cba: fece22 addi a2, a14, -2 +40006cbd: 202250 or a2, a2, a5 +40006cc0: 825240 mull a5, a2, a4 +40006cc3: a24240 muluh a4, a2, a4 +40006cc6: c03380 sub a3, a3, a8 +40006cc9: 0b3347 bltu a3, a4, 40006cd8 <__udivdi3+0xa8> +40006ccc: c06340 sub a6, a3, a4 +40006ccf: 083656 bnez a6, 40006d56 <__udivdi3+0x126> +40006cd2: a17a00 sll a7, a10 +40006cd5: 7db757 bgeu a7, a5, 40006d56 <__udivdi3+0x126> +40006cd8: 220b addi.n a2, a2, -1 +40006cda: 030c movi.n a3, 0 +40006cdc: f01d retw.n +40006cde: 000000 ill +40006ce1: 75b347 bgeu a3, a4, 40006d5a <__udivdi3+0x12a> +40006ce4: 40f450 nsau a5, a4 +40006ce7: a58c beqz.n a5, 40006cf5 <__udivdi3+0xc5> +40006ce9: 401500 ssl a5 +40006cec: a19400 sll a9, a4 +40006cef: 813320 src a3, a3, a2 +40006cf2: a12200 sll a2, a2 +40006cf5: f44090 extui a4, a9, 0, 16 +40006cf8: 404010 ssai 16 +40006cfb: f55090 extui a5, a9, 16, 16 +40006cfe: e2c350 remu a12, a3, a5 +40006d01: c2b350 quou a11, a3, a5 +40006d04: 0bad mov.n a10, a11 +40006d06: 81cc20 src a12, a12, a2 +40006d09: 8284b0 mull a8, a4, a11 +40006d0c: 0c3d mov.n a3, a12 +40006d0e: 0ebc87 bgeu a12, a8, 40006d20 <__udivdi3+0xf0> +40006d11: ab0b addi.n a10, a11, -1 +40006d13: 3c9a add.n a3, a12, a9 +40006d15: 073397 bltu a3, a9, 40006d20 <__udivdi3+0xf0> +40006d18: 04b387 bgeu a3, a8, 40006d20 <__udivdi3+0xf0> +40006d1b: fecba2 addi a10, a11, -2 +40006d1e: 393a add.n a3, a9, a3 +40006d20: c0d380 sub a13, a3, a8 +40006d23: c28d50 quou a8, a13, a5 +40006d26: 083d mov.n a3, a8 +40006d28: 824480 mull a4, a4, a8 +40006d2b: e2dd50 remu a13, a13, a5 +40006d2e: 0bfd20 depbits a2, a13, 16, 16 +40006d31: 17b247 bgeu a2, a4, 40006d4c <__udivdi3+0x11c> +40006d34: 380b addi.n a3, a8, -1 +40006d36: 229a add.n a2, a2, a9 +40006d38: 103297 bltu a2, a9, 40006d4c <__udivdi3+0x11c> +40006d3b: 0db247 bgeu a2, a4, 40006d4c <__udivdi3+0x11c> +40006d3e: 112a00 slli a2, a10, 16 +40006d41: fec832 addi a3, a8, -2 +40006d44: 202320 or a2, a3, a2 +40006d47: 030c movi.n a3, 0 +40006d49: f01d retw.n +40006d4b: 2a0000 depbits a0, a0, 2, 1 +40006d4e: 232011 l32r a1, 3ffcf9d0 <_start-0x30630> +40006d51: 030c20 rsr.scompare1 a2 +40006d54: f01d retw.n +40006d56: 030c movi.n a3, 0 +40006d58: f01d retw.n +40006d5a: 34cc bnez.n a4, 40006d61 <__udivdi3+0x131> +40006d5c: 190c movi.n a9, 1 +40006d5e: c29940 quou a9, a9, a4 +40006d61: 40f940 nsau a4, a9 +40006d64: 0ec416 beqz a4, 40006e54 <__udivdi3+0x224> +40006d67: 401400 ssl a4 +40006d6a: c06b40 sub a6, a11, a4 +40006d6d: a19900 sll a9, a9 +40006d70: 400600 ssr a6 +40006d73: 91d080 srl a13, a8 +40006d76: 401400 ssl a4 +40006d79: a15800 sll a5, a8 +40006d7c: 400600 ssr a6 +40006d7f: 9130a0 srl a3, a10 +40006d82: 401400 ssl a4 +40006d85: 203350 or a3, a3, a5 +40006d88: f44090 extui a4, a9, 0, 16 +40006d8b: a12a00 sll a2, a10 +40006d8e: f55090 extui a5, a9, 16, 16 +40006d91: 404010 ssai 16 +40006d94: c2cd50 quou a12, a13, a5 +40006d97: 0cbd mov.n a11, a12 +40006d99: e2dd50 remu a13, a13, a5 +40006d9c: 81dd30 src a13, a13, a3 +40006d9f: 82a4c0 mull a10, a4, a12 +40006da2: 0d8d mov.n a8, a13 +40006da4: 10bda7 bgeu a13, a10, 40006db8 <__udivdi3+0x188> +40006da7: ffccb2 addi a11, a12, -1 +40006daa: 808d90 add a8, a13, a9 +40006dad: 073897 bltu a8, a9, 40006db8 <__udivdi3+0x188> +40006db0: 04b8a7 bgeu a8, a10, 40006db8 <__udivdi3+0x188> +40006db3: feccb2 addi a11, a12, -2 +40006db6: 898a add.n a8, a9, a8 +40006db8: c0d8a0 sub a13, a8, a10 +40006dbb: e2ed50 remu a14, a13, a5 +40006dbe: 0bfe30 depbits a3, a14, 16, 16 +40006dc1: c2dd50 quou a13, a13, a5 +40006dc4: 0dad mov.n a10, a13 +40006dc6: 82c4d0 mull a12, a4, a13 +40006dc9: 038d mov.n a8, a3 +40006dcb: 0eb3c7 bgeu a3, a12, 40006ddd <__udivdi3+0x1ad> +40006dce: ad0b addi.n a10, a13, -1 +40006dd0: 839a add.n a8, a3, a9 +40006dd2: 073897 bltu a8, a9, 40006ddd <__udivdi3+0x1ad> +40006dd5: 04b8c7 bgeu a8, a12, 40006ddd <__udivdi3+0x1ad> +40006dd8: fecda2 addi a10, a13, -2 +40006ddb: 898a add.n a8, a9, a8 +40006ddd: c038c0 sub a3, a8, a12 +40006de0: 11bb00 slli a11, a11, 16 +40006de3: 20bab0 or a11, a10, a11 +40006de6: 404010 ssai 16 +40006de9: e2d350 remu a13, a3, a5 +40006dec: c2c350 quou a12, a3, a5 +40006def: 8284c0 mull a8, a4, a12 +40006df2: 0c3d mov.n a3, a12 +40006df4: 81dd20 src a13, a13, a2 +40006df7: 0dad mov.n a10, a13 +40006df9: 0fbd87 bgeu a13, a8, 40006e0c <__udivdi3+0x1dc> +40006dfc: 3c0b addi.n a3, a12, -1 +40006dfe: ad9a add.n a10, a13, a9 +40006e00: 083a97 bltu a10, a9, 40006e0c <__udivdi3+0x1dc> +40006e03: 05ba87 bgeu a10, a8, 40006e0c <__udivdi3+0x1dc> +40006e06: fecc32 addi a3, a12, -2 +40006e09: 80a9a0 add a10, a9, a10 +40006e0c: c0ea80 sub a14, a10, a8 +40006e0f: c28e50 quou a8, a14, a5 +40006e12: e2ee50 remu a14, a14, a5 +40006e15: 0bfe20 depbits a2, a14, 16, 16 +40006e18: 825480 mull a5, a4, a8 +40006e1b: 084d mov.n a4, a8 +40006e1d: 23b257 bgeu a2, a5, 40006e44 <__udivdi3+0x214> +40006e20: 480b addi.n a4, a8, -1 +40006e22: 229a add.n a2, a2, a9 +40006e24: 1c3297 bltu a2, a9, 40006e44 <__udivdi3+0x214> +40006e27: 19b257 bgeu a2, a5, 40006e44 <__udivdi3+0x214> +40006e2a: 113300 slli a3, a3, 16 +40006e2d: fec822 addi a2, a8, -2 +40006e30: 202230 or a2, a2, a3 +40006e33: 0b3d mov.n a3, a11 +40006e35: f01d retw.n +40006e37: 353700 extui a3, a0, 23, 4 +40006e3a: 324702 s8i a0, a7, 50 +40006e3d: 0f .byte 0xf +40006e3e: 120c movi.n a2, 1 +40006e40: 030c movi.n a3, 0 +40006e42: f01d retw.n +40006e44: 112300 slli a2, a3, 16 +40006e47: 202420 or a2, a4, a2 +40006e4a: 0b3d mov.n a3, a11 +40006e4c: f01d retw.n +40006e4e: 020c movi.n a2, 0 +40006e50: 030c movi.n a3, 0 +40006e52: f01d retw.n +40006e54: c03890 sub a3, a8, a9 +40006e57: f55090 extui a5, a9, 16, 16 +40006e5a: f44090 extui a4, a9, 0, 16 +40006e5d: 1b0c movi.n a11, 1 +40006e5f: ffe0c6 j 40006de6 <__udivdi3+0x1b6> + ... + +40006e64 <__umoddi3>: +40006e64: 00a136 entry a1, 80 +40006e67: 029d mov.n a9, a2 +40006e69: 03ad mov.n a10, a3 +40006e6b: 0b2c movi.n a11, 32 +40006e6d: 048d mov.n a8, a4 +40006e6f: 0d2516 beqz a5, 40006f45 <__umoddi3+0xe1> +40006e72: 02b357 bgeu a3, a5, 40006e78 <__umoddi3+0x14> +40006e75: 0031c6 j 40006f40 <__umoddi3+0xdc> +40006e78: 40f580 nsau a8, a5 +40006e7b: 151816 beqz a8, 40006fd0 <__umoddi3+0x16c> +40006e7e: c0eb80 sub a14, a11, a8 +40006e81: 400e00 ssr a14 +40006e84: 916030 srl a6, a3 +40006e87: 401800 ssl a8 +40006e8a: 81c320 src a12, a3, a2 +40006e8d: 815540 src a5, a5, a4 +40006e90: f4b050 extui a11, a5, 0, 16 +40006e93: f5d050 extui a13, a5, 16, 16 +40006e96: 404010 ssai 16 +40006e99: c2f6d0 quou a15, a6, a13 +40006e9c: 0f9d mov.n a9, a15 +40006e9e: 82abf0 mull a10, a11, a15 +40006ea1: e266d0 remu a6, a6, a13 +40006ea4: 8166c0 src a6, a6, a12 +40006ea7: 063d mov.n a3, a6 +40006ea9: 0fb6a7 bgeu a6, a10, 40006ebc <__umoddi3+0x58> +40006eac: 9f0b addi.n a9, a15, -1 +40006eae: 356a add.n a3, a5, a6 +40006eb0: 083357 bltu a3, a5, 40006ebc <__umoddi3+0x58> +40006eb3: 05b3a7 bgeu a3, a10, 40006ebc <__umoddi3+0x58> +40006eb6: fecf92 addi a9, a15, -2 +40006eb9: 803530 add a3, a5, a3 +40006ebc: 401800 ssl a8 +40006ebf: 119900 slli a9, a9, 16 +40006ec2: c063a0 sub a6, a3, a10 +40006ec5: c2f6d0 quou a15, a6, a13 +40006ec8: 0fad mov.n a10, a15 +40006eca: 82bbf0 mull a11, a11, a15 +40006ecd: e266d0 remu a6, a6, a13 +40006ed0: 0bf6c0 depbits a12, a6, 16, 16 +40006ed3: 0c3d mov.n a3, a12 +40006ed5: 0fbcb7 bgeu a12, a11, 40006ee8 <__umoddi3+0x84> +40006ed8: af0b addi.n a10, a15, -1 +40006eda: 35ca add.n a3, a5, a12 +40006edc: 083357 bltu a3, a5, 40006ee8 <__umoddi3+0x84> +40006edf: 05b3b7 bgeu a3, a11, 40006ee8 <__umoddi3+0x84> +40006ee2: fecfa2 addi a10, a15, -2 +40006ee5: 803530 add a3, a5, a3 +40006ee8: c033b0 sub a3, a3, a11 +40006eeb: 209a90 or a9, a10, a9 +40006eee: a1b400 sll a11, a4 +40006ef1: 8249b0 mull a4, a9, a11 +40006ef4: a299b0 muluh a9, a9, a11 +40006ef7: c0b4b0 sub a11, a4, a11 +40006efa: 09ad mov.n a10, a9 +40006efc: 0d3397 bltu a3, a9, 40006f0d <__umoddi3+0xa9> +40006eff: a12200 sll a2, a2 +40006f02: 199397 bne a3, a9, 40006f1f <__umoddi3+0xbb> +40006f05: 16b247 bgeu a2, a4, 40006f1f <__umoddi3+0xbb> +40006f08: 000106 j 40006f10 <__umoddi3+0xac> +40006f0b: 000000 ill +40006f0e: 0ca122 movi a2, 0x10c +40006f11: 5019 s32i.n a1, a0, 20 +40006f13: c0ca add.n a12, a0, a12 +40006f15: 0134b7 bltu a4, a11, 40006f1a <__umoddi3+0xb6> +40006f18: 090c movi.n a9, 0 +40006f1a: 0b4d mov.n a4, a11 +40006f1c: c09c90 sub a9, a12, a9 +40006f1f: c03390 sub a3, a3, a9 +40006f22: c04240 sub a4, a2, a4 +40006f25: 02b247 bgeu a2, a4, 40006f2b <__umoddi3+0xc7> +40006f28: 002346 j 40006fb9 <__umoddi3+0x155> +40006f2b: 401e00 ssl a14 +40006f2e: a16300 sll a6, a3 +40006f31: 400800 ssr a8 +40006f34: 913030 srl a3, a3 +40006f37: 912040 srl a2, a4 +40006f3a: 202260 or a2, a2, a6 +40006f3d: f01d retw.n +40006f3f: f01d00 subx8 a1, a13, a0 +40006f42: 000000 ill +40006f45: 023347 bltu a3, a4, 40006f4b <__umoddi3+0xe7> +40006f48: 002986 j 40006ff2 <__umoddi3+0x18e> +40006f4b: 40f450 nsau a5, a4 +40006f4e: a58c beqz.n a5, 40006f5c <__umoddi3+0xf8> +40006f50: 401500 ssl a5 +40006f53: a18400 sll a8, a4 +40006f56: 813320 src a3, a3, a2 +40006f59: a19200 sll a9, a2 +40006f5c: f42080 extui a2, a8, 0, 16 +40006f5f: 404010 ssai 16 +40006f62: f54080 extui a4, a8, 16, 16 +40006f65: c27340 quou a7, a3, a4 +40006f68: e2b340 remu a11, a3, a4 +40006f6b: 81bb90 src a11, a11, a9 +40006f6e: 823270 mull a3, a2, a7 +40006f71: 0bad mov.n a10, a11 +40006f73: 09bb37 bgeu a11, a3, 40006f80 <__umoddi3+0x11c> +40006f76: ab8a add.n a10, a11, a8 +40006f78: 043a87 bltu a10, a8, 40006f80 <__umoddi3+0x11c> +40006f7b: 01ba37 bgeu a10, a3, 40006f80 <__umoddi3+0x11c> +40006f7e: a8aa add.n a10, a8, a10 +40006f80: c06a30 sub a6, a10, a3 +40006f83: e2c640 remu a12, a6, a4 +40006f86: 0bfc90 depbits a9, a12, 16, 16 +40006f89: c24640 quou a4, a6, a4 +40006f8c: 824240 mull a4, a2, a4 +40006f8f: 093d mov.n a3, a9 +40006f91: 17b947 bgeu a9, a4, 40006fac <__umoddi3+0x148> +40006f94: 398a add.n a3, a9, a8 +40006f96: 123387 bltu a3, a8, 40006fac <__umoddi3+0x148> +40006f99: 0fb347 bgeu a3, a4, 40006fac <__umoddi3+0x148> +40006f9c: 400500 ssr a5 +40006f9f: 383a add.n a3, a8, a3 +40006fa1: c02340 sub a2, a3, a4 +40006fa4: 912020 srl a2, a2 +40006fa7: 030c movi.n a3, 0 +40006fa9: f01d retw.n +40006fab: 234000 sext a4, a0, 7 +40006fae: 0500c0 extui a0, a12, 16, 1 +40006fb1: 030c40 rsr.scompare1 a4 +40006fb4: 912020 srl a2, a2 +40006fb7: f01d retw.n +40006fb9: 401e00 ssl a14 +40006fbc: 330b addi.n a3, a3, -1 +40006fbe: a16300 sll a6, a3 +40006fc1: 400800 ssr a8 +40006fc4: 913030 srl a3, a3 +40006fc7: 912040 srl a2, a4 +40006fca: 202260 or a2, a2, a6 +40006fcd: f01d retw.n +40006fcf: 353700 extui a3, a0, 23, 4 +40006fd2: b24705 call0 3ffb9444 <_start-0x46bbc> +40006fd5: 3ac602 addi a0, a6, 58 +40006fd8: 424000 xorb b4, b0, b0 +40006fdb: 3247c0 orbc b4, b7, b12 +40006fde: 2d08 l32i.n a0, a13, 8 +40006fe0: 6a5004 excw +40006fe3: 063dc0 excw +40006fe6: f01d retw.n +40006fe8: 042d mov.n a2, a4 +40006fea: c06a50 sub a6, a10, a5 +40006fed: 360b addi.n a3, a6, -1 +40006fef: f01d retw.n +40006ff1: 34cc00 extui a12, a0, 12, 4 +40006ff4: 180c movi.n a8, 1 +40006ff6: c28840 quou a8, a8, a4 +40006ff9: 40f850 nsau a5, a8 +40006ffc: 0c8516 beqz a5, 400070c8 <__umoddi3+0x264> +40006fff: 401500 ssl a5 +40007002: c06b50 sub a6, a11, a5 +40007005: a18800 sll a8, a8 +40007008: 400600 ssr a6 +4000700b: 91c0a0 srl a12, a10 +4000700e: 401500 ssl a5 +40007011: a14a00 sll a4, a10 +40007014: 400600 ssr a6 +40007017: 913020 srl a3, a2 +4000701a: 203340 or a3, a3, a4 +4000701d: 401500 ssl a5 +40007020: a19200 sll a9, a2 +40007023: f54080 extui a4, a8, 16, 16 +40007026: f42080 extui a2, a8, 0, 16 +40007029: c2bc40 quou a11, a12, a4 +4000702c: 404010 ssai 16 +4000702f: e2cc40 remu a12, a12, a4 +40007032: 81cc30 src a12, a12, a3 +40007035: 82b2b0 mull a11, a2, a11 +40007038: 0cad mov.n a10, a12 +4000703a: 0abcb7 bgeu a12, a11, 40007048 <__umoddi3+0x1e4> +4000703d: 80ac80 add a10, a12, a8 +40007040: 043a87 bltu a10, a8, 40007048 <__umoddi3+0x1e4> +40007043: 01bab7 bgeu a10, a11, 40007048 <__umoddi3+0x1e4> +40007046: a8aa add.n a10, a8, a10 +40007048: c0bab0 sub a11, a10, a11 +4000704b: e2cb40 remu a12, a11, a4 +4000704e: 0bfc30 depbits a3, a12, 16, 16 +40007051: c2bb40 quou a11, a11, a4 +40007054: 82b2b0 mull a11, a2, a11 +40007057: 20a330 or a10, a3, a3 +4000705a: 0bb3b7 bgeu a3, a11, 40007069 <__umoddi3+0x205> +4000705d: 80a380 add a10, a3, a8 +40007060: 053a87 bltu a10, a8, 40007069 <__umoddi3+0x205> +40007063: 02bab7 bgeu a10, a11, 40007069 <__umoddi3+0x205> +40007066: 80a8a0 add a10, a8, a10 +40007069: c03ab0 sub a3, a10, a11 +4000706c: e2b340 remu a11, a3, a4 +4000706f: 404010 ssai 16 +40007072: c23340 quou a3, a3, a4 +40007075: 81bb90 src a11, a11, a9 +40007078: 823230 mull a3, a2, a3 +4000707b: 0bad mov.n a10, a11 +4000707d: 0bbb37 bgeu a11, a3, 4000708c <__umoddi3+0x228> +40007080: 80ab80 add a10, a11, a8 +40007083: 053a87 bltu a10, a8, 4000708c <__umoddi3+0x228> +40007086: 02ba37 bgeu a10, a3, 4000708c <__umoddi3+0x228> +40007089: 80a8a0 add a10, a8, a10 +4000708c: c07a30 sub a7, a10, a3 +4000708f: e26740 remu a6, a7, a4 +40007092: 0bf690 depbits a9, a6, 16, 16 +40007095: c24740 quou a4, a7, a4 +40007098: 824240 mull a4, a2, a4 +4000709b: 093d mov.n a3, a9 +4000709d: 17b947 bgeu a9, a4, 400070b8 <__umoddi3+0x254> +400070a0: 398a add.n a3, a9, a8 +400070a2: 123387 bltu a3, a8, 400070b8 <__umoddi3+0x254> +400070a5: 0fb347 bgeu a3, a4, 400070b8 <__umoddi3+0x254> +400070a8: 400500 ssr a5 +400070ab: 383a add.n a3, a8, a3 +400070ad: c02340 sub a2, a3, a4 +400070b0: 912020 srl a2, a2 +400070b3: 030c movi.n a3, 0 +400070b5: f01d retw.n +400070b7: 234000 sext a4, a0, 7 +400070ba: 0500c0 extui a0, a12, 16, 1 +400070bd: 030c40 rsr.scompare1 a4 +400070c0: 912020 srl a2, a2 +400070c3: f01d retw.n +400070c5: f01d retw.n +400070c7: 3a8000 depbits a0, a0, 3, 9 +400070ca: 4080c0 rotw -4 +400070cd: 2080f5 call12 400278dc <__bss_start+0x178dc> +400070d0: e5c6f4 excw +400070d3: ff .byte 0xff +400070d4: ffc000 excw +400070d7: 3f .byte 0x3f +400070d8: ffc864 excw +400070db: 3f .byte 0x3f +400070dc: 007104 excw +400070df: c87040 excw +400070e2: ff .byte 0xff +400070e3: 3f .byte 0x3f +400070e4: ffcd40 excw +400070e7: 3f .byte 0x3f +400070e8: 007974 excw +400070eb: cd4040 excw +400070ee: ff .byte 0xff +400070ef: 3f .byte 0x3f +400070f0: ffcd44 excw +400070f3: 3f .byte 0x3f +400070f4: 007e44 excw +400070f7: 000040 excw + ... +4000710a: b80000 excw +4000710d: 400066 bnei a0, -1, 40007151 <__umoddi3+0x2ed> +40007110: 0818 l32i.n a1, a8, 0 +40007112: b84000 excw +40007115: 400066 bnei a0, -1, 40007159 <__umoddi3+0x2f5> +40007118: 66b8 l32i.n a11, a6, 24 +4000711a: 9c4000 excw +4000711d: 0008 l32i.n a0, a0, 0 +4000711f: 001040 movsp a4, a0 +40007122: b84000 excw +40007125: 400066 bnei a0, -1, 40007169 <__umoddi3+0x305> +40007128: 66b8 l32i.n a11, a6, 24 +4000712a: b84000 excw +4000712d: 400066 bnei a0, -1, 40007171 <__umoddi3+0x30d> +40007130: 66b8 l32i.n a11, a6, 24 +40007132: b84000 excw +40007135: 400066 bnei a0, -1, 40007179 <__umoddi3+0x315> +40007138: 66b8 l32i.n a11, a6, 24 +4000713a: 304000 xor a4, a0, a0 +4000713d: 0e .byte 0xe +4000713e: 304000 xor a4, a0, a0 +40007141: 0e .byte 0xe +40007142: 304000 xor a4, a0, a0 +40007145: 0e .byte 0xe +40007146: 304000 xor a4, a0, a0 +40007149: 0e .byte 0xe +4000714a: b84000 excw +4000714d: 400066 bnei a0, -1, 40007191 <__umoddi3+0x32d> +40007150: 66b8 l32i.n a11, a6, 24 +40007152: b84000 excw +40007155: 400066 bnei a0, -1, 40007199 <__umoddi3+0x335> +40007158: 66b8 l32i.n a11, a6, 24 +4000715a: b84000 excw +4000715d: 400066 bnei a0, -1, 400071a1 <__umoddi3+0x33d> +40007160: 66b8 l32i.n a11, a6, 24 +40007162: b84000 excw +40007165: 400066 bnei a0, -1, 400071a9 <__umoddi3+0x345> +40007168: 66b8 l32i.n a11, a6, 24 +4000716a: b84000 excw +4000716d: 400066 bnei a0, -1, 400071b1 <__umoddi3+0x34d> +40007170: 66b8 l32i.n a11, a6, 24 +40007172: b84000 excw +40007175: 400066 bnei a0, -1, 400071b9 <__umoddi3+0x355> +40007178: 66b8 l32i.n a11, a6, 24 +4000717a: b84000 excw +4000717d: 400066 bnei a0, -1, 400071c1 <__umoddi3+0x35d> +40007180: 66b8 l32i.n a11, a6, 24 +40007182: b84000 excw +40007185: 400066 bnei a0, -1, 400071c9 <__umoddi3+0x365> +40007188: 66b8 l32i.n a11, a6, 24 +4000718a: b84000 excw +4000718d: 400066 bnei a0, -1, 400071d1 <__umoddi3+0x36d> +40007190: 66b8 l32i.n a11, a6, 24 +40007192: b84000 excw +40007195: 400066 bnei a0, -1, 400071d9 <__umoddi3+0x375> +40007198: 66b8 l32i.n a11, a6, 24 +4000719a: b84000 excw +4000719d: 400066 bnei a0, -1, 400071e1 <__umoddi3+0x37d> +400071a0: 66b8 l32i.n a11, a6, 24 +400071a2: b84000 excw +400071a5: 400066 bnei a0, -1, 400071e9 <__umoddi3+0x385> +400071a8: 66b8 l32i.n a11, a6, 24 +400071aa: b84000 excw +400071ad: 400066 bnei a0, -1, 400071f1 <__umoddi3+0x38d> +400071b0: 66b8 l32i.n a11, a6, 24 +400071b2: b84000 excw +400071b5: 400066 bnei a0, -1, 400071f9 <__umoddi3+0x395> +400071b8: 66b8 l32i.n a11, a6, 24 +400071ba: b84000 excw +400071bd: 400066 bnei a0, -1, 40007201 <__umoddi3+0x39d> +400071c0: 66b8 l32i.n a11, a6, 24 +400071c2: b84000 excw +400071c5: 400066 bnei a0, -1, 40007209 <__umoddi3+0x3a5> +400071c8: 66b8 l32i.n a11, a6, 24 +400071ca: b84000 excw +400071cd: 400066 bnei a0, -1, 40007211 <__umoddi3+0x3ad> +400071d0: 66b8 l32i.n a11, a6, 24 +400071d2: b84000 excw +400071d5: 400066 bnei a0, -1, 40007219 <__umoddi3+0x3b5> +400071d8: 66b8 l32i.n a11, a6, 24 +400071da: b84000 excw +400071dd: 400066 bnei a0, -1, 40007221 <__umoddi3+0x3bd> +400071e0: 66b8 l32i.n a11, a6, 24 +400071e2: b84000 excw +400071e5: 400066 bnei a0, -1, 40007229 <__umoddi3+0x3c5> +400071e8: 66b8 l32i.n a11, a6, 24 +400071ea: b84000 excw +400071ed: 400066 bnei a0, -1, 40007231 <__umoddi3+0x3cd> +400071f0: 66b8 l32i.n a11, a6, 24 +400071f2: b84000 excw +400071f5: 400066 bnei a0, -1, 40007239 <__umoddi3+0x3d5> +400071f8: 66b8 l32i.n a11, a6, 24 +400071fa: b84000 excw +400071fd: 400066 bnei a0, -1, 40007241 <__umoddi3+0x3dd> +40007200: 66b8 l32i.n a11, a6, 24 +40007202: b84000 excw +40007205: 400066 bnei a0, -1, 40007249 <__umoddi3+0x3e5> +40007208: 66b8 l32i.n a11, a6, 24 +4000720a: 684000 excw +4000720d: 400066 bnei a0, -1, 40007251 <__umoddi3+0x3ed> +40007210: 6668 l32i.n a6, a6, 24 +40007212: 684000 excw +40007215: 400066 bnei a0, -1, 40007259 <__umoddi3+0x3f5> +40007218: 6668 l32i.n a6, a6, 24 +4000721a: 684000 excw +4000721d: 400066 bnei a0, -1, 40007261 <__umoddi3+0x3fd> +40007220: 6668 l32i.n a6, a6, 24 +40007222: 684000 excw +40007225: 400066 bnei a0, -1, 40007269 <__umoddi3+0x405> +40007228: 6668 l32i.n a6, a6, 24 +4000722a: 684000 excw +4000722d: 400066 bnei a0, -1, 40007271 <__umoddi3+0x40d> +40007230: 6668 l32i.n a6, a6, 24 +40007232: 684000 excw +40007235: 400066 bnei a0, -1, 40007279 <__umoddi3+0x415> +40007238: 6668 l32i.n a6, a6, 24 +4000723a: 684000 excw +4000723d: 400066 bnei a0, -1, 40007281 <__umoddi3+0x41d> +40007240: 6668 l32i.n a6, a6, 24 +40007242: 684000 excw +40007245: 400066 bnei a0, -1, 40007289 <__umoddi3+0x425> +40007248: 6668 l32i.n a6, a6, 24 +4000724a: 684000 excw +4000724d: 400066 bnei a0, -1, 40007291 <__umoddi3+0x42d> +40007250: 6668 l32i.n a6, a6, 24 +40007252: 684000 excw +40007255: 400066 bnei a0, -1, 40007299 <__umoddi3+0x435> +40007258: 6668 l32i.n a6, a6, 24 +4000725a: 684000 excw +4000725d: 400066 bnei a0, -1, 400072a1 <__umoddi3+0x43d> +40007260: 6668 l32i.n a6, a6, 24 +40007262: 684000 excw +40007265: 400066 bnei a0, -1, 400072a9 <__umoddi3+0x445> +40007268: 6668 l32i.n a6, a6, 24 +4000726a: 684000 excw +4000726d: 400066 bnei a0, -1, 400072b1 <__umoddi3+0x44d> +40007270: 6668 l32i.n a6, a6, 24 +40007272: 684000 excw +40007275: 400066 bnei a0, -1, 400072b9 <__umoddi3+0x455> +40007278: 6668 l32i.n a6, a6, 24 +4000727a: 684000 excw +4000727d: 400066 bnei a0, -1, 400072c1 <__umoddi3+0x45d> +40007280: 6668 l32i.n a6, a6, 24 +40007282: 684000 excw +40007285: 400066 bnei a0, -1, 400072c9 <__umoddi3+0x465> +40007288: 6668 l32i.n a6, a6, 24 +4000728a: 684000 excw +4000728d: 400066 bnei a0, -1, 400072d1 <__umoddi3+0x46d> +40007290: 6668 l32i.n a6, a6, 24 +40007292: 684000 excw +40007295: 400066 bnei a0, -1, 400072d9 <__umoddi3+0x475> +40007298: 6668 l32i.n a6, a6, 24 +4000729a: 684000 excw +4000729d: 400066 bnei a0, -1, 400072e1 <__umoddi3+0x47d> +400072a0: 6668 l32i.n a6, a6, 24 +400072a2: 684000 excw +400072a5: 400066 bnei a0, -1, 400072e9 <__umoddi3+0x485> +400072a8: 6668 l32i.n a6, a6, 24 +400072aa: 684000 excw +400072ad: 400066 bnei a0, -1, 400072f1 <__umoddi3+0x48d> +400072b0: 6668 l32i.n a6, a6, 24 +400072b2: 684000 excw +400072b5: 400066 bnei a0, -1, 400072f9 <__umoddi3+0x495> +400072b8: 6668 l32i.n a6, a6, 24 +400072ba: 684000 excw +400072bd: 400066 bnei a0, -1, 40007301 <__umoddi3+0x49d> +400072c0: 6668 l32i.n a6, a6, 24 +400072c2: 684000 excw +400072c5: 400066 bnei a0, -1, 40007309 <__umoddi3+0x4a5> +400072c8: 6668 l32i.n a6, a6, 24 +400072ca: 684000 excw +400072cd: 400066 bnei a0, -1, 40007311 <__umoddi3+0x4ad> +400072d0: 6668 l32i.n a6, a6, 24 +400072d2: 684000 excw +400072d5: 400066 bnei a0, -1, 40007319 <__umoddi3+0x4b5> +400072d8: 6668 l32i.n a6, a6, 24 +400072da: 684000 excw +400072dd: 400066 bnei a0, -1, 40007321 <__umoddi3+0x4bd> +400072e0: 6668 l32i.n a6, a6, 24 +400072e2: 684000 excw +400072e5: 400066 bnei a0, -1, 40007329 <__umoddi3+0x4c5> +400072e8: 6668 l32i.n a6, a6, 24 +400072ea: 684000 excw +400072ed: 400066 bnei a0, -1, 40007331 <__umoddi3+0x4cd> +400072f0: 6668 l32i.n a6, a6, 24 +400072f2: 684000 excw +400072f5: 400066 bnei a0, -1, 40007339 <__umoddi3+0x4d5> +400072f8: 6668 l32i.n a6, a6, 24 +400072fa: 684000 excw +400072fd: 400066 bnei a0, -1, 40007341 <__umoddi3+0x4dd> +40007300: 6668 l32i.n a6, a6, 24 +40007302: 684000 excw +40007305: 400066 bnei a0, -1, 40007349 <__umoddi3+0x4e5> +40007308: 6668 l32i.n a6, a6, 24 +4000730a: 004000 break 0, 0 + ... +40007315: 000000 ill +40007318: ff .byte 0xff +40007319: ff .byte 0xff +4000731a: ff .byte 0xff +4000731b: ff .byte 0xff +4000731c: 0066b0 rsil a11, 6 +4000731f: 001f40 movsp a4, a15 +40007322: b00000 addx8 a0, a0, a0 +40007325: 400066 bnei a0, -1, 40007369 <__umoddi3+0x505> +40007328: 1e .byte 0x1e +40007329: 000000 ill +4000732c: 0066b0 rsil a11, 6 +4000732f: 001d40 movsp a4, a13 +40007332: b00000 addx8 a0, a0, a0 +40007335: 400066 bnei a0, -1, 40007379 <__umoddi3+0x515> +40007338: 001c movi.n a0, 16 +4000733a: b00000 addx8 a0, a0, a0 +4000733d: 400066 bnei a0, -1, 40007381 <__umoddi3+0x51d> +40007340: 001b addi.n a0, a0, 1 +40007342: b00000 addx8 a0, a0, a0 +40007345: 400066 bnei a0, -1, 40007389 <__umoddi3+0x525> +40007348: 001a add.n a0, a0, a1 +4000734a: b00000 addx8 a0, a0, a0 +4000734d: 400066 bnei a0, -1, 40007391 <__umoddi3+0x52d> +40007350: 0019 s32i.n a1, a0, 0 +40007352: b00000 addx8 a0, a0, a0 +40007355: 400066 bnei a0, -1, 40007399 <__umoddi3+0x535> +40007358: 0018 l32i.n a1, a0, 0 +4000735a: b00000 addx8 a0, a0, a0 +4000735d: 400066 bnei a0, -1, 400073a1 <__umoddi3+0x53d> +40007360: 000017 bnone a0, a1, 40007364 <__umoddi3+0x500> +40007363: 66b000 excw +40007366: 164000 excw +40007369: 000000 ill +4000736c: 0066b0 rsil a11, 6 +4000736f: 001540 movsp a4, a5 +40007372: b00000 addx8 a0, a0, a0 +40007375: 400066 bnei a0, -1, 400073b9 <__umoddi3+0x555> +40007378: 000014 excw +4000737b: 66b000 excw +4000737e: 134000 excw +40007381: 000000 ill +40007384: 0066b0 rsil a11, 6 +40007387: 001240 movsp a4, a2 +4000738a: b00000 addx8 a0, a0, a0 +4000738d: 400066 bnei a0, -1, 400073d1 <__umoddi3+0x56d> +40007390: 000011 l32r a1, 3ffc7390 <_start-0x38c70> +40007393: 66b000 excw +40007396: 104000 and a4, a0, a0 +40007399: 000000 ill +4000739c: 0066b0 rsil a11, 6 +4000739f: 000f40 excw +400073a2: b00000 addx8 a0, a0, a0 +400073a5: 400066 bnei a0, -1, 400073e9 <__umoddi3+0x585> +400073a8: 0e .byte 0xe +400073a9: 000000 ill +400073ac: 0066b0 rsil a11, 6 +400073af: 000d40 excw +400073b2: b00000 addx8 a0, a0, a0 +400073b5: 400066 bnei a0, -1, 400073f9 <__umoddi3+0x595> +400073b8: 000c movi.n a0, 0 +400073ba: b00000 addx8 a0, a0, a0 +400073bd: 400066 bnei a0, -1, 40007401 <__umoddi3+0x59d> +400073c0: 000b addi.n a0, a0, -1 +400073c2: b00000 addx8 a0, a0, a0 +400073c5: 400066 bnei a0, -1, 40007409 <__umoddi3+0x5a5> +400073c8: 000a add.n a0, a0, a0 +400073ca: b00000 addx8 a0, a0, a0 +400073cd: 400066 bnei a0, -1, 40007411 <__umoddi3+0x5ad> +400073d0: 0009 s32i.n a0, a0, 0 +400073d2: b00000 addx8 a0, a0, a0 +400073d5: 400066 bnei a0, -1, 40007419 <__umoddi3+0x5b5> +400073d8: 0008 l32i.n a0, a0, 0 +400073da: b00000 addx8 a0, a0, a0 +400073dd: 400066 bnei a0, -1, 40007421 <__umoddi3+0x5bd> +400073e0: 000007 bnone a0, a0, 400073e4 <__umoddi3+0x580> +400073e3: 66b000 excw +400073e6: 064000 excw +400073e9: 000000 ill +400073ec: 0066b0 rsil a11, 6 +400073ef: 000540 excw +400073f2: b00000 addx8 a0, a0, a0 +400073f5: 400066 bnei a0, -1, 40007439 <__umoddi3+0x5d5> +400073f8: 000004 excw +400073fb: 66b000 excw +400073fe: 034000 excw +40007401: 000000 ill +40007404: 0066b0 rsil a11, 6 +40007407: 000240 excw +4000740a: b00000 addx8 a0, a0, a0 +4000740d: 400066 bnei a0, -1, 40007451 <__umoddi3+0x5ed> +40007410: 000001 l32r a0, 3ffc7410 <_start-0x38bf0> +40007413: 66b000 excw +40007416: 004000 break 0, 0 +40007419: 000000 ill +4000741c: 81c000 src a12, a0, a0 +4000741f: ff .byte 0xff +40007420: 000000 ill +40007423: c00080 sub a0, a0, a8 +40007426: 00ff81 l32r a8, 3ffc7824 <_start-0x387dc> +40007429: 400000 ssr a0 +4000742c: 81c000 src a12, a0, a0 +4000742f: ff .byte 0xff +40007430: 000000 ill +40007433: c00020 sub a0, a0, a2 +40007436: 00ff81 l32r a8, 3ffc7834 <_start-0x387cc> +40007439: 100000 and a0, a0, a0 +4000743c: 81c000 src a12, a0, a0 +4000743f: ff .byte 0xff +40007440: 000000 ill +40007443: 0008 l32i.n a0, a0, 0 +40007445: ff81c0 excw +40007448: 000000 ill +4000744b: c00004 excw +4000744e: 00ff81 l32r a8, 3ffc784c <_start-0x387b4> +40007451: 020000 andb b0, b0, b0 +40007454: 81c000 src a12, a0, a0 +40007457: ff .byte 0xff +40007458: 000000 ill +4000745b: c00001 l32r a0, 3fff745c <_start-0x8ba4> +4000745e: 00ff81 l32r a8, 3ffc785c <_start-0x387a4> +40007461: 008000 any4 b0, b0:b1:b2:b3 +40007464: 81c000 src a12, a0, a0 +40007467: ff .byte 0xff +40007468: 400000 ssr a0 +4000746b: c00000 sub a0, a0, a0 +4000746e: 00ffc1 l32r a12, 3ffc786c <_start-0x38794> +40007471: 002000 isync +40007474: e1c000 excw +40007477: ff .byte 0xff +40007478: 100000 and a0, a0, a0 +4000747b: c00000 sub a0, a0, a0 +4000747e: 00fff1 l32r a15, 3ffc787c <_start-0x38784> +40007481: 000800 excw +40007484: f9c000 excw +40007487: ff .byte 0xff +40007488: 040000 extui a0, a0, 0, 1 +4000748b: c00000 sub a0, a0, a0 +4000748e: fffd excw +40007490: 020000 andb b0, b0, b0 +40007493: c00000 sub a0, a0, a0 +40007496: ff .byte 0xff +40007497: ff .byte 0xff +40007498: 010000 slli a0, a0, 32 +4000749b: c00000 sub a0, a0, a0 +4000749e: ff .byte 0xff +4000749f: ff .byte 0xff +400074a0: 008000 any4 b0, b0:b1:b2:b3 +400074a3: c00000 sub a0, a0, a0 +400074a6: ff .byte 0xff +400074a7: ff .byte 0xff +400074a8: 004000 break 0, 0 +400074ab: c00000 sub a0, a0, a0 +400074ae: ff .byte 0xff +400074af: ff .byte 0xff +400074b0: 002000 isync +400074b3: e00000 subx4 a0, a0, a0 +400074b6: ff .byte 0xff +400074b7: ff .byte 0xff +400074b8: 001000 movsp a0, a0 +400074bb: f00000 subx8 a0, a0, a0 +400074be: ff .byte 0xff +400074bf: ff .byte 0xff +400074c0: 000800 excw +400074c3: f80000 excw +400074c6: ff .byte 0xff +400074c7: ff .byte 0xff +400074c8: 000400 excw +400074cb: fc0000 excw +400074ce: ff .byte 0xff +400074cf: ff .byte 0xff +400074d0: 000200 excw +400074d3: fe0000 excw +400074d6: ff .byte 0xff +400074d7: ff .byte 0xff +400074d8: 000100 excw +400074db: ff0000 excw +400074de: ff .byte 0xff +400074df: ff .byte 0xff +400074e0: 000080 ret +400074e3: ff8000 excw +400074e6: ff .byte 0xff +400074e7: ff .byte 0xff +400074e8: 000040 excw +400074eb: ffc000 excw +400074ee: ff .byte 0xff +400074ef: ff .byte 0xff +400074f0: 000020 excw +400074f3: ffe000 excw +400074f6: ff .byte 0xff +400074f7: ff .byte 0xff +400074f8: 000010 excw +400074fb: fff000 excw +400074fe: ff .byte 0xff +400074ff: ff .byte 0xff +40007500: 0008 l32i.n a0, a0, 0 +40007502: f80000 excw +40007505: ff .byte 0xff +40007506: ff .byte 0xff +40007507: ff .byte 0xff +40007508: 000004 excw +4000750b: fffc00 excw +4000750e: ff .byte 0xff +4000750f: ff .byte 0xff +40007510: 000002 l8ui a0, a0, 0 +40007513: fffe00 excw +40007516: ff .byte 0xff +40007517: ff .byte 0xff +40007518: 000001 l32r a0, 3ffc7518 <_start-0x38ae8> +4000751b: 000000 ill +4000751e: 040000 extui a0, a0, 0, 1 +40007521: 3fffc7 bbsi a15, 28, 40007564 <__umoddi3+0x700> +40007524: c76c movi.n a7, -20 +40007526: ff .byte 0xff +40007527: 3f .byte 0x3f +40007528: ffc7d4 excw +4000752b: 3f .byte 0x3f + ... +40007550: ffca50 excw +40007553: 3f .byte 0x3f + ... +400075c4: 000001 l32r a0, 3ffc75c4 <_start-0x38a3c> +400075c7: 000000 ill +400075ca: 0e0000 read_impwire a0 +400075cd: abcd33 excw +400075d0: 6d1234 excw +400075d3: deece6 bgei a12, 128, 400075b5 <__umoddi3+0x751> +400075d6: 0b0005 call0 400125d8 <__bss_start+0x25d8> + ... +40007941: 000000 ill +40007944: c418 l32i.n a1, a4, 48 +40007946: ff .byte 0xff +40007947: 3f .byte 0x3f +40007948: 000d mov.n a0, a0 +4000794a: 4c0000 excw +4000794d: ffc8 l32i.n a12, a15, 60 +4000794f: 3f .byte 0x3f +40007950: ef .byte 0xef +40007951: 001540 movsp a4, a5 +40007954: 200000 or a0, a0, a0 +40007957: 000000 ill +4000795a: 000001 l32r a0, 3ffc795c <_start-0x386a4> +4000795d: 000010 excw +40007960: 000100 excw +40007963: ffff00 excw + ... +40007972: 180000 excw +40007975: 3fffc4 excw + ... +40007984: 737465 call8 4007b0cc <__bss_start+0x6b0cc> +40007987: 5f .byte 0x5f +40007988: 706e75 call12 40078070 <__bss_start+0x68070> +4000798b: 6b6361 l32r a6, 3ffe2718 <_start-0x1d8e8> +4000798e: 5f .byte 0x5f +4000798f: 616c66 bnei a12, 6, 400079f4 <__umoddi3+0xb90> +40007992: 5f6873 excw +40007995: 646f63 excw +40007998: 000065 call8 400079a0 <__umoddi3+0xb3c> + ... +400079a3: 26b300 excw +400079a6: 684000 excw +400079a9: 400024 excw +400079ac: 2468 l32i.n a6, a4, 8 +400079ae: 684000 excw +400079b1: 400024 excw +400079b4: 2468 l32i.n a6, a4, 8 +400079b6: 684000 excw +400079b9: 400024 excw +400079bc: 2468 l32i.n a6, a4, 8 +400079be: 684000 excw +400079c1: 400024 excw +400079c4: 2468 l32i.n a6, a4, 8 +400079c6: 684000 excw +400079c9: 400024 excw +400079cc: 2468 l32i.n a6, a4, 8 +400079ce: 684000 excw +400079d1: 400024 excw +400079d4: 2468 l32i.n a6, a4, 8 +400079d6: 684000 excw +400079d9: 400024 excw +400079dc: 2468 l32i.n a6, a4, 8 +400079de: 684000 excw +400079e1: 400024 excw +400079e4: 2468 l32i.n a6, a4, 8 +400079e6: 684000 excw +400079e9: 400024 excw +400079ec: 2468 l32i.n a6, a4, 8 +400079ee: 684000 excw +400079f1: 400024 excw +400079f4: 2468 l32i.n a6, a4, 8 +400079f6: 684000 excw +400079f9: 400024 excw +400079fc: 2468 l32i.n a6, a4, 8 +400079fe: 684000 excw +40007a01: 400024 excw +40007a04: 2468 l32i.n a6, a4, 8 +40007a06: 684000 excw +40007a09: 400024 excw +40007a0c: 2468 l32i.n a6, a4, 8 +40007a0e: 684000 excw +40007a11: 400024 excw +40007a14: 2468 l32i.n a6, a4, 8 +40007a16: 684000 excw +40007a19: 400024 excw +40007a1c: 002681 l32r a8, 3ffc7ab4 <_start-0x3854c> +40007a1f: 253840 extui a3, a4, 24, 3 +40007a22: 684000 excw +40007a25: 400024 excw +40007a28: 2468 l32i.n a6, a4, 8 +40007a2a: 684000 excw +40007a2d: 400024 excw +40007a30: 2468 l32i.n a6, a4, 8 +40007a32: 684000 excw +40007a35: 400024 excw +40007a38: 2468 l32i.n a6, a4, 8 +40007a3a: 684000 excw +40007a3d: 400024 excw +40007a40: 2468 l32i.n a6, a4, 8 +40007a42: 684000 excw +40007a45: 400024 excw +40007a48: 2468 l32i.n a6, a4, 8 +40007a4a: 684000 excw +40007a4d: 400024 excw +40007a50: 2468 l32i.n a6, a4, 8 +40007a52: 684000 excw +40007a55: 400024 excw +40007a58: 2468 l32i.n a6, a4, 8 +40007a5a: 354000 extui a4, a0, 16, 4 +40007a5d: 400026 beqi a0, -1, 40007aa1 <__umoddi3+0xc3d> +40007a60: 2468 l32i.n a6, a4, 8 +40007a62: 384000 excw +40007a65: 400025 call8 40047a68 <__bss_start+0x37a68> +40007a68: 2468 l32i.n a6, a4, 8 +40007a6a: 684000 excw +40007a6d: 400024 excw +40007a70: 2538 l32i.n a3, a5, 8 +40007a72: 684000 excw +40007a75: 400024 excw +40007a78: 2468 l32i.n a6, a4, 8 +40007a7a: 684000 excw +40007a7d: 400024 excw +40007a80: 2468 l32i.n a6, a4, 8 +40007a82: 684000 excw +40007a85: 400024 excw +40007a88: 2468 l32i.n a6, a4, 8 +40007a8a: 684000 excw +40007a8d: 400024 excw +40007a90: 2468 l32i.n a6, a4, 8 +40007a92: 684000 excw +40007a95: 400024 excw +40007a98: 2468 l32i.n a6, a4, 8 +40007a9a: 814000 src a4, a0, a0 +40007a9d: 400026 beqi a0, -1, 40007ae1 <__umoddi3+0xc7d> +40007aa0: 2538 l32i.n a3, a5, 8 +40007aa2: 684000 excw +40007aa5: 400024 excw +40007aa8: 2468 l32i.n a6, a4, 8 +40007aaa: 684000 excw +40007aad: 400024 excw +40007ab0: 2468 l32i.n a6, a4, 8 +40007ab2: 684000 excw +40007ab5: 400024 excw +40007ab8: 2468 l32i.n a6, a4, 8 +40007aba: 684000 excw +40007abd: 400024 excw +40007ac0: 2468 l32i.n a6, a4, 8 +40007ac2: 684000 excw +40007ac5: 400024 excw +40007ac8: 2468 l32i.n a6, a4, 8 +40007aca: 684000 excw +40007acd: 400024 excw +40007ad0: 002527 blt a5, a2, 40007ad4 <__umoddi3+0xc70> +40007ad3: 246840 extui a6, a4, 8, 3 +40007ad6: 684000 excw +40007ad9: 400024 excw +40007adc: 002635 call12 40007d40 <__umoddi3+0xedc> +40007adf: 246840 extui a6, a4, 8, 3 +40007ae2: 384000 excw +40007ae5: 400025 call8 40047ae8 <__bss_start+0x37ae8> +40007ae8: 2468 l32i.n a6, a4, 8 +40007aea: 684000 excw +40007aed: 400024 excw +40007af0: 2538 l32i.n a3, a5, 8 +40007af2: 124000 andbc b4, b0, b0 +40007af5: 400030 excw +40007af8: 003012 excw +40007afb: 30c240 xor a12, a2, a4 +40007afe: b04000 addx8 a4, a0, a0 +40007b01: 400030 excw +40007b04: 3098 l32i.n a9, a0, 12 +40007b06: 1e4000 excw +40007b09: 400030 excw +40007b0c: 003084 excw +40007b0f: 307240 xor a7, a2, a4 +40007b12: 624000 excw +40007b15: 400030 excw +40007b18: 003052 excw +40007b1b: 304040 xor a4, a0, a4 +40007b1e: 304000 xor a4, a0, a0 +40007b21: 400030 excw +40007b24: 00dbc0 excw + ... +40007b33: 010100 slli a0, a1, 32 +40007b36: 010101 l32r a0, 3ffc7f3c <_start-0x380c4> +40007b39: 010101 l32r a0, 3ffc7f40 <_start-0x380c0> +40007b3c: 010101 l32r a0, 3ffc7f40 <_start-0x380c0> +40007b3f: 010101 l32r a0, 3ffc7f44 <_start-0x380bc> +40007b42: 050307 bnone a3, a0, 40007b4b <__umoddi3+0xce7> +40007b45: 010101 l32r a0, 3ffc7f4c <_start-0x380b4> +40007b48: 010101 l32r a0, 3ffc7f4c <_start-0x380b4> +40007b4b: 020202 l8ui a0, a2, 2 +40007b4e: 030302 l8ui a0, a3, 3 +40007b51: 050403 excw +40007b54: 000043 excw +40007b57: 200a00 or a0, a10, a0 +40007b5a: 737465 call8 4007b2a0 <__bss_start+0x6b2a0> +40007b5d: 732520 maxu a2, a5, a2 +40007b60: 722c movi.n a2, 39 +40007b62: 207473 excw +40007b65: 756163 excw +40007b68: 3a6573 excw +40007b6b: 2c6425 call8 400341ac <__bss_start+0x241ac> +40007b6e: 6f6220 excw +40007b71: 6f .byte 0x6f +40007b72: 6d2074 excw +40007b75: 6f .byte 0x6f +40007b76: 3a6564 excw +40007b79: 2528 l32i.n a2, a5, 8 +40007b7b: 2978 l32i.n a7, a9, 8 +40007b7d: 0a0a add.n a0, a10, a0 +40007b7f: 754a00 extui a4, a0, 26, 8 +40007b82: 206c movi.n a0, -30 +40007b84: 203520 or a3, a5, a2 +40007b87: 313032 excw +40007b8a: 250035 call12 4002cb8c <__bss_start+0x1cb8c> +40007b8d: 252073 excw +40007b90: 0a2075 call12 40011d98 <__bss_start+0x1d98> +40007b93: 746500 extui a6, a0, 5, 8 +40007b96: 6d5f73 excw +40007b99: 6e6961 l32r a6, 3ffe3540 <_start-0x1cac0> +40007b9c: 2e .byte 0x2e +40007b9d: 000063 excw +40007ba0: 657375 call12 4006d2d8 <__bss_start+0x5d2d8> +40007ba3: 632072 l32i a7, a0, 0x18c +40007ba6: 6f .byte 0x6f +40007ba7: 206564 excw +40007baa: 6e6f64 excw +40007bad: 000a65 call8 40007c54 <__umoddi3+0xdf0> +40007bb0: 696177 bbci a1, 7, 40007c1d <__umoddi3+0xdb9> +40007bb3: 6e6974 excw +40007bb6: 662067 blt a0, a6, 40007c20 <__umoddi3+0xdbc> +40007bb9: 6f .byte 0x6f +40007bba: 682072 l32i a7, a0, 0x1a0 +40007bbd: 6f .byte 0x6f +40007bbe: 0a7473 excw +40007bc1: 000000 ill +40007bc4: 697073 excw +40007bc7: 6d6320 excw +40007bca: 6c2064 excw +40007bcd: 206e65 call8 400282b4 <__bss_start+0x182b4> +40007bd0: 0a6425 call8 40012214 <__bss_start+0x2214> +40007bd3: 642500 extui a2, a0, 5, 7 +40007bd6: 696c20 excw +40007bd9: 6e .byte 0x6e +40007bda: 202c65 call8 40027ea0 <__bss_start+0x17ea0> +40007bdd: 206425 call8 40028220 <__bss_start+0x18220> +40007be0: 6f6d excw +40007be2: 0a6564 excw +40007be5: 000000 ill +40007be8: 6f6c movi.n a15, -26 +40007bea: 206461 l32r a6, 3ffcfd7c <_start-0x30284> +40007bed: 257830 extui a7, a3, 24, 3 +40007bf0: 783830 excw +40007bf3: 202c movi.n a0, 34 +40007bf5: 656c movi.n a5, -26 +40007bf7: 6e .byte 0x6e +40007bf8: 642520 extui a2, a2, 5, 7 +40007bfb: 202c movi.n a0, 34 +40007bfd: 6f6f72 s32i a7, a15, 0x1bc +40007c00: 206d excw +40007c02: 0a6425 call8 40012244 <__bss_start+0x2244> +40007c05: 000000 ill +40007c08: 616c66 bnei a12, 6, 40007c6d <__umoddi3+0xe09> +40007c0b: 206873 excw +40007c0e: 616572 s32i a7, a5, 0x184 +40007c11: 652064 excw +40007c14: 2c7272 excw +40007c17: 732520 maxu a2, a5, a2 +40007c1a: 000a add.n a0, a0, a0 +40007c1c: 696174 excw +40007c1f: 206c movi.n a0, -30 +40007c21: 0a6425 call8 40012264 <__bss_start+0x2264> +40007c24: 000000 ill +40007c27: 686300 excw +40007c2a: 736b addi.n a7, a3, 6 +40007c2c: 206d75 call12 40028304 <__bss_start+0x18304> +40007c2f: 257830 extui a7, a3, 24, 3 +40007c32: 783230 excw +40007c35: 000a add.n a0, a0, a0 +40007c37: 736300 maxu a6, a3, a0 +40007c3a: 206d75 call12 40028310 <__bss_start+0x18310> +40007c3d: 257830 extui a7, a3, 24, 3 +40007c40: 783230 excw +40007c43: 000a add.n a0, a0, a0 +40007c45: 000000 ill +40007c48: 757363 excw +40007c4b: 206d excw +40007c4d: 727265 call8 4007a374 <__bss_start+0x6a374> +40007c50: 000a add.n a0, a0, a0 +40007c52: 680000 excw +40007c55: 6f .byte 0x6f +40007c56: 642520 extui a2, a2, 5, 7 +40007c59: 617420 excw +40007c5c: 6c69 s32i.n a6, a12, 24 +40007c5e: 642520 extui a2, a2, 5, 7 +40007c61: 6f7220 excw +40007c64: 6f .byte 0x6f +40007c65: 206d excw +40007c67: 0a6425 call8 400122a8 <__bss_start+0x22a8> +40007c6a: 090000 l32e a0, a0, -64 +40007c6d: 735f20 maxu a5, a15, a2 +40007c70: 636174 excw +40007c73: 5f6b addi.n a5, a15, 6 +40007c75: 6e6573 excw +40007c78: 797274 excw +40007c7b: 203a add.n a2, a0, a3 +40007c7d: 2c7025 call8 40034380 <__bss_start+0x24380> +40007c80: 200920 or a0, a9, a2 +40007c83: 5f .byte 0x5f +40007c84: 5f .byte 0x5f +40007c85: 617473 excw +40007c88: 3a6b63 excw +40007c8b: 783020 excw +40007c8e: 383025 call8 4003ff90 <__bss_start+0x2ff90> +40007c91: 2c78 l32i.n a7, a12, 8 +40007c93: 200a20 or a0, a10, a2 +40007c96: 2009 s32i.n a0, a0, 8 +40007c98: 5f .byte 0x5f +40007c99: 737362 excw +40007c9c: 5f .byte 0x5f +40007c9d: 617473 excw +40007ca0: 3a7472 excw +40007ca3: 783020 excw +40007ca6: 383025 call8 4003ffa8 <__bss_start+0x2ffa8> +40007ca9: 2c78 l32i.n a7, a12, 8 +40007cab: 200920 or a0, a9, a2 +40007cae: 5f .byte 0x5f +40007caf: 737362 excw +40007cb2: 5f .byte 0x5f +40007cb3: 646e65 call8 4006c398 <__bss_start+0x5c398> +40007cb6: 203a add.n a2, a0, a3 +40007cb8: 257830 extui a7, a3, 24, 3 +40007cbb: 783830 excw +40007cbe: 202c movi.n a0, 34 +40007cc0: 200a add.n a2, a0, a0 +40007cc2: 2009 s32i.n a0, a0, 8 +40007cc4: 5f .byte 0x5f +40007cc5: 746164 excw +40007cc8: 735f61 l32r a6, 3ffe4a44 <_start-0x1b5bc> +40007ccb: 726174 excw +40007cce: 203a74 excw +40007cd1: 2c7025 call8 400343d4 <__bss_start+0x243d4> +40007cd4: 200920 or a0, a9, a2 +40007cd7: 5f .byte 0x5f +40007cd8: 746164 excw +40007cdb: 655f61 l32r a6, 3ffe1258 <_start-0x1eda8> +40007cde: 6e .byte 0x6e +40007cdf: 203a64 excw +40007ce2: 207025 call8 400283e4 <__bss_start+0x183e4> +40007ce5: 000a add.n a0, a0, a0 +40007ce7: 707300 excw +40007cea: 702520 excw +40007ced: 000a20 excw +40007cf0: 5f6673 excw +40007cf3: 6d7564 excw +40007cf6: 255b70 extui a5, a7, 27, 3 +40007cf9: 205d64 excw +40007cfc: 3a3061 l32r a6, 3ffd65bc <_start-0x29a44> +40007cff: 783020 excw +40007d02: 383025 call8 40040004 <__bss_start+0x30004> +40007d05: 2078 l32i.n a7, a0, 8 +40007d07: 316120 srai a6, a2, 17 +40007d0a: 203a add.n a2, a0, a3 +40007d0c: 257830 extui a7, a3, 24, 3 +40007d0f: 783830 excw +40007d12: 612020 xsr.m0 a2 +40007d15: 203a32 excw +40007d18: 257830 extui a7, a3, 24, 3 +40007d1b: 783830 excw +40007d1e: 612020 xsr.m0 a2 +40007d21: 203a33 excw +40007d24: 257830 extui a7, a3, 24, 3 +40007d27: 783830 excw +40007d2a: 000a20 excw +40007d2d: 000000 ill +40007d30: 637065 call8 4006b438 <__bss_start+0x5b438> +40007d33: 303d31 l32r a3, 3ffd3e28 <_start-0x2c1d8> +40007d36: 2578 l32i.n a7, a5, 8 +40007d38: 783830 excw +40007d3b: 202c movi.n a0, 34 +40007d3d: 637065 call8 4006b444 <__bss_start+0x5b444> +40007d40: 303d32 excw +40007d43: 2578 l32i.n a7, a5, 8 +40007d45: 783830 excw +40007d48: 202c movi.n a0, 34 +40007d4a: 637065 call8 4006b450 <__bss_start+0x5b450> +40007d4d: 303d33 excw +40007d50: 2578 l32i.n a7, a5, 8 +40007d52: 783830 excw +40007d55: 202c movi.n a0, 34 +40007d57: 637865 call8 4006b4dc <__bss_start+0x5b4dc> +40007d5a: 646176 excw +40007d5d: 3d7264 excw +40007d60: 257830 extui a7, a3, 24, 3 +40007d63: 783830 excw +40007d66: 202c movi.n a0, 34 +40007d68: 706564 excw +40007d6b: 303d63 excw +40007d6e: 2578 l32i.n a7, a5, 8 +40007d70: 783830 excw +40007d73: 000a add.n a0, a0, a0 +40007d75: 000000 ill +40007d78: 746146 j 40024f01 <__bss_start+0x14f01> +40007d7b: 206c61 l32r a6, 3ffcff2c <_start-0x300d4> +40007d7e: 637865 call8 4006b504 <__bss_start+0x5b504> +40007d81: 747065 call8 4007c488 <__bss_start+0x6c488> +40007d84: 6f69 s32i.n a6, a15, 24 +40007d86: 6e .byte 0x6e +40007d87: 252820 extui a2, a2, 24, 3 +40007d8a: 3a2964 excw +40007d8d: 000a20 excw +40007d90: 727065 call8 4007a498 <__bss_start+0x6a498> +40007d93: 6e69 s32i.n a6, a14, 24 +40007d95: 206674 excw +40007d98: 6e .byte 0x6e +40007d99: 6f .byte 0x6f +40007d9a: 756220 extui a6, a2, 18, 8 +40007d9d: 000a66 bnei a10, -1, 40007da1 <__umoddi3+0xf3d> +40007da0: 323130 orbc b3, b1, b3 +40007da3: 353433 excw +40007da6: 383736 entry a7, 0x1c18 +40007da9: 0039 s32i.n a3, a0, 0 +40007dab: 313000 srai a3, a0, 16 +40007dae: 343332 excw +40007db1: 373635 call12 4003f114 <__bss_start+0x2f114> +40007db4: 3938 l32i.n a3, a9, 12 +40007db6: 636261 l32r a6, 3ffe0b40 <_start-0x1f4c0> +40007db9: 666564 excw +40007dbc: 000000 ill +40007dbf: 313000 srai a3, a0, 16 +40007dc2: 343332 excw +40007dc5: 373635 call12 4003f128 <__bss_start+0x2f128> +40007dc8: 3938 l32i.n a3, a9, 12 +40007dca: 434241 l32r a4, 3ffd8ad4 <_start-0x2752c> +40007dcd: 464544 excw +40007dd0: 000000 ill +40007dd3: 6e3c00 excw +40007dd6: 6c6c75 call12 4007449c <__bss_start+0x6449c> +40007dd9: 3e .byte 0x3e +40007dda: 650000 extui a0, a0, 16, 7 +40007ddd: 5f7374 excw +40007de0: 6d6974 excw +40007de3: 2e7265 call8 40036508 <__bss_start+0x26508> +40007de6: 6e0063 excw +40007de9: 6f .byte 0x6f +40007dea: 647220 extui a7, a2, 2, 7 +40007ded: 000a73 excw +40007df0: 716573 excw +40007df3: 752520 extui a2, a2, 21, 8 +40007df6: 202c movi.n a0, 34 +40007df8: 207525 call8 4002854c <__bss_start+0x1854c> +40007dfb: 207364 excw +40007dfe: 0a7025 call8 40012500 <__bss_start+0x2500> +40007e01: 000000 ill +40007e04: 000c movi.n a0, 0 +40007e06: 000000 ill +40007e09: 000000 ill +40007e0c: 010001 l32r a0, 3ffc820c <_start-0x37df4> +40007e0f: 007c movi.n a0, -16 +40007e11: 010c movi.n a1, 0 +40007e13: 001000 movsp a0, a0 +40007e16: 140000 extui a0, a0, 0, 2 +40007e19: 000000 ill +40007e1c: 006c30 rsil a3, 12 +40007e1f: 023240 andb b3, b2, b4 +40007e22: 0e0000 read_impwire a0 +40007e25: 000040 excw +40007e28: 000010 excw +40007e2b: 002800 excw +40007e2e: 640000 extui a0, a0, 0, 7 +40007e31: 6e .byte 0x6e +40007e32: 704000 excw +40007e35: 000002 l8ui a0, a0, 0 +40007e38: 0e .byte 0xe +40007e39: 000050 excw +40007e3c: cd48 l32i.n a4, a13, 48 +40007e3e: ff .byte 0xff +40007e3f: 3f .byte 0x3f +40007e40: ffe1d0 excw +40007e43: 3f .byte 0x3f +40007e44: c008 l32i.n a0, a0, 48 +40007e46: ff .byte 0xff +40007e47: 3f .byte 0x3f + ... diff --git a/src/mongoose-6.11/src/common/platforms/esp31/rom/disasm.sh b/src/mongoose-6.11/src/common/platforms/esp31/rom/disasm.sh new file mode 100755 index 0000000..5ab8fe5 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp31/rom/disasm.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +xtensa-esp108-elf-gcc -Wl,-N,-Ttext,0x40000000 -nostdlib rom.S -o rom.elf && \ + xtensa-esp108-elf-objdump -d rom.elf > ESP31B_ROM.txt diff --git a/src/mongoose-6.11/src/common/platforms/esp31/rom/notes.c b/src/mongoose-6.11/src/common/platforms/esp31/rom/notes.c new file mode 100644 index 0000000..431d718 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp31/rom/notes.c @@ -0,0 +1,163 @@ +/* Just some notes scribbled while disassembling */ + +/* + * RTC = 0x60008000 + * RTC+0x18: ??c????? ???????? ???????? ???????? + * RTC+0x34: ???????? ??bbbbbb bbbb???? ??aaaaaa + */ +int _X_get_rst_cause(void) { + int ret; + int a = GET_PERI_REG_BITS(RTC_STATE1, 6, 0); + if (a == 5) { + int b = (RTC_STATE1 >> 12) && 0xfff; + if (b != 1) { + ret = (b == 8 ? a : 0); + } else { + ret = 20; + } + } else { + ret = a; + } + CLEAR_PERI_REG_MASK(RTC_STATE0, RTC_CNTL_SLP_WAKEUP); + return ret; +} + +/* + * RTC = 0x60008000 + * RTC+0x38: ???????? ???????? ???????? ??cccccc + * RTC+0x74: ???????? ???????? ???????? dddddddd + * RTC+0x80: ???????? ??????a? ???b???? ???????? + */ +void main(void) { + uint32_t rst_cause = _X_get_rst_cause(); + CLEAR_PERI_REG_MASK(RTC+0x80, BIT(17)); // a + SET_PERI_REG_MASK(RTC+0x80, BIT(12)); // b + uint32_t boot_mode = GET_PERI_REG_BITS(GPIO_STRAP, 6, 0); // c + if (boot_mode & (BIT(5) | BIT(4)) == (BIT(5) | BIT(4)) || boot_mode == 24 || boot_mode == 26) { + CLEAR_PERI_REG_MASK(RTC+0x74, 0xff); + } + if (boot_mode & (BIT(5) | BIT(4)) == BIT(5)) { + CLEAR_PERI_REG_MASK(RTC+0x94, BIT(31)); + CLEAR_PERI_REG_MASK(RTC+0x98, BIT(31)); + CLEAR_PERI_REG_MASK(RTC+0x9c, BIT(31)); + CLEAR_PERI_REG_MASK(RTC+0xa0, BIT(31)); + CLEAR_PERI_REG_MASK(RTC+0xa4, BIT(31)); + CLEAR_PERI_REG_MASK(RTC+0xa8, BIT(31)); + CLEAR_PERI_REG_MASK(RTC+0xac, BIT(31)); + } + if (boot_mode & (BIT(5) | BIT(3)) == 0) { + // ... 1405 + } + CLEAR_PERI_REG_MASK(RTC+0x74, 0xff); + _X_uart_attach(); + _X_uart_init(0); + // GPIO_STRAP ... + ets_printf(boot_banner, fw_build, rst_cause, boot_mode); + // rst_cause + if (rst_cause == 1 || rst_cause == 2) { + + } else { + // ... + } + ets_printf("%s %u", "ets_main.c", 305); + while(1) {} +} + +/* + * GPIO strap mapping: + * + * 0011 1111 1011 0011 + * || |||| |||| |||| + * || |||| |||| |||`- IO5 + * || |||| |||| ||`-- IO15 + * || |||| |||| |`--- IO4 + * || |||| |||| `---- IO2 + * || |||| |||`------ ? + * || |||| ||`------- IO0 + * || |||| |`-------- IO12 + * || |||| `--------- ? + * || |||`----------- CLK + * || ||`------------ ? + * || |`------------- SD0 + * || `-------------- SD1 + * |`---------------- ? SD2 + * `----------------- SD3 + */ + +struct uartdev { + uint32_t baud_rate; // 0 + uint32_t ud4; + uint32_t ud8; + uint32_t ud12; + uint32_t ud16; + uint32_t ud20; + uint8_t ud24; + uint8_t ud25; + uint32_t ud28; + uint32_t ud32; + uint32_t ud36; + uint8_t ud40; + uint32_t ud48; + uint32_t ud52; +}; + +void _X_uart_attach(void) { + // zero uartdev + uartdev.baud_rate = 115200; + _X_xtos_ints_off(1 << ETS_UART_INUM); + // INTR_MAP_REG_C + // 11111111 11111111 11111100 00011111 & + // 00000000 00000000 00000000 10100000 | + // PRODPORT_INTR_MAP_13 -> 5 = ETS_UART_INUM + // 11111111 11111111 10000011 11111111 & + // 00000000 00000000 00010100 11111111 | + // PRODPORT_INTR_MAP_14 -> 5 = ETS_UART_INUM + _xtos_set_interrupt_handler_arg(ETS_UART_INUM, uart_int_handler, _c_0x3fffdb2c_uart_int_handler_arg); +} + +void _X_uart_init(uint32_t a) { + // GPIO_FUNC_IN_SEL3 + // xx999999 88888877 77776666 66555555 + // 11111111 11111100 00001111 11111111 = 0xfffc0fff + // 00000000 00000000 10010000 00000000 = 0x00009000 + // GPIO17 func => 9 + // 00000000 00000010 00000000 00000000 + uart_div_modify(13000000 / uartdev.baud_rate); + // ... +} + +struct _st_0x3fffdc90 { +}; + +struct _st_0x3fffdf70 { + void *fp1; // 20 + void *fp2; // 24 + uint32_t d28; + uint32_t d32; + uint32_t d36; + struct _st_0x3fffdc90 *st; // 44 +} stdf70; + +void _X_slc_init_attach(void *fp1, void *fp2, struct _st_0x3fffdc90 *st, uint32_t gpio_mode) { + stdf70.fp1 = fp1; + stdf70.fp2 = fp2; + stdf70.st = st; + d28 = d32 = d36 = 0; + SET_PERI_REG_MASK(WIFI_RST_EN, PRODPORT_SDIO_RST); + CLEAR_PERI_REG_MASK(WIFI_RST_EN, PRODPORT_SDIO_RST); + if (gpio_mode == 4) { + SET_PERI_REG((READ_PERI_REG(PERIPHS_HINF_BASEADDR+4) & 0xf0000000) | 0x01110013); + } else { + SET_PERI_REG((READ_PERI_REG(PERIPHS_HINF_BASEADDR+4) & 0xf0000000) | 0x02320017); + } + SET_PERI_REG(PERIPHS_HINF_BASEADDR, 0x11116666); + _X_slc_set_host_io_max_window(); + ... +} + +#define SLC_TOKEN1 (PERIPHS_SLC_BASEADDR + 0x54) +#define SLC_BRIDGE_CONF (PERIPHS_SLC_BASEADDR + 0x74) + +void _X_slc_set_host_io_max_window(void) { + SET_PERI_REG(SLC_BRIDGE_CONF, (READ_PERI_REG(SLC_BRIDGE_CONF) & 0xfffff0c0) | 0x720); +} diff --git a/src/mongoose-6.11/src/common/platforms/esp31/rom/rom.S b/src/mongoose-6.11/src/common/platforms/esp31/rom/rom.S new file mode 100644 index 0000000..55737a5 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp31/rom/rom.S @@ -0,0 +1,16 @@ +.text +.org 0 +.globl _start + +// xtensa-esp108-elf-gcc -Wl,-N,-Ttext,0x40000000 -nostdlib rom.S -o rom.elf + +here = . +#define PROVIDE(name, addr) name = here + addr - 0x40000000 + +#include "rom_functions.S" + +.text + +_start: +.incbin "rom.bin" +_end: diff --git a/src/mongoose-6.11/src/common/platforms/esp31/rom/rom.bin b/src/mongoose-6.11/src/common/platforms/esp31/rom/rom.bin new file mode 100644 index 0000000..355329d Binary files /dev/null and b/src/mongoose-6.11/src/common/platforms/esp31/rom/rom.bin differ diff --git a/src/mongoose-6.11/src/common/platforms/esp31/rom/rom_functions.S b/src/mongoose-6.11/src/common/platforms/esp31/rom/rom_functions.S new file mode 100644 index 0000000..5711a5c --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp31/rom/rom_functions.S @@ -0,0 +1,294 @@ +// These come from linker script + +PROVIDE ( Cache_Read_Disable , 0x4000444c ); +PROVIDE ( Cache_Read_Enable , 0x4000438c ); + +PROVIDE ( ets_delay_us , 0x40002db4 ); + +PROVIDE ( bzero , 0x40002a54 ); + +PROVIDE ( memcmp , 0x400068ec ); +PROVIDE ( memcpy , 0x40006974 ); +PROVIDE ( memmove , 0x40006a6c ); +PROVIDE ( memset , 0x40006be4 ); + +PROVIDE ( strcmp , 0x40005bb8 ); +PROVIDE ( strcpy , 0x40005cdc ); +PROVIDE ( strlen , 0x40005d6c ); +PROVIDE ( strncmp , 0x40005dd0 ); +PROVIDE ( strncpy , 0x40005e90 ); +PROVIDE ( strstr , 0x40005f6c ); + +PROVIDE ( ets_install_putc1 , 0x40002774 ); +PROVIDE ( ets_printf , 0x40002804 ); +PROVIDE ( ets_putc , 0x40002b14 ); + +PROVIDE ( ets_str2macaddr , 0x40002a64 ); + +PROVIDE ( gpio_output_set , 0x400049d8 ); +PROVIDE ( gpio_output_set_high , 0x400049f8 ); + +PROVIDE ( ets_get_cpu_frequency , 0x40002de8 ); +PROVIDE ( ets_update_cpu_frequency , 0x40002ddc ); + +PROVIDE ( lldesc_build_chain , 0x40004c8c ); + +PROVIDE ( multofup , 0x400068a0 ); +PROVIDE ( roundup2 , 0x40006890 ); + +PROVIDE (software_reset_cpu , 0x40002998 ); + +PROVIDE ( SPIEraseSector , 0x40004708 ); +PROVIDE ( SPIRead , 0x40004898 ); +PROVIDE ( SPIWrite , 0x40004738 ); + +PROVIDE ( uart_div_modify , 0x400034e8 ); +PROVIDE ( uart_tx_one_char , 0x4000362c ); + +PROVIDE ( __divsi3 , 0x40006888 ); +PROVIDE ( __udivdi3 , 0x40006c30 ); +PROVIDE ( __umoddi3 , 0x40006e64 ); + +PROVIDE ( _xtos_set_intlevel , 0x40006670 ); + +// These have been reverse-engineered. + +PROVIDE(_XX_Vec40, 0x40000040) +PROVIDE(_XX_ExcVec50, 0x40000050) +PROVIDE(_XX_ExcVec80, 0x40000080) + +PROVIDE(_XX_Vec400, 0x40000300) + +PROVIDE(_WindowOverflowHandler, 0x40000100) +PROVIDE(_WindowUnderflowHandler, 0x40000140) + +PROVIDE(_X_ResetVector, 0x40000500) + +PROVIDE(_c_stack, 0x40000700) +PROVIDE(_c_bss_start, 0x40000708) +PROVIDE(_c_bss_end, 0x4000070c) +PROVIDE(_c_0x3fffc210, 0x40000734) +PROVIDE(_c_0x80000000, 0x40000738) +PROVIDE(_c_0x40000000, 0x40000760) +PROVIDE(_c_0x7fffffff, 0x40000780) +PROVIDE(_c_0x00ff0000, 0x40000798) + +PROVIDE(_X_start, 0x400007ac) + +PROVIDE(_c_0x3fffd820, 0x40000f50) +PROVIDE(_X_ets_task, 0x40000f54) +PROVIDE(_XX_unk0f84, 0x40000f84) +PROVIDE(_XX_unk0f96, 0x40000f98) + +PROVIDE(_c_ets_critical_level, 0x400010a4) +PROVIDE(_X_ets_enter_critical, 0x400010a8) +PROVIDE(_X_ets_exit_critical, 0x400010bc) +PROVIDE(_X_ets_exit_critical_and_wait_int, 0x400010d4) +PROVIDE(_X_ets_isr_attach, 0x400010e8) // 3 args +PROVIDE(_X_ets_isr_mask, 0x400010f8) // 1 arg +PROVIDE(_X_ets_isr_unmask, 0x40001104) // 1 arg + +PROVIDE(_c_0x3fffda30, 0x40001110) +PROVIDE(_XX_set_0x3fffda30_0, 0x40001114) +PROVIDE(_XX_set_0x3fffda30_4, 0x40001120) +PROVIDE(_c_0xfffdffff, 0x4000112c) +PROVIDE(_c_0x60003e00, 0x40001130) +PROVIDE(_c_0x60008200, 0x40001134) +PROVIDE(_c_0x60007e00, 0x40001138) +PROVIDE(_c_0x1000, 0x4000113c) +PROVIDE(_s_fw_build, 0x40001140) +PROVIDE(_s_boot_banner, 0x40001144) +PROVIDE(_s_pct_s_pct_u, 0x40001148) +PROVIDE(_s_ets_main_c, 0x4000114c) +PROVIDE(_X_main, 0x4000115c) + +PROVIDE(_l_strap_0x0xxx, 0x4000125a) +PROVIDE(_l_strap_init_uart0, 0x40001269) +PROVIDE(_l_strap_0x0x00, 0x400012e2) +PROVIDE(_l_boot, 0x400012ea) +PROVIDE(_l_rst_cause_345, 0x40001336) +PROVIDE(_l_rst_cause_12, 0x40001342) +PROVIDE(_l_strap_NxNxxx, 0x40001405) +PROVIDE(_l_strap_0010xx, 0x4000144c) +PROVIDE(_l_strap_001000_0x110x, 0x400014b0) +PROVIDE(_l_strap_0x0x11_loader, 0x400014c9) // loader +PROVIDE(_l_strap_0x0x01, 0x400014d4) +PROVIDE(_l_strap_0x0x10, 0x400014e6) +PROVIDE(_c_0xffff8fff, 0x400014f0) +PROVIDE(_c_0x60008e00, 0x400014f4) + +PROVIDE(_s_waiting_for_host, 0x4000152c) +PROVIDE(_s_mem_banner, 0x40001cdc) +PROVIDE(_c_stack_sentry, 0x40001ce0) +PROVIDE(_XX_unk153c, 0x4000153c) + +PROVIDE(_c_data_end, 0x40001cd8) +PROVIDE(_c_data_start, 0x40001ce4) +PROVIDE(_X_print_mem_banner, 0x40001ce8) + +PROVIDE(_s_exc_sp_fmt, 0x40001d0c) +PROVIDE(_s_exc_sf_dump_fmt, 0x40001d10) +PROVIDE(_s_exc_regs_fmt, 0x40001d14) +PROVIDE(_X_exc_handler, 0x40001d18) + +PROVIDE(_XX_unk1d90, 0x40001d90) + +PROVIDE(_X_ets_memset, 0x40001db4) +PROVIDE(_X_ets_memcpy, 0x40001dc4) +PROVIDE(_X_ets_memmove, 0x40001dd4) +PROVIDE(_X_ets_memcmp, 0x40001de4) + +PROVIDE(_st_0x3fffda9c, 0x40002150) // struct + +PROVIDE(_X_ets_uart_putc, 0x4000223c) +PROVIDE(_X_ets_unk225c, 0x4000225c) + +PROVIDE(_c_0x4000223c_ets_uart_putc, 0x40002780) +PROVIDE(_X_ets_install_uart_printf, 0x40002784) +PROVIDE(_c_0x400027dc, 0x40002790) +PROVIDE(_X_ets_install_external_printf, 0x40002794) +PROVIDE(_X_ets_install_putc2, 0x400027b4) +PROVIDE(_X_ets_get_printf_buf_remain_len, 0x400027c0) +PROVIDE(_X_ets_reset_printf_buf_len, 0x400027cc) +PROVIDE(_X_ets_putc, 0x400027dc) + +PROVIDE(_c_0xdfffffff, 0x400028d4) +PROVIDE(_X_get_rst_cause, 0x400028d8) +PROVIDE(_XX_unk2948, 0x40002948) +PROVIDE(_l_2970, 0x40002970) +PROVIDE(_X_sw_sys_rst, 0x4000297c) +PROVIDE(_c_0x00400000, 0x400029b4) +PROVIDE(_c_0xffbfffff, 0x400029b8) +PROVIDE(_XX_apb_bridge_toggle, 0x400029bc) // turns RTC_CNTL_APB2RTC_BRIDGE_SEL on and off +PROVIDE(_X_ets_strcpy, 0x400029ec) +PROVIDE(_X_ets_strncpy, 0x40002a00) +PROVIDE(_X_ets_strcmp, 0x40002a10) +PROVIDE(_X_ets_strncmp, 0x40002a24) +PROVIDE(_X_ets_strlen, 0x40002a34) +PROVIDE(_X_ets_strstr, 0x40002a40) + +PROVIDE(_st_0x3fffdb10_uartdev, 0x40002e4c) // some struct - uartdev? + +PROVIDE(_c_0x3fffdb00, 0x40002e50) +PROVIDE(_c_0x3fffdb04, 0x40002f64) +PROVIDE(_XX_unk2e58, 0x40002e58) +PROVIDE(_X_UartDwnLdProc, 0x40002f6c) +PROVIDE(_c_0x00001800, 0x400030ec) +PROVIDE(_X_FlashDwnLdStartMsgProc, 0x400030f0) +PROVIDE(_XX_unk313c, 0x4000313c) +PROVIDE(_XX_unk31bc, 0x400031bc) +PROVIDE(_XX_unk31e4, 0x400031e4) +PROVIDE(_XX_unk3210, 0x40003210) +PROVIDE(_XX_unk3240, 0x40003240) +PROVIDE(_X_MemDwnLdStopReqMsgProc, 0x4000329c) +PROVIDE(_X_UartConnectProc, 0x400032c4) +PROVIDE(_X_UartRegWriteProc, 0x400032d4) +PROVIDE(_X_UartRegReadProc, 0x40003318) + +PROVIDE(_c_115200, 0x4000332c) +PROVIDE(_c_0x3feffe00, 0x40003330) +PROVIDE(_c_0xffff83ff, 0x40003334) +PROVIDE(_c_0x00001400, 0x40003338) +PROVIDE(_c_0x40003728_uart_int_handler, 0x4000333c) +PROVIDE(_c_0x3fffdb2c_uart_int_handler_arg, 0x40003340) +PROVIDE(_X_uart_attach, 0x40003344) +PROVIDE(_XX_uart_set_unk33c0, 0x400033c0) +PROVIDE(_c_0x5ffffe00, 0x400033cc) +PROVIDE(_c_0x0000ffff, 0x400033d0) +PROVIDE(_c_0x000fffff, 0x40003448) +PROVIDE(_c_0x00060000, 0x400034e0) +PROVIDE(_c_0xfff9ffff, 0x400034e4) + +PROVIDE(_c_0xfffc0fff, 0x40003520) +PROVIDE(_c_0x00009000, 0x40003524) +PROVIDE(_c_0x00020000, 0x40003528) +PROVIDE(_c_13000000, 0x4000352c) +PROVIDE(_c_0x08000000, 0x40003530) +PROVIDE(_X_uart_init, 0x40003534) +PROVIDE(_l_35f4, 0x400035f4) +PROVIDE(_X_uart_wait_tx_empty, 0x4000369c) + +PROVIDE(_X_uart_int_handler, 0x40003728) + +PROVIDE(_X_uart_tx_one_char2, 0x40003664) + +PROVIDE(_X_send_packet, 0x400037d4) +PROVIDE(_X_SendMsg, 0x40003828) +PROVIDE(_X_recv_packet, 0x4000383c) +PROVIDE(_X_RcvMsg, 0x40003988) +PROVIDE(_X_uart_rx_readbuff, 0x400039a0) + +PROVIDE(_c_0x60004e00, 0x40003a18) +PROVIDE(_X_SelectSpiFunction, 0x40003a1c) // 1 arg - SPI number +PROVIDE(_c_0x60002e00, 0x40003c78) +PROVIDE(_X_SPI_chip_erase, 0x40003c7c) +PROVIDE(_c_0x00ffffff, 0x40003cb4) +PROVIDE(_c_0x01000000, 0x40003cb8) +PROVIDE(_XX_unk3cbc, 0x40003cbc) +PROVIDE(_c_0x00800000, 0x40003d00) +PROVIDE(_c_0x02000000, 0x40003d4c) +PROVIDE(_XX_unk3e24, 0x40003e24) +PROVIDE(_X_SPI_read_status, 0x40003efc) +PROVIDE(_c_0x90000000, 0x40003f48) +PROVIDE(_c_0x70000035, 0x40003f4c) +PROVIDE(_c_0x00040000, 0x40003f50) +PROVIDE(_XX_unk3f54, 0x40003f54) +PROVIDE(_c_0x04000000, 0x40003fcc) +PROVIDE(_XX_unk4010, 0x40004010) +PROVIDE(_X_SPI_write_enable, 0x400041bc) +PROVIDE(_X_Wait_SPI_Idle, 0x40004208) +PROVIDE(_l_4228, 0x40004228) +PROVIDE(_l_4234, 0x40004234) +PROVIDE(_XX_unk4238, 0x40004238) +PROVIDE(_X_SPIFlashModeConfig, 0x400042d8) +PROVIDE(_X_spi_flash_attach, 0x40004370) // 2 args: SPI num, ??? +PROVIDE(_X_SPIReadModeConfig, 0x40004538) + +PROVIDE(_X_SPIEraseArea, 0x400048b4) +PROVIDE(_XX_unk4940, 0x40004940) + +PROVIDE(_st_0x3fffdc90, 0x40004d80) +PROVIDE(_XX_unk4d88, 0x40004d88) +PROVIDE(_XX_unk4f6c, 0x40004f6c) +PROVIDE(_XX_unk4fc8, 0x40004fc8) + +PROVIDE(_c_0xbfffffff, 0x40004c80) +PROVIDE(_c_0xff000fff, 0x40004c88) +PROVIDE(_XX_unk4f14, 0x40004f14) +PROVIDE(_s_no_rds, 0x40005008) +PROVIDE(_XX_unk500c, 0x4000500c) + +PROVIDE(_fp_0x40004f6c, 0x40005164) +PROVIDE(_fp_0x40004fc8, 0x40005168) +PROVIDE(_X_sip_init_attach, 0x40005170) // 1 arg, boot_mode? +PROVIDE(_XX_unk51ac, 0x400051ac) +PROVIDE(_c_0x60017e00, 0x40005478) +PROVIDE(_st_0x3fffdf70, 0x400054a4) +PROVIDE(_c_0x6000ae00, 0x400054b8) +PROVIDE(_c_0xf0000000, 0x400054bc) +PROVIDE(_c_0x02320017, 0x400054c0) +PROVIDE(_c_0x11116666, 0x400054c4) +PROVIDE(_c_0x01110013, 0x400054e4) +PROVIDE(_X_slc_init_attach, 0x400054e8) // 4 args - fp, fp, st, boot_mode; PRODPORT_SDIO_RST +PROVIDE(_l_slc_boot_mode_4, 0x40005654) +PROVIDE(_X_slc_enable, 0x40005678) +PROVIDE(_X_slc_select_tohost_gpio_mode, 0x400056fc) +PROVIDE(_X_slc_select_tohost_gpio, 0x40005708) +PROVIDE(_c_0xff300000, 0x40005730) +PROVIDE(_XX_unk5734, 0x40005734) +PROVIDE(_XX_unk57b8, 0x400057b8) +PROVIDE(_XX_unk57f4, 0x400057f4) +PROVIDE(_XX_unk5848, 0x40005848) + +PROVIDE(_c_0xfffff0c0, 0x40005988) +PROVIDE(_X_slc_set_host_io_max_window, 0x4000598c) +PROVIDE(_X_slc_init_credit, 0x400059ac) +PROVIDE(_X_slc_add_credits, 0x400059c4) + +PROVIDE(_X_xtos_set_interrupt_handler_arg, 0x400059d8) +PROVIDE(_X_xtos_set_interrupt_handler, 0x40005a24) +PROVIDE(_X_xtos_ints_on, 0x40005a34) +PROVIDE(_X_xtos_ints_off, 0x40005a58) + +PROVIDE(_XX_xtos_exc_unk5a80, 0x40005a80) +PROVIDE(_XX_xtos_exc_unk5b94, 0x40005b94) diff --git a/src/mongoose-6.11/src/common/platforms/esp32/rom/.gitattributes b/src/mongoose-6.11/src/common/platforms/esp32/rom/.gitattributes new file mode 100644 index 0000000..b2dd225 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp32/rom/.gitattributes @@ -0,0 +1,2 @@ +rom.bin -nodiff +rom.elf -nodiff diff --git a/src/mongoose-6.11/src/common/platforms/esp32/rom/disasm.sh b/src/mongoose-6.11/src/common/platforms/esp32/rom/disasm.sh new file mode 100755 index 0000000..ae719ac --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp32/rom/disasm.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +xtensa-esp32-elf-gcc -Wl,-N,-Ttext,0x40000000 -nostdlib rom.S -o rom.elf && \ + xtensa-esp32-elf-objdump -d rom.elf > ESP32_ROM.txt diff --git a/src/mongoose-6.11/src/common/platforms/esp32/rom/rom.S b/src/mongoose-6.11/src/common/platforms/esp32/rom/rom.S new file mode 100644 index 0000000..11bd296 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp32/rom/rom.S @@ -0,0 +1,28 @@ +.text +.org 0 +.globl _start + +// xtensa-esp32-elf-gcc -Wl,-N,-Ttext,0x40000000 -nostdlib rom.S -o rom.elf + +here = . +#define PROVIDE(name, addr) name = here + addr - 0x40000000 + +#include "rom_functions.S" + +PROVIDE ( _x_unk_40061b88, 0x40061b88 ) +PROVIDE ( _x_unk_spi_400622c0, 0x400622c0 ) +PROVIDE ( _c_3ff000c8, 0x40062df0 ) +PROVIDE ( _c_3ff5b024, 0x40062e0c ) +PROVIDE ( _c_3ff5b000, 0x40062e10 ) +PROVIDE ( _c_3ff5b020, 0x40062e14 ) +PROVIDE ( _c_3ff5b028, 0x40062e18 ) +PROVIDE ( _l_40062e90, 0x40062e90 ) +PROVIDE ( _l_SPI_Prepare_Encrypt_Data_loop, 0x40062e34 ) +PROVIDE ( _l_SPI_Prepare_Encrypt_Data_wait, 0x40062e54 ) +PROVIDE ( _l_SPI_Prepare_Encrypt_Data_out, 0x40062e5e ) + +.text + +_start: +.incbin "rom.bin" +_end: diff --git a/src/mongoose-6.11/src/common/platforms/esp32/rom/rom.bin b/src/mongoose-6.11/src/common/platforms/esp32/rom/rom.bin new file mode 100644 index 0000000..8f0bb3f Binary files /dev/null and b/src/mongoose-6.11/src/common/platforms/esp32/rom/rom.bin differ diff --git a/src/mongoose-6.11/src/common/platforms/esp32/rom/rom.elf b/src/mongoose-6.11/src/common/platforms/esp32/rom/rom.elf new file mode 100755 index 0000000..f65fbf0 Binary files /dev/null and b/src/mongoose-6.11/src/common/platforms/esp32/rom/rom.elf differ diff --git a/src/mongoose-6.11/src/common/platforms/esp32/rom/rom_functions.S b/src/mongoose-6.11/src/common/platforms/esp32/rom/rom_functions.S new file mode 100644 index 0000000..ec3b76f --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp32/rom/rom_functions.S @@ -0,0 +1,1852 @@ +// From esp-idf/components/esp32/ld/esp32.rom.ld: +// sort -k 5 esp32.rom.ld | perl -npe 's/=/,/; s/;//' + +PROVIDE ( __month_lengths , 0x3ff9609c ) +PROVIDE ( __ctype_ptr__ , 0x3ff96350 ) +PROVIDE ( _ctype_ , 0x3ff96354 ) +PROVIDE ( __sf_fake_stderr , 0x3ff96458 ) +PROVIDE ( __sf_fake_stdout , 0x3ff96478 ) +PROVIDE ( __sf_fake_stdin , 0x3ff96498 ) +PROVIDE ( __mb_cur_max , 0x3ff96530 ) +PROVIDE ( __wctomb , 0x3ff96540 ) +PROVIDE ( __nsau_data , 0x3ff96544 ) +PROVIDE ( __popcount_tab , 0x3ff96544 ) +PROVIDE ( lmp_ext_desc_tab , 0x3ff96d9c ) +PROVIDE ( lmp_desc_tab , 0x3ff96e6c ) +PROVIDE ( co_sca2ppm , 0x3ff971e8 ) +PROVIDE ( one_bits , 0x3ff971f8 ) +PROVIDE ( dbg_default_handler , 0x3ff97218 ) +PROVIDE ( ecc_Jacobian_InfinityPoint256 , 0x3ff972e8 ) +PROVIDE ( veryBigHexP256 , 0x3ff9736c ) +PROVIDE ( bigHexP256 , 0x3ff973bc ) +PROVIDE ( DebugE256SecretKey , 0x3ff973e8 ) +PROVIDE ( DebugE256PublicKey_y , 0x3ff97408 ) +PROVIDE ( DebugE256PublicKey_x , 0x3ff97428 ) +PROVIDE ( maxSecretKey_256 , 0x3ff97448 ) +PROVIDE ( BasePoint_y_256 , 0x3ff97468 ) +PROVIDE ( BasePoint_x_256 , 0x3ff97488 ) +PROVIDE ( hci_evt_le_desc_tab , 0x3ff974b4 ) +PROVIDE ( hci_evt_dbg_desc_tab , 0x3ff9750c ) +PROVIDE ( hci_evt_desc_tab , 0x3ff9751c ) +PROVIDE ( hci_cmd_desc_root_tab , 0x3ff976d4 ) +PROVIDE ( hci_cmd_desc_tab_vs , 0x3ff97714 ) +PROVIDE ( hci_cmd_desc_tab_le , 0x3ff97870 ) +PROVIDE ( hci_cmd_desc_tab_testing , 0x3ff97a98 ) +PROVIDE ( hci_cmd_desc_tab_stat_par , 0x3ff97ac8 ) +PROVIDE ( hci_cmd_desc_tab_info_par , 0x3ff97b1c ) +PROVIDE ( hci_cmd_desc_tab_ctrl_bb , 0x3ff97b70 ) +PROVIDE ( hci_cmd_desc_tab_lk_pol , 0x3ff97f3c ) +PROVIDE ( hci_cmd_desc_tab_lk_ctrl , 0x3ff97fc0 ) +PROVIDE ( lb_default_handler , 0x3ff982b8 ) +PROVIDE ( lc_default_handler , 0x3ff98648 ) +PROVIDE ( ld_pcm_settings_dft , 0x3ff98a0c ) +PROVIDE ( ld_acl_edr_sizes , 0x3ff98a14 ) +PROVIDE ( ld_acl_edr_types , 0x3ff98a22 ) +PROVIDE ( ld_acl_br_sizes , 0x3ff98a2a ) +PROVIDE ( ld_acl_br_types , 0x3ff98a36 ) +PROVIDE ( ld_sync_train_channels , 0x3ff98a3c ) +PROVIDE ( llc_default_handler , 0x3ff98b3c ) +PROVIDE ( lld_pdu_llcp_pk_desc_tab , 0x3ff98b68 ) +PROVIDE ( lld_pdu_adv_pk_desc_tab , 0x3ff98c70 ) +PROVIDE ( llm_local_data_len_values , 0x3ff98d1c ) +PROVIDE ( llm_local_le_states , 0x3ff98d28 ) +PROVIDE ( llm_local_le_feats , 0x3ff98d30 ) +PROVIDE ( llm_local_cmds , 0x3ff98d38 ) +PROVIDE ( llm_default_handler , 0x3ff98d80 ) +PROVIDE ( LLM_AA_CT2 , 0x3ff98d88 ) +PROVIDE ( LLM_AA_CT1 , 0x3ff98d8a ) +PROVIDE ( lm_default_handler , 0x3ff990e0 ) +PROVIDE ( lm_n_page_tab , 0x3ff990e8 ) +PROVIDE ( lm_local_supp_feats , 0x3ff990ee ) +PROVIDE ( rwip_coex_cfg , 0x3ff9914c ) +PROVIDE ( rwip_priority , 0x3ff99159 ) +PROVIDE ( exc_cause_table , 0x3ff991d0 ) +PROVIDE ( spi_modes , 0x3ff99270 ) +PROVIDE ( sha_blk_bits_bytes , 0x3ff99288 ) +PROVIDE ( sha_blk_hash_bytes , 0x3ff9928c ) +PROVIDE ( sha_blk_bits , 0x3ff99290 ) +PROVIDE ( dh_group18_prime , 0x3ff9a0dc ) +PROVIDE ( dh_group18_generator , 0x3ff9a4dc ) +PROVIDE ( dh_group17_prime , 0x3ff9a4dd ) +PROVIDE ( dh_group17_generator , 0x3ff9a7dd ) +PROVIDE ( dh_group16_prime , 0x3ff9a7de ) +PROVIDE ( dh_group16_generator , 0x3ff9a9de ) +PROVIDE ( dh_group15_prime , 0x3ff9a9df ) +PROVIDE ( dh_group15_generator , 0x3ff9ab5f ) +PROVIDE ( dh_group14_prime , 0x3ff9ab60 ) +PROVIDE ( dh_group14_generator , 0x3ff9ac60 ) +PROVIDE ( dh_group5_prime , 0x3ff9ac61 ) +PROVIDE ( dh_group5_generator , 0x3ff9ad21 ) +PROVIDE ( dh_group2_prime , 0x3ff9ad22 ) +PROVIDE ( dh_group2_generator , 0x3ff9ada2 ) +PROVIDE ( dh_group1_prime , 0x3ff9ada3 ) +PROVIDE ( dh_group1_generator , 0x3ff9ae03 ) +PROVIDE ( Xthal_intlevel , 0x3ff9c2b4 ) +PROVIDE ( syscall_table_ptr_app , 0x3ffae020 ) +PROVIDE ( syscall_table_ptr_pro , 0x3ffae024 ) +PROVIDE ( _tzname , 0x3ffae030 ) +PROVIDE ( _timezone , 0x3ffae0a0 ) +PROVIDE ( _daylight , 0x3ffae0a4 ) +PROVIDE ( _global_impure_ptr , 0x3ffae0b0 ) +PROVIDE ( environ , 0x3ffae0b4 ) +PROVIDE ( rom_phyFuns , 0x3ffae0c0 ) +PROVIDE ( g_phyFuns_instance , 0x3ffae0c4 ) +PROVIDE ( g_rom_flashchip , 0x3ffae270 ) +PROVIDE ( SPI_flashchip_data , 0x3ffae270 ) +PROVIDE ( dummy_len_plus , 0x3ffae290 ) +PROVIDE ( sig_matrix , 0x3ffae293 ) +PROVIDE ( r_btdm_option_data , 0x3ffae6e0 ) +PROVIDE ( _data_start_btdm , 0x3ffae6e0) +PROVIDE ( co_default_bdaddr , 0x3ffae704 ) +PROVIDE ( r_ip_funcs_p , 0x3ffae70c ) +PROVIDE ( r_ip_funcs , 0x3ffae710 ) +PROVIDE ( r_import_rf_phy_func_p , 0x3ffafd64 ) +PROVIDE ( r_modules_funcs_p , 0x3ffafd68 ) +PROVIDE ( r_modules_funcs , 0x3ffafd6c ) +PROVIDE ( _data_end_btdm , 0x3ffaff10) +PROVIDE ( _bss_start_btdm , 0x3ffb8000) +PROVIDE ( co_null_bdaddr , 0x3ffb80e0 ) +PROVIDE ( ld_sco_env , 0x3ffb824c ) +PROVIDE ( ld_acl_env , 0x3ffb8258 ) +PROVIDE ( ld_bcst_acl_env , 0x3ffb8274 ) +PROVIDE ( ld_csb_rx_env , 0x3ffb8278 ) +PROVIDE ( ld_csb_tx_env , 0x3ffb827c ) +PROVIDE ( ld_fm_env , 0x3ffb8284 ) +PROVIDE ( ld_inq_env , 0x3ffb82e4 ) +PROVIDE ( ld_iscan_env , 0x3ffb82e8 ) +PROVIDE ( ld_page_env , 0x3ffb82f0 ) +PROVIDE ( ld_pca_env , 0x3ffb82f4 ) +PROVIDE ( ld_pscan_env , 0x3ffb8308 ) +PROVIDE ( ld_sched_env , 0x3ffb830c ) +PROVIDE ( ld_sscan_env , 0x3ffb832c ) +PROVIDE ( ld_strain_env , 0x3ffb8330 ) +PROVIDE ( ld_active_ch_map , 0x3ffb8334 ) +PROVIDE ( r_import_rf_phy_func , 0x3ffb8354 ) +PROVIDE ( r_plf_funcs_p , 0x3ffb8360 ) +PROVIDE ( RFPLL_ICP_TABLE , 0x3ffb8b7c ) +PROVIDE ( bt_util_buf_env , 0x3ffb8bd4 ) +PROVIDE ( sw_to_hw , 0x3ffb8d40 ) +PROVIDE ( dbg_state , 0x3ffb8d5d ) +PROVIDE ( ecc_env , 0x3ffb8d60 ) +PROVIDE ( em_buf_env , 0x3ffb8d74 ) +PROVIDE ( hci_fc_env , 0x3ffb9340 ) +PROVIDE ( hci_env , 0x3ffb9350 ) +PROVIDE ( ke_env , 0x3ffb93cc ) +PROVIDE ( lb_env , 0x3ffb9424 ) +PROVIDE ( lb_state , 0x3ffb94e8 ) +PROVIDE ( lc_env , 0x3ffb94ec ) +PROVIDE ( lc_state , 0x3ffb9508 ) +PROVIDE ( ld_env , 0x3ffb9510 ) +PROVIDE ( ld_env , 0x3ffb9510 ) +PROVIDE ( ld_sched_params , 0x3ffb96c0 ) +PROVIDE ( ld_sched_params , 0x3ffb96c0 ) +PROVIDE ( llc_env , 0x3ffb96d0 ) +PROVIDE ( llc_state , 0x3ffb96f8 ) +PROVIDE ( lld_evt_env , 0x3ffb9704 ) +PROVIDE ( llm_le_env , 0x3ffb976c ) +PROVIDE ( llm_state , 0x3ffb985c ) +PROVIDE ( lm_env , 0x3ffb9860 ) +PROVIDE ( lm_state , 0x3ffb9a1c ) +PROVIDE ( rwip_rf , 0x3ffbdb28 ) +PROVIDE ( _bss_end_btdm , 0x3ffbff70) +PROVIDE ( gpio_pending_mask , 0x3ffe0038 ) +PROVIDE ( app_gpio_arg , 0x3ffe003c ) +PROVIDE ( app_gpio_handler , 0x3ffe0040 ) +PROVIDE ( gpio_pending_mask_high , 0x3ffe0044 ) +PROVIDE ( gTxMsg , 0x3ffe0050 ) +PROVIDE ( RecvBuff , 0x3ffe009c ) +PROVIDE ( UartDev , 0x3ffe019c ) +PROVIDE ( ets_readySet_ , 0x3ffe01f0 ) +PROVIDE ( ets_intr_count , 0x3ffe03fc ) +PROVIDE ( user_code_start , 0x3ffe0400 ) +PROVIDE ( ets_startup_callback , 0x3ffe0404 ) +PROVIDE ( debug_timer , 0x3ffe042c ) +PROVIDE ( debug_timerfn , 0x3ffe0430 ) +PROVIDE ( _xtos_exc_handler_table , 0x3ffe0448 ) +PROVIDE ( _xtos_c_handler_table , 0x3ffe0548 ) +PROVIDE ( _Pri_4_HandlerAddress , 0x3ffe0648 ) +PROVIDE ( _Pri_5_HandlerAddress , 0x3ffe064c ) +PROVIDE ( _xtos_enabled , 0x3ffe0650 ) +PROVIDE ( _xtos_intstruct , 0x3ffe0650 ) +PROVIDE ( _xtos_vpri_enabled , 0x3ffe0654 ) +PROVIDE ( _xtos_interrupt_table , 0x3ffe0658 ) +PROVIDE ( _xtos_interrupt_mask_table , 0x3ffe0758 ) +PROVIDE ( _stack_sentry , 0x3ffe1320 ) +PROVIDE ( __stack , 0x3ffe3f20 ) +PROVIDE ( _stack_sentry_app , 0x3ffe5230 ) +PROVIDE ( __stack_app , 0x3ffe7e30 ) +PROVIDE ( _WindowOverflow4 , 0x40000000 ) +PROVIDE ( _xtos_alloca_handler , 0x40000010 ) +PROVIDE ( _WindowUnderflow4 , 0x40000040 ) +PROVIDE ( _WindowOverflow8 , 0x40000080 ) +PROVIDE ( _WindowUnderflow8 , 0x400000c0 ) +PROVIDE ( _WindowOverflow12 , 0x40000100 ) +PROVIDE ( _WindowUnderflow12 , 0x40000140 ) +PROVIDE ( _Level2Vector , 0x40000180 ) +PROVIDE ( _Level3Vector , 0x400001c0 ) +PROVIDE ( _Level4Vector , 0x40000200 ) +PROVIDE ( _Level5Vector , 0x40000240 ) +PROVIDE ( _DebugExceptionVector , 0x40000280 ) +PROVIDE ( _NMIExceptionVector , 0x400002c0 ) +PROVIDE ( _KernelExceptionVector , 0x40000300 ) +PROVIDE ( _UserExceptionVector , 0x40000340 ) +PROVIDE ( _DoubleExceptionVector , 0x400003c0 ) +PROVIDE ( _ResetVector , 0x40000400 ) +PROVIDE ( _ResetHandler , 0x40000450 ) +PROVIDE ( _stext , 0x40000560 ) +PROVIDE ( _start , 0x40000704 ) +PROVIDE ( _xtos_set_exception_handler , 0x4000074c ) +PROVIDE ( _xtos_syscall_handler , 0x40000790 ) +PROVIDE ( _SyscallException , 0x400007cf ) +PROVIDE ( _xtos_l1int_handler , 0x40000814 ) +PROVIDE ( _LevelOneInterrupt , 0x40000835 ) +PROVIDE ( _xtos_restore_intlevel , 0x40000928 ) +PROVIDE ( _xtos_set_vpri , 0x40000934 ) +PROVIDE ( _Level2FromVector , 0x40000954 ) +PROVIDE ( _Level3FromVector , 0x40000a28 ) +PROVIDE ( _Level4FromVector , 0x40000af8 ) +PROVIDE ( _Level5FromVector , 0x40000c68 ) +PROVIDE ( _xtos_cause3_handler , 0x40000dd8 ) +PROVIDE ( _xtos_c_wrapper_handler , 0x40000de8 ) +PROVIDE ( _GeneralException , 0x40000e14 ) +PROVIDE ( creat , 0x40000e8c ) +PROVIDE ( _isatty_r , 0x40000ea0 ) +PROVIDE ( asctime_r , 0x40000ec8 ) +PROVIDE ( isalnum , 0x40000f04 ) +PROVIDE ( isalpha , 0x40000f18 ) +PROVIDE ( isblank , 0x40000f2c ) +PROVIDE ( iscntrl , 0x40000f50 ) +PROVIDE ( isdigit , 0x40000f64 ) +PROVIDE ( islower , 0x40000f78 ) +PROVIDE ( isgraph , 0x40000f94 ) +PROVIDE ( isprint , 0x40000fa8 ) +PROVIDE ( ispunct , 0x40000fc0 ) +PROVIDE ( isspace , 0x40000fd4 ) +PROVIDE ( isupper , 0x40000fe8 ) +PROVIDE ( srand , 0x40001004 ) +PROVIDE ( rand , 0x40001058 ) +PROVIDE ( rand_r , 0x400010d4 ) +PROVIDE ( __sread , 0x40001118 ) +PROVIDE ( __seofread , 0x40001148 ) +PROVIDE ( __swrite , 0x40001150 ) +PROVIDE ( __sseek , 0x40001184 ) +PROVIDE ( __sclose , 0x400011b8 ) +PROVIDE ( strcasecmp , 0x400011cc ) +PROVIDE ( strcasestr , 0x40001210 ) +PROVIDE ( strcmp , 0x40001274 ) +PROVIDE ( strcoll , 0x40001398 ) +PROVIDE ( strcpy , 0x400013ac ) +PROVIDE ( strdup , 0x4000143c ) +PROVIDE ( _strdup_r , 0x40001450 ) +PROVIDE ( strlcat , 0x40001470 ) +PROVIDE ( strlen , 0x400014c0 ) +PROVIDE ( strlwr , 0x40001524 ) +PROVIDE ( strncasecmp , 0x40001550 ) +PROVIDE ( strncpy , 0x400015d4 ) +PROVIDE ( strndup , 0x400016b0 ) +PROVIDE ( _strndup_r , 0x400016c4 ) +PROVIDE ( strrchr , 0x40001708 ) +PROVIDE ( strsep , 0x40001734 ) +PROVIDE ( strupr , 0x4000174c ) +PROVIDE ( close , 0x40001778 ) +PROVIDE ( open , 0x4000178c ) +PROVIDE ( read , 0x400017dc ) +PROVIDE ( sbrk , 0x400017f4 ) +PROVIDE ( times , 0x40001808 ) +PROVIDE ( write , 0x4000181c ) +PROVIDE ( __get_current_time_locale , 0x40001834 ) +PROVIDE ( __time_load_locale , 0x4000183c ) +PROVIDE ( time , 0x40001844 ) +PROVIDE ( tolower , 0x40001868 ) +PROVIDE ( toupper , 0x40001884 ) +PROVIDE ( __tzcalc_limits , 0x400018a0 ) +PROVIDE ( __tz_lock , 0x40001a04 ) +PROVIDE ( __tz_unlock , 0x40001a10 ) +PROVIDE ( tzset , 0x40001a1c ) +PROVIDE ( _tzset_r , 0x40001a28 ) +PROVIDE ( _cleanup_r , 0x40001d48 ) +PROVIDE ( __sfmoreglue , 0x40001dc8 ) +PROVIDE ( _cleanup , 0x40001df8 ) +PROVIDE ( __sfp_lock_acquire , 0x40001e08 ) +PROVIDE ( __sfp_lock_release , 0x40001e14 ) +PROVIDE ( __sinit_lock_acquire , 0x40001e20 ) +PROVIDE ( __sinit_lock_release , 0x40001e2c ) +PROVIDE ( __sinit , 0x40001e38 ) +PROVIDE ( __sfp , 0x40001e90 ) +PROVIDE ( __fp_lock_all , 0x40001f1c ) +PROVIDE ( __fp_unlock_all , 0x40001f30 ) +PROVIDE ( _findenv_r , 0x40001f44 ) +PROVIDE ( _getenv_r , 0x40001fbc ) +PROVIDE ( __gettzinfo , 0x40001fcc ) +PROVIDE ( __env_lock , 0x40001fd4 ) +PROVIDE ( __env_unlock , 0x40001fe0 ) +PROVIDE ( _fclose_r , 0x40001fec ) +PROVIDE ( fclose , 0x400020ac ) +PROVIDE ( __negsf2 , 0x400020c0 ) +PROVIDE ( __addsf3 , 0x400020e8 ) +PROVIDE ( __subsf3 , 0x400021d0 ) +PROVIDE ( __divsf3 , 0x4000234c ) +PROVIDE ( __fixsfsi , 0x4000240c ) +PROVIDE ( __fixsfdi , 0x4000244c ) +PROVIDE ( __fixunssfsi , 0x400024ac ) +PROVIDE ( __fixunssfdi , 0x40002504 ) +PROVIDE ( __adddf3 , 0x40002590 ) +PROVIDE ( __subdf3 , 0x400026e4 ) +PROVIDE ( __divdf3 , 0x40002954 ) +PROVIDE ( __fixdfsi , 0x40002a78 ) +PROVIDE ( __fixdfdi , 0x40002ac4 ) +PROVIDE ( __fixunsdfsi , 0x40002b30 ) +PROVIDE ( __truncdfsf2 , 0x40002b90 ) +PROVIDE ( __extendsfdf2 , 0x40002c34 ) +PROVIDE ( __addvsi3 , 0x40002c98 ) +PROVIDE ( __addvdi3 , 0x40002cbc ) +PROVIDE ( __subvsi3 , 0x40002cf8 ) +PROVIDE ( __subvdi3 , 0x40002d20 ) +PROVIDE ( __mulvsi3 , 0x40002d60 ) +PROVIDE ( __mulvdi3 , 0x40002d78 ) +PROVIDE ( __negvsi2 , 0x40002e78 ) +PROVIDE ( __negvdi2 , 0x40002e98 ) +PROVIDE ( __popcountsi2 , 0x40002ed0 ) +PROVIDE ( __popcountdi2 , 0x40002ef8 ) +PROVIDE ( __paritysi2 , 0x40002f3c ) +PROVIDE ( rom_phy_disable_agc , 0x40002f6c ) +PROVIDE ( rom_phy_enable_agc , 0x40002f88 ) +PROVIDE ( rom_disable_agc , 0x40002fa4 ) +PROVIDE ( rom_enable_agc , 0x40002fcc ) +PROVIDE ( rom_phy_disable_cca , 0x40003000 ) +PROVIDE ( rom_phy_enable_cca , 0x4000302c ) +PROVIDE ( rom_pow_usr , 0x40003044 ) +PROVIDE ( rom_set_loopback_gain , 0x40003060 ) +PROVIDE ( rom_set_cal_rxdc , 0x400030b8 ) +PROVIDE ( rom_loopback_mode_en , 0x400030f8 ) +PROVIDE ( rom_get_data_sat , 0x4000312c ) +PROVIDE ( rom_set_pbus_mem , 0x400031a4 ) +PROVIDE ( rom_write_gain_mem , 0x4000348c ) +PROVIDE ( rom_rx_gain_force , 0x4000351c ) +PROVIDE ( rom_set_txclk_en , 0x40003564 ) +PROVIDE ( rom_set_rxclk_en , 0x40003594 ) +PROVIDE ( rom_start_tx_tone_step , 0x400035d0 ) +PROVIDE ( rom_start_tx_tone , 0x400036b4 ) +PROVIDE ( rom_clk_force_on_vit , 0x40003710 ) +PROVIDE ( rom_bb_rx_ht20_cen_bcov_en , 0x40003734 ) +PROVIDE ( rom_bb_tx_ht20_cen , 0x40003760 ) +PROVIDE ( rom_spur_reg_write_one_tone , 0x400037f0 ) +PROVIDE ( rom_spur_coef_cfg , 0x40003ac8 ) +PROVIDE ( rom_bb_wdg_test_en , 0x40003b70 ) +PROVIDE ( rom_bb_bss_cbw40_dig , 0x40003bac ) +PROVIDE ( rom_noise_floor_auto_set , 0x40003bdc ) +PROVIDE ( rom_phy_get_noisefloor , 0x40003c2c ) +PROVIDE ( rom_check_noise_floor , 0x40003c78 ) +PROVIDE ( rom_set_noise_floor , 0x40003d48 ) +PROVIDE ( rom_chip_v7_rx_rifs_en , 0x40003d90 ) +PROVIDE ( rom_rtc_mem_backup , 0x40003db4 ) +PROVIDE ( rom_rtc_mem_recovery , 0x40003df4 ) +PROVIDE ( rom_gen_rx_gain_table , 0x40003e3c ) +PROVIDE ( rom_stop_tx_tone , 0x40003f98 ) +PROVIDE ( rom_bb_bss_bw_40_en , 0x4000401c ) +PROVIDE ( rom_mhz2ieee , 0x4000404c ) +PROVIDE ( rom_cbw2040_cfg , 0x400040b0 ) +PROVIDE ( phy_get_romfuncs , 0x40004100 ) +PROVIDE ( rom_chip_i2c_readReg , 0x40004110 ) +PROVIDE ( rom_i2c_readReg , 0x40004148 ) +PROVIDE ( rom_chip_i2c_writeReg , 0x40004168 ) +PROVIDE ( rom_i2c_writeReg , 0x400041a4 ) +PROVIDE ( rom_i2c_readReg_Mask , 0x400041c0 ) +PROVIDE ( rom_i2c_writeReg_Mask , 0x400041fc ) +PROVIDE ( rom_pbus_force_mode , 0x40004270 ) +PROVIDE ( rom_pbus_rd_addr , 0x40004334 ) +PROVIDE ( rom_pbus_rd_shift , 0x40004374 ) +PROVIDE ( rom_pbus_force_test , 0x400043c0 ) +PROVIDE ( rom_pbus_rd , 0x40004414 ) +PROVIDE ( rom_pbus_debugmode , 0x40004458 ) +PROVIDE ( rom_pbus_workmode , 0x4000446c ) +PROVIDE ( rom_pbus_set_rxgain , 0x40004480 ) +PROVIDE ( rom_pbus_xpd_rx_off , 0x40004508 ) +PROVIDE ( rom_pbus_xpd_rx_on , 0x4000453c ) +PROVIDE ( rom_pbus_xpd_tx_off , 0x40004590 ) +PROVIDE ( rom_pbus_xpd_tx_on , 0x400045e0 ) +PROVIDE ( rom_pbus_set_dco , 0x40004638 ) +PROVIDE ( rom_rfpll_reset , 0x40004680 ) +PROVIDE ( rom_restart_cal , 0x400046e0 ) +PROVIDE ( rom_write_rfpll_sdm , 0x40004740 ) +PROVIDE ( rom_wait_rfpll_cal_end , 0x400047a8 ) +PROVIDE ( rom_rfpll_set_freq , 0x400047f8 ) +PROVIDE ( rom_set_channel_freq , 0x40004880 ) +PROVIDE ( rom_phy_freq_correct , 0x40004b44 ) +PROVIDE ( rom_set_rf_freq_offset , 0x40004ca8 ) +PROVIDE ( rom_chip_v7_rx_init , 0x40004cec ) +PROVIDE ( rom_chip_v7_tx_init , 0x40004d18 ) +PROVIDE ( rom_chip_v7_bt_init , 0x40004d8c ) +PROVIDE ( rom_txbbgain_to_index , 0x40004dc0 ) +PROVIDE ( rom_index_to_txbbgain , 0x40004df8 ) +PROVIDE ( rom_txdc_cal_init , 0x40004e10 ) +PROVIDE ( rom_txdc_cal_v70 , 0x40004ea4 ) +PROVIDE ( rom_en_pwdet , 0x4000506c ) +PROVIDE ( rom_txcal_work_mode , 0x4000510c ) +PROVIDE ( rom_txiq_set_reg , 0x40005154 ) +PROVIDE ( rom_read_sar_dout , 0x400051c0 ) +PROVIDE ( rom_get_fm_sar_dout , 0x40005204 ) +PROVIDE ( rom_txtone_linear_pwr , 0x40005290 ) +PROVIDE ( rom_txiq_get_mis_pwr , 0x400052dc ) +PROVIDE ( rom_txiq_cover , 0x4000538c ) +PROVIDE ( rom_abs_temp , 0x400054f0 ) +PROVIDE ( rom_iq_est_enable , 0x40005514 ) +PROVIDE ( rom_iq_est_disable , 0x40005590 ) +PROVIDE ( rom_dc_iq_est , 0x400055c8 ) +PROVIDE ( rom_pbus_rx_dco_cal , 0x40005620 ) +PROVIDE ( rom_rxiq_get_mis , 0x400058e4 ) +PROVIDE ( rom_rxiq_set_reg , 0x40005a00 ) +PROVIDE ( rom_rxiq_cover_mg_mp , 0x40005a68 ) +PROVIDE ( rom_rfcal_rxiq , 0x40005b4c ) +PROVIDE ( rom_get_rfcal_rxiq_data , 0x40005bbc ) +PROVIDE ( rom_set_chan_cal_interp , 0x40005ce0 ) +PROVIDE ( rom_set_txcap_reg , 0x40005d50 ) +PROVIDE ( rom_rfcal_txcap , 0x40005dec ) +PROVIDE ( rom_linear_to_db , 0x40005f64 ) +PROVIDE ( rom_get_power_db , 0x40005fc8 ) +PROVIDE ( rom_meas_tone_pwr_db , 0x40006004 ) +PROVIDE ( rom_rfcal_pwrctrl , 0x40006058 ) +PROVIDE ( rom_tx_atten_set_interp , 0x400061cc ) +PROVIDE ( rom_target_power_add_backoff , 0x40006268 ) +PROVIDE ( rom_get_rf_gain_qdb , 0x40006290 ) +PROVIDE ( rom_correct_rf_ana_gain , 0x400062a8 ) +PROVIDE ( rom_phy_get_vdd33 , 0x4000642c ) +PROVIDE ( rom_get_sar_dout , 0x40006564 ) +PROVIDE ( rom_get_pwctrl_correct , 0x400065d4 ) +PROVIDE ( rom_tx_pwctrl_bg_init , 0x4000662c ) +PROVIDE ( ets_set_idle_cb , 0x40006674 ) +PROVIDE ( ets_task , 0x40006688 ) +PROVIDE ( ets_run , 0x400066bc ) +PROVIDE ( ets_post , 0x4000673c ) +PROVIDE ( ets_intr_lock , 0x400067b0 ) +PROVIDE ( ets_intr_unlock , 0x400067c4 ) +PROVIDE ( ets_waiti0 , 0x400067d8 ) +PROVIDE ( ets_isr_attach , 0x400067ec ) +PROVIDE ( ets_isr_mask , 0x400067fc ) +PROVIDE ( ets_isr_unmask , 0x40006808 ) +PROVIDE ( intr_matrix_set , 0x4000681c ) +PROVIDE ( ets_set_user_start , 0x4000687c ) +PROVIDE ( ets_set_startup_callback , 0x4000688c ) +PROVIDE ( ets_set_appcpu_boot_addr , 0x4000689c ) +PROVIDE ( check_pos , 0x400068b8 ) +PROVIDE ( ets_unpack_flash_code_legacy , 0x4000694c ) +PROVIDE ( ets_unpack_flash_code , 0x40007018 ) +PROVIDE ( rom_main , 0x400076c4 ) +PROVIDE ( ets_install_putc1 , 0x40007d18 ) +PROVIDE ( ets_install_uart_printf , 0x40007d28 ) +PROVIDE ( ets_install_putc2 , 0x40007d38 ) +PROVIDE ( ets_printf , 0x40007d54 ) +PROVIDE ( calc_rtc_memory_crc , 0x40008170 ) +PROVIDE ( rtc_get_reset_reason , 0x400081d4 ) +PROVIDE ( rtc_get_wakeup_cause , 0x400081f4 ) +PROVIDE ( set_rtc_memory_crc , 0x40008208 ) +PROVIDE ( rtc_boot_control , 0x4000821c ) +PROVIDE ( software_reset , 0x4000824c ) +PROVIDE ( software_reset_cpu , 0x40008264 ) +PROVIDE ( rtc_select_apb_bridge , 0x40008288 ) +PROVIDE ( ets_timer_setfn , 0x40008350 ) +PROVIDE ( ets_timer_arm , 0x40008368 ) +PROVIDE ( ets_timer_arm_us , 0x400083ac ) +PROVIDE ( ets_timer_disarm , 0x400083ec ) +PROVIDE ( ets_timer_done , 0x40008428 ) +PROVIDE ( ets_timer_handler_isr , 0x40008454 ) +PROVIDE ( ets_timer_init , 0x400084e8 ) +PROVIDE ( ets_delay_us , 0x40008534 ) +PROVIDE ( ets_update_cpu_frequency , 0x40008550 ) +PROVIDE ( ets_get_cpu_frequency , 0x4000855c ) +PROVIDE ( ets_get_xtal_scale , 0x4000856c ) +PROVIDE ( ets_get_detected_xtal_freq , 0x40008588 ) +PROVIDE ( ets_efuse_read_op , 0x40008600 ) +PROVIDE ( ets_efuse_program_op , 0x40008628 ) +PROVIDE ( ets_efuse_get_spiconfig , 0x40008658 ) +PROVIDE ( ets_efuse_get_8M_clock , 0x40008710 ) +PROVIDE ( UartConnCheck , 0x40008738 ) +PROVIDE ( FlashDwnLdStartMsgProc , 0x40008820 ) +PROVIDE ( FilePacketSendReqMsgProc , 0x40008860 ) +PROVIDE ( FlashDwnLdStopReqMsgProc , 0x400088ec ) +PROVIDE ( FlashDwnLdParamCfgMsgProc , 0x4000891c ) +PROVIDE ( MemDwnLdStartMsgProc , 0x40008948 ) +PROVIDE ( MemPacketSendReqMsgProc , 0x40008978 ) +PROVIDE ( MemDwnLdStopReqMsgProc , 0x400089dc ) +PROVIDE ( UartConnectProc , 0x40008a04 ) +PROVIDE ( UartRegWriteProc , 0x40008a14 ) +PROVIDE ( UartRegReadProc , 0x40008a58 ) +PROVIDE ( UartSpiAttachProc , 0x40008a6c ) +PROVIDE ( UartSpiReadProc , 0x40008a80 ) +PROVIDE ( UartSetBaudProc , 0x40008aac ) +PROVIDE ( FlashDwnLdDeflatedStartMsgProc , 0x40008ad8 ) +PROVIDE ( FilePacketSendDeflatedReqMsgProc , 0x40008b24 ) +PROVIDE ( FlashDwnLdStopDeflatedReqMsgProc , 0x40008c18 ) +PROVIDE ( VerifyFlashMd5Proc , 0x40008c44 ) +PROVIDE ( UartDwnLdProc , 0x40008ce8 ) +PROVIDE ( uart_rx_intr_handler , 0x40008f4c ) +PROVIDE ( uartAttach , 0x40008fd0 ) +PROVIDE ( uart_tx_switch , 0x40009028 ) +PROVIDE ( uart_baudrate_detect , 0x40009034 ) +PROVIDE ( uart_div_modify , 0x400090cc ) +PROVIDE ( Uart_Init , 0x40009120 ) +PROVIDE ( uart_tx_one_char , 0x40009200 ) +PROVIDE ( uart_tx_one_char2 , 0x4000922c ) +PROVIDE ( uart_tx_flush , 0x40009258 ) +PROVIDE ( uart_tx_wait_idle , 0x40009278 ) +PROVIDE ( uart_rx_one_char_block , 0x400092a4 ) +PROVIDE ( uart_rx_one_char , 0x400092d0 ) +PROVIDE ( UartRxString , 0x400092fc ) +PROVIDE ( send_packet , 0x40009340 ) +PROVIDE ( SendMsg , 0x40009384 ) +PROVIDE ( uart_rx_readbuff , 0x40009394 ) +PROVIDE ( uart_buff_switch , 0x400093c0 ) +PROVIDE ( recv_packet , 0x40009424 ) +PROVIDE ( RcvMsg , 0x4000954c ) +PROVIDE ( UartGetCmdLn , 0x40009564 ) +PROVIDE ( GetUartDevice , 0x40009598 ) +PROVIDE ( mmu_init , 0x400095a4 ) +PROVIDE ( cache_flash_mmu_set , 0x400095e0 ) +PROVIDE ( cache_sram_mmu_set , 0x400097f4 ) +PROVIDE ( Cache_Read_Init , 0x40009950 ) +PROVIDE ( Cache_Flush , 0x40009a14 ) +PROVIDE ( Cache_Read_Enable , 0x40009a84 ) +PROVIDE ( Cache_Read_Disable , 0x40009ab8 ) +PROVIDE ( gpio_output_set , 0x40009b24 ) +PROVIDE ( gpio_output_set_high , 0x40009b5c ) +PROVIDE ( gpio_input_get , 0x40009b88 ) +PROVIDE ( gpio_input_get_high , 0x40009b9c ) +PROVIDE ( gpio_register_set , 0x40009bbc ) +PROVIDE ( gpio_init , 0x40009c20 ) +PROVIDE ( gpio_register_get , 0x40009cbc ) +PROVIDE ( gpio_intr_pending , 0x40009cec ) +PROVIDE ( gpio_intr_pending_high , 0x40009cf8 ) +PROVIDE ( gpio_pin_intr_state_set , 0x40009d04 ) +PROVIDE ( gpio_intr_ack , 0x40009dd4 ) +PROVIDE ( gpio_intr_ack_high , 0x40009e1c ) +PROVIDE ( gpio_intr_handler_register , 0x40009e6c ) +PROVIDE ( gpio_pin_wakeup_enable , 0x40009e7c ) +PROVIDE ( gpio_pin_wakeup_disable , 0x40009eb0 ) +PROVIDE ( gpio_matrix_in , 0x40009edc ) +PROVIDE ( gpio_matrix_out , 0x40009f0c ) +PROVIDE ( gpio_pad_select_gpio , 0x40009fdc ) +PROVIDE ( gpio_pad_set_drv , 0x4000a11c ) +PROVIDE ( gpio_pad_pullup , 0x4000a22c ) +PROVIDE ( gpio_pad_pulldown , 0x4000a348 ) +PROVIDE ( gpio_pad_unhold , 0x4000a484 ) +PROVIDE ( gpio_pad_hold , 0x4000a734 ) +PROVIDE ( lldesc_build_chain , 0x4000a850 ) +PROVIDE ( lldesc_num2link , 0x4000a948 ) +PROVIDE ( lldesc_set_owner , 0x4000a974 ) +PROVIDE ( roundup2 , 0x4000ab7c ) +PROVIDE ( multofup , 0x4000ab8c ) +PROVIDE ( sip_alloc_to_host_evt , 0x4000ab9c ) +PROVIDE ( sip_to_host_evt_send_done , 0x4000ac04 ) +PROVIDE ( sip_reclaim_tx_data_pkt , 0x4000ad5c ) +PROVIDE ( sip_reclaim_from_host_cmd , 0x4000adbc ) +PROVIDE ( sip_install_rx_ctrl_cb , 0x4000ae10 ) +PROVIDE ( sip_install_rx_data_cb , 0x4000ae20 ) +PROVIDE ( sip_get_state , 0x4000ae2c ) +PROVIDE ( sip_init_attach , 0x4000ae58 ) +PROVIDE ( sip_post_init , 0x4000aed8 ) +PROVIDE ( sip_to_host_chain_append , 0x4000aef8 ) +PROVIDE ( sip_send , 0x4000af54 ) +PROVIDE ( sip_get_ptr , 0x4000b34c ) +PROVIDE ( sip_after_tx_complete , 0x4000b358 ) +PROVIDE ( sip_is_active , 0x4000b3c0 ) +PROVIDE ( slc_has_pkt_to_host , 0x4000b5fc ) +PROVIDE ( slc_reattach , 0x4000b62c ) +PROVIDE ( slc_enable , 0x4000b64c ) +PROVIDE ( slc_send_to_host_chain , 0x4000b6a0 ) +PROVIDE ( slc_to_host_chain_recycle , 0x4000b758 ) +PROVIDE ( slc_from_host_chain_fetch , 0x4000b7e8 ) +PROVIDE ( slc_set_host_io_max_window , 0x4000b89c ) +PROVIDE ( slc_init_attach , 0x4000b918 ) +PROVIDE ( slc_init_credit , 0x4000badc ) +PROVIDE ( slc_add_credits , 0x4000baf4 ) +PROVIDE ( slc_from_host_chain_recycle , 0x4000bb10 ) +PROVIDE ( abort , 0x4000bba4 ) +PROVIDE ( _malloc_r , 0x4000bbb4 ) +PROVIDE ( _free_r , 0x4000bbcc ) +PROVIDE ( _realloc_r , 0x4000bbe0 ) +PROVIDE ( _calloc_r , 0x4000bbf8 ) +PROVIDE ( _system_r , 0x4000bc10 ) +PROVIDE ( _rename_r , 0x4000bc28 ) +PROVIDE ( _times_r , 0x4000bc40 ) +PROVIDE ( _gettimeofday_r , 0x4000bc58 ) +PROVIDE ( _raise_r , 0x4000bc70 ) +PROVIDE ( _unlink_r , 0x4000bc84 ) +PROVIDE ( _link_r , 0x4000bc9c ) +PROVIDE ( _stat_r , 0x4000bcb4 ) +PROVIDE ( _fstat_r , 0x4000bccc ) +PROVIDE ( _sbrk_r , 0x4000bce4 ) +PROVIDE ( _getpid_r , 0x4000bcfc ) +PROVIDE ( _kill_r , 0x4000bd10 ) +PROVIDE ( _exit_r , 0x4000bd28 ) +PROVIDE ( _close_r , 0x4000bd3c ) +PROVIDE ( _open_r , 0x4000bd54 ) +PROVIDE ( _write_r , 0x4000bd70 ) +PROVIDE ( _lseek_r , 0x4000bd8c ) +PROVIDE ( _read_r , 0x4000bda8 ) +PROVIDE ( _lock_init , 0x4000bdc4 ) +PROVIDE ( _lock_init_recursive , 0x4000bdd8 ) +PROVIDE ( _lock_close , 0x4000bdec ) +PROVIDE ( _lock_close_recursive , 0x4000be00 ) +PROVIDE ( _lock_acquire , 0x4000be14 ) +PROVIDE ( _lock_acquire_recursive , 0x4000be28 ) +PROVIDE ( _lock_try_acquire , 0x4000be3c ) +PROVIDE ( _lock_try_acquire_recursive , 0x4000be50 ) +PROVIDE ( _lock_release , 0x4000be64 ) +PROVIDE ( _lock_release_recursive , 0x4000be78 ) +PROVIDE ( __getreent , 0x4000be8c ) +PROVIDE ( malloc , 0x4000bea0 ) +PROVIDE ( free , 0x4000beb8 ) +PROVIDE ( realloc , 0x4000becc ) +PROVIDE ( calloc , 0x4000bee4 ) +PROVIDE ( _printf_float , 0x4000befc ) +PROVIDE ( _scanf_float , 0x4000bf18 ) +PROVIDE ( _xtos_set_interrupt_handler_arg , 0x4000bf34 ) +PROVIDE ( _xtos_set_interrupt_handler , 0x4000bf78 ) +PROVIDE ( _xtos_ints_on , 0x4000bf88 ) +PROVIDE ( _xtos_ints_off , 0x4000bfac ) +PROVIDE ( _xtos_p_none , 0x4000bfd4 ) +PROVIDE ( _xtos_set_intlevel , 0x4000bfdc ) +PROVIDE ( _xtos_set_min_intlevel , 0x4000bff8 ) +PROVIDE ( _xtos_unhandled_interrupt , 0x4000c01c ) +PROVIDE ( _xtos_unhandled_exception , 0x4000c024 ) +PROVIDE ( _xtos_return_from_exc , 0x4000c034 ) +PROVIDE ( xthal_get_ccount , 0x4000c050 ) +PROVIDE ( xthal_set_ccompare , 0x4000c058 ) +PROVIDE ( xthal_get_ccompare , 0x4000c078 ) +PROVIDE ( xthal_bcopy , 0x4000c098 ) +PROVIDE ( xthal_memcpy , 0x4000c0bc ) +PROVIDE ( xthal_copy123 , 0x4000c124 ) +PROVIDE ( xthal_get_interrupt , 0x4000c1e4 ) +PROVIDE ( xthal_get_intread , 0x4000c1e4 ) +PROVIDE ( xthal_set_intclear , 0x4000c1ec ) +PROVIDE ( bzero , 0x4000c1f4 ) +PROVIDE ( isascii , 0x4000c20c ) +PROVIDE ( memccpy , 0x4000c220 ) +PROVIDE ( memchr , 0x4000c244 ) +PROVIDE ( memcmp , 0x4000c260 ) +PROVIDE ( memcpy , 0x4000c2c8 ) +PROVIDE ( memmove , 0x4000c3c0 ) +PROVIDE ( memrchr , 0x4000c400 ) +PROVIDE ( memset , 0x4000c44c ) +PROVIDE ( __sccl , 0x4000c498 ) +PROVIDE ( strcat , 0x4000c518 ) +PROVIDE ( strchr , 0x4000c53c ) +PROVIDE ( strcspn , 0x4000c558 ) +PROVIDE ( strlcpy , 0x4000c584 ) +PROVIDE ( strncat , 0x4000c5c4 ) +PROVIDE ( strncmp , 0x4000c5f4 ) +PROVIDE ( strnlen , 0x4000c628 ) +PROVIDE ( strspn , 0x4000c648 ) +PROVIDE ( strstr , 0x4000c674 ) +PROVIDE ( __strtok_r , 0x4000c6a8 ) +PROVIDE ( strtok_r , 0x4000c70c ) +PROVIDE ( toascii , 0x4000c720 ) +PROVIDE ( __dummy_lock , 0x4000c728 ) +PROVIDE ( __dummy_lock_try , 0x4000c730 ) +PROVIDE ( _fwalk , 0x4000c738 ) +PROVIDE ( _fwalk_reent , 0x4000c770 ) +PROVIDE ( __mulsi3 , 0x4000c7b0 ) +PROVIDE ( __divsi3 , 0x4000c7b8 ) +PROVIDE ( __modsi3 , 0x4000c7c0 ) +PROVIDE ( __udivsi3 , 0x4000c7c8 ) +PROVIDE ( __umodsi3 , 0x4000c7d0 ) +PROVIDE ( __umulsidi3 , 0x4000c7d8 ) +PROVIDE ( __clzsi2 , 0x4000c7e8 ) +PROVIDE ( __ctzsi2 , 0x4000c7f0 ) +PROVIDE ( __ffssi2 , 0x4000c804 ) +PROVIDE ( __ashldi3 , 0x4000c818 ) +PROVIDE ( __ashrdi3 , 0x4000c830 ) +PROVIDE ( __lshrdi3 , 0x4000c84c ) +PROVIDE ( __floatunsisf , 0x4000c864 ) +PROVIDE ( __floatsisf , 0x4000c870 ) +PROVIDE ( __floatundisf , 0x4000c8b0 ) +PROVIDE ( __floatdisf , 0x4000c8c0 ) +PROVIDE ( __floatunsidf , 0x4000c938 ) +PROVIDE ( __floatsidf , 0x4000c944 ) +PROVIDE ( __floatundidf , 0x4000c978 ) +PROVIDE ( __floatdidf , 0x4000c988 ) +PROVIDE ( __muldi3 , 0x4000c9fc ) +PROVIDE ( __negdi2 , 0x4000ca14 ) +PROVIDE ( __ffsdi2 , 0x4000ca2c ) +PROVIDE ( __clzdi2 , 0x4000ca50 ) +PROVIDE ( __ctzdi2 , 0x4000ca64 ) +PROVIDE ( __divdi3 , 0x4000ca84 ) +PROVIDE ( __moddi3 , 0x4000cd4c ) +PROVIDE ( __udivdi3 , 0x4000cff8 ) +PROVIDE ( __umoddi3 , 0x4000d280 ) +PROVIDE ( _data_start_btdm_rom , 0x4000d4f4 ) +PROVIDE ( _data_end_btdm_rom , 0x4000d4f8 ) +PROVIDE ( _data_start , 0x4000d4f8 ) +PROVIDE ( _rom_store_table , 0x4000d4f8 ) +PROVIDE ( _data_end , 0x4000d5c8 ) +PROVIDE ( _etext , 0x4000d66c ) +PROVIDE ( _rom_store , 0x4000d66c ) +PROVIDE ( btdm_r_btdm_option_data_p_get , 0x40010004 ) +PROVIDE ( btdm_r_data_init , 0x4001002c ) +PROVIDE ( btdm_r_btdm_rom_version_get , 0x40010078 ) +PROVIDE ( r_bt_util_buf_init , 0x400100e4 ) +PROVIDE ( r_bt_util_buf_lmp_tx_alloc , 0x400101d0 ) +PROVIDE ( r_bt_util_buf_lmp_tx_free , 0x400101ec ) +PROVIDE ( r_bt_util_buf_acl_rx_alloc , 0x40010218 ) +PROVIDE ( r_bt_util_buf_acl_rx_free , 0x40010234 ) +PROVIDE ( r_bt_util_buf_acl_tx_alloc , 0x40010268 ) +PROVIDE ( r_bt_util_buf_acl_tx_free , 0x40010280 ) +PROVIDE ( r_bt_util_buf_sync_init , 0x400102c4 ) +PROVIDE ( r_bt_util_buf_sync_clear , 0x400103c8 ) +PROVIDE ( r_bt_util_buf_sync_tx_alloc , 0x400103ec ) +PROVIDE ( r_bt_util_buf_sync_tx_free , 0x40010428 ) +PROVIDE ( r_bt_util_buf_sync_rx_alloc , 0x40010468 ) +PROVIDE ( r_bt_util_buf_sync_rx_free , 0x4001049c ) +PROVIDE ( r_E1 , 0x400108e8 ) +PROVIDE ( r_E21 , 0x40010968 ) +PROVIDE ( r_E22 , 0x400109b4 ) +PROVIDE ( r_E3 , 0x40010a58 ) +PROVIDE ( r_KPrimC , 0x40010ad4 ) +PROVIDE ( r_XorKey , 0x400112c0 ) +PROVIDE ( r_LM_MakeRandVec , 0x400112d8 ) +PROVIDE ( r_lmp_pack , 0x4001135c ) +PROVIDE ( r_lmp_unpack , 0x4001149c ) +PROVIDE ( r_lm_n_is_zero , 0x40012170 ) +PROVIDE ( r_lm_sp_sha256_calculate , 0x400121a0 ) +PROVIDE ( r_lm_sp_n_one , 0x400123a4 ) +PROVIDE ( r_lm_sp_n192_convert_wnaf , 0x400123c0 ) +PROVIDE ( r_lm_sp_p192_point_to_inf , 0x40012458 ) +PROVIDE ( r_lm_sp_p192_point_jacobian_to_affine , 0x40012468 ) +PROVIDE ( r_lm_sp_p192_points_jacobian_to_affine , 0x400124e4 ) +PROVIDE ( r_lm_sp_pre_compute_points , 0x40012640 ) +PROVIDE ( r_lm_sp_p192_dbl , 0x4001268c ) +PROVIDE ( r_lm_sp_p192_add , 0x40012828 ) +PROVIDE ( r_lm_sp_p192_invert , 0x40012b6c ) +PROVIDE ( r_lm_f1 , 0x40012bb8 ) +PROVIDE ( r_lm_f2 , 0x40012cfc ) +PROVIDE ( r_lm_oob_f1 , 0x40012e54 ) +PROVIDE ( r_lm_g , 0x40012f90 ) +PROVIDE ( r_lm_f3 , 0x40013050 ) +PROVIDE ( r_lm_get_nonce , 0x400131c4 ) +PROVIDE ( r_lm_dhkey_calc_init , 0x40013234 ) +PROVIDE ( r_lm_dhkey_compare , 0x400132d8 ) +PROVIDE ( r_F1_256 , 0x400133e4 ) +PROVIDE ( r_G_256 , 0x40013470 ) +PROVIDE ( r_F2_256 , 0x40013568 ) +PROVIDE ( r_F3_256 , 0x40013664 ) +PROVIDE ( r_H3 , 0x40013760 ) +PROVIDE ( r_H4 , 0x40013830 ) +PROVIDE ( r_H5 , 0x400138dc ) +PROVIDE ( r_HMAC , 0x40013968 ) +PROVIDE ( r_hashConcat , 0x40013a38 ) +PROVIDE ( r_SHA_256 , 0x40013a90 ) +PROVIDE ( r_co_list_init , 0x40013f14 ) +PROVIDE ( r_co_list_pool_init , 0x40013f30 ) +PROVIDE ( r_co_list_push_back , 0x40013fb8 ) +PROVIDE ( r_co_list_push_front , 0x40013ff4 ) +PROVIDE ( r_co_list_pop_front , 0x40014028 ) +PROVIDE ( r_co_list_extract , 0x4001404c ) +PROVIDE ( r_co_list_extract_after , 0x40014118 ) +PROVIDE ( r_co_list_find , 0x4001419c ) +PROVIDE ( r_co_list_merge , 0x400141bc ) +PROVIDE ( r_co_list_insert_before , 0x40014200 ) +PROVIDE ( r_co_list_insert_after , 0x40014254 ) +PROVIDE ( r_co_list_size , 0x400142ac ) +PROVIDE ( r_co_list_check_size_available , 0x400142c4 ) +PROVIDE ( r_co_bytes_to_string , 0x400142e4 ) +PROVIDE ( r_co_bdaddr_compare , 0x40014324 ) +PROVIDE ( r_co_slot_to_duration , 0x40014348 ) +PROVIDE ( r_co_nb_good_channels , 0x40014360 ) +PROVIDE ( r_dbg_init , 0x40014394 ) +PROVIDE ( r_dbg_platform_reset_complete , 0x400143d0 ) +PROVIDE ( r_dbg_swdiag_init , 0x40014470 ) +PROVIDE ( r_dbg_swdiag_read , 0x400144a4 ) +PROVIDE ( r_dbg_swdiag_write , 0x400144d0 ) +PROVIDE ( r_ea_elt_cancel , 0x400150d0 ) +PROVIDE ( r_ea_init , 0x40015228 ) +PROVIDE ( r_ea_elt_create , 0x40015264 ) +PROVIDE ( r_ea_elt_insert , 0x400152a8 ) +PROVIDE ( r_ea_elt_remove , 0x400154f0 ) +PROVIDE ( r_ea_interval_create , 0x4001555c ) +PROVIDE ( r_ea_interval_insert , 0x4001557c ) +PROVIDE ( r_ea_interval_remove , 0x40015590 ) +PROVIDE ( r_ea_interval_delete , 0x400155a8 ) +PROVIDE ( r_ea_finetimer_isr , 0x400155d4 ) +PROVIDE ( r_ea_sw_isr , 0x40015724 ) +PROVIDE ( r_ea_offset_req , 0x40015748 ) +PROVIDE ( r_ea_time_get_halfslot_rounded , 0x40015894 ) +PROVIDE ( r_ea_time_get_slot_rounded , 0x400158d4 ) +PROVIDE ( r_ea_sleep_check , 0x40015928 ) +PROVIDE ( r_ea_interval_duration_req , 0x4001597c ) +PROVIDE ( r_ea_alarm_set , 0x40015a10 ) +PROVIDE ( r_ea_alarm_clear , 0x40015ab4 ) +PROVIDE ( notEqual256 , 0x40015b04 ) +PROVIDE ( AddBigHex256 , 0x40015b28 ) +PROVIDE ( Add2SelfBigHex256 , 0x40015b7c ) +PROVIDE ( SubtractBigHex256 , 0x40015bcc ) +PROVIDE ( SubtractFromSelfBigHex256 , 0x40015c20 ) +PROVIDE ( AddP256 , 0x40015c74 ) +PROVIDE ( AddBigHexModP256 , 0x40015c98 ) +PROVIDE ( AddPdiv2_256 , 0x40015ce0 ) +PROVIDE ( SubtractFromSelfBigHexSign256 , 0x40015dc8 ) +PROVIDE ( SubtractBigHexMod256 , 0x40015e8c ) +PROVIDE ( SubtractBigHexUint32_256 , 0x40015f8c ) +PROVIDE ( MultiplyByU32ModP256 , 0x40015fdc ) +PROVIDE ( specialModP256 , 0x4001600c ) +PROVIDE ( MultiplyBigHexModP256 , 0x400160b8 ) +PROVIDE ( MultiplyBigHexByUint32_256 , 0x40016214 ) +PROVIDE ( GF_Jacobian_Point_Double256 , 0x40016260 ) +PROVIDE ( GF_Jacobian_Point_Addition256 , 0x400163a4 ) +PROVIDE ( bigHexInversion256 , 0x400168f0 ) +PROVIDE ( GF_Point_Jacobian_To_Affine256 , 0x40016b0c ) +PROVIDE ( r_ecc_init , 0x40016dbc ) +PROVIDE ( r_ecc_generate_key256 , 0x40016e00 ) +PROVIDE ( r_ecc_abort_key256_generation , 0x40017070 ) +PROVIDE ( r_ecc_gen_new_public_key , 0x400170c0 ) +PROVIDE ( r_ecc_gen_new_secret_key , 0x400170e4 ) +PROVIDE ( r_ecc_get_debug_Keys , 0x40017224 ) +PROVIDE ( r_em_buf_init , 0x4001729c ) +PROVIDE ( r_em_buf_rx_free , 0x400173c4 ) +PROVIDE ( r_em_buf_rx_buff_addr_get , 0x400173e8 ) +PROVIDE ( r_em_buf_tx_buff_addr_get , 0x40017404 ) +PROVIDE ( r_em_buf_tx_free , 0x4001741c ) +PROVIDE ( r_h4tl_init , 0x40017878 ) +PROVIDE ( r_h4tl_write , 0x400178d0 ) +PROVIDE ( r_h4tl_start , 0x40017924 ) +PROVIDE ( r_h4tl_stop , 0x40017934 ) +PROVIDE ( r_hci_fc_init , 0x40017974 ) +PROVIDE ( r_hci_fc_acl_buf_size_set , 0x40017988 ) +PROVIDE ( r_hci_fc_sync_buf_size_set , 0x400179b0 ) +PROVIDE ( r_hci_fc_acl_en , 0x400179d8 ) +PROVIDE ( r_hci_fc_sync_en , 0x40017a30 ) +PROVIDE ( r_hci_fc_acl_packet_sent , 0x40017a3c ) +PROVIDE ( r_hci_fc_sync_packet_sent , 0x40017a54 ) +PROVIDE ( r_hci_fc_host_nb_acl_pkts_complete , 0x40017a6c ) +PROVIDE ( r_hci_fc_host_nb_sync_pkts_complete , 0x40017a88 ) +PROVIDE ( r_hci_fc_check_host_available_nb_acl_packets , 0x40017aa4 ) +PROVIDE ( r_hci_fc_check_host_available_nb_sync_packets , 0x40017ac8 ) +PROVIDE ( r_hci_look_for_cmd_desc , 0x40018454 ) +PROVIDE ( r_hci_look_for_evt_desc , 0x400184a0 ) +PROVIDE ( r_hci_look_for_dbg_evt_desc , 0x400184c4 ) +PROVIDE ( r_hci_look_for_le_evt_desc , 0x400184e0 ) +PROVIDE ( r_hci_init , 0x40018538 ) +PROVIDE ( r_hci_reset , 0x4001856c ) +PROVIDE ( r_hci_send_2_host , 0x400185bc ) +PROVIDE ( r_hci_bt_acl_bdaddr_register , 0x40018900 ) +PROVIDE ( r_hci_bt_acl_conhdl_register , 0x4001895c ) +PROVIDE ( r_hci_bt_acl_bdaddr_unregister , 0x400189ac ) +PROVIDE ( r_hci_evt_mask_set , 0x400189e4 ) +PROVIDE ( r_hci_evt_filter_add , 0x40018a64 ) +PROVIDE ( r_hci_voice_settings_get , 0x40018bdc ) +PROVIDE ( r_hci_voice_settings_set , 0x40018be8 ) +PROVIDE ( r_hci_tl_send , 0x40019228 ) +PROVIDE ( r_hci_tl_init , 0x40019290 ) +PROVIDE ( r_hci_cmd_get_max_param_size , 0x400192d0 ) +PROVIDE ( r_hci_cmd_received , 0x400192f8 ) +PROVIDE ( r_hci_acl_tx_data_alloc , 0x4001951c ) +PROVIDE ( r_hci_acl_tx_data_received , 0x40019654 ) +PROVIDE ( r_hci_sync_tx_data_alloc , 0x40019754 ) +PROVIDE ( r_hci_sync_tx_data_received , 0x400197c0 ) +PROVIDE ( r_hci_util_pack , 0x40019874 ) +PROVIDE ( r_hci_util_unpack , 0x40019998 ) +PROVIDE ( btdm_r_ip_func_p_get , 0x40019af0 ) +PROVIDE ( btdm_r_ip_func_p_set , 0x40019afc ) +PROVIDE ( btdm_r_ble_bt_handler_tab_p_get , 0x40019b0c ) +PROVIDE ( r_ke_event_init , 0x40019b90 ) +PROVIDE ( r_ke_event_callback_set , 0x40019ba8 ) +PROVIDE ( r_ke_event_set , 0x40019be0 ) +PROVIDE ( r_ke_event_clear , 0x40019c2c ) +PROVIDE ( r_ke_event_get , 0x40019c78 ) +PROVIDE ( r_ke_event_get_all , 0x40019cc0 ) +PROVIDE ( r_ke_event_flush , 0x40019ccc ) +PROVIDE ( r_ke_event_schedule , 0x40019cdc ) +PROVIDE ( r_ke_mem_init , 0x40019d3c ) +PROVIDE ( r_ke_mem_is_empty , 0x40019d8c ) +PROVIDE ( r_ke_check_malloc , 0x40019de0 ) +PROVIDE ( r_ke_malloc , 0x40019eb4 ) +PROVIDE ( r_ke_free , 0x4001a014 ) +PROVIDE ( r_ke_is_free , 0x4001a184 ) +PROVIDE ( r_ke_get_mem_usage , 0x4001a1a0 ) +PROVIDE ( r_ke_get_max_mem_usage , 0x4001a1c8 ) +PROVIDE ( r_ke_msg_alloc , 0x4001a1e0 ) +PROVIDE ( r_ke_msg_send , 0x4001a234 ) +PROVIDE ( r_ke_msg_send_basic , 0x4001a26c ) +PROVIDE ( r_ke_msg_forward , 0x4001a290 ) +PROVIDE ( r_ke_msg_forward_new_id , 0x4001a2ac ) +PROVIDE ( r_ke_msg_free , 0x4001a2cc ) +PROVIDE ( r_ke_msg_dest_id_get , 0x4001a2e0 ) +PROVIDE ( r_ke_msg_src_id_get , 0x4001a2ec ) +PROVIDE ( r_ke_msg_in_queue , 0x4001a2f8 ) +PROVIDE ( r_ke_init , 0x4001a318 ) +PROVIDE ( r_ke_flush , 0x4001a374 ) +PROVIDE ( r_ke_sleep_check , 0x4001a3d8 ) +PROVIDE ( r_ke_stats_get , 0x4001a3f0 ) +PROVIDE ( r_ke_task_init , 0x4001a650 ) +PROVIDE ( r_ke_task_create , 0x4001a674 ) +PROVIDE ( r_ke_task_delete , 0x4001a6c0 ) +PROVIDE ( r_ke_state_set , 0x4001a6fc ) +PROVIDE ( r_ke_state_get , 0x4001a7d8 ) +PROVIDE ( r_ke_msg_discard , 0x4001a850 ) +PROVIDE ( r_ke_msg_save , 0x4001a858 ) +PROVIDE ( r_ke_task_msg_flush , 0x4001a860 ) +PROVIDE ( r_ke_task_check , 0x4001a8a4 ) +PROVIDE ( r_ke_timer_init , 0x4001aa9c ) +PROVIDE ( r_ke_timer_set , 0x4001aac0 ) +PROVIDE ( r_ke_timer_clear , 0x4001ab90 ) +PROVIDE ( r_ke_timer_active , 0x4001ac08 ) +PROVIDE ( r_ke_timer_adjust_all , 0x4001ac30 ) +PROVIDE ( r_ke_timer_sleep_check , 0x4001ac50 ) +PROVIDE ( r_LM_ExtractMaxEncKeySize , 0x4001aca4 ) +PROVIDE ( r_lb_init , 0x4001acd4 ) +PROVIDE ( r_lb_reset , 0x4001ad38 ) +PROVIDE ( r_lb_util_get_nb_broadcast , 0x4001ad80 ) +PROVIDE ( r_lb_util_set_nb_broadcast , 0x4001ad8c ) +PROVIDE ( r_lb_util_get_res_lt_addr , 0x4001ad98 ) +PROVIDE ( r_lb_util_get_csb_mode , 0x4001ada4 ) +PROVIDE ( r_lb_send_lmp , 0x4001adbc ) +PROVIDE ( r_lb_clk_adj_activate , 0x4001ae70 ) +PROVIDE ( r_lb_clk_adj_id_get , 0x4001af14 ) +PROVIDE ( r_lb_clk_adj_period_update , 0x4001af20 ) +PROVIDE ( r_lb_send_pdu_clk_adj , 0x4001af3c ) +PROVIDE ( r_lb_mst_key_cmp , 0x4001af74 ) +PROVIDE ( r_lb_mst_key , 0x4001afc0 ) +PROVIDE ( r_lb_mst_key_restart_enc , 0x4001b0d4 ) +PROVIDE ( r_lb_mst_start_act_bcst_enc , 0x4001b198 ) +PROVIDE ( r_lb_mst_stop_act_bcst_enc , 0x4001b24c ) +PROVIDE ( r_LM_GetMasterKey , 0x4001b260 ) +PROVIDE ( r_LM_GetMasterKeyRand , 0x4001b274 ) +PROVIDE ( r_LM_GetMasterEncRand , 0x4001b288 ) +PROVIDE ( r_LM_GetMasterEncKeySize , 0x4001b29c ) +PROVIDE ( lb_hci_cmd_handler_tab_p_get , 0x4001c18c ) +PROVIDE ( lb_default_state_tab_p_get , 0x4001c198 ) +PROVIDE ( r_lc_send_lmp , 0x4001c1a8 ) +PROVIDE ( r_lc_send_pdu_acc , 0x4001c21c ) +PROVIDE ( r_lc_send_pdu_acc_ext4 , 0x4001c240 ) +PROVIDE ( r_lc_send_pdu_not_acc , 0x4001c26c ) +PROVIDE ( r_lc_send_pdu_not_acc_ext4 , 0x4001c294 ) +PROVIDE ( r_lc_send_pdu_set_afh , 0x4001c2c8 ) +PROVIDE ( r_lc_send_pdu_au_rand , 0x4001c308 ) +PROVIDE ( r_lc_send_pdu_in_rand , 0x4001c338 ) +PROVIDE ( r_lc_send_pdu_comb_key , 0x4001c368 ) +PROVIDE ( r_lc_send_pdu_unit_key , 0x4001c398 ) +PROVIDE ( r_lc_send_pdu_max_slot , 0x4001c3c8 ) +PROVIDE ( r_lc_send_pdu_max_slot_req , 0x4001c3ec ) +PROVIDE ( r_lc_send_pdu_encaps_payl , 0x4001c410 ) +PROVIDE ( r_lc_send_pdu_encaps_head , 0x4001c440 ) +PROVIDE ( r_lc_send_pdu_clk_adj_ack , 0x4001c46c ) +PROVIDE ( r_lc_send_pdu_clk_adj_req , 0x4001c494 ) +PROVIDE ( r_lc_send_pdu_ptt_req , 0x4001c4c0 ) +PROVIDE ( r_lc_send_pdu_sp_nb , 0x4001c4e8 ) +PROVIDE ( r_lc_send_pdu_sp_cfm , 0x4001c518 ) +PROVIDE ( r_lc_send_pdu_sres , 0x4001c548 ) +PROVIDE ( r_lc_send_pdu_sco_lk_rem_req , 0x4001c580 ) +PROVIDE ( r_lc_send_pdu_esco_lk_rem_req , 0x4001c5a8 ) +PROVIDE ( r_lc_send_pdu_auto_rate , 0x4001c5d0 ) +PROVIDE ( r_lc_send_pdu_sniff_req , 0x4001c5f0 ) +PROVIDE ( r_lc_send_pdu_lsto , 0x4001c64c ) +PROVIDE ( r_lc_send_pdu_enc_key_sz_req , 0x4001c670 ) +PROVIDE ( r_lc_send_pdu_feats_res , 0x4001c694 ) +PROVIDE ( r_lc_send_pdu_tim_acc , 0x4001c6cc ) +PROVIDE ( r_lc_send_pdu_feats_ext_req , 0x4001c6ec ) +PROVIDE ( r_lc_send_pdu_io_cap_res , 0x4001c72c ) +PROVIDE ( r_lc_send_pdu_num_comp_fail , 0x4001c770 ) +PROVIDE ( r_lc_send_pdu_pause_enc_aes_req , 0x4001c794 ) +PROVIDE ( r_lc_send_pdu_paus_enc_req , 0x4001c7c0 ) +PROVIDE ( r_lc_send_pdu_resu_enc_req , 0x4001c7e4 ) +PROVIDE ( r_lc_send_pdu_setup_cmp , 0x4001c808 ) +PROVIDE ( r_lc_send_pdu_qos_req , 0x4001c82c ) +PROVIDE ( r_lc_send_pdu_slot_off , 0x4001c854 ) +PROVIDE ( r_lc_send_pdu_unsniff_req , 0x4001c894 ) +PROVIDE ( r_lc_send_pdu_vers_req , 0x4001c8b4 ) +PROVIDE ( r_lc_send_pdu_dhkey_chk , 0x4001c8e8 ) +PROVIDE ( r_lc_cmd_stat_send , 0x4001c914 ) +PROVIDE ( r_lc_init , 0x4001c948 ) +PROVIDE ( r_lc_reset , 0x4001c99c ) +PROVIDE ( r_lc_start , 0x4001ca28 ) +PROVIDE ( r_lc_afh_set , 0x4001cc74 ) +PROVIDE ( r_lc_auth_cmp , 0x4001cd54 ) +PROVIDE ( r_lc_calc_link_key , 0x4001ce7c ) +PROVIDE ( r_lc_chg_pkt_type_cont , 0x4001cfbc ) +PROVIDE ( r_lc_chg_pkt_type_cmp , 0x4001d038 ) +PROVIDE ( r_lc_chg_pkt_type_retry , 0x4001d0ac ) +PROVIDE ( r_lc_afh_start , 0x4001d240 ) +PROVIDE ( r_lc_chk_to , 0x4001d2a8 ) +PROVIDE ( r_lc_comb_key_svr , 0x4001d30c ) +PROVIDE ( r_lc_con_cmp , 0x4001d44c ) +PROVIDE ( r_lc_con_cmp_evt_send , 0x4001d4fc ) +PROVIDE ( r_lc_dhkey , 0x4001d564 ) +PROVIDE ( r_lc_locepr_rsw , 0x4001d5d0 ) +PROVIDE ( r_lc_locepr_lkref , 0x4001d648 ) +PROVIDE ( r_lc_enc_key_refresh , 0x4001d720 ) +PROVIDE ( r_lc_end_chk_colli , 0x4001d858 ) +PROVIDE ( r_lc_enc_cmp , 0x4001d8bc ) +PROVIDE ( r_lc_end_of_sniff_nego , 0x4001d9a4 ) +PROVIDE ( r_lc_epr_cmp , 0x4001da88 ) +PROVIDE ( r_lc_epr_change_lk , 0x4001db38 ) +PROVIDE ( r_lc_rsw_done , 0x4001db94 ) +PROVIDE ( r_lc_rsw_clean_up , 0x4001dc70 ) +PROVIDE ( r_lc_epr_rsw_cmp , 0x4001dd40 ) +PROVIDE ( r_lc_enter_sniff_mode , 0x4001ddb8 ) +PROVIDE ( r_lc_init_calc_f3 , 0x4001deb0 ) +PROVIDE ( r_lc_init_start_mutual_auth , 0x4001df60 ) +PROVIDE ( r_lc_init_passkey_loop , 0x4001dfc0 ) +PROVIDE ( r_lc_initiator_epr , 0x4001e064 ) +PROVIDE ( r_lc_epr_resp , 0x4001e0b4 ) +PROVIDE ( r_lc_key_exch_end , 0x4001e140 ) +PROVIDE ( r_lc_legacy_pair , 0x4001e1c0 ) +PROVIDE ( r_lc_local_switch , 0x4001e22c ) +PROVIDE ( r_lc_local_trans_mode , 0x4001e2e4 ) +PROVIDE ( r_lc_local_untrans_mode , 0x4001e3a0 ) +PROVIDE ( r_lc_max_slot_mgt , 0x4001e410 ) +PROVIDE ( r_lc_mutual_auth_end2 , 0x4001e4f4 ) +PROVIDE ( r_lc_mutual_auth_end , 0x4001e670 ) +PROVIDE ( r_lc_mst_key , 0x4001e7c0 ) +PROVIDE ( r_lc_mst_send_mst_key , 0x4001e8f4 ) +PROVIDE ( r_lc_mst_qos_done , 0x4001ea80 ) +PROVIDE ( r_lc_pairing_cont , 0x4001eafc ) +PROVIDE ( r_lc_passkey_comm , 0x4001ed20 ) +PROVIDE ( r_lc_proc_rcv_dhkey , 0x4001edec ) +PROVIDE ( r_lc_ptt , 0x4001ee2c ) +PROVIDE ( r_lc_ptt_cmp , 0x4001eeec ) +PROVIDE ( r_lc_qos_setup , 0x4001ef50 ) +PROVIDE ( r_lc_rd_rem_name , 0x4001efd0 ) +PROVIDE ( r_lc_restore_afh_reporting , 0x4001f028 ) +PROVIDE ( r_lc_rem_switch , 0x4001f070 ) +PROVIDE ( r_lc_rem_enc , 0x4001f124 ) +PROVIDE ( r_lc_rem_nego_trans_mode , 0x4001f1b4 ) +PROVIDE ( r_lc_rem_name_cont , 0x4001f290 ) +PROVIDE ( r_lc_rem_trans_mode , 0x4001f314 ) +PROVIDE ( r_lc_rem_untrans_mode , 0x4001f36c ) +PROVIDE ( r_lc_sec_auth_compute_sres , 0x4001f3ec ) +PROVIDE ( r_lc_resp_sec_auth , 0x4001f4a0 ) +PROVIDE ( r_lc_resp_auth , 0x4001f518 ) +PROVIDE ( r_lc_resp_oob_wait_nonce , 0x4001f66c ) +PROVIDE ( r_lc_resp_oob_nonce , 0x4001f694 ) +PROVIDE ( r_lc_resp_calc_f3 , 0x4001f710 ) +PROVIDE ( r_lc_resp_wait_dhkey_cont , 0x4001f86c ) +PROVIDE ( r_lc_release , 0x4001f8a8 ) +PROVIDE ( r_lc_restart_enc , 0x4001f8ec ) +PROVIDE ( r_lc_restart_enc_cont , 0x4001f940 ) +PROVIDE ( r_lc_restore_to , 0x4001f9e0 ) +PROVIDE ( r_lc_ret_sniff_max_slot_chg , 0x4001fa30 ) +PROVIDE ( r_lc_start_lmp_to , 0x4001fae8 ) +PROVIDE ( r_lc_start_enc , 0x4001fb28 ) +PROVIDE ( r_lc_start_enc_key_size , 0x4001fd9c ) +PROVIDE ( r_lc_start_key_exch , 0x4001fe10 ) +PROVIDE ( r_lc_start_passkey , 0x4001feac ) +PROVIDE ( r_lc_start_passkey_loop , 0x4001ff88 ) +PROVIDE ( r_lc_start_oob , 0x4001fffc ) +PROVIDE ( r_lc_resp_num_comp , 0x40020074 ) +PROVIDE ( r_lc_stop_enc , 0x40020110 ) +PROVIDE ( r_lc_stop_afh_report , 0x40020184 ) +PROVIDE ( r_lc_skip_hl_oob_req , 0x400201bc ) +PROVIDE ( r_lc_send_enc_mode , 0x40020220 ) +PROVIDE ( r_lc_semi_key_cmp , 0x40020294 ) +PROVIDE ( r_lc_detach , 0x4002037c ) +PROVIDE ( r_lc_switch_cmp , 0x40020448 ) +PROVIDE ( r_lc_sp_fail , 0x40020470 ) +PROVIDE ( r_lc_sp_oob_tid_fail , 0x400204cc ) +PROVIDE ( r_lc_sniff_sub_mode , 0x400204fc ) +PROVIDE ( r_lc_sniff_max_slot_chg , 0x40020590 ) +PROVIDE ( r_lc_upd_to , 0x4002065c ) +PROVIDE ( r_lc_unit_key_svr , 0x400206d8 ) +PROVIDE ( r_lc_unsniff_cont , 0x40020750 ) +PROVIDE ( r_lc_rem_unsniff , 0x400207a0 ) +PROVIDE ( r_lc_unsniff_cmp , 0x40020810 ) +PROVIDE ( r_lc_resp_pair , 0x400208a4 ) +PROVIDE ( r_lc_feat , 0x40020984 ) +PROVIDE ( r_lc_hl_connect , 0x400209e8 ) +PROVIDE ( r_lc_version , 0x40020a30 ) +PROVIDE ( r_lc_loc_sniff , 0x40020a6c ) +PROVIDE ( r_lc_rem_sniff_sub_rate , 0x40020b10 ) +PROVIDE ( r_lc_unsniff , 0x40020c50 ) +PROVIDE ( r_lc_rem_sniff , 0x40020ca4 ) +PROVIDE ( r_lc_ext_feat , 0x40020d6c ) +PROVIDE ( r_lc_pair , 0x40020ddc ) +PROVIDE ( r_lc_loc_auth , 0x40020ecc ) +PROVIDE ( r_lc_packet_type , 0x40021038 ) +PROVIDE ( r_lc_sniff_slot_unchange , 0x40021100 ) +PROVIDE ( r_lc_ssr_nego , 0x4002125c ) +PROVIDE ( r_lc_conn_seq_done , 0x40021334 ) +PROVIDE ( r_lc_send_enc_chg_evt , 0x4002134c ) +PROVIDE ( r_lc_sp_end , 0x400213a8 ) +PROVIDE ( r_lc_prepare_all_links_for_clk_adj , 0x40021430 ) +PROVIDE ( r_lc_sco_init , 0x40021dc8 ) +PROVIDE ( r_lc_sco_reset , 0x40021dfc ) +PROVIDE ( r_lc_sco_detach , 0x40021e40 ) +PROVIDE ( r_lc_sco_release , 0x40021eec ) +PROVIDE ( r_lc_sco_host_request , 0x40021f4c ) +PROVIDE ( r_lc_sco_host_accept , 0x40022118 ) +PROVIDE ( r_lc_sco_host_reject , 0x400222b8 ) +PROVIDE ( r_lc_sco_host_request_disc , 0x4002235c ) +PROVIDE ( r_lc_sco_peer_request , 0x4002240c ) +PROVIDE ( r_lc_sco_peer_accept , 0x40022780 ) +PROVIDE ( r_lc_sco_peer_reject , 0x40022824 ) +PROVIDE ( r_lc_sco_peer_request_disc , 0x400228ec ) +PROVIDE ( r_lc_sco_peer_accept_disc , 0x40022a08 ) +PROVIDE ( r_lc_sco_peer_reject_disc , 0x40022a8c ) +PROVIDE ( r_lc_sco_baseband_ack , 0x40022b00 ) +PROVIDE ( r_lc_sco_timeout , 0x40022bd4 ) +PROVIDE ( r_lc_sniff_init , 0x40022cac ) +PROVIDE ( r_lc_sniff_reset , 0x40022cc8 ) +PROVIDE ( r_LM_AddSniff , 0x40022d20 ) +PROVIDE ( r_LM_RemoveSniff , 0x40023124 ) +PROVIDE ( r_LM_SniffSubratingHlReq , 0x40023154 ) +PROVIDE ( r_LM_SniffSubratingPeerReq , 0x400231dc ) +PROVIDE ( r_LM_GetSniffSubratingParam , 0x4002325c ) +PROVIDE ( r_LM_SniffSubrateNegoRequired , 0x40023334 ) +PROVIDE ( r_LM_ComputeSniffSubRate , 0x400233ac ) +PROVIDE ( r_LM_RemoveSniffSubrating , 0x400233c4 ) +PROVIDE ( r_LM_GetLinkTimeout , 0x400233ec ) +PROVIDE ( lc_hci_cmd_handler_tab_p_get , 0x4002f488 ) +PROVIDE ( lc_default_state_tab_p_get , 0x4002f494 ) +PROVIDE ( r_lc_util_get_max_packet_size , 0x4002f4ac ) +PROVIDE ( r_lc_util_set_loc_trans_coll , 0x4002f500 ) +PROVIDE ( r_lc_util_get_offset_clkn , 0x4002f51c ) +PROVIDE ( r_lc_util_get_offset_clke , 0x4002f538 ) +PROVIDE ( r_LM_ComputePacketType , 0x4002f554 ) +PROVIDE ( r_LM_UpdateAclPacketType , 0x4002f584 ) +PROVIDE ( r_LM_UpdateAclEdrPacketType , 0x4002f5d8 ) +PROVIDE ( r_LM_SuppressAclPacket , 0x4002f658 ) +PROVIDE ( r_LM_MaxSlot , 0x4002f694 ) +PROVIDE ( r_LM_GetQoSParam , 0x4002f6e0 ) +PROVIDE ( r_LM_GetSwitchInstant , 0x4002f7f8 ) +PROVIDE ( r_LM_MakeCof , 0x4002f84c ) +PROVIDE ( r_LM_GetAFHSwitchInstant , 0x4002f86c ) +PROVIDE ( r_LM_CheckSwitchInstant , 0x4002f8c0 ) +PROVIDE ( r_LM_CheckEdrFeatureRequest , 0x4002f90c ) +PROVIDE ( r_LM_GetFeature , 0x4002f924 ) +PROVIDE ( r_lm_look_for_stored_link_key , 0x4002f948 ) +PROVIDE ( r_lc_util_convert_pref_rate_to_packet_type , 0x4002f9b0 ) +PROVIDE ( r_lm_get_common_pkt_types , 0x4002fa1c ) +PROVIDE ( r_ld_acl_init , 0x40034d08 ) +PROVIDE ( r_ld_acl_reset , 0x40034d24 ) +PROVIDE ( r_ld_acl_start , 0x40034ddc ) +PROVIDE ( r_ld_acl_stop , 0x4003532c ) +PROVIDE ( r_ld_acl_flow_off , 0x40035400 ) +PROVIDE ( r_ld_acl_flow_on , 0x4003541c ) +PROVIDE ( r_ld_acl_data_tx , 0x4003544c ) +PROVIDE ( r_ld_acl_data_flush , 0x400357bc ) +PROVIDE ( r_ld_acl_lmp_tx , 0x40035b34 ) +PROVIDE ( r_ld_acl_lmp_flush , 0x40035d80 ) +PROVIDE ( r_ld_acl_rsw_req , 0x40035e74 ) +PROVIDE ( r_ld_acl_flush_timeout_get , 0x40035f9c ) +PROVIDE ( r_ld_acl_flush_timeout_set , 0x40035fe0 ) +PROVIDE ( r_ld_acl_t_poll_get , 0x40036024 ) +PROVIDE ( r_ld_acl_t_poll_set , 0x40036068 ) +PROVIDE ( r_ld_acl_sniff_trans , 0x400360a8 ) +PROVIDE ( r_ld_acl_sniff , 0x4003617c ) +PROVIDE ( r_ld_acl_unsniff , 0x400361e0 ) +PROVIDE ( r_ld_acl_ssr_set , 0x40036274 ) +PROVIDE ( r_ld_acl_tx_enc , 0x400362f8 ) +PROVIDE ( r_ld_acl_rx_enc , 0x40036344 ) +PROVIDE ( r_ld_acl_bcst_rx_dec , 0x40036394 ) +PROVIDE ( r_ld_acl_enc_key_load , 0x40036404 ) +PROVIDE ( r_ld_acl_clock_offset_get , 0x400364c0 ) +PROVIDE ( r_ld_acl_slot_offset_get , 0x4003653c ) +PROVIDE ( r_ld_acl_slot_offset_set , 0x40036658 ) +PROVIDE ( r_ld_acl_lsto_get , 0x400366b4 ) +PROVIDE ( r_ld_acl_lsto_set , 0x400366f8 ) +PROVIDE ( r_ld_acl_timing_accuracy_set , 0x4003673c ) +PROVIDE ( r_ld_acl_edr_set , 0x4003678c ) +PROVIDE ( r_ld_acl_allowed_tx_packet_types_set , 0x40036810 ) +PROVIDE ( r_ld_acl_current_tx_power_get , 0x400368f0 ) +PROVIDE ( r_ld_acl_clk_set , 0x40036950 ) +PROVIDE ( r_ld_acl_clk_adj_set , 0x40036a00 ) +PROVIDE ( r_ld_acl_clk_off_get , 0x40036b00 ) +PROVIDE ( r_ld_acl_bit_off_get , 0x40036b18 ) +PROVIDE ( r_ld_acl_role_get , 0x40036b30 ) +PROVIDE ( r_ld_acl_afh_set , 0x40036b60 ) +PROVIDE ( r_ld_acl_afh_prepare , 0x40036c84 ) +PROVIDE ( r_ld_acl_afh_confirm , 0x40036d40 ) +PROVIDE ( r_ld_acl_active_hop_types_get , 0x40036e10 ) +PROVIDE ( r_ld_acl_rx_max_slot_get , 0x40036e58 ) +PROVIDE ( r_ld_acl_rx_max_slot_set , 0x40036ea0 ) +PROVIDE ( r_ld_acl_test_mode_set , 0x40036f24 ) +PROVIDE ( r_ld_acl_rssi_delta_get , 0x40037028 ) +PROVIDE ( r_ld_sco_start , 0x40037110 ) +PROVIDE ( r_ld_sco_update , 0x40037a74 ) +PROVIDE ( r_ld_sco_stop , 0x40037c40 ) +PROVIDE ( r_ld_sco_audio_isr , 0x40037cc8 ) +PROVIDE ( r_ld_sco_data_tx , 0x40037ee8 ) +PROVIDE ( r_ld_bcst_acl_start , 0x4003882c ) +PROVIDE ( r_ld_bcst_acl_init , 0x40038bd0 ) +PROVIDE ( r_ld_bcst_acl_reset , 0x40038bdc ) +PROVIDE ( r_ld_bcst_lmp_tx , 0x40038bf8 ) +PROVIDE ( r_ld_bcst_acl_data_tx , 0x40038d3c ) +PROVIDE ( r_ld_bcst_afh_update , 0x40038f3c ) +PROVIDE ( r_ld_bcst_tx_enc , 0x40038ff8 ) +PROVIDE ( r_ld_bcst_enc_key_load , 0x4003906c ) +PROVIDE ( r_ld_csb_rx_init , 0x40039690 ) +PROVIDE ( r_ld_csb_rx_reset , 0x4003969c ) +PROVIDE ( r_ld_csb_rx_start , 0x4003972c ) +PROVIDE ( r_ld_csb_rx_afh_update , 0x40039af4 ) +PROVIDE ( r_ld_csb_rx_stop , 0x40039bb8 ) +PROVIDE ( r_ld_csb_tx_init , 0x4003a0e8 ) +PROVIDE ( r_ld_csb_tx_reset , 0x4003a0f8 ) +PROVIDE ( r_ld_csb_tx_en , 0x4003a1c0 ) +PROVIDE ( r_ld_csb_tx_dis , 0x4003a5e8 ) +PROVIDE ( r_ld_csb_tx_afh_update , 0x4003a5fc ) +PROVIDE ( r_ld_csb_tx_set_data , 0x4003a6c0 ) +PROVIDE ( r_ld_csb_tx_clr_data , 0x4003a71c ) +PROVIDE ( r_ld_fm_init , 0x4003a760 ) +PROVIDE ( r_ld_fm_reset , 0x4003a794 ) +PROVIDE ( r_ld_fm_clk_isr , 0x4003a7a8 ) +PROVIDE ( r_ld_fm_rx_isr , 0x4003a7f4 ) +PROVIDE ( r_ld_fm_frame_isr , 0x4003a82c ) +PROVIDE ( r_ld_fm_sket_isr , 0x4003a8a4 ) +PROVIDE ( r_ld_fm_prog_enable , 0x4003a944 ) +PROVIDE ( r_ld_fm_prog_disable , 0x4003a984 ) +PROVIDE ( r_ld_fm_prog_push , 0x4003a9d4 ) +PROVIDE ( r_ld_fm_prog_check , 0x4003ab28 ) +PROVIDE ( r_ld_inq_init , 0x4003b15c ) +PROVIDE ( r_ld_inq_reset , 0x4003b168 ) +PROVIDE ( r_ld_inq_start , 0x4003b1f0 ) +PROVIDE ( r_ld_inq_stop , 0x4003b4f0 ) +PROVIDE ( r_ld_iscan_init , 0x4003b9f0 ) +PROVIDE ( r_ld_iscan_reset , 0x4003ba14 ) +PROVIDE ( r_ld_iscan_restart , 0x4003ba44 ) +PROVIDE ( r_ld_iscan_start , 0x4003bb28 ) +PROVIDE ( r_ld_iscan_stop , 0x4003bf1c ) +PROVIDE ( r_ld_iscan_eir_set , 0x4003bfa0 ) +PROVIDE ( r_ld_iscan_eir_get , 0x4003c118 ) +PROVIDE ( r_ld_iscan_tx_pwr_get , 0x4003c138 ) +PROVIDE ( r_ld_channel_assess , 0x4003c184 ) +PROVIDE ( r_ld_init , 0x4003c294 ) +PROVIDE ( r_ld_reset , 0x4003c714 ) +PROVIDE ( r_ld_read_clock , 0x4003c9e4 ) +PROVIDE ( r_ld_bd_addr_get , 0x4003ca20 ) +PROVIDE ( r_ld_class_of_dev_get , 0x4003ca34 ) +PROVIDE ( r_ld_class_of_dev_set , 0x4003ca50 ) +PROVIDE ( r_ld_version_get , 0x4003ca6c ) +PROVIDE ( r_ld_timing_accuracy_get , 0x4003caac ) +PROVIDE ( r_ld_active_check , 0x4003cac4 ) +PROVIDE ( r_ld_afh_ch_assess_data_get , 0x4003caec ) +PROVIDE ( r_ld_wlcoex_set , 0x4003caf8 ) +PROVIDE ( r_ld_page_init , 0x4003d808 ) +PROVIDE ( r_ld_page_reset , 0x4003d814 ) +PROVIDE ( r_ld_page_start , 0x4003d848 ) +PROVIDE ( r_ld_page_stop , 0x4003da54 ) +PROVIDE ( r_ld_pca_init , 0x4003deb4 ) +PROVIDE ( r_ld_pca_reset , 0x4003df0c ) +PROVIDE ( r_ld_pca_local_config , 0x4003df6c ) +PROVIDE ( r_ld_pca_reporting_enable , 0x4003e018 ) +PROVIDE ( r_ld_pca_update_target_offset , 0x4003e050 ) +PROVIDE ( r_ld_pca_mws_frame_sync , 0x4003e104 ) +PROVIDE ( r_ld_pca_mws_moment_offset_gt , 0x4003e278 ) +PROVIDE ( r_ld_pca_mws_moment_offset_lt , 0x4003e280 ) +PROVIDE ( r_ld_pca_coarse_clock_adjust , 0x4003e324 ) +PROVIDE ( r_ld_pca_initiate_clock_dragging , 0x4003e4ac ) +PROVIDE ( r_ld_pscan_evt_handler , 0x4003f238 ) +PROVIDE ( r_ld_pscan_init , 0x4003f474 ) +PROVIDE ( r_ld_pscan_reset , 0x4003f498 ) +PROVIDE ( r_ld_pscan_restart , 0x4003f4b8 ) +PROVIDE ( r_ld_pscan_start , 0x4003f514 ) +PROVIDE ( r_ld_pscan_stop , 0x4003f618 ) +PROVIDE ( r_ld_sched_compute , 0x4003f6f8 ) +PROVIDE ( r_ld_sched_init , 0x4003f7ac ) +PROVIDE ( r_ld_sched_reset , 0x4003f7d4 ) +PROVIDE ( r_ld_sched_iscan_add , 0x4003f7e8 ) +PROVIDE ( r_ld_sched_iscan_remove , 0x4003f808 ) +PROVIDE ( r_ld_sched_pscan_add , 0x4003f828 ) +PROVIDE ( r_ld_sched_pscan_remove , 0x4003f848 ) +PROVIDE ( r_ld_sched_sscan_add , 0x4003f868 ) +PROVIDE ( r_ld_sched_sscan_remove , 0x4003f888 ) +PROVIDE ( r_ld_sched_inq_add , 0x4003f8a8 ) +PROVIDE ( r_ld_sched_inq_remove , 0x4003f8d0 ) +PROVIDE ( r_ld_sched_page_add , 0x4003f910 ) +PROVIDE ( r_ld_sched_page_remove , 0x4003f938 ) +PROVIDE ( r_ld_sched_acl_add , 0x4003f978 ) +PROVIDE ( r_ld_sched_acl_remove , 0x4003f99c ) +PROVIDE ( r_ld_sched_sniff_add , 0x4003f9c4 ) +PROVIDE ( r_ld_sched_sniff_remove , 0x4003fa0c ) +PROVIDE ( r_ld_sched_sco_add , 0x4003fa4c ) +PROVIDE ( r_ld_sched_sco_remove , 0x4003fa9c ) +PROVIDE ( r_ld_sscan_init , 0x400402f0 ) +PROVIDE ( r_ld_sscan_reset , 0x400402fc ) +PROVIDE ( r_ld_sscan_activated , 0x4004031c ) +PROVIDE ( r_ld_sscan_start , 0x40040384 ) +PROVIDE ( r_ld_strain_init , 0x400409f4 ) +PROVIDE ( r_ld_strain_reset , 0x40040a00 ) +PROVIDE ( r_ld_strain_start , 0x40040a8c ) +PROVIDE ( r_ld_strain_stop , 0x40040df0 ) +PROVIDE ( r_ld_util_fhs_unpk , 0x40040e54 ) +PROVIDE ( r_ld_util_bch_create , 0x40040fcc ) +PROVIDE ( r_ld_util_fhs_pk , 0x400411c8 ) +PROVIDE ( r_ld_util_active_master_afh_map_set , 0x40041308 ) +PROVIDE ( r_ld_util_active_master_afh_map_get , 0x4004131c ) +PROVIDE ( r_ld_util_stp_unpk , 0x40041324 ) +PROVIDE ( r_ld_util_stp_pk , 0x400413f4 ) +PROVIDE ( r_llc_ch_assess_local , 0x40041494 ) +PROVIDE ( r_llc_ch_assess_get_local_ch_map , 0x4004150c ) +PROVIDE ( r_llc_ch_assess_get_current_ch_map , 0x40041574 ) +PROVIDE ( r_llc_ch_assess_merge_ch , 0x40041588 ) +PROVIDE ( r_llc_ch_assess_reass_ch , 0x400415c0 ) +PROVIDE ( llc_hci_cmd_handler_tab_p_get , 0x40042358 ) +PROVIDE ( llc_hci_command_handler , 0x40042360 ) +PROVIDE ( llc_hci_acl_data_tx_handler , 0x40042398 ) +PROVIDE ( llcp_pdu_handler_tab_p_get , 0x40043f64 ) +PROVIDE ( r_llc_llcp_version_ind_pdu_send , 0x40043f6c ) +PROVIDE ( r_llc_llcp_ch_map_update_pdu_send , 0x40043f94 ) +PROVIDE ( r_llc_llcp_pause_enc_req_pdu_send , 0x40043fd8 ) +PROVIDE ( r_llc_llcp_pause_enc_rsp_pdu_send , 0x40044010 ) +PROVIDE ( r_llc_llcp_enc_req_pdu_send , 0x40044064 ) +PROVIDE ( r_llc_llcp_enc_rsp_pdu_send , 0x40044160 ) +PROVIDE ( r_llc_llcp_start_enc_rsp_pdu_send , 0x400441f8 ) +PROVIDE ( r_llc_llcp_reject_ind_pdu_send , 0x4004425c ) +PROVIDE ( r_llc_llcp_con_update_pdu_send , 0x400442c4 ) +PROVIDE ( r_llc_llcp_con_param_req_pdu_send , 0x400442fc ) +PROVIDE ( r_llc_llcp_con_param_rsp_pdu_send , 0x40044358 ) +PROVIDE ( r_llc_llcp_feats_req_pdu_send , 0x400443b4 ) +PROVIDE ( r_llc_llcp_feats_rsp_pdu_send , 0x400443f0 ) +PROVIDE ( r_llc_llcp_start_enc_req_pdu_send , 0x4004441c ) +PROVIDE ( r_llc_llcp_terminate_ind_pdu_send , 0x400444b0 ) +PROVIDE ( r_llc_llcp_unknown_rsp_send_pdu , 0x40044534 ) +PROVIDE ( r_llc_llcp_ping_req_pdu_send , 0x4004454c ) +PROVIDE ( r_llc_llcp_ping_rsp_pdu_send , 0x40044560 ) +PROVIDE ( r_llc_llcp_length_req_pdu_send , 0x40044574 ) +PROVIDE ( r_llc_llcp_length_rsp_pdu_send , 0x400445ac ) +PROVIDE ( r_llc_llcp_tester_send , 0x400445e4 ) +PROVIDE ( r_llc_llcp_recv_handler , 0x40044678 ) +PROVIDE ( r_llc_llcp_get_autorize , 0x4004475c ) +PROVIDE ( r_llc_init , 0x40044778 ) +PROVIDE ( r_llc_reset , 0x400447b8 ) +PROVIDE ( r_llc_start , 0x400447f4 ) +PROVIDE ( r_llc_stop , 0x400449ac ) +PROVIDE ( r_llc_discon_event_complete_send , 0x40044a30 ) +PROVIDE ( r_llc_le_con_cmp_evt_send , 0x40044a78 ) +PROVIDE ( r_llc_con_update_complete_send , 0x40044d68 ) +PROVIDE ( r_llc_ltk_req_send , 0x40044dc0 ) +PROVIDE ( r_llc_feats_rd_event_send , 0x40044e0c ) +PROVIDE ( r_llc_version_rd_event_send , 0x40044e60 ) +PROVIDE ( r_llc_common_cmd_complete_send , 0x40044eac ) +PROVIDE ( r_llc_common_cmd_status_send , 0x40044ee0 ) +PROVIDE ( r_llc_common_flush_occurred_send , 0x40044f0c ) +PROVIDE ( r_llc_common_enc_key_ref_comp_evt_send , 0x40044f38 ) +PROVIDE ( r_llc_common_enc_change_evt_send , 0x40044f6c ) +PROVIDE ( r_llc_common_nb_of_pkt_comp_evt_send , 0x40045000 ) +PROVIDE ( r_llc_con_update_ind , 0x40045038 ) +PROVIDE ( r_llc_lsto_con_update , 0x40045098 ) +PROVIDE ( r_llc_map_update_ind , 0x400450f0 ) +PROVIDE ( r_llc_con_update_finished , 0x4004518c ) +PROVIDE ( r_llc_map_update_finished , 0x40045260 ) +PROVIDE ( llc_default_state_tab_p_get , 0x40046058 ) +PROVIDE ( r_llc_util_get_free_conhdl , 0x400460c8 ) +PROVIDE ( r_llc_util_get_nb_active_link , 0x40046100 ) +PROVIDE ( r_llc_util_dicon_procedure , 0x40046130 ) +PROVIDE ( r_llc_util_update_channel_map , 0x400461ac ) +PROVIDE ( r_llc_util_set_llcp_discard_enable , 0x400461c8 ) +PROVIDE ( r_llc_util_set_auth_payl_to_margin , 0x400461f4 ) +PROVIDE ( r_llc_util_clear_operation_ptr , 0x40046234 ) +PROVIDE ( r_llc_util_bw_mgt , 0x4004629c ) +PROVIDE ( r_llc_end_evt_defer , 0x40046330 ) +PROVIDE ( r_llc_pdu_llcp_tx_ack_defer , 0x400463ac ) +PROVIDE ( r_llc_pdu_acl_tx_ack_defer , 0x400464dc ) +PROVIDE ( r_llc_pdu_defer , 0x40046528 ) +PROVIDE ( r_lld_evt_delete_elt_handler , 0x40046974 ) +PROVIDE ( r_lld_evt_delete_elt_push , 0x40046a3c ) +PROVIDE ( r_lld_evt_channel_next , 0x40046aac ) +PROVIDE ( r_lld_evt_init , 0x40046b3c ) +PROVIDE ( r_lld_evt_init_evt , 0x40046cd0 ) +PROVIDE ( r_lld_evt_restart , 0x40046d50 ) +PROVIDE ( r_lld_evt_elt_insert , 0x400474c8 ) +PROVIDE ( r_lld_evt_elt_delete , 0x40047538 ) +PROVIDE ( r_lld_evt_drift_compute , 0x40047670 ) +PROVIDE ( r_lld_evt_schedule_next_instant , 0x400476a8 ) +PROVIDE ( r_lld_evt_schedule_next , 0x400477dc ) +PROVIDE ( r_lld_evt_schedule , 0x40047908 ) +PROVIDE ( r_lld_evt_prevent_stop , 0x40047adc ) +PROVIDE ( r_lld_evt_scan_create , 0x40047ae8 ) +PROVIDE ( r_lld_evt_move_to_master , 0x40047ba0 ) +PROVIDE ( r_lld_evt_update_create , 0x40047cd8 ) +PROVIDE ( r_lld_evt_move_to_slave , 0x40047e18 ) +PROVIDE ( r_lld_evt_slave_update , 0x40048138 ) +PROVIDE ( r_lld_evt_adv_create , 0x400481f4 ) +PROVIDE ( r_lld_evt_deffered_elt_handler , 0x400482bc ) +PROVIDE ( r_lld_evt_end , 0x400483e8 ) +PROVIDE ( r_lld_evt_rx , 0x40048578 ) +PROVIDE ( r_lld_evt_canceled , 0x400485c8 ) +PROVIDE ( r_lld_evt_end_isr , 0x4004862c ) +PROVIDE ( r_lld_evt_rx_isr , 0x40048678 ) +PROVIDE ( r_lld_init , 0x4004873c ) +PROVIDE ( r_lld_core_reset , 0x40048a9c ) +PROVIDE ( r_lld_adv_start , 0x40048b38 ) +PROVIDE ( r_lld_adv_stop , 0x40048ea0 ) +PROVIDE ( r_lld_scan_start , 0x40048ee0 ) +PROVIDE ( r_lld_scan_stop , 0x40049190 ) +PROVIDE ( r_lld_con_start , 0x400491f8 ) +PROVIDE ( r_lld_move_to_master , 0x400499e0 ) +PROVIDE ( r_lld_con_update_req , 0x40049b60 ) +PROVIDE ( r_lld_con_update_after_param_req , 0x40049bcc ) +PROVIDE ( r_lld_con_param_rsp , 0x40049e00 ) +PROVIDE ( r_lld_con_param_req , 0x40049f0c ) +PROVIDE ( r_lld_con_stop , 0x40049fdc ) +PROVIDE ( r_lld_get_mode , 0x40049ff8 ) +PROVIDE ( r_lld_move_to_slave , 0x4004a024 ) +PROVIDE ( r_lld_ch_map_ind , 0x4004a2f4 ) +PROVIDE ( r_lld_con_update_ind , 0x4004a30c ) +PROVIDE ( r_lld_crypt_isr , 0x4004a324 ) +PROVIDE ( r_lld_test_mode_tx , 0x4004a350 ) +PROVIDE ( r_lld_test_mode_rx , 0x4004a540 ) +PROVIDE ( r_lld_test_stop , 0x4004a710 ) +PROVIDE ( r_lld_ral_renew_req , 0x4004a73c ) +PROVIDE ( r_lld_pdu_check , 0x4004ac34 ) +PROVIDE ( r_lld_pdu_tx_loop , 0x4004ae40 ) +PROVIDE ( r_lld_pdu_data_tx_push , 0x4004aecc ) +PROVIDE ( r_lld_pdu_data_send , 0x4004b018 ) +PROVIDE ( r_lld_pdu_tx_push , 0x4004b080 ) +PROVIDE ( r_lld_pdu_tx_prog , 0x4004b120 ) +PROVIDE ( r_lld_pdu_tx_flush , 0x4004b414 ) +PROVIDE ( r_lld_pdu_adv_pack , 0x4004b488 ) +PROVIDE ( r_lld_pdu_rx_handler , 0x4004b4d4 ) +PROVIDE ( r_lld_pdu_send_packet , 0x4004b774 ) +PROVIDE ( r_lld_util_instant_get , 0x4004b890 ) +PROVIDE ( r_lld_util_get_bd_address , 0x4004b8ac ) +PROVIDE ( r_lld_util_set_bd_address , 0x4004b8f8 ) +PROVIDE ( r_lld_util_ral_force_rpa_renew , 0x4004b980 ) +PROVIDE ( r_lld_util_freq2chnl , 0x4004b9e4 ) +PROVIDE ( r_lld_util_get_local_offset , 0x4004ba10 ) +PROVIDE ( r_lld_util_get_peer_offset , 0x4004ba24 ) +PROVIDE ( r_lld_util_connection_param_set , 0x4004ba40 ) +PROVIDE ( r_lld_util_dle_set_cs_fields , 0x4004ba90 ) +PROVIDE ( r_lld_util_anchor_point_move , 0x4004bacc ) +PROVIDE ( r_lld_util_flush_list , 0x4004bbd8 ) +PROVIDE ( r_lld_util_instant_ongoing , 0x4004bbfc ) +PROVIDE ( r_lld_util_compute_ce_max , 0x4004bc0c ) +PROVIDE ( r_lld_util_elt_programmed , 0x4004bce0 ) +PROVIDE ( r_lld_util_priority_set , 0x4004bd10 ) +PROVIDE ( r_lld_util_priority_update , 0x4004bd78 ) +PROVIDE ( r_lld_util_get_tx_pkt_cnt , 0x4004bd80 ) +PROVIDE ( r_lld_util_eff_tx_time_set , 0x4004bd88 ) +PROVIDE ( r_lld_wlcoex_set , 0x4004bd98 ) +PROVIDE ( llm_hci_cmd_handler_tab_p_get , 0x4004c920 ) +PROVIDE ( hci_command_handler , 0x4004c928 ) +PROVIDE ( r_llm_init , 0x4004c9f8 ) +PROVIDE ( r_llm_ble_ready , 0x4004cc34 ) +PROVIDE ( r_llm_con_req_ind , 0x4004cc54 ) +PROVIDE ( r_llm_le_adv_report_ind , 0x4004cdf4 ) +PROVIDE ( r_llm_con_req_tx_cfm , 0x4004d158 ) +PROVIDE ( r_llm_common_cmd_complete_send , 0x4004d288 ) +PROVIDE ( r_llm_common_cmd_status_send , 0x4004d2b4 ) +PROVIDE ( r_llm_test_mode_start_tx , 0x4004d2fc ) +PROVIDE ( r_llm_test_mode_start_rx , 0x4004d534 ) +PROVIDE ( r_llm_set_adv_param , 0x4004d5f4 ) +PROVIDE ( r_llm_set_adv_en , 0x4004d7ec ) +PROVIDE ( r_llm_set_adv_data , 0x4004d960 ) +PROVIDE ( r_llm_set_scan_rsp_data , 0x4004da14 ) +PROVIDE ( r_llm_set_scan_param , 0x4004dac8 ) +PROVIDE ( r_llm_set_scan_en , 0x4004db64 ) +PROVIDE ( r_llm_wl_clr , 0x4004dc54 ) +PROVIDE ( r_llm_wl_dev_add , 0x4004dcc0 ) +PROVIDE ( r_llm_wl_dev_rem , 0x4004dcfc ) +PROVIDE ( r_llm_wl_dev_add_hdl , 0x4004dd38 ) +PROVIDE ( r_llm_wl_dev_rem_hdl , 0x4004dde0 ) +PROVIDE ( r_llm_create_con , 0x4004de78 ) +PROVIDE ( r_llm_encryption_done , 0x4004dff8 ) +PROVIDE ( r_llm_encryption_start , 0x4004e128 ) +PROVIDE ( r_llm_ral_clear , 0x4004e1fc ) +PROVIDE ( r_llm_ral_dev_add , 0x4004e23c ) +PROVIDE ( r_llm_ral_dev_rm , 0x4004e3bc ) +PROVIDE ( r_llm_ral_get_rpa , 0x4004e400 ) +PROVIDE ( r_llm_ral_set_timeout , 0x4004e4a0 ) +PROVIDE ( r_llm_ral_update , 0x4004e4f8 ) +PROVIDE ( llm_default_state_tab_p_get , 0x4004e718 ) +PROVIDE ( r_llm_util_bd_addr_wl_position , 0x4004e720 ) +PROVIDE ( r_llm_util_bd_addr_in_wl , 0x4004e788 ) +PROVIDE ( r_llm_util_check_address_validity , 0x4004e7e4 ) +PROVIDE ( r_llm_util_check_map_validity , 0x4004e800 ) +PROVIDE ( r_llm_util_apply_bd_addr , 0x4004e868 ) +PROVIDE ( r_llm_util_set_public_addr , 0x4004e89c ) +PROVIDE ( r_llm_util_check_evt_mask , 0x4004e8b0 ) +PROVIDE ( r_llm_util_get_channel_map , 0x4004e8d4 ) +PROVIDE ( r_llm_util_get_supp_features , 0x4004e8e8 ) +PROVIDE ( r_llm_util_adv_data_update , 0x4004e8fc ) +PROVIDE ( r_llm_util_bl_check , 0x4004e930 ) +PROVIDE ( r_llm_util_bl_add , 0x4004e9ac ) +PROVIDE ( r_llm_util_bl_rem , 0x4004ea70 ) +PROVIDE ( r_llm_util_bd_addr_in_ral , 0x4004eb08 ) +PROVIDE ( r_llm_end_evt_defer , 0x4004eb6c ) +PROVIDE ( r_llm_pdu_defer , 0x4004ec48 ) +PROVIDE ( r_lm_init , 0x4004ed34 ) +PROVIDE ( r_lm_lt_addr_alloc , 0x4004ef1c ) +PROVIDE ( r_lm_lt_addr_reserve , 0x4004ef48 ) +PROVIDE ( r_lm_lt_addr_free , 0x4004ef74 ) +PROVIDE ( r_lm_get_nb_acl , 0x4004ef9c ) +PROVIDE ( r_lm_role_switch_start , 0x4004efe0 ) +PROVIDE ( r_lm_role_switch_finished , 0x4004f028 ) +PROVIDE ( r_lm_read_features , 0x4004f0d8 ) +PROVIDE ( r_lm_acl_disc , 0x4004f148 ) +PROVIDE ( r_lm_get_auth_en , 0x4004f1ac ) +PROVIDE ( r_lm_get_sp_en , 0x4004f1c0 ) +PROVIDE ( r_lm_get_sec_con_host_supp , 0x4004f1d4 ) +PROVIDE ( r_LM_GetPINType , 0x4004f1e8 ) +PROVIDE ( r_LM_GetConnectionAcceptTimeout , 0x4004f1f4 ) +PROVIDE ( r_LM_GetLocalNameSeg , 0x4004f200 ) +PROVIDE ( r_lm_get_loopback_mode , 0x4004f248 ) +PROVIDE ( r_lm_get_pub_key_192 , 0x4004f258 ) +PROVIDE ( r_lm_get_priv_key_192 , 0x4004f278 ) +PROVIDE ( r_lm_get_pub_key_256 , 0x4004f298 ) +PROVIDE ( r_lm_get_priv_key_256 , 0x4004f2b8 ) +PROVIDE ( r_lm_get_oob_local_data_192 , 0x4004f2d4 ) +PROVIDE ( r_lm_get_oob_local_data_256 , 0x4004f318 ) +PROVIDE ( r_lm_get_oob_local_commit , 0x4004f374 ) +PROVIDE ( r_lm_sp_debug_mode_get , 0x4004f398 ) +PROVIDE ( r_lm_debug_key_compare_192 , 0x4004f3a8 ) +PROVIDE ( r_lm_debug_key_compare_256 , 0x4004f3d0 ) +PROVIDE ( r_lm_dut_mode_en_get , 0x4004f3ec ) +PROVIDE ( r_lm_afh_ch_ass_en_get , 0x4004f3f8 ) +PROVIDE ( r_lm_sync_flow_ctrl_en_get , 0x4004f404 ) +PROVIDE ( r_lm_afh_host_ch_class_get , 0x4004f410 ) +PROVIDE ( r_lm_afh_peer_ch_class_set , 0x4004f418 ) +PROVIDE ( r_lm_afh_master_ch_map_get , 0x4004f43c ) +PROVIDE ( r_lm_afh_activate_timer , 0x4004f444 ) +PROVIDE ( r_lm_is_acl_con , 0x4004f47c ) +PROVIDE ( r_lm_is_acl_con_role , 0x4004f49c ) +PROVIDE ( r_lm_is_clk_adj_instant_pending , 0x4004f4c8 ) +PROVIDE ( r_lm_clk_adj_instant_pending_set , 0x4004f4d8 ) +PROVIDE ( r_lm_is_clk_adj_ack_pending , 0x4004f4e8 ) +PROVIDE ( r_lm_num_clk_adj_ack_pending_set , 0x4004f500 ) +PROVIDE ( r_lm_clk_adj_ack_pending_clear , 0x4004f514 ) +PROVIDE ( r_lm_local_ext_fr_configured , 0x4004f540 ) +PROVIDE ( r_lm_pca_sscan_link_set , 0x4004f550 ) +PROVIDE ( r_lm_pca_sscan_link_get , 0x4004f560 ) +PROVIDE ( r_lm_get_sync_param , 0x400503b4 ) +PROVIDE ( r_lm_init_sync , 0x400512d8 ) +PROVIDE ( r_lm_reset_sync , 0x40051304 ) +PROVIDE ( r_lm_check_active_sync , 0x40051334 ) +PROVIDE ( r_lm_add_sync , 0x40051358 ) +PROVIDE ( r_lm_modif_sync , 0x40051578 ) +PROVIDE ( r_lm_check_sync_hl_rsp , 0x4005169c ) +PROVIDE ( r_lm_get_synchdl , 0x4005175c ) +PROVIDE ( r_lm_look_for_sync , 0x40051774 ) +PROVIDE ( r_lm_get_nb_sync_link , 0x4005179c ) +PROVIDE ( r_lm_get_min_sync_intv , 0x400517a8 ) +PROVIDE ( r_lm_remove_sync , 0x400517c8 ) +PROVIDE ( r_lm_sco_nego_end , 0x40051828 ) +PROVIDE ( r_lm_master_clk_adj_req_handler , 0x40054180 ) +PROVIDE ( lm_hci_cmd_handler_tab_p_get , 0x4005425c ) +PROVIDE ( lm_default_state_tab_p_get , 0x40054268 ) +PROVIDE ( btdm_r_modules_func_p_set , 0x40054270 ) +PROVIDE ( btdm_r_modules_func_p_get , 0x4005427c ) +PROVIDE ( btdm_r_plf_func_p_set , 0x40054288 ) +PROVIDE ( btdm_r_import_rf_phy_func_p_get , 0x40054298 ) +PROVIDE ( r_nvds_init , 0x40054410 ) +PROVIDE ( r_nvds_get , 0x40054488 ) +PROVIDE ( r_nvds_del , 0x400544c4 ) +PROVIDE ( r_nvds_lock , 0x400544fc ) +PROVIDE ( r_nvds_put , 0x40054534 ) +PROVIDE ( r_rf_rw_bt_init , 0x40054868 ) +PROVIDE ( r_rf_rw_le_init , 0x400549d0 ) +PROVIDE ( r_rf_rw_init , 0x40054b0c ) +PROVIDE ( r_rwble_init , 0x40054bf4 ) +PROVIDE ( r_rwble_reset , 0x40054ce8 ) +PROVIDE ( r_rwble_sleep_check , 0x40054d78 ) +PROVIDE ( r_rwble_activity_ongoing_check , 0x40054d8c ) +PROVIDE ( r_rwble_version , 0x40054dac ) +PROVIDE ( r_rwble_isr , 0x40054e08 ) +PROVIDE ( r_rwbt_init , 0x40055160 ) +PROVIDE ( r_rwbt_reset , 0x400551bc ) +PROVIDE ( r_rwbt_version , 0x4005520c ) +PROVIDE ( r_rwbt_isr , 0x40055248 ) +PROVIDE ( r_rwbt_sleep_check , 0x4005577c ) +PROVIDE ( r_rwbt_sleep_enter , 0x400557a4 ) +PROVIDE ( r_rwbt_sleep_wakeup , 0x400557fc ) +PROVIDE ( r_rwbt_sleep_wakeup_end , 0x400558cc ) +PROVIDE ( rwip_rf_p_get , 0x400558f4 ) +PROVIDE ( r_rwip_check_wakeup_boundary , 0x400558fc ) +PROVIDE ( r_rwip_init , 0x4005595c ) +PROVIDE ( r_rwip_reset , 0x40055ab8 ) +PROVIDE ( r_rwip_version , 0x40055b20 ) +PROVIDE ( r_rwip_schedule , 0x40055b38 ) +PROVIDE ( r_rwip_sleep , 0x40055b5c ) +PROVIDE ( r_rwip_wakeup , 0x40055dc4 ) +PROVIDE ( r_rwip_wakeup_end , 0x40055e18 ) +PROVIDE ( r_rwip_wakeup_delay_set , 0x40055e4c ) +PROVIDE ( r_rwip_prevent_sleep_set , 0x40055e64 ) +PROVIDE ( r_rwip_prevent_sleep_clear , 0x40055ec8 ) +PROVIDE ( r_rwip_sleep_enable , 0x40055f30 ) +PROVIDE ( r_rwip_ext_wakeup_enable , 0x40055f3c ) +PROVIDE ( r_rwip_pca_clock_dragging_only , 0x40055f48 ) +PROVIDE ( r_rwip_wlcoex_set , 0x40055f60 ) +PROVIDE ( r_rwip_assert_err , 0x40055f88 ) +PROVIDE ( r_ke_queue_extract , 0x40055fd0 ) +PROVIDE ( r_ke_queue_insert , 0x40056020 ) +PROVIDE ( __utoa , 0x400561f0 ) +PROVIDE ( utoa , 0x40056258 ) +PROVIDE ( setjmp , 0x40056268 ) +PROVIDE ( longjmp , 0x400562cc ) +PROVIDE ( abs , 0x40056340 ) +PROVIDE ( div , 0x40056348 ) +PROVIDE ( labs , 0x40056370 ) +PROVIDE ( ldiv , 0x40056378 ) +PROVIDE ( qsort , 0x40056424 ) +PROVIDE ( __itoa , 0x40056678 ) +PROVIDE ( itoa , 0x400566b4 ) +PROVIDE ( atoi , 0x400566c4 ) +PROVIDE ( _atoi_r , 0x400566d4 ) +PROVIDE ( atol , 0x400566ec ) +PROVIDE ( _atol_r , 0x400566fc ) +PROVIDE ( _strtol_r , 0x40056714 ) +PROVIDE ( strtol , 0x4005681c ) +PROVIDE ( _strtoul_r , 0x40056834 ) +PROVIDE ( strtoul , 0x4005692c ) +PROVIDE ( _iprintf_r , 0x40056944 ) +PROVIDE ( _printf_r , 0x40056944 ) +PROVIDE ( iprintf , 0x40056978 ) +PROVIDE ( printf , 0x40056978 ) +PROVIDE ( viprintf , 0x400569b4 ) +PROVIDE ( vprintf , 0x400569b4 ) +PROVIDE ( _viprintf_r , 0x400569e4 ) +PROVIDE ( _vprintf_r , 0x400569e4 ) +PROVIDE ( _vsniprintf_r , 0x40056a14 ) +PROVIDE ( _vsnprintf_r , 0x40056a14 ) +PROVIDE ( vsniprintf , 0x40056a68 ) +PROVIDE ( vsnprintf , 0x40056a68 ) +PROVIDE ( _vsiprintf_r , 0x40056a90 ) +PROVIDE ( _vsprintf_r , 0x40056a90 ) +PROVIDE ( vsiprintf , 0x40056ac4 ) +PROVIDE ( vsprintf , 0x40056ac4 ) +PROVIDE ( _sniprintf_r , 0x40056ae4 ) +PROVIDE ( _snprintf_r , 0x40056ae4 ) +PROVIDE ( sniprintf , 0x40056b4c ) +PROVIDE ( snprintf , 0x40056b4c ) +PROVIDE ( _siprintf_r , 0x40056bbc ) +PROVIDE ( _sprintf_r , 0x40056bbc ) +PROVIDE ( siprintf , 0x40056c08 ) +PROVIDE ( sprintf , 0x40056c08 ) +PROVIDE ( _asniprintf_r , 0x40056c64 ) +PROVIDE ( _asnprintf_r , 0x40056c64 ) +PROVIDE ( asniprintf , 0x40056cd8 ) +PROVIDE ( asnprintf , 0x40056cd8 ) +PROVIDE ( _asiprintf_r , 0x40056d4c ) +PROVIDE ( _asprintf_r , 0x40056d4c ) +PROVIDE ( asiprintf , 0x40056d9c ) +PROVIDE ( asprintf , 0x40056d9c ) +PROVIDE ( _vasniprintf_r , 0x40056df8 ) +PROVIDE ( _vasnprintf_r , 0x40056df8 ) +PROVIDE ( vasniprintf , 0x40056e58 ) +PROVIDE ( vasnprintf , 0x40056e58 ) +PROVIDE ( _vasiprintf_r , 0x40056e80 ) +PROVIDE ( _vasprintf_r , 0x40056e80 ) +PROVIDE ( vasiprintf , 0x40056eb8 ) +PROVIDE ( vasprintf , 0x40056eb8 ) +PROVIDE ( _fiprintf_r , 0x40056ed8 ) +PROVIDE ( _fprintf_r , 0x40056ed8 ) +PROVIDE ( fiprintf , 0x40056efc ) +PROVIDE ( fprintf , 0x40056efc ) +PROVIDE ( __ssputs_r , 0x40056f2c ) +PROVIDE ( __ssprint_r , 0x40056ff8 ) +PROVIDE ( _svfiprintf_r , 0x40057100 ) +PROVIDE ( _svfprintf_r , 0x40057100 ) +PROVIDE ( _printf_common , 0x40057338 ) +PROVIDE ( _printf_i , 0x40057404 ) +PROVIDE ( __sfputs_r , 0x40057790 ) +PROVIDE ( __sprint_r , 0x400577e4 ) +PROVIDE ( _vfiprintf_r , 0x40057850 ) +PROVIDE ( _vfprintf_r , 0x40057850 ) +PROVIDE ( vfiprintf , 0x40057ae8 ) +PROVIDE ( vfprintf , 0x40057ae8 ) +PROVIDE ( __svfiscanf_r , 0x40057b08 ) +PROVIDE ( __svfscanf_r , 0x40057b08 ) +PROVIDE ( vfiscanf , 0x40057eb8 ) +PROVIDE ( vfscanf , 0x40057eb8 ) +PROVIDE ( __svfscanf , 0x40057f04 ) +PROVIDE ( _vfiscanf_r , 0x40057f24 ) +PROVIDE ( _vfscanf_r , 0x40057f24 ) +PROVIDE ( _sungetc_r , 0x40057f6c ) +PROVIDE ( __ssrefill_r , 0x40057fec ) +PROVIDE ( __ssvfiscanf_r , 0x4005802c ) +PROVIDE ( __ssvfscanf_r , 0x4005802c ) +PROVIDE ( _scanf_chars , 0x40058384 ) +PROVIDE ( _scanf_i , 0x4005845c ) +PROVIDE ( viscanf , 0x40058698 ) +PROVIDE ( vscanf , 0x40058698 ) +PROVIDE ( _viscanf_r , 0x400586c8 ) +PROVIDE ( _vscanf_r , 0x400586c8 ) +PROVIDE ( _vsiscanf_r , 0x400586f8 ) +PROVIDE ( _vsscanf_r , 0x400586f8 ) +PROVIDE ( vsiscanf , 0x40058740 ) +PROVIDE ( vsscanf , 0x40058740 ) +PROVIDE ( iscanf , 0x40058760 ) +PROVIDE ( scanf , 0x40058760 ) +PROVIDE ( _iscanf_r , 0x4005879c ) +PROVIDE ( _scanf_r , 0x4005879c ) +PROVIDE ( siscanf , 0x400587d0 ) +PROVIDE ( sscanf , 0x400587d0 ) +PROVIDE ( _siscanf_r , 0x40058830 ) +PROVIDE ( _sscanf_r , 0x40058830 ) +PROVIDE ( fiscanf , 0x40058884 ) +PROVIDE ( fscanf , 0x40058884 ) +PROVIDE ( _fiscanf_r , 0x400588b4 ) +PROVIDE ( _fscanf_r , 0x400588b4 ) +PROVIDE ( _wcrtomb_r , 0x400588d8 ) +PROVIDE ( wcrtomb , 0x40058920 ) +PROVIDE ( __sfvwrite_r , 0x4005893c ) +PROVIDE ( __swbuf_r , 0x40058bec ) +PROVIDE ( __swbuf , 0x40058cb4 ) +PROVIDE ( __swsetup_r , 0x40058cc8 ) +PROVIDE ( __fputwc , 0x40058da0 ) +PROVIDE ( _fputwc_r , 0x40058e4c ) +PROVIDE ( fputwc , 0x40058ea8 ) +PROVIDE ( __ascii_wctomb , 0x40058ef0 ) +PROVIDE ( _wctomb_r , 0x40058f14 ) +PROVIDE ( __submore , 0x40058f3c ) +PROVIDE ( _ungetc_r , 0x40058fa0 ) +PROVIDE ( ungetc , 0x400590f4 ) +PROVIDE ( __smakebuf_r , 0x40059108 ) +PROVIDE ( __sflush_r , 0x400591e0 ) +PROVIDE ( _fflush_r , 0x40059320 ) +PROVIDE ( fflush , 0x40059394 ) +PROVIDE ( __srefill_r , 0x400593d4 ) +PROVIDE ( _setlocale_r , 0x4005950c ) +PROVIDE ( __locale_charset , 0x40059540 ) +PROVIDE ( __locale_mb_cur_max , 0x40059548 ) +PROVIDE ( __locale_msgcharset , 0x40059550 ) +PROVIDE ( __locale_cjk_lang , 0x40059558 ) +PROVIDE ( _localeconv_r , 0x40059560 ) +PROVIDE ( setlocale , 0x40059568 ) +PROVIDE ( localeconv , 0x4005957c ) +PROVIDE ( asctime , 0x40059588 ) +PROVIDE ( ctime , 0x400595b0 ) +PROVIDE ( ctime_r , 0x400595c4 ) +PROVIDE ( localtime , 0x400595dc ) +PROVIDE ( localtime_r , 0x400595fc ) +PROVIDE ( gmtime , 0x40059848 ) +PROVIDE ( gmtime_r , 0x40059868 ) +PROVIDE ( strftime , 0x40059ab4 ) +PROVIDE ( mktime , 0x4005a5e8 ) +PROVIDE ( start_tb_console , 0x4005a980 ) +PROVIDE ( ets_sha_enable , 0x4005c07c ) +PROVIDE ( ets_sha_disable , 0x4005c0a8 ) +PROVIDE ( ets_sha_init , 0x4005c0d4 ) +PROVIDE ( ets_sha_finish , 0x4005c104 ) +PROVIDE ( ets_sha_update , 0x4005c2a0 ) +PROVIDE ( ets_bigint_enable , 0x4005c498 ) +PROVIDE ( ets_bigint_disable , 0x4005c4e0 ) +PROVIDE ( ets_bigint_wait_finish , 0x4005c520 ) +PROVIDE ( ets_bigint_mod_power_prepare , 0x4005c54c ) +PROVIDE ( ets_bigint_mod_power_getz , 0x4005c614 ) +PROVIDE ( ets_bigint_mult_prepare , 0x4005c630 ) +PROVIDE ( ets_bigint_mult_getz , 0x4005c6e8 ) +PROVIDE ( ets_bigint_montgomery_mult_prepare , 0x4005c6fc ) +PROVIDE ( ets_bigint_montgomery_mult_getz , 0x4005c7a4 ) +PROVIDE ( ets_bigint_mod_mult_prepare , 0x4005c7b4 ) +PROVIDE ( ets_bigint_mod_mult_getz , 0x4005c818 ) +PROVIDE ( ets_aes_enable , 0x4005c8cc ) +PROVIDE ( ets_aes_disable , 0x4005c8f8 ) +PROVIDE ( ets_aes_set_endian , 0x4005c928 ) +PROVIDE ( ets_aes_setkey_enc , 0x4005c97c ) +PROVIDE ( ets_aes_setkey_dec , 0x4005c994 ) +PROVIDE ( ets_aes_crypt , 0x4005c9b8 ) +PROVIDE ( ets_secure_boot_start , 0x4005ca34 ) +PROVIDE ( ets_secure_boot_finish , 0x4005ca84 ) +PROVIDE ( ets_secure_boot_hash , 0x4005cad4 ) +PROVIDE ( ets_secure_boot_obtain , 0x4005cb14 ) +PROVIDE ( ets_secure_boot_check , 0x4005cb40 ) +PROVIDE ( ets_secure_boot_rd_iv , 0x4005cb84 ) +PROVIDE ( ets_secure_boot_rd_abstract , 0x4005cba8 ) +PROVIDE ( ets_secure_boot_check_start , 0x4005cbcc ) +PROVIDE ( ets_secure_boot_check_finish , 0x4005cc04 ) +PROVIDE ( aes_128_cbc_encrypt , 0x4005cc18 ) +PROVIDE ( aes_128_cbc_decrypt , 0x4005cc7c ) +PROVIDE ( aes_unwrap , 0x4005ccf0 ) +PROVIDE ( base64_encode , 0x4005cdbc ) +PROVIDE ( base64_decode , 0x4005ced8 ) +PROVIDE ( crc32_le , 0x4005cfec ) +PROVIDE ( crc32_be , 0x4005d024 ) +PROVIDE ( crc16_le , 0x4005d05c ) +PROVIDE ( crc16_be , 0x4005d09c ) +PROVIDE ( crc8_le , 0x4005d0e0 ) +PROVIDE ( crc8_be , 0x4005d114 ) +PROVIDE ( esp_crc8 , 0x4005d144 ) +PROVIDE ( hmac_md5_vector , 0x4005d17c ) +PROVIDE ( hmac_md5 , 0x4005d264 ) +PROVIDE ( MD5Init , 0x4005da7c ) +PROVIDE ( MD5Update , 0x4005da9c ) +PROVIDE ( MD5Final , 0x4005db1c ) +PROVIDE ( md5_vector , 0x4005db80 ) +PROVIDE ( mz_adler32 , 0x4005edbc ) +PROVIDE ( mz_crc32 , 0x4005ee88 ) +PROVIDE ( mz_free , 0x4005eed4 ) +PROVIDE ( tinfl_decompress , 0x4005ef30 ) +PROVIDE ( tinfl_decompress_mem_to_mem , 0x40060050 ) +PROVIDE ( tinfl_decompress_mem_to_callback , 0x40060090 ) +PROVIDE ( tdefl_compress , 0x400600bc ) +PROVIDE ( tdefl_compress_buffer , 0x400607f4 ) +PROVIDE ( tdefl_init , 0x40060810 ) +PROVIDE ( tdefl_get_prev_return_status , 0x400608d0 ) +PROVIDE ( tdefl_get_adler32 , 0x400608d8 ) +PROVIDE ( tdefl_compress_mem_to_output , 0x400608e0 ) +PROVIDE ( tdefl_compress_mem_to_mem , 0x40060900 ) +PROVIDE ( tdefl_write_image_to_png_file_in_memory_ex , 0x40060910 ) +PROVIDE ( tdefl_write_image_to_png_file_in_memory , 0x4006091c ) +PROVIDE ( rc4_skip , 0x40060928 ) +PROVIDE ( hmac_sha1_vector , 0x400609e4 ) +PROVIDE ( hmac_sha1 , 0x40060acc ) +PROVIDE ( sha1_prf , 0x40060ae8 ) +PROVIDE ( sha1_vector , 0x40060b64 ) +PROVIDE ( pbkdf2_sha1 , 0x40060ba4 ) +PROVIDE ( hmac_sha256_vector , 0x40060c84 ) +PROVIDE ( hmac_sha256 , 0x40060d58 ) +PROVIDE ( sha256_prf , 0x40060d70 ) +PROVIDE ( sha256_vector , 0x40060e08 ) +PROVIDE ( jd_prepare , 0x40060fa8 ) +PROVIDE ( jd_decomp , 0x400613e8 ) +PROVIDE ( spi_dummy_len_fix , 0x40061d90 ) +PROVIDE ( SelectSpiQIO , 0x40061ddc ) +PROVIDE ( SetSpiDrvs , 0x40061e78 ) +PROVIDE ( SelectSpiFunction , 0x40061f84 ) +PROVIDE ( SPI_user_command_read , 0x400621b0 ) +PROVIDE ( SPI_read_status , 0x4006226c ) +PROVIDE ( SPI_write_status , 0x400622f0 ) +PROVIDE ( SPI_write_enable , 0x40062320 ) +PROVIDE ( SPI_read_status_high , 0x40062448 ) +PROVIDE ( SPI_Common_Command , 0x4006246c ) +PROVIDE ( spi_cache_sram_init , 0x400626e4 ) +PROVIDE ( SPIUnlockXXX , 0x400628b0 ) +PROVIDE ( SPILock , 0x400628f0 ) +PROVIDE ( SPIReadModeCnfig , 0x40062944 ) +PROVIDE ( spi_flash_attach , 0x40062a6c ) +PROVIDE ( SPIMasterReadModeCnfig , 0x40062b64 ) +PROVIDE ( SPIClkConfig , 0x40062bc8 ) +PROVIDE ( SPIEraseChip , 0x40062c14 ) +PROVIDE ( SPIEraseBlock , 0x40062c4c ) +PROVIDE ( SPIEraseSector , 0x40062ccc ) +PROVIDE ( SPIWrite , 0x40062d50 ) +PROVIDE ( SPI_Write_Encrypt_Enable , 0x40062df4 ) +PROVIDE ( SPI_Prepare_Encrypt_Data , 0x40062e1c ) +PROVIDE ( SPI_Write_Encrypt_Disable , 0x40062e60 ) +PROVIDE ( SPI_Encrypt_Write , 0x40062e78 ) +PROVIDE ( SPIRead , 0x40062ed8 ) +PROVIDE ( SPIEraseArea , 0x400631ac ) +PROVIDE ( SPIParamCfg , 0x40063238 ) +PROVIDE ( __mulsf3 , 0x400632c8 ) +PROVIDE ( __eqsf2 , 0x40063374 ) +PROVIDE ( __nesf2 , 0x40063374 ) +PROVIDE ( __gtsf2 , 0x400633a0 ) +PROVIDE ( __lesf2 , 0x400633c0 ) +PROVIDE ( __gesf2 , 0x4006340c ) +PROVIDE ( __ltsf2 , 0x4006342c ) +PROVIDE ( __unordsf2 , 0x40063478 ) +PROVIDE ( __negdf2 , 0x400634a0 ) +PROVIDE ( __muldf3 , 0x4006358c ) +PROVIDE ( __eqdf2 , 0x400636a8 ) +PROVIDE ( __nedf2 , 0x400636a8 ) +PROVIDE ( __gtdf2 , 0x400636dc ) +PROVIDE ( __ledf2 , 0x40063704 ) +PROVIDE ( __gedf2 , 0x40063768 ) +PROVIDE ( __ltdf2 , 0x40063790 ) +PROVIDE ( __unorddf2 , 0x400637f4 ) +PROVIDE ( __cmpdi2 , 0x40063820 ) +PROVIDE ( __ucmpdi2 , 0x40063840 ) +PROVIDE ( __clear_cache , 0x40063860 ) +PROVIDE ( __absvsi2 , 0x40063868 ) +PROVIDE ( __absvdi2 , 0x4006387c ) +PROVIDE ( __powisf2 , 0x4006389c ) +PROVIDE ( __powidf2 , 0x400638d4 ) +PROVIDE ( __mulsc3 , 0x40063934 ) +PROVIDE ( __muldc3 , 0x40063bf4 ) +PROVIDE ( __divsc3 , 0x40064200 ) +PROVIDE ( __divdc3 , 0x40064460 ) +PROVIDE ( __bswapsi2 , 0x4006499c ) +PROVIDE ( __bswapdi2 , 0x400649c4 ) +PROVIDE ( __clrsbsi2 , 0x40064a20 ) +PROVIDE ( __clrsbdi2 , 0x40064a38 ) +PROVIDE ( __gcc_bcmp , 0x40064a70 ) +PROVIDE ( __udiv_w_sdiv , 0x40064aa8 ) +PROVIDE ( __udivmoddi4 , 0x40064ab0 ) diff --git a/src/mongoose-6.11/src/common/platforms/esp32/stubs/Makefile b/src/mongoose-6.11/src/common/platforms/esp32/stubs/Makefile new file mode 100644 index 0000000..857512a --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp32/stubs/Makefile @@ -0,0 +1,51 @@ +# +# Copyright (c) 2015 Cesanta Software Limited +# All rights reserved +# + +STUB = stub_hello.c +LIBS = +PARAMS = +PORT = /dev/ttyUSB0 + +BUILD_DIR = .build +COMMON_STUB_DIR = ../../esp +STUB_ELF = $(BUILD_DIR)/$(patsubst %.c,%.elf,$(notdir $(STUB))) +STUB_JSON ?= $(BUILD_DIR)/$(patsubst %.c,%.json,$(notdir $(STUB))) +SDK = $(shell cat ../../../../fw/platforms/esp32/sdk.version) +XT_CC = xtensa-esp32-elf-gcc + +.PHONY: all clean run wrap + +all: $(STUB_ELF) + +$(STUB_ELF): $(STUB) $(LIBS) + @echo " CC $^ -> $@" + @[ -d $(BUILD_DIR) ] || mkdir $(BUILD_DIR) + @docker run --rm -i -v $(CURDIR)/../../../..:/src $(SDK) //bin/bash -c \ + "cd /src/common/platforms/esp32/stubs && \ + $(XT_CC) -std=c99 -Wall -Werror -Os -DESP32 \ + -mtext-section-literals -mlongcalls -nostdlib -fno-builtin \ + -I. -I/src/common/platforms/esp \ + -I/opt/Espressif/esp-idf/components/esp32/include \ + -I/opt/Espressif/esp-idf/components/soc/esp32/include \ + -L/opt/Espressif/esp-idf -Wl,-static \ + -ffunction-sections -Wl,--gc-sections \ + -Tstub.ld -o $@ $^" + +wrap: $(STUB_JSON) + +$(STUB_JSON): $(STUB_ELF) $(COMMON_STUB_DIR)/esptool.py + @echo " WRAP $< -> $@" + @docker run --rm -i -v $(CURDIR)/../../../..:/src $(SDK) //bin/bash -c \ + "cd /src/common/platforms/esp32/stubs && \ + $(COMMON_STUB_DIR)/esptool.py wrap_stub $<" > $@ + +run: $(STUB_JSON) + @echo " RUN $< $(PARAMS) -> $(PORT)" + @docker run --rm -i --privileged -v $(CURDIR)/../../../..:/src $(SDK) //bin/bash -c \ + "cd /src/common/platforms/esp32/stubs && \ + $(COMMON_STUB_DIR)/esptool.py --port $(PORT) run_stub $< $(PARAMS)" + +clean: + @rm -rf $(BUILD_DIR) diff --git a/src/mongoose-6.11/src/common/platforms/esp32/stubs/README.md b/src/mongoose-6.11/src/common/platforms/esp32/stubs/README.md new file mode 100644 index 0000000..9265771 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp32/stubs/README.md @@ -0,0 +1,13 @@ +This is a ESP boot loader stub development environment. + +Code produced in this environment can be loaded and executed +in the bootloader environment. Usually it is used to implement +functionality not found in the bootloader. + +Stubs can be executed using the `run_stub` command of the modified esptool.py provided. +`wrap_stub` produces a JSON represenattion of the stub that can later be reused +or built into other tools. + +Example usage: + $ make run STUB=stub_flash_size.c PORT=/dev/ttyUSB0 + $ make run STUB=stub_md5.c PORT=/dev/ttyUSB0 PARAMS="0x11000 10000 1" diff --git a/src/mongoose-6.11/src/common/platforms/esp32/stubs/led.c b/src/mongoose-6.11/src/common/platforms/esp32/stubs/led.c new file mode 100644 index 0000000..6cfc766 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp32/stubs/led.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2014-2017 Cesanta Software Limited + * All rights reserved + */ + +#include "soc/gpio_reg.h" + +void led_setup(int io) { + if (io < 32) { + WRITE_PERI_REG(GPIO_ENABLE_W1TS_REG, 1 << io); + } else { + WRITE_PERI_REG(GPIO_ENABLE1_W1TS_REG, 1 << (io - 32)); + } +} + +void led_on(int io) { + if (io < 32) { + WRITE_PERI_REG(GPIO_OUT_W1TS_REG, 1 << io); + } else { + WRITE_PERI_REG(GPIO_OUT1_W1TS_REG, 1 << (io - 32)); + } +} + +void led_off(int io) { + if (io < 32) { + WRITE_PERI_REG(GPIO_OUT_W1TC_REG, 1 << io); + } else { + WRITE_PERI_REG(GPIO_OUT1_W1TC_REG, 1 << (io - 32)); + } +} + +void led_toggle(int io) { + if (READ_PERI_REG(GPIO_OUT_REG & (1 << io))) { + WRITE_PERI_REG(GPIO_OUT_W1TC_REG, 1 << io); + } else { + WRITE_PERI_REG(GPIO_OUT_W1TS_REG, 1 << io); + } +} diff --git a/src/mongoose-6.11/src/common/platforms/esp32/stubs/led.h b/src/mongoose-6.11/src/common/platforms/esp32/stubs/led.h new file mode 100644 index 0000000..505a3f6 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp32/stubs/led.h @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2014-2017 Cesanta Software Limited + * All rights reserved + */ + +#pragma once + +void led_setup(int io); +void led_on(int io); +void led_off(int io); +void led_toggle(int io); diff --git a/src/mongoose-6.11/src/common/platforms/esp32/stubs/rom_functions.h b/src/mongoose-6.11/src/common/platforms/esp32/stubs/rom_functions.h new file mode 100644 index 0000000..1b2eee4 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp32/stubs/rom_functions.h @@ -0,0 +1,10 @@ +#ifndef CS_COMMON_PLATFORMS_ESP32_STUBS_ROM_FUNCTIONS_H_ +#define CS_COMMON_PLATFORMS_ESP32_STUBS_ROM_FUNCTIONS_H_ + +#include "rom/ets_sys.h" +#include "rom/spi_flash.h" +#include "rom/md5_hash.h" +#include "rom/uart.h" +#include "rom/rtc.h" + +#endif /* CS_COMMON_PLATFORMS_ESP32_STUBS_ROM_FUNCTIONS_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp32/stubs/sdkconfig.h b/src/mongoose-6.11/src/common/platforms/esp32/stubs/sdkconfig.h new file mode 100644 index 0000000..0ecac13 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp32/stubs/sdkconfig.h @@ -0,0 +1 @@ +#define CONFIG_SPI_FLASH_ROM_DRIVER_PATCH 1 diff --git a/src/mongoose-6.11/src/common/platforms/esp32/stubs/stub.ld b/src/mongoose-6.11/src/common/platforms/esp32/stubs/stub.ld new file mode 100644 index 0000000..6c2af65 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp32/stubs/stub.ld @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016 Cesanta Software Limited + * All rights reserved + */ + +MEMORY { + iram : org = 0x40090000, len = 0x10000 + /* DRAM startin at 0x3FFC0000 gets stomped by something before mem_finish + * and is thus not suitable for initialized data, but works fine for BSS. */ + dram_bss : org = 0x3FFC0000, len = 0x10000 + dram : org = 0x3FFD0000, len = 0x10000 +} + +ENTRY(stub_main) + +SECTIONS { + .params 0x40090000 : { + _params_start = ABSOLUTE(.); + *(.params) + _params_end = ABSOLUTE(.); + } > iram + + .text : ALIGN(4) { + _code_start = ABSOLUTE(.); + *(.literal) + *(.text .text.*) + } > iram + + .bss : ALIGN(4) { + _bss_start = ABSOLUTE(.); + *(.bss) + _bss_end = ABSOLUTE(.); + } > dram + + .data : ALIGN(4) { + _data_start = ABSOLUTE(.); + *(.data) + *(.rodata .rodata.*) + } > dram +} + +INCLUDE "components/esp32/ld/esp32.rom.ld" +INCLUDE "components/esp32/ld/esp32.rom.spiram_incompatible_fns.ld" + +PROVIDE(esp_rom_spiflash_attach = 0x40062a6c); +PROVIDE(esp_rom_spiflash_config_clk = 0x40062bc8); diff --git a/src/mongoose-6.11/src/common/platforms/esp32/stubs/stub_hello.c b/src/mongoose-6.11/src/common/platforms/esp32/stubs/stub_hello.c new file mode 100644 index 0000000..a4c75eb --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp32/stubs/stub_hello.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2015 Cesanta Software Limited + * All rights reserved + * + * + * Stub template. + */ + +#include +#include +#include "slip.h" + +/* Define the args vector and put it into the ".params" section. */ +uint32_t params[3] __attribute__((section(".params"))); + +/* Define a function called stub_main. Do not return or reboot. + * Use send_packet to communicate to the host. */ + +const char *hello = "Hello"; + +static char buf[1024]; +extern uint32_t _bss_start, _bss_end; + +void stub_main(void) { + uint32_t greeting = 0x4941484f; + SLIP_send(&greeting, 4); + memset(&_bss_start, 0, (&_bss_end - &_bss_start)); + buf[1] = 123; + SLIP_send(hello, 5); + while (1) { + } +} diff --git a/src/mongoose-6.11/src/common/platforms/esp32/stubs/uart.c b/src/mongoose-6.11/src/common/platforms/esp32/stubs/uart.c new file mode 100644 index 0000000..c6eb25d --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp32/stubs/uart.c @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#include "uart.h" + +#include "rom_functions.h" + +void set_baud_rate(uint32_t uart_no, uint32_t baud_rate) { + uint32_t master_freq = ets_get_detected_xtal_freq() << 4; + master_freq += (baud_rate / 2); + uint32_t div = master_freq / baud_rate; + uart_div_modify(uart_no, div); +} diff --git a/src/mongoose-6.11/src/common/platforms/esp32/stubs/uart.h b/src/mongoose-6.11/src/common/platforms/esp32/stubs/uart.h new file mode 100644 index 0000000..aa3c4ed --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp32/stubs/uart.h @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_ESP32_STUBS_UART_H_ +#define CS_COMMON_PLATFORMS_ESP32_STUBS_UART_H_ + +#include + +void set_baud_rate(uint32_t uart_no, uint32_t baud_rate); + +#endif /* CS_COMMON_PLATFORMS_ESP32_STUBS_UART_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/common.mk b/src/mongoose-6.11/src/common/platforms/esp8266/common.mk new file mode 100644 index 0000000..5345221 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/common.mk @@ -0,0 +1,76 @@ +CFLAGS_EXTRA ?= + +XTENSA_TOOLS_ROOT ?= /opt/Espressif/crosstool-NG/builds/xtensa-lx106-elf/bin +SDK_PATH ?= /opt/Espressif/ESP8266_SDK +ESPTOOL ?= esptool.py +ESPPORT ?= /dev/ttyACM0 +ESPSPEED ?= 230400 +# For flash = > 16Mbit +ESPFLASHARGS = --flash_mode dio --flash_size 32m + +VERBOSE ?= 0 + +CC := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-gcc +CXX := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-g++ +AR := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-ar +LD := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-gcc +OBJCOPY := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-objcopy +NM := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-nm +CC_WRAPPER ?= + +define link +$(vecho) "LD $@" +$(Q) $(CC_WRAPPER) $(LD) $(LIBDIRS) -T$(LD_SCRIPT) $(LDFLAGS) -o $@ \ +-Wl,-Map=$@.map -Wl,--start-group $1 -Wl,--end-group +endef + +define compile_params +$(vecho) "$5 $1 -> $2" +$(Q) $(CC_WRAPPER) $3 -MD -MP $(INCDIRS) $4 -c $1 -o $2 +endef + +define compile +$(call compile_params,$<,$@, $(CC), $(CFLAGS),"CC") +endef + +define compile_cxx +$(call compile_params,$<,$@, $(CXX), $(CXXFLAGS),"CXX") +endef + +# some of these flags works around for gdb 7.5.x stacktrace issue +# while still allowing -Os to remove padding between data in .rodata +# section, allowing us to gain about 1k of ram. +# text section is 4k bigger, but we care more about ram at the moment. +# TODO(mkm): figure out which flag(s). +NO_Os_FLAGS= -fno-expensive-optimizations -fno-thread-jumps \ + -fno-align-functions -fno-align-jumps \ + -fno-align-loops -fno-align-labels -fno-caller-saves \ + -fno-crossjumping -fno-cse-follow-jumps -fno-cse-skip-blocks \ + -fno-delete-null-pointer-checks -fno-devirtualize \ + -fno-gcse -fno-gcse-lm -fno-hoist-adjacent-loads \ + -fno-inline-small-functions -fno-indirect-inlining -fno-partial-inlining \ + -fno-ipa-cp -fno-ipa-sra -fno-peephole2 -fno-optimize-sibling-calls -fno-optimize-strlen \ + -fno-reorder-blocks -fno-reorder-blocks-and-partition -fno-reorder-functions \ + -fno-sched-interblock -fno-sched-spec -fno-rerun-cse-after-loop \ + -fno-schedule-insns -fno-schedule-insns2 -fno-strict-aliasing -fno-strict-overflow \ + -fno-tree-builtin-call-dce -fno-tree-switch-conversion -fno-tree-tail-merge \ + -fno-tree-pre -fno-tree-vrp + +C_CXX_FLAGS = -W -Wall -Werror -Wundef -Wno-comment -Wno-variadic-macros -Wpointer-arith \ + -Os $(NO_Os_FLAGS) -g3 \ + -Wl,-EL -fno-inline-functions \ + -D_XOPEN_SOURCE=500 \ + -nostdlib -mlongcalls -mtext-section-literals -D__ets__ -DSTATIC=static \ + -Wno-parentheses \ + -DIRAM='__attribute__((section(".fast.text")))' \ + -DICACHE_RAM_ATTR=IRAM \ + -DNOINSTR='__attribute__((no_instrument_function))' \ + -DCS_PLATFORM=3 \ + -ffunction-sections -fdata-sections + +CFLAGS = -std=c99 $(C_CXX_FLAGS) +CXXFLAGS = -std=gnu++11 -fno-exceptions $(C_CXX_FLAGS) + +# linker flags used to generate the main object file +LDFLAGS = -nostdlib -Wl,--no-check-sections -u call_user_start \ + -Wl,-static -Wl,--gc-sections diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/esp_crypto.c b/src/mongoose-6.11/src/common/platforms/esp8266/esp_crypto.c new file mode 100644 index 0000000..822dab1 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/esp_crypto.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#include +#include +#include + +#include "mongoose/mongoose.h" + +#ifdef RTOS_SDK +#include "esp_libc.h" +#else +#include "osapi.h" +#endif + +extern int sha1_vector(size_t num_msgs, const uint8_t *msgs[], + const size_t *msg_lens, uint8_t *digest); + +extern int md5_vector(size_t num_msgs, const uint8_t *msgs[], + const size_t *msg_lens, uint8_t *digest); + +/* For digest auth. */ +void mg_hash_md5_v(size_t num_msgs, const uint8_t *msgs[], + const size_t *msg_lens, uint8_t *digest) { + (void) md5_vector(num_msgs, msgs, msg_lens, digest); +} + +/* For WebSocket handshake. */ +void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[], + const size_t *msg_lens, uint8_t *digest) { + (void) sha1_vector(num_msgs, msgs, msg_lens, digest); +} + +#if MG_ENABLE_SSL + +#include "mbedtls/aes.h" +#include "mbedtls/sha256.h" + +#define AES_PRIV_NR_POS (4 * 15) + +/* + * Crypto functions in ROM/SDK. + * They come from wpa_supplicant, you can find them here https://w1.fi/cgit/ + * + * Note that ROM version of the key setup function is older, does not take the + * number of bits argument and only supports AES-128. This prototype doesn't + * suit it, but since the difference is in the last aegument, it doesn't matter. + */ + +extern void rijndaelKeySetupDec(void *ctx, const uint8_t *key, int bits); +extern int rijndaelKeySetupEnc(void *ctx, const uint8_t *key, int bits); +void aes_encrypt(void *ctx, const uint8_t *plain, uint8_t *crypt); +void aes_decrypt(void *ctx, const uint8_t *crypt, uint8_t *plain); + +/* + * AES that comes with wpa_supplicant allocates its own AES context in + * aes_{encrypt,decrypt}_init. Ideally, we'd take that pointer and store it in + * our mbedtls_aes_context, but then a lot of space would be wasted. + * We do not call _init and go directly to key setup functions and poke number + * of rounds into the right place too. This is a bit hacky, but works fine. + * There is also a difference between older function in ROM and the one coming + * with SDK which is newer: the older one actually takes two arguments, not 3. + * But it doesn't matter, extra argument doesn't hurt and this works with both. + */ +int mbedtls_aes_setkey_enc(mbedtls_aes_context *ctx, const unsigned char *key, + unsigned int keybits) { + if (keybits != 128) return MBEDTLS_ERR_AES_INVALID_KEY_LENGTH; + ((uint32_t *) ctx)[AES_PRIV_NR_POS] = 10; + rijndaelKeySetupEnc(ctx, key, 128); + return 0; +} + +int mbedtls_aes_setkey_dec(mbedtls_aes_context *ctx, const unsigned char *key, + unsigned int keybits) { + if (keybits != 128) return MBEDTLS_ERR_AES_INVALID_KEY_LENGTH; + ((uint32_t *) ctx)[AES_PRIV_NR_POS] = 10; + rijndaelKeySetupDec(ctx, key, 128); + return 0; +} + +int mbedtls_internal_aes_encrypt(mbedtls_aes_context *ctx, + const unsigned char input[16], + unsigned char output[16]) { + aes_encrypt(ctx, input, output); + return 0; +} + +int mbedtls_internal_aes_decrypt(mbedtls_aes_context *ctx, + const unsigned char input[16], + unsigned char output[16]) { + aes_decrypt(ctx, input, output); + return 0; +} + +/* os_get_random uses hardware RNG, so it's cool. */ +int mg_ssl_if_mbed_random(void *ctx, unsigned char *buf, size_t len) { + os_get_random(buf, len); + (void) ctx; + return 0; +} + +/* For CryptoAuthLib host crypto. */ +int atcac_sw_sha2_256(const uint8_t *data, size_t data_size, + uint8_t digest[32]) { + mbedtls_sha256(data, data_size, digest, false /* is_224 */); + return 0; +} + +#endif /* MG_ENABLE_SSL */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/esp_hw_wdt_register.h b/src/mongoose-6.11/src/common/platforms/esp8266/esp_hw_wdt_register.h new file mode 100644 index 0000000..32c2f3c --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/esp_hw_wdt_register.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2014-2017 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_ESP8266_ESP_HW_WDT_REGISTER_H_ +#define CS_COMMON_PLATFORMS_ESP8266_ESP_HW_WDT_REGISTER_H_ + +#ifdef RTOS_SDK +#include +#else +#include +#endif + +#define REG_WDT_BASE 0x60000900 + +#define WDT_CTL (REG_WDT_BASE + 0x0) +#define WDT_CTL_ENABLE (BIT(0)) +#define WDT_CTL_STAGE1_NO_RESET (BIT(1)) +#define WDT_CTL_STAGE1_DISABLE (BIT(2)) +#define WDT_CTL_UNK3 (BIT(3)) +#define WDT_CTL_UNK4 (BIT(4)) +#define WDT_CTL_UNK5 (BIT(5)) + +/* Bits 3, 4, 5 - ???; set to 1 by ROM. */ + +#define WDT_RELOAD_STAGE0 (REG_WDT_BASE + 0x4) +#define WDT_RELOAD_STAGE0_V (0xf) +#define WDT_RELOAD_STAGE0_S (0) + +#define WDT_RELOAD_STAGE1 (REG_WDT_BASE + 0x8) +#define WDT_RELOAD_STAGE1_V (0xf) +#define WDT_RELOAD_STAGE1_S (0) + +#define WDT_COUNT (REG_WDT_BASE + 0xc) /* Counts at CPU_CLK (80 MHz) */ +#define WDT_COUNT_V (0xffffffff) +#define WDT_COUNT_S (0) + +#define WDT_STAGE (REG_WDT_BASE + 0x10) +#define WDT_STAGE_V (1) +#define WDT_STAGE_S (0) + +#define WDT_RESET (REG_WDT_BASE + 0x14) +#define WDT_RESET_V (0xff) +#define WDT_RESET_S (0) + +#define WDT_RESET_STAGE (REG_WDT_BASE + 0x18) +#define WDT_RESET_STAGE_V (0xff) +#define WDT_RESET_STAGE_S (0) + +#define WDT_RESET_VALUE 0x73 + +#endif /* CS_COMMON_PLATFORMS_ESP8266_ESP_HW_WDT_REGISTER_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/esp_missing_includes.h b/src/mongoose-6.11/src/common/platforms/esp8266/esp_missing_includes.h new file mode 100644 index 0000000..fc7d69f --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/esp_missing_includes.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_ESP8266_ESP_MISSING_INCLUDES_H_ +#define CS_COMMON_PLATFORMS_ESP8266_ESP_MISSING_INCLUDES_H_ + +#include +#include + +void pp_soft_wdt_init(void); +void pp_soft_wdt_stop(void); +void pp_soft_wdt_feed(void); +void pp_soft_wdt_restart(void); +void system_soft_wdt_stop(void); /* Alias for pp_soft_wdt_stop */ + +void Cache_Read_Disable(void); +void Cache_Read_Enable(uint32_t, uint32_t, uint32_t); +void Cache_Read_Disable_2(void); +void Cache_Read_Enable_2(void); +void Cache_Read_Enable_New(void); + +int SPIEraseBlock(uint32_t block); +uint32_t SPIRead(uint32_t addr, void *dst, uint32_t size); + +#ifndef RTOS_SDK + +#include + +/* There are no declarations for these anywhere in the SDK (as of 1.2.0). */ +void ets_isr_mask(unsigned intr); +void ets_isr_unmask(unsigned intr); +void system_restart_local(void); +int os_printf_plus(const char *format, ...); + +void ets_wdt_init(void); +void ets_wdt_enable(uint32_t mode, uint32_t arg1, uint32_t arg2); +void ets_wdt_disable(void); +void ets_wdt_restore(uint32_t mode); +uint32_t ets_wdt_get_mode(void); + +void _xtos_l1int_handler(void); +void _xtos_set_exception_handler(); +void xthal_set_intenable(unsigned); + +/* These are present in mem.h but are commented out. */ +void *pvPortMalloc(size_t xWantedSize, const char *file, int line); +void vPortFree(void *pv, const char *file, int line); +void *pvPortZalloc(size_t size, const char *file, int line); +void *pvPortRealloc(void *pv, size_t size, const char *file, int line); + +#else /* !RTOS_SDK */ + +#define BIT(nr) (1UL << (nr)) +void system_soft_wdt_feed(void); +void system_soft_wdt_restart(void); +void ets_putc(char c); + +#endif /* RTOS_SDK */ + +void _ResetVector(void); + +#endif /* CS_COMMON_PLATFORMS_ESP8266_ESP_MISSING_INCLUDES_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/esp_ssl_krypton.h b/src/mongoose-6.11/src/common/platforms/esp8266/esp_ssl_krypton.h new file mode 100644 index 0000000..086c6b5 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/esp_ssl_krypton.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_ESP8266_ESP_SSL_KRYPTON_H_ +#define CS_COMMON_PLATFORMS_ESP8266_ESP_SSL_KRYPTON_H_ + +#include "krypton/krypton.h" + +struct mg_connection; + +void mg_lwip_ssl_do_hs(struct mg_connection *nc); +void mg_lwip_ssl_send(struct mg_connection *nc); +void mg_lwip_ssl_recv(struct mg_connection *nc); + +#endif /* CS_COMMON_PLATFORMS_ESP8266_ESP_SSL_KRYPTON_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/esp_umm_malloc.c b/src/mongoose-6.11/src/common/platforms/esp8266/esp_umm_malloc.c new file mode 100644 index 0000000..3dd4a0e --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/esp_umm_malloc.c @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#include +#include +#include "common/umm_malloc/umm_malloc.h" +#include "esp_umm_malloc.h" + +#if ESP_UMM_ENABLE + +/* + * ESP-specific glue for the `umm_malloc`. + * + * In SDK (https://github.com/cesanta/esp-open-sdk), there is an archive + * `sdk/lib/libmain.a` which contains some files, including `mem_manager.o`. + * + * The `mem_manager.o` contains all the heap-related functions: `pvPortMalloc`, + * etc. We have weaken all symbols from `mem_manager.o` by + * `xtensa-lx106-elf-objcopy` (see exact commands in Dockerfile: + * `docker/esp8266/Dockerfile-esp8266-build-oss`), and provide our own + * implementations in this file. + * + * ------------------------------------ + * + * NOTE that not all public functions from `mem_manager.o` need to be replaced: + * some of them are used only internally: + * + * - system_show_malloc() + * - pvShowMalloc() + * - prvInsertBlockIntoUsedList() + * - prvRemoveBlockFromUsedList() + * - check_memleak_debug_enable() + * - vPortInitialiseBlocks() + * + * So when we replace all the rest (`pvPortMalloc`, etc), we can check with + * `objdump` that resulting binary (for SJ, it's `fw.out`) doesn't contain + * any of the "internal" functions. + * + * ------------------------------------ + * + * NOTE that to make linker actually consider implementations in this file, + * you should explicitly reference some function from it. This is what + * `esp_umm_init()` is for: it is a dummy no-op function that must be called + * from somewhere outside. + * + * If you don't do this, linker will merely garbage-collect this file, and + * will use heap implementation from SDK. + */ + +void *pvPortMalloc(size_t size, const char *file, unsigned line) { + (void) file; + (void) line; + + return umm_malloc(size); +} + +void *pvPortCalloc(size_t num, size_t size, const char *file, unsigned line) { + (void) file; + (void) line; + + return umm_calloc(num, size); +} + +void *pvPortZalloc(size_t size, const char *file, unsigned line) { + void *ret; + + (void) file; + (void) line; + + ret = umm_malloc(size); + if (ret != NULL) memset(ret, 0, size); + return ret; +} + +void *pvPortRealloc(void *ptr, size_t size, const char *file, unsigned line) { + (void) file; + (void) line; + + return umm_realloc(ptr, size); +} + +void vPortFree(void *ptr, const char *file, unsigned line) { + (void) file; + (void) line; + + umm_free(ptr); +} + +size_t xPortGetFreeHeapSize(void) { + return umm_free_heap_size(); +} + +size_t xPortWantedSizeAlign(void) { + return 4; +} + +void esp_umm_init(void) { + /* Nothing to do, see header for details */ +} + +void esp_umm_oom_cb(size_t size, size_t blocks_cnt) { + fprintf(stderr, "E:M %u (%u blocks)\n", (unsigned int) size, + (unsigned int) blocks_cnt); +} + +#endif /* ESP_UMM_ENABLE */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/esp_umm_malloc.h b/src/mongoose-6.11/src/common/platforms/esp8266/esp_umm_malloc.h new file mode 100644 index 0000000..efb6f4b --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/esp_umm_malloc.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#if ESP_UMM_ENABLE + +/* + * This is a no-op dummy function that is merely needed because we have to + * reference any function from the file `esp_umm_malloc.c`, so that linker + * won't garbage-collect the whole compilation unit. + */ +void esp_umm_init(void); + +/* + * Callback that gets called by umm_malloc in case of Out-of-memory error. + * + * `size` is the size requested by user, and `block_cnt` is a number of heap + * blocks that umm_malloc failed to allocate + */ +void esp_umm_oom_cb(size_t size, size_t blocks_cnt); + +#endif /* CS_COMMON_PLATFORMS_ESP8266_ESP_UMM_MALLOC_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/Makefile b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/Makefile new file mode 100644 index 0000000..4cccf2a --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/Makefile @@ -0,0 +1,26 @@ +# +# Makefile for esptool2 +# https://github.com/raburton/esp8266 +# + +CFLAGS = -O2 -Wall +CC = gcc +LD = gcc +BUILD_DIR = ../../build + +all: $(BUILD_DIR) $(BUILD_DIR)/esptool2 + +$(BUILD_DIR): + @mkdir -p $(BUILD_DIR) + +$(BUILD_DIR)/esptool2.o: esptool2.c esptool2.h esptool2_elf.h elf.h + @echo "CC $<" + $(CC) $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/esptool2_elf.o: esptool2_elf.c esptool2.h esptool2_elf.h elf.h + @echo "CC $<" + $(CC) $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/esptool2: $(BUILD_DIR)/esptool2.o $(BUILD_DIR)/esptool2_elf.o + @echo "LD $@" + $(LD) -o $@ $^ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/elf.h b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/elf.h new file mode 100644 index 0000000..a699718 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/elf.h @@ -0,0 +1,76 @@ +// Based on a small portion of ELF.h from LLVM + +//===-- llvm/Support/ELF.h - ELF constants and data structures --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. +// +//===----------------------------------------------------------------------===// +// +// This header contains common, non-processor-specific data structures and +// constants for the ELF file format. +// +// The details of the ELF32 bits in this file are largely based on the Tool +// Interface Standard (TIS) Executable and Linking Format (ELF) Specification +// Version 1.2, May 1995. The ELF64 stuff is based on ELF-64 Object File Format +// Version 1.5, Draft 2, May 1998 as well as OpenBSD header files. +// +//===----------------------------------------------------------------------===// + +#ifndef CS_COMMON_PLATFORMS_ESP8266_RBOOT_ESPTOOL2_ELF_H_ +#define CS_COMMON_PLATFORMS_ESP8266_RBOOT_ESPTOOL2_ELF_H_ + +typedef uint32_t Elf32_Addr; // Program address +typedef uint32_t Elf32_Off; // File offset +typedef uint16_t Elf32_Half; +typedef uint32_t Elf32_Word; +typedef int32_t Elf32_Sword; + +// e_ident size and indices +enum { + EI_MAG0 = 0, // File identification index. + EI_MAG1 = 1, // File identification index. + EI_MAG2 = 2, // File identification index. + EI_MAG3 = 3, // File identification index. + EI_CLASS = 4, // File class. + EI_DATA = 5, // Data encoding. + EI_VERSION = 6, // File version. + EI_OSABI = 7, // OS/ABI identification. + EI_ABIVERSION = 8, // ABI version. + EI_PAD = 9, // Start of padding bytes. + EI_NIDENT = 16 // Number of bytes in e_ident. +}; + +typedef struct { + unsigned char e_ident[EI_NIDENT]; // ELF Identification bytes + Elf32_Half e_type; // Type of file (see ET_* below) + Elf32_Half e_machine; // Required architecture for this file (see EM_*) + Elf32_Word e_version; // Must be equal to 1 + Elf32_Addr e_entry; // Address to jump to in order to start program + Elf32_Off e_phoff; // Program header table's file offset, in bytes + Elf32_Off e_shoff; // Section header table's file offset, in bytes + Elf32_Word e_flags; // Processor-specific flags + Elf32_Half e_ehsize; // Size of ELF header, in bytes + Elf32_Half e_phentsize; // Size of an entry in the program header table + Elf32_Half e_phnum; // Number of entries in the program header table + Elf32_Half e_shentsize; // Size of an entry in the section header table + Elf32_Half e_shnum; // Number of entries in the section header table + Elf32_Half e_shstrndx; // Sect hdr table index of sect name string table +} Elf32_Ehdr; + +typedef struct { + Elf32_Word sh_name; // Section name (index into string table) + Elf32_Word sh_type; // Section type (SHT_*) + Elf32_Word sh_flags; // Section flags (SHF_*) + Elf32_Addr sh_addr; // Address where section is to be loaded + Elf32_Off sh_offset; // File offset of section data, in bytes + Elf32_Word sh_size; // Size of section, in bytes + Elf32_Word sh_link; // Section type-specific header table index link + Elf32_Word sh_info; // Section type-specific extra information + Elf32_Word sh_addralign; // Section address alignment + Elf32_Word sh_entsize; // Size of records contained within the section +} Elf32_Shdr; + +#endif /* CS_COMMON_PLATFORMS_ESP8266_RBOOT_ESPTOOL2_ELF_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2.c b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2.c new file mode 100644 index 0000000..aa9b457 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2.c @@ -0,0 +1,561 @@ +/********************************************************************************** +* +* Copyright (c) 2015 Richard A Burton +* +* This file is part of esptool2. +* +* esptool2 is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* esptool2 is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with esptool2. If not, see . +* +**********************************************************************************/ +/* clang-format off */ + +#include +#include +#include + +#include "esptool2.h" +#include "esptool2_elf.h" + +#define IMAGE_PADDING 16 +#define SECTION_PADDING 4 +#define IROM_SECTION_PADDING 4096 +#define CHECKSUM_INIT 0xEF +#define BIN_MAGIC_IROM 0xEA +#define BIN_MAGIC_FLASH 0xE9 + +typedef struct { + Elf32_Addr addr; + Elf32_Word size; +} Section_Header; + +typedef struct { + unsigned char magic; + unsigned char count; + unsigned char byte2; + unsigned char byte3; + Elf32_Addr entry; +} Image_Header; + +static const char PADDING[IROM_SECTION_PADDING] = {0}; + +static bool debugon = false; +static bool quieton = false; + +// Print a standard info message (unless quiet mode) +void print(const char* format, ...) { + va_list args; + + if (!quieton) { + va_start(args, format); + vprintf(format, args); + va_end(args); + } +} + +// Print a debug message (if debug mode) +void debug(const char* format, ...) { + va_list args; + + if (debugon) { + va_start(args, format); + vprintf(format, args); + va_end(args); + } +} + +// Print an error message (always) +void error(const char* format, ...) { + va_list args; + + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); +} + +// Write an elf section (by name) to an existing file. +// Parameters: +// headed - add a header to the output +// zeroaddr - force zero entry point in header (default is the real entry point) +// padded - output will be padded to multiple of SECTION_PADDING bytes +// chksum - pointer to existing checksum to add this data to (zero if not needed) +// Produces error message on failure (so caller doesn't need to). +bool WriteElfSection(MyElf_File *elf, FILE *outfile, char* name, bool headed, + bool zeroaddr, int padto, unsigned char *chksum) { + + int i, pad = 0; + bool ret = false; + unsigned char *bindata = 0; + Section_Header sechead; + MyElf_Section *sect; + + // get elf section header + sect = GetElfSection(elf, name); + if(!sect) { + error("Error: Section '%s' not found in elf file.\r\n", name); + goto end_function; + } + + // create image section header + sechead.addr = (zeroaddr ? 0 : sect->address); + sechead.size = sect->size; + + // do we need to pad the section? + if (padto) { + pad = sechead.size % padto; + if (pad > 0) { + pad = padto - pad; + sechead.size += pad; + } + } + + debug("Adding section '%s', addr: 0x%08x, size: %d (+%d bytes(s) padding).\r\n", + name, sect->address, sect->size, pad); + + // get elf section binary data + bindata = GetElfSectionData(elf, sect); + if (!bindata) { + goto end_function; + } + + // write section (and pad if required) + if((headed && fwrite(&sechead, 1, sizeof(sechead), outfile) != sizeof(sechead)) + || fwrite(bindata, 1, sect->size, outfile) != sect->size + || (pad > 0 && fwrite(PADDING, 1, pad, outfile) != pad)) { + error("Error: Failed to write section '%s' to image file.\r\n", name); + goto end_function; + } + + // include section data in the checksum + if(chksum) { + for(i = 0; i < (int)sect->size; i++) { + *chksum ^= bindata[i]; + } + } + + ret = true; + +end_function: + if (bindata) free(bindata); + return ret; +} + +// Load an elf file and export a section of it to a new file, without +// header, padding or checksum. For exporting the .irom0.text library. +// Produces error message on failure (so caller doesn't need to). +bool ExportElfSection(char *infile, char *outfile, char *name) { + + bool ret = false; + FILE *fd = 0; + MyElf_File *elf = 0; + + // load elf file + elf = LoadElf(infile); + if (!elf) { + goto end_function; + } + + // open output file + fd = fopen(outfile, "wb"); + if(!fd) { + error("Error: Can't open output file '%s' for writing.\r\n", outfile); + goto end_function; + } + + // actually do the export + ret = WriteElfSection(elf, fd, name, false, false, false, 0); + +end_function: + // clean up + if (fd) fclose(fd); + UnloadElf(elf); + + return ret; +} + +// Create the main binary firmware image, from specified elf sections. +// Can produce for standard standalone app (separate .irom0.text) +// or sdk bootloaded apps (integrated .irom0.text). +// Choice of type requires appropriately linked elf file. +// Produces error message on failure (so caller doesn't need to). +bool CreateHeaderFile(char *elffile, char *imagefile, char *sections[], int numsec) { + + bool ret = false; + int i; + unsigned int j, len; + FILE *outfile = 0; + MyElf_File *elf = 0; + MyElf_Section *sect; + unsigned char *bindata = 0; + char name[31]; + + // load elf file + elf = LoadElf(elffile); + if (!elf) { + goto end_function; + } + + // open output file + outfile = fopen(imagefile, "wb"); + if(outfile == NULL) { + error("Error: Failed to open output file '%s' for writing.\r\n", imagefile); + goto end_function; + } + + // add entry point + fprintf(outfile, "const uint32 entry_addr = 0x%08x;\r\n", elf->header.e_entry); + + // add sections + for (i = 0; i < numsec; i++) { + // get elf section header + sect = GetElfSection(elf, sections[i]); + if(!sect) { + error("Error: Section '%s' not found in elf file.\r\n", sections[i]); + goto end_function; + } + + // simple name fix name + strncpy(name, sect->name, 31); + len = strlen(name); + for (j = 0; j < len; j++) { + if (name[j] == '.') name[j] = '_'; + } + + // add address, length and start the data block + debug("Adding section '%s', addr: 0x%08x, size: %d.\r\n", sections[i], sect->address, sect->size); + fprintf(outfile, "\r\nconst uint32 %s_addr = 0x%08x;\r\nconst uint32 %s_len = %d;\r\nconst uint8 %s_data[] = {", + name, sect->address, name, sect->size, name); + + // get elf section binary data + bindata = GetElfSectionData(elf, sect); + if (!bindata) { + goto end_function; + } + + // add the data and finish off the block + for (j = 0; j < sect->size; j++) { + if (j % 16 == 0) fprintf(outfile, "\r\n 0x%02x,", bindata[j]); + else fprintf(outfile, " 0x%02x,", bindata[j]); + } + fprintf(outfile, "\r\n};\r\n"); + free(bindata); + bindata = 0; + } + + // if we got this far everything worked! + ret = true; + +end_function: + // clean up + if (outfile) fclose(outfile); + if (elf) UnloadElf(elf); + if (bindata) free(bindata); + + return ret; +} + +// Create the main binary firmware image, from specified elf sections. +// Can produce for standard standalone app (separate .irom0.text) +// or sdk bootloaded apps (integrated .irom0.text). +// Choice of type requires appropriately linked elf file. +// Produces error message on failure (so caller doesn't need to). +bool CreateBinFile(char *elffile, char *imagefile, int bootver, unsigned char mode, + unsigned char clock, unsigned char size, bool iromchksum, char *sections[], int numsec) { + + bool ret = false; + int i, pad, len; + unsigned char chksum = CHECKSUM_INIT; + unsigned char *data = 0; + FILE *outfile = 0; + MyElf_File *elf = 0; + Image_Header imghead; + + // load elf file + elf = LoadElf(elffile); + if (!elf) { + goto end_function; + } + + // open output file + outfile = fopen(imagefile, "wb"); + if(outfile == NULL) { + error("Error: Failed to open output file '%s' for writing.\r\n", imagefile); + goto end_function; + } + + // set options common to standard and boot v1.2+ headers + imghead.byte2 = mode; + //imghead.byte3 = (int)((int)size << 4 | clock) && 0xff; + imghead.byte3 = ((size << 4) | clock) & 0xff; + imghead.entry = elf->header.e_entry; + debug("Size = %02x\r\n", size); + debug("Byte2 = %02x\r\n", imghead.byte2); + debug("Byte3 = %02x\r\n", imghead.byte3); + debug("Entry = %08x\r\n", imghead.entry); + + // boot v1.2+ header + if (bootver == 2) { + // extra header + imghead.magic = BIN_MAGIC_IROM; + imghead.count = 4; // probably a version number here, not a count + if(fwrite(&imghead, 1, sizeof(imghead), outfile) != sizeof(imghead)) { + error("Error: Failed to write header to image file.\r\n"); + goto end_function; + } + if(!WriteElfSection(elf, outfile, ".irom0.text", true, true, IROM_SECTION_PADDING, (iromchksum ? &chksum : 0))) { + goto end_function; + } + } + + // standard header + imghead.magic = BIN_MAGIC_FLASH; + imghead.count = numsec; + // write header + if(fwrite(&imghead, 1, sizeof(imghead), outfile) != sizeof(imghead)) { + error("Error: Failed to write header to image file.\r\n"); + goto end_function; + } + + // add sections + for (i = 0; i < numsec; i++) { + if(!WriteElfSection(elf, outfile, sections[i], true, false, SECTION_PADDING, &chksum)) { + goto end_function; + } + } + + // get image length (plus a byte for the checksum) + len = ftell(outfile) + 1; + + // do we need to pad the image? + pad = len % IMAGE_PADDING; + if (pad > 0) { + pad = IMAGE_PADDING - pad; + debug("Padding image with %d byte(s).\r\n", pad); + if(fwrite(PADDING, 1, pad, outfile) != pad) { + error("Error: Failed to write padding to image file.\r\n"); + goto end_function; + } + } + + // write checksum + if(fwrite(&chksum, 1, 1, outfile) != 1) { + error("Error: Failed to write checksum to image file.\r\n"); + goto end_function; + } + + // boot v1.1 + if(bootver == 1) { + // write 'ff' padding up to the position of the library + len = 0x10000 - ftell(outfile); + debug("Adding boot v1.1 padding, %d bytes of '0xff'.\r\n", len); + data = (unsigned char*)malloc(len); + memset(data, 0xff, len); + if(fwrite(data, 1, len, outfile) != len) { + error("Error: Failed to write boot v1.1 spacer.\r\n"); + goto end_function; + } + + // write the library + if(!WriteElfSection(elf, outfile, ".irom0.text", false, false, 0, 0)) { + goto end_function; + } + } + + // if we got this far everything worked! + ret = true; + +end_function: + // clean up + if (outfile) fclose(outfile); + if (data) free(data); + if (elf) UnloadElf(elf); + + return ret; +} + +int main(int argc, char *argv[]) { + + int i; + char *infile; + char *outfile; + int numstr; + bool binfile = false; + bool libfile = false; + bool headerfile = false; + bool paramerror = false; + bool iromchksum = false; + int bootver = 0; + /* Overwrite-friendly by default */ + unsigned char mode = 0xff; + unsigned char size = 0xff; + unsigned char clock = 0; + int opts = 0; + + // parse options + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-bin")) { + binfile = true; + opts++; + } else if (!strcmp(argv[i], "-lib")) { + libfile = true; + opts++; + } else if (!strcmp(argv[i], "-header")) { + headerfile = true; + opts++; + } else if (!strcmp(argv[i], "-quiet")) { + quieton = true; + } else if (!strcmp(argv[i], "-debug")) { + debugon = true; + } else if (!strcmp(argv[i], "-boot0")) { + bootver = 0; + } else if (!strcmp(argv[i], "-boot1")) { + bootver = 1; + } else if (!strcmp(argv[i], "-boot2")) { + bootver = 2; + } else if (!strcmp(argv[i], "-qio")) { + mode = 0; + } else if (!strcmp(argv[i], "-qout")) { + mode = 1; + } else if (!strcmp(argv[i], "-dio")) { + mode = 2; + } else if (!strcmp(argv[i], "-dout")) { + mode = 3; + } else if (!strcmp(argv[i], "-256")) { + size = 1; + } else if (!strcmp(argv[i], "-512")) { + size = 0; + } else if (!strcmp(argv[i], "-1024")) { + size = 2; + } else if (!strcmp(argv[i], "-2048")) { + size = 3; + } else if (!strcmp(argv[i], "-4096")) { + size = 4; + } else if (!strcmp(argv[i], "-20")) { + clock = 2; + } else if (!strcmp(argv[i], "-26.7")) { + clock = 1; + } else if (!strcmp(argv[i], "-40")) { + clock = 0; + } else if (!strcmp(argv[i], "-80")) { + clock = 15; + } else if (!strcmp(argv[i], "-iromchksum")) { + iromchksum = true; + } else if (!strcmp(argv[i], "--")) { + i++; + break; + } else if (argv[i][0] == '-') { + paramerror = true; + break; + } else { + break; + } + } + + print("esptool2 v2.0.0 - (c) 2015 Richard A Burton \r\n"); + print("This program is licensed under the GPL v3.\r\n"); + print("See the file LICENSE for details.\r\n\r\n"); + + if (paramerror) { + error("Error: Unrecognised option '%s'.\r\n", argv[i]); + return -1; + } + + if (argc < 2) { + print("Usage:\r\n"); + print("esptool2 -lib [options] \r\n"); + print("esptool2 -bin [options] ...\r\n"); + print("esptool2 -header [options] ...\r\n"); + print("\r\n"); + print(" -lib\r\n"); + print(" Export the sdk library (.irom0.text), for a standalone app.\r\n"); + print(" e.g. esptool2 -elf esp8266_iot.out out.bin\r\n"); + print("\r\n"); + print(" -header\r\n"); + print(" Export elf sections as bytes to a C header file.\r\n"); + print(" e.g. esptool2 -elf esp8266_iot.out out.h .text .data .rodata\r\n"); + print("\r\n"); + print(" -bin\r\n"); + print(" Create binary program image, for standalone and bootloaded apps, with\r\n"); + print(" specified elf sections. Includes sdk library for bootloaded apps.\r\n"); + print(" e.g. esptool2 -bin esp8266_iot.out out.bin .text .data .rodata\r\n"); + print(" Options:\r\n"); + print(" bootloader: -boot0 -boot1 -boot2 (default -boot0)\r\n"); + print(" -boot0 = standalone app, not built for bootloader use\r\n"); + print(" -boot1 = built for bootloader v1.1\r\n"); + print(" -boot2 = built for bootloader v1.2+ (use for rBoot roms)\r\n"); + print(" (elf file must have been linked appropriately for chosen option)\r\n"); + print(" spi size (kb): -256 -512 -1024 -2048 -4096 (default -512)\r\n"); + print(" spi mode: -qio -qout -dio -dout (default -qio)\r\n"); + print(" spi speed: -20 -26.7 -40 -80 (default -40)\r\n"); + print(" include irom in checksum: -iromchksum (also needs enabling in rBoot)\r\n"); + print("\r\n"); + print("General options:\r\n"); + print(" -quiet prints only error messages\r\n"); + print(" -debug print extra debug information\r\n"); + print(" -- no more options follow (needed if your elf file starts with a '-')\r\n"); + print("\r\n"); + print("Returns:\r\n"); + print(" 0 on success\r\n"); + print(" -1 on failure\r\n"); + print("\r\n"); + return -1; + } + + // validate command line options + if (opts != 1) { + error("Error: You must specify -bin OR -lib OR -header for build type.\r\n"); + return -1; + } + + if (quieton && debugon) { + error("Error: You cannot specify -quiet and -debug.\r\n"); + return -1; + } + + // check enough parameters + if ((libfile && i + 2 > argc) || ((binfile | headerfile) && i + 3 > argc)) { + error("Error: Not enough arguments supplied.\r\n"); + return -1; + } else if (libfile && i + 2 < argc) { + error("Error: Too many arguments supplied.\r\n"); + return -1; + } + + // get parameters + infile = argv[i++]; + outfile = argv[i++]; + numstr = argc - i; + + // do it + if (binfile) { + if (!CreateBinFile(infile, outfile, bootver, mode, clock, size, iromchksum, &argv[i], numstr)) { + remove(outfile); + return -1; + } + } else if (headerfile) { + if (!CreateHeaderFile(infile, outfile, &argv[i], numstr)) { + remove(outfile); + return -1; + } + } else { + if (!ExportElfSection(infile, outfile, ".irom0.text")) { + remove(outfile); + return -1; + } + + } + + print("Successfully created '%s'.\r\n", outfile); + return 0; + +} diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2.h b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2.h new file mode 100644 index 0000000..b0eba08 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2.h @@ -0,0 +1,44 @@ +/********************************************************************************** +* +* Copyright (c) 2015 Richard A Burton +* +* This file is part of esptool2. +* +* esptool2 is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* esptool2 is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with esptool2. If not, see . +* +**********************************************************************************/ + +#ifndef CS_COMMON_PLATFORMS_ESP8266_RBOOT_ESPTOOL2_ESPTOOL2_H_ +#define CS_COMMON_PLATFORMS_ESP8266_RBOOT_ESPTOOL2_ESPTOOL2_H_ + +#ifdef WIN32 +typedef signed __int8 int8_t; +typedef signed __int16 int16_t; +typedef signed __int32 int32_t; +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +#else +#include +#endif + +#define true 1 +#define false 0 +#define bool char + +void debug( const char* format, ... ); +void print( const char* format, ... ); +void error( const char* format, ... ); + +#endif /* CS_COMMON_PLATFORMS_ESP8266_RBOOT_ESPTOOL2_ESPTOOL2_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2_elf.c b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2_elf.c new file mode 100644 index 0000000..88ede4e --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2_elf.c @@ -0,0 +1,183 @@ +/********************************************************************************** +* +* Copyright (c) 2015 Richard A Burton +* +* This file is part of esptool2. +* +* esptool2 is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* esptool2 is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with esptool2. If not, see . +* +**********************************************************************************/ + +#include +#include + +#include "esptool2.h" +#include "esptool2_elf.h" + +// Find a section in an elf file by name. +// Returns pointer to section if found, else returns zero. +// Does not produce any messages. +MyElf_Section* GetElfSection(MyElf_File *elf, char *name) { + + int i; + + for(i = 0; i < elf->header.e_shnum - 1; i++) { + if(!strcmp(name, elf->sections[i].name)) { + debug("Found section '%s'.\r\n", name); + return &elf->sections[i]; + } + } + + debug("Could not find section '%s'.\r\n", name); + return 0; +} + +// Reads an elf section (actual data) from the elf file. +// Returns a pointer to newly allocated memory (or zero on error), +// which should be freed by the caller when finished with. +// Produces error message on failure (so caller doesn't need to). +unsigned char* GetElfSectionData(MyElf_File *elf, MyElf_Section *section) { + + unsigned char *data = 0; + + if (section->size && section->offset) { + + data = (unsigned char*)malloc(section->size); + if(!data) { + error("Error: Out of memory!\r\n"); + return 0; + } + + if(fseek(elf->fd, section->offset, SEEK_SET) || + fread(data, 1, section->size, elf->fd) != section->size) { + error("Error: Can't read section '%s' data from elf file.\r\n", section->name); + free(data); + return 0; + } + + } else { + error("Error: Section '%s' has no data to read.\r\n", section->name); + } + + return data; +} + +// Opens an elf file and reads the string table and file & section headers. +// Returns a pointer to a MyElf_File structure (or zero on error). +// UnloadElf should be called to dispose of the MyElf_File structure. +// Produces error message on failure (so caller doesn't need to). +MyElf_File* LoadElf(char *infile) { + + int i; + MyElf_File *elf; + Elf32_Shdr temp; + + // allocate the elf structure + elf = (MyElf_File*)malloc(sizeof(MyElf_File)); + if(!elf) { + error("Error: Out of memory!\r\n"); + goto error_exit; + } + memset(elf, 0, sizeof(MyElf_File)); + + // open the file + elf->fd = fopen(infile, "rb"); + if(!elf->fd) { + error("Error: Can't open elf file '%s'.\r\n", infile); + goto error_exit; + } + + // read the header + if(fread(&elf->header, 1, sizeof(Elf32_Ehdr), elf->fd) != sizeof(Elf32_Ehdr)) { + error("Error: Can't read elf file header.\r\n"); + goto error_exit; + } + + // check the file header + if (memcmp(elf->header.e_ident, "\x7f" "ELF", 4)) { + error("Error: Input files doesn't look like an elf file (bad header).\r\n"); + goto error_exit; + } + + // is there a string table section (we need one) + if(!elf->header.e_shstrndx) { + error("Error: Elf file does not contain a string table.\r\n"); + goto error_exit; + } + + // get the string table section header + if(fseek(elf->fd, elf->header.e_shoff + (elf->header.e_shentsize * elf->header.e_shstrndx), SEEK_SET) || + fread(&temp, 1, sizeof(Elf32_Shdr), elf->fd) != sizeof(Elf32_Shdr)) { + + error("Error: Can't read string table section from elf file.\r\n"); + goto error_exit; + } + + // read the actual string table + if(!temp.sh_size) { + error("Error: Elf file contains an empty string table.\r\n"); + goto error_exit; + } + elf->strings = (char*)malloc(temp.sh_size); + if(!elf->strings) { + error("Error: Out of memory!\r\n"); + goto error_exit; + } + if(fseek(elf->fd, temp.sh_offset, SEEK_SET) || + fread(elf->strings, 1, temp.sh_size, elf->fd) != temp.sh_size) { + error("Error: Failed to read string stable from elf file.\r\n"); + goto error_exit; + } + + // read section headers + elf->sections = (MyElf_Section*)malloc(sizeof(MyElf_Section) * elf->header.e_shnum); + if(!elf->sections) { + error("Error: Out of memory!\r\n"); + goto error_exit; + } + for(i = 1; i < elf->header.e_shnum; i++) { + if(fseek(elf->fd, elf->header.e_shoff + (elf->header.e_shentsize * i), SEEK_SET) + || fread(&temp, 1, sizeof(Elf32_Shdr), elf->fd) != sizeof(Elf32_Shdr)) { + error("Error: Can't read section %d from elf file.\r\n", i); + break; + } + debug("Read section %d '%s'.\r\n", i, elf->strings + temp.sh_name); + elf->sections[i-1].address = temp.sh_addr; + elf->sections[i-1].offset = temp.sh_offset; + elf->sections[i-1].size = temp.sh_size; + elf->sections[i-1].name = elf->strings + temp.sh_name; + } + + return elf; + +error_exit: + if (elf) { + if (elf->fd) fclose(elf->fd); + if (elf->strings) free(elf->strings); + free(elf); + } + return 0; + +} + +// Close an elf file and dispose of the MyElf_File structure. +void UnloadElf(MyElf_File *elf) { + if (elf) { + debug("Unloading elf file.\r\n"); + if(elf->fd) fclose(elf->fd); + if(elf->strings) free(elf->strings); + if(elf->sections) free(elf->sections); + free(elf); + } +} diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2_elf.h b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2_elf.h new file mode 100644 index 0000000..ef98511 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2_elf.h @@ -0,0 +1,48 @@ +/********************************************************************************** +* +* Copyright (c) 2015 Richard A Burton +* +* This file is part of esptool2. +* +* esptool2 is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* esptool2 is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with esptool2. If not, see . +* +**********************************************************************************/ + +#ifndef CS_COMMON_PLATFORMS_ESP8266_RBOOT_ESPTOOL2_ESPTOOL2_ELF_H_ +#define CS_COMMON_PLATFORMS_ESP8266_RBOOT_ESPTOOL2_ESPTOOL2_ELF_H_ + +#include + +#include "elf.h" + +typedef struct { + Elf32_Off offset; + Elf32_Addr address; + Elf32_Word size; + char *name; +} MyElf_Section; + +typedef struct { + FILE *fd; + Elf32_Ehdr header; + char *strings; + MyElf_Section *sections; +} MyElf_File; + +MyElf_File* LoadElf(char *infile); +void UnloadElf(MyElf_File *e_object); +MyElf_Section* GetElfSection(MyElf_File *e_object, char *name); +unsigned char* GetElfSectionData(MyElf_File *e_object, MyElf_Section *section); + +#endif /* CS_COMMON_PLATFORMS_ESP8266_RBOOT_ESPTOOL2_ESPTOOL2_ELF_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/license.txt b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/license.txt new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/license.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/readme.txt b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/readme.txt new file mode 100644 index 0000000..eb9f0ef --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/readme.txt @@ -0,0 +1,19 @@ +Esptool2 +richardaburton@gmail.com +http://richard.burtons.org/ + +Esptool2 is a tool for creating rom images for the ESP8266. It is an alternative +to using the SDK supplied shell script/Makefile/python script combo, which is a +mess. It was inspired by the windows esptool v0.0.2 by mamalala and found on +www.esp8266.com but made somewhat simpler in code and usage. It also adds +support for boot loader v1.2+ rom types, which was the main reason I wrote it. + +It was written for my own use and the name was simply to distinguish it for the +other version on my system. The 2 is not intended to imply it is better than the +original. The original has since been updated to v0.0.3 which can write to the +flash, but I currently have no intention to add that to esptool2, it is purely a +rom creating utility. It has become an integral part of my build process now and +has added functionality needed for building the rBoot boot loader. Since I have +released rBoot I needed to release this as well. + +Run tool for full usage instructions, or look at the code. diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/appcode/rboot-api.c b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/appcode/rboot-api.c new file mode 100644 index 0000000..2d2e83d --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/appcode/rboot-api.c @@ -0,0 +1,152 @@ +////////////////////////////////////////////////// +// rBoot OTA and config API for ESP8266. +// Copyright 2015 Richard A Burton +// richardaburton@gmail.com +// See license.txt for license terms. +// OTA code based on SDK sample from Espressif. +////////////////////////////////////////////////// + +#ifdef RBOOT_INTEGRATION +#include +#endif + +#include +#include +#include + +#ifdef RTOS_SDK +#include "spi_flash.h" +#else +#include +#endif + +#include "rboot-api.h" +#include "../rboot-private.h" +#include "esp_missing_includes.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// get the rboot config +rboot_config ICACHE_FLASH_ATTR rboot_get_config(void) { + rboot_config conf; + spi_flash_read(BOOT_CONFIG_SECTOR * SECTOR_SIZE, (uint32*)&conf, sizeof(rboot_config)); + return conf; +} + +// write the rboot config +// preserves the contents of the rest of the sector, +// so the rest of the sector can be used to store user data +// updates checksum automatically (if enabled) +bool ICACHE_FLASH_ATTR rboot_set_config(rboot_config *conf) { + uint32_t buffer[sizeof(*conf) / sizeof(uint32_t) + 1]; +#ifdef BOOT_CONFIG_CHKSUM + uint8 chksum; + uint8 *ptr; +#endif + +#ifdef BOOT_CONFIG_CHKSUM + chksum = CHKSUM_INIT; + for (ptr = (uint8*)conf; ptr < &conf->chksum; ptr++) { + chksum ^= *ptr; + } + conf->chksum = chksum; +#endif + + memset(buffer, 0xff, sizeof(buffer)); + memcpy(buffer, conf, sizeof(*conf)); + if (spi_flash_erase_sector(BOOT_CONFIG_SECTOR) != SPI_FLASH_RESULT_OK || + spi_flash_write(BOOT_CONFIG_SECTOR * SECTOR_SIZE, buffer, sizeof(buffer)) != SPI_FLASH_RESULT_OK) { + return false; + } + return true; +} + +// get current boot rom +uint8 ICACHE_FLASH_ATTR rboot_get_current_rom(void) { + rboot_config conf; + conf = rboot_get_config(); + return conf.current_rom; +} + +// set current boot rom +bool ICACHE_FLASH_ATTR rboot_set_current_rom(uint8 rom) { + rboot_config conf; + conf = rboot_get_config(); + if (rom >= conf.count) return false; + conf.current_rom = rom; + return rboot_set_config(&conf); +} + +// create the write status struct, based on supplied start address +rboot_write_status ICACHE_FLASH_ATTR rboot_write_init(uint32 start_addr) { + rboot_write_status status = {0}; + status.start_addr = start_addr; + status.start_sector = start_addr / SECTOR_SIZE; + //status.max_sector_count = 200; + //os_printf("init addr: 0x%08x\r\n", start_addr); + + return status; +} + +// function to do the actual writing to flash +// call repeatedly with more data (max len per write is the flash sector size (4k)) +bool ICACHE_FLASH_ATTR rboot_write_flash(rboot_write_status *status, uint8 *data, uint16 len) { + + bool ret = false; + uint8 *buffer; + + if (data == NULL || len == 0) { + return true; + } + + // get a buffer + buffer = (uint8 *) calloc(1, len + status->extra_count); + if (!buffer) { + //os_printf("No ram!\r\n"); + return false; + } + + // copy in any remaining bytes from last chunk + memcpy(buffer, status->extra_bytes, status->extra_count); + // copy in new data + memcpy(buffer + status->extra_count, data, len); + + // calculate length, must be multiple of 4 + // save any remaining bytes for next go + len += status->extra_count; + status->extra_count = len % 4; + len -= status->extra_count; + memcpy(status->extra_bytes, buffer + len, status->extra_count); + + // check data will fit + //if (status->start_addr + len < (status->start_sector + status->max_sector_count) * SECTOR_SIZE) { + + if (len > SECTOR_SIZE) { + // to support larger writes we would need to erase current + // (if not already done), next and possibly later sectors too + } else { + // check if the sector the write finishes in has been erased yet, + // this is fine as long as data len < sector size + if (status->last_sector_erased != (status->start_addr + len) / SECTOR_SIZE) { + status->last_sector_erased = (status->start_addr + len) / SECTOR_SIZE; + spi_flash_erase_sector(status->last_sector_erased); + } + } + + // write current chunk + //os_printf("write addr: 0x%08x, len: 0x%04x\r\n", status->start_addr, len); + if (spi_flash_write(status->start_addr, (uint32 *)buffer, len) == SPI_FLASH_RESULT_OK) { + ret = true; + status->start_addr += len; + } + //} + + free(buffer); + return ret; +} + +#ifdef __cplusplus +} +#endif diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/appcode/rboot-api.h b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/appcode/rboot-api.h new file mode 100644 index 0000000..c4601d3 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/appcode/rboot-api.h @@ -0,0 +1,38 @@ +#ifndef CS_COMMON_PLATFORMS_ESP8266_RBOOT_RBOOT_APPCODE_RBOOT_API_H_ +#define CS_COMMON_PLATFORMS_ESP8266_RBOOT_RBOOT_APPCODE_RBOOT_API_H_ + +////////////////////////////////////////////////// +// rBoot OTA and config API for ESP8266. +// Copyright 2015 Richard A Burton +// richardaburton@gmail.com +// See license.txt for license terms. +// OTA code based on SDK sample from Espressif. +////////////////////////////////////////////////// + +#include "../rboot.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint32 start_addr; + uint32 start_sector; + //uint32 max_sector_count; + uint32 last_sector_erased; + uint8 extra_count; + uint8 extra_bytes[4]; +} rboot_write_status; + +rboot_config ICACHE_FLASH_ATTR rboot_get_config(void); +bool ICACHE_FLASH_ATTR rboot_set_config(rboot_config *conf); +uint8 ICACHE_FLASH_ATTR rboot_get_current_rom(void); +bool ICACHE_FLASH_ATTR rboot_set_current_rom(uint8 rom); +rboot_write_status ICACHE_FLASH_ATTR rboot_write_init(uint32 start_addr); +bool ICACHE_FLASH_ATTR rboot_write_flash(rboot_write_status *status, uint8 *data, uint16 len); + +#ifdef __cplusplus +} +#endif + +#endif /* CS_COMMON_PLATFORMS_ESP8266_RBOOT_RBOOT_APPCODE_RBOOT_API_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/appcode/rboot-bigflash.c b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/appcode/rboot-bigflash.c new file mode 100644 index 0000000..cd8a180 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/appcode/rboot-bigflash.c @@ -0,0 +1,61 @@ +////////////////////////////////////////////////// +// rBoot open source boot loader for ESP8266. +// Copyright 2015 Richard A Burton +// richardaburton@gmail.com +// See license.txt for license terms. +////////////////////////////////////////////////// + +/* clang-format off */ + +#ifdef RBOOT_INTEGRATION +#include +#endif + +typedef unsigned int uint32; +typedef unsigned char uint8; + +#include + +#if 0 //def BOOT_BIG_FLASH + +// plain sdk defaults to iram +#ifndef IRAM_ATTR +#define IRAM_ATTR +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +extern void Cache_Read_Disable(); +extern uint32 SPIRead(uint32, void*, uint32); +extern void Cache_Read_Enable(uint32, uint32, uint32); + +uint8 rBoot_mmap_1 = 0xff; +uint8 rBoot_mmap_2 = 0xff; + +// this function must remain in iram +IRAM NOINSTR void __wrap_Cache_Read_Enable_New(void) { + if (rBoot_mmap_1 == 0xff) { + uint32 addr; + rboot_config conf; + + Cache_Read_Disable(); + + SPIRead(BOOT_CONFIG_SECTOR * SECTOR_SIZE, &conf, sizeof(rboot_config)); + + addr = conf.roms[conf.current_rom]; + addr /= 0x100000; + + rBoot_mmap_2 = addr / 2; + rBoot_mmap_1 = addr % 2; + } + + Cache_Read_Enable(rBoot_mmap_1, rBoot_mmap_2, 1); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/firmware/rboot.bin b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/firmware/rboot.bin new file mode 100644 index 0000000..a928e0b Binary files /dev/null and b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/firmware/rboot.bin differ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/license.txt b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/license.txt new file mode 100644 index 0000000..04903d7 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/license.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Richard A Burton (richardaburton@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot-private.h b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot-private.h new file mode 100644 index 0000000..5b4d52f --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot-private.h @@ -0,0 +1,74 @@ +#ifndef CS_COMMON_PLATFORMS_ESP8266_RBOOT_RBOOT_RBOOT_PRIVATE_H_ +#define CS_COMMON_PLATFORMS_ESP8266_RBOOT_RBOOT_RBOOT_PRIVATE_H_ + +////////////////////////////////////////////////// +// rBoot open source boot loader for ESP8266. +// Copyright 2015 Richard A Burton +// richardaburton@gmail.com +// See license.txt for license terms. +////////////////////////////////////////////////// + +typedef int int32; +typedef unsigned int uint32; +typedef unsigned char uint8; + +#include "rboot.h" + +#define NOINLINE __attribute__ ((noinline)) + +#define ROM_MAGIC 0xe9 +#define ROM_MAGIC_NEW1 0xea +#define ROM_MAGIC_NEW2 0x04 + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +// buffer size, must be at least 0x10 (size of rom_header_new structure) +#define BUFFER_SIZE 0x100 + +// esp8266 built in rom functions +extern void ets_printf(const char*, ...); +extern uint32 SPIRead(uint32 addr, void *outptr, uint32 len); +extern uint32 SPIEraseSector(int); +extern uint32 SPIWrite(uint32 addr, void *inptr, uint32 len); + +// functions we'll call by address +typedef void stage2a(uint32); +typedef void usercode(void); + +// standard rom header +typedef struct { + // general rom header + uint8 magic; + uint8 count; + uint8 flags1; + uint8 flags2; + usercode* entry; +} rom_header; + +typedef struct { + uint8* address; + uint32 length; +} section_header; + +// new rom header (irom section first) there is +// another 8 byte header straight afterward the +// standard header +typedef struct { + // general rom header + uint8 magic; + uint8 count; // second magic for new header + uint8 flags1; + uint8 flags2; + uint32 entry; + // new type rom, lib header + uint32 add; // zero + uint32 len; // length of irom section +} rom_header_new; + +#endif /* CS_COMMON_PLATFORMS_ESP8266_RBOOT_RBOOT_RBOOT_PRIVATE_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot-stage2a.c b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot-stage2a.c new file mode 100644 index 0000000..9df98a8 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot-stage2a.c @@ -0,0 +1,80 @@ +////////////////////////////////////////////////// +// rBoot open source boot loader for ESP8266. +// Copyright 2015 Richard A Burton +// richardaburton@gmail.com +// See license.txt for license terms. +////////////////////////////////////////////////// + +#ifdef RBOOT_INTEGRATION +#include +#endif + +#include "rboot-private.h" + +usercode* NOINLINE load_rom(uint32 readpos) { + + uint8 buffer[BUFFER_SIZE]; + uint8 sectcount; + uint8 *writepos; + uint32 remaining; + usercode* usercode; + + rom_header *header = (rom_header*)buffer; + section_header *section = (section_header*)buffer; + + // read rom header + SPIRead(readpos, header, sizeof(rom_header)); + readpos += sizeof(rom_header); + + // create function pointer for entry point + usercode = header->entry; + + // copy all the sections + for (sectcount = header->count; sectcount > 0; sectcount--) { + + // read section header + SPIRead(readpos, section, sizeof(section_header)); + readpos += sizeof(section_header); + + // get section address and length + writepos = section->address; + remaining = section->length; + + while (remaining > 0) { + // work out how much to read, up to 16 bytes at a time + uint32 readlen = (remaining < BUFFER_SIZE) ? remaining : BUFFER_SIZE; + // read the block + SPIRead(readpos, buffer, readlen); + readpos += readlen; + // copy the block + ets_memcpy(writepos, buffer, readlen); + // increment next write position + writepos += readlen; + // decrement remaining count + remaining -= readlen; + } + } + + return usercode; +} + +#ifdef BOOT_NO_ASM + +void call_user_start(uint32 readpos) { + usercode* user; + user = load_rom(readpos); + user(); +} + +#else + +void call_user_start(uint32 readpos) { + __asm volatile ( + "mov a15, a0\n" // store return addr, we already splatted a15! + "call0 load_rom\n" // load the rom + "mov a0, a15\n" // restore return addr + "jx a2\n" // now jump to the rom code + ); +} + +#endif diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot-stage2a.ld b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot-stage2a.ld new file mode 100644 index 0000000..7af83ea --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot-stage2a.ld @@ -0,0 +1,211 @@ +/* This linker script generated from xt-genldscripts.tpp for LSP . */ +/* Linker Script for ld -N */ +MEMORY +{ + dport0_0_seg : org = 0x3FF00000, len = 0x10 + dram0_0_seg : org = 0x3FFE8000, len = 0x14000 + iram1_0_seg : org = 0x4010FC00, len = 0x400 + irom0_0_seg : org = 0x40240000, len = 0x3C000 +} + +PHDRS +{ + dport0_0_phdr PT_LOAD; + dram0_0_phdr PT_LOAD; + dram0_0_bss_phdr PT_LOAD; + iram1_0_phdr PT_LOAD; + irom0_0_phdr PT_LOAD; +} + + +/* Default entry point: */ +ENTRY(call_user_start) +EXTERN(_DebugExceptionVector) +EXTERN(_DoubleExceptionVector) +EXTERN(_KernelExceptionVector) +EXTERN(_NMIExceptionVector) +EXTERN(_UserExceptionVector) +PROVIDE(_memmap_vecbase_reset = 0x40000000); +/* Various memory-map dependent cache attribute settings: */ +_memmap_cacheattr_wb_base = 0x00000110; +_memmap_cacheattr_wt_base = 0x00000110; +_memmap_cacheattr_bp_base = 0x00000220; +_memmap_cacheattr_unused_mask = 0xFFFFF00F; +_memmap_cacheattr_wb_trapnull = 0x2222211F; +_memmap_cacheattr_wba_trapnull = 0x2222211F; +_memmap_cacheattr_wbna_trapnull = 0x2222211F; +_memmap_cacheattr_wt_trapnull = 0x2222211F; +_memmap_cacheattr_bp_trapnull = 0x2222222F; +_memmap_cacheattr_wb_strict = 0xFFFFF11F; +_memmap_cacheattr_wt_strict = 0xFFFFF11F; +_memmap_cacheattr_bp_strict = 0xFFFFF22F; +_memmap_cacheattr_wb_allvalid = 0x22222112; +_memmap_cacheattr_wt_allvalid = 0x22222112; +_memmap_cacheattr_bp_allvalid = 0x22222222; +PROVIDE(_memmap_cacheattr_reset = _memmap_cacheattr_wb_trapnull); + +SECTIONS +{ + + .dport0.rodata : ALIGN(4) + { + _dport0_rodata_start = ABSOLUTE(.); + *(.dport0.rodata) + *(.dport.rodata) + _dport0_rodata_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .dport0.literal : ALIGN(4) + { + _dport0_literal_start = ABSOLUTE(.); + *(.dport0.literal) + *(.dport.literal) + _dport0_literal_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .dport0.data : ALIGN(4) + { + _dport0_data_start = ABSOLUTE(.); + *(.dport0.data) + *(.dport.data) + _dport0_data_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .data : ALIGN(4) + { + _data_start = ABSOLUTE(.); + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + *(.data1) + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + *(.jcr) + _data_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_phdr + + .rodata : ALIGN(4) + { + _rodata_start = ABSOLUTE(.); + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + *(.rodata1) + __XT_EXCEPTION_TABLE__ = ABSOLUTE(.); + *(.xt_except_table) + *(.gcc_except_table) + *(.gnu.linkonce.e.*) + *(.gnu.version_r) + *(.eh_frame) + /* C++ constructor and destructor tables, properly ordered: */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + /* C++ exception handlers table: */ + __XT_EXCEPTION_DESCS__ = ABSOLUTE(.); + *(.xt_except_desc) + *(.gnu.linkonce.h.*) + __XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.); + *(.xt_except_desc_end) + *(.dynamic) + *(.gnu.version_d) + . = ALIGN(4); /* this table MUST be 4-byte aligned */ + _bss_table_start = ABSOLUTE(.); + LONG(_bss_start) + LONG(_bss_end) + _bss_table_end = ABSOLUTE(.); + _rodata_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_phdr + + .bss ALIGN(8) (NOLOAD) : ALIGN(4) + { + . = ALIGN (8); + _bss_start = ABSOLUTE(.); + *(.dynsbss) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + *(.scommon) + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + *(.dynbss) + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN (8); + _bss_end = ABSOLUTE(.); + _heap_start = ABSOLUTE(.); +/* _stack_sentry = ALIGN(0x8); */ + } >dram0_0_seg :dram0_0_bss_phdr +/* __stack = 0x3ffc8000; */ + + .text : ALIGN(4) + { + _stext = .; + _text_start = ABSOLUTE(.); + *(.UserEnter.text) + . = ALIGN(16); + *(.DebugExceptionVector.text) + . = ALIGN(16); + *(.NMIExceptionVector.text) + . = ALIGN(16); + *(.KernelExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN(16); + *(.UserExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN(16); + *(.DoubleExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN (16); + *(.entry.text) + *(.init.literal) + *(.init) + *(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.fini.literal) + *(.fini) + *(.gnu.version) + _text_end = ABSOLUTE(.); + _etext = .; + } >iram1_0_seg :iram1_0_phdr + + .lit4 : ALIGN(4) + { + _lit4_start = ABSOLUTE(.); + *(*.lit4) + *(.lit4.*) + *(.gnu.linkonce.lit4.*) + _lit4_end = ABSOLUTE(.); + } >iram1_0_seg :iram1_0_phdr + + .irom0.text : ALIGN(4) + { + _irom0_text_start = ABSOLUTE(.); + *(.irom0.literal .irom.literal .irom.text.literal .irom0.text .irom.text) + _irom0_text_end = ABSOLUTE(.); + } >irom0_0_seg :irom0_0_phdr + +} + +/* get ROM code address */ +INCLUDE "rboot_rom.ld" diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.c b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.c new file mode 100644 index 0000000..4ac5df1 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.c @@ -0,0 +1,414 @@ +////////////////////////////////////////////////// +// rBoot open source boot loader for ESP8266. +// Copyright 2015 Richard A Burton +// richardaburton@gmail.com +// See license.txt for license terms. +////////////////////////////////////////////////// +/* clang-format off */ + +#ifdef RBOOT_INTEGRATION +#include +#endif + +#include "rboot-private.h" +#include + +static uint32 check_image(uint32 readpos) { + + uint8 buffer[BUFFER_SIZE]; + uint8 sectcount; + uint8 sectcurrent; + uint8 *writepos; + uint8 chksum = CHKSUM_INIT; + uint32 loop; + uint32 remaining; + uint32 romaddr; + + rom_header_new *header = (rom_header_new*)buffer; + section_header *section = (section_header*)buffer; + + if (readpos == 0 || readpos == 0xffffffff) { + return 0; + } + + // read rom header + if (SPIRead(readpos, header, sizeof(rom_header_new)) != 0) { + return 0; + } + + // check header type + if (header->magic == ROM_MAGIC) { + // old type, no extra header or irom section to skip over + romaddr = readpos; + readpos += sizeof(rom_header); + sectcount = header->count; + } else if (header->magic == ROM_MAGIC_NEW1 && header->count == ROM_MAGIC_NEW2) { + // new type, has extra header and irom section first + romaddr = readpos + header->len + sizeof(rom_header_new); +#ifdef BOOT_IROM_CHKSUM + // we will set the real section count later, when we read the header + sectcount = 0xff; + // just skip the first part of the header + // rest is processed for the chksum + readpos += sizeof(rom_header); +#else + // skip the extra header and irom section + readpos = romaddr; + // read the normal header that follows + if (SPIRead(readpos, header, sizeof(rom_header)) != 0) { + return 0; + } + sectcount = header->count; + readpos += sizeof(rom_header); +#endif + } else { + return 0; + } + + // test each section + for (sectcurrent = 0; sectcurrent < sectcount; sectcurrent++) { + + // read section header + if (SPIRead(readpos, section, sizeof(section_header)) != 0) { + return 0; + } + readpos += sizeof(section_header); + + // get section address and length + writepos = section->address; + remaining = section->length; + + while (remaining > 0) { + // work out how much to read, up to BUFFER_SIZE + uint32 readlen = (remaining < BUFFER_SIZE) ? remaining : BUFFER_SIZE; + // read the block + if (SPIRead(readpos, buffer, readlen) != 0) { + return 0; + } + // increment next read and write positions + readpos += readlen; + writepos += readlen; + // decrement remaining count + remaining -= readlen; + // add to chksum + for (loop = 0; loop < readlen; loop++) { + chksum ^= buffer[loop]; + } + } + +#ifdef BOOT_IROM_CHKSUM + if (sectcount == 0xff) { + // just processed the irom section, now + // read the normal header that follows + if (SPIRead(readpos, header, sizeof(rom_header)) != 0) { + return 0; + } + sectcount = header->count + 1; + readpos += sizeof(rom_header); + } +#endif + } + + // round up to next 16 and get checksum + readpos = readpos | 0x0f; + if (SPIRead(readpos, buffer, 1) != 0) { + return 0; + } + + // compare calculated and stored checksums + if (buffer[0] != chksum) { + return 0; + } + + return romaddr; +} + +#define ETS_UNCACHED_ADDR(addr) (addr) +#define READ_PERI_REG(addr) (*((volatile uint32 *)ETS_UNCACHED_ADDR(addr))) +#define WRITE_PERI_REG(addr, val) (*((volatile uint32 *)ETS_UNCACHED_ADDR(addr))) = (uint32)(val) +#define PERIPHS_RTC_BASEADDR 0x60000700 +#define REG_RTC_BASE PERIPHS_RTC_BASEADDR +#define RTC_GPIO_OUT (REG_RTC_BASE + 0x068) +#define RTC_GPIO_ENABLE (REG_RTC_BASE + 0x074) +#define RTC_GPIO_IN_DATA (REG_RTC_BASE + 0x08C) +#define RTC_GPIO_CONF (REG_RTC_BASE + 0x090) +#define PAD_XPD_DCDC_CONF (REG_RTC_BASE + 0x0A0) +static uint32 get_gpio16(void) { + // set output level to 1 + WRITE_PERI_REG(RTC_GPIO_OUT, (READ_PERI_REG(RTC_GPIO_OUT) & (uint32)0xfffffffe) | (uint32)(1)); + + // read level + WRITE_PERI_REG(PAD_XPD_DCDC_CONF, (READ_PERI_REG(PAD_XPD_DCDC_CONF) & 0xffffffbc) | (uint32)0x1); // mux configuration for XPD_DCDC and rtc_gpio0 connection + WRITE_PERI_REG(RTC_GPIO_CONF, (READ_PERI_REG(RTC_GPIO_CONF) & (uint32)0xfffffffe) | (uint32)0x0); //mux configuration for out enable + WRITE_PERI_REG(RTC_GPIO_ENABLE, READ_PERI_REG(RTC_GPIO_ENABLE) & (uint32)0xfffffffe); //out disable + + uint32 x = (READ_PERI_REG(RTC_GPIO_IN_DATA) & 1); + + return x; +} + +#ifdef BOOT_CONFIG_CHKSUM +// calculate checksum for block of data +// from start up to (but excluding) end +static uint8 calc_chksum(uint8 *start, uint8 *end) { + uint8 chksum = CHKSUM_INIT; + while(start < end) { + chksum ^= *start; + start++; + } + return chksum; +} +#endif + +#define UART_CLKDIV_26MHZ(B) (52000000 + B / 2) / B + +// prevent this function being placed inline with main +// to keep main's stack size as small as possible +// don't mark as static or it'll be optimised out when +// using the assembler stub +uint32 NOINLINE find_image(void) { + + uint8 flag; + uint32 runAddr; + uint32 flashsize; + int32 romToBoot; + uint8 gpio_boot = FALSE; + uint8 updateConfig = FALSE; + uint8 buffer[SECTOR_SIZE]; + + rboot_config *romconf = (rboot_config*)buffer; + rom_header *header = (rom_header*)buffer; + + // delay to slow boot (help see messages when debugging) + //ets_delay_us(2000000); + + uart_div_modify(0, UART_CLKDIV_26MHZ(115200)); + ets_delay_us(1000); + + ets_printf("\r\nrBoot v1.2.1 - richardaburton@gmail.com\r\n"); + + // read rom header + SPIRead(0, header, sizeof(rom_header)); + + // print and get flash size + ets_printf("Flash Size: "); + flag = header->flags2 >> 4; + if (flag == 0) { + ets_printf("4 Mbit\r\n"); + flashsize = 0x80000; + } else if (flag == 1) { + ets_printf("2 Mbit\r\n"); + flashsize = 0x40000; + } else if (flag == 2) { + ets_printf("8 Mbit\r\n"); + flashsize = 0x100000; + } else { +#ifdef BOOT_BIG_FLASH + if (flag == 3) { + ets_printf("16 Mbit\r\n"); + flashsize = 0x200000; + } else if (flag == 4) { + ets_printf("32 Mbit\r\n"); + flashsize = 0x400000; + } else if (flag == 8) { + ets_printf("64 Mbit\r\n"); + flashsize = 0x800000; + } else if (flag == 9) { + ets_printf("128 Mbit\r\n"); + flashsize = 0x1000000; + } else { + ets_printf("unknown\r\n"); + flashsize = 0x100000; // assume 8Mbit + } +#else + ets_printf("8 Mbit\r\n"); + flashsize = 0x100000; // limit to 8Mbit +#endif + } + + // print spi mode + ets_printf("Flash Mode: "); + if (header->flags1 == 0) { + ets_printf("QIO\r\n"); + } else if (header->flags1 == 1) { + ets_printf("QOUT\r\n"); + } else if (header->flags1 == 2) { + ets_printf("DIO\r\n"); + } else if (header->flags1 == 3) { + ets_printf("DOUT\r\n"); + } else { + ets_printf("unknown\r\n"); + } + + // print spi speed + ets_printf("Flash Speed: "); + flag = header->flags2 & 0x0f; + if (flag == 0) ets_printf("40 MHz\r\n"); + else if (flag == 1) ets_printf("26.7 MHz\r\n"); + else if (flag == 2) ets_printf("20 MHz\r\n"); + else if (flag == 0x0f) ets_printf("80 MHz\r\n"); + else ets_printf("unknown\r\n"); + + // print enabled options +#ifdef BOOT_BIG_FLASH + ets_printf("rBoot Option: Big flash\r\n"); +#endif +#ifdef BOOT_CONFIG_CHKSUM + ets_printf("rBoot Option: Config chksum\r\n"); +#endif +#ifdef BOOT_IROM_CHKSUM + ets_printf("rBoot Option: irom chksum\r\n"); +#endif + + ets_printf("\r\n"); + + // read boot config + SPIRead(BOOT_CONFIG_SECTOR * SECTOR_SIZE, buffer, SECTOR_SIZE); + // fresh install or old version? + if (romconf->magic != BOOT_CONFIG_MAGIC || romconf->version != BOOT_CONFIG_VERSION +#ifdef BOOT_CONFIG_CHKSUM + || romconf->chksum != calc_chksum((uint8*)romconf, (uint8*)&romconf->chksum) +#endif + ) { + /* Modified by Cesanta */ + ets_printf("Writing default boot config @ 0x%x.\r\n", BOOT_CONFIG_SECTOR * SECTOR_SIZE); + ets_memset(romconf, 0x00, sizeof(rboot_config)); + romconf->magic = BOOT_CONFIG_MAGIC; + romconf->version = BOOT_CONFIG_VERSION; + romconf->count = 2; + romconf->mode = MODE_STANDARD; + /* FWx_ADDR, FWx_FS_ADDR and FS_SIZE, FW_SIZE must be defined by -D */ + romconf->roms[0] = FW1_ADDR; + romconf->roms[1] = FW2_ADDR; + romconf->fs_addresses[0] = FW1_FS_ADDR; + romconf->fs_addresses[1] = FW2_FS_ADDR; + romconf->fs_sizes[0] = romconf->fs_sizes[1] = FS_SIZE; + romconf->roms_sizes[0] = romconf->roms_sizes[1] = FW_SIZE; +#ifdef BOOT_CONFIG_CHKSUM + romconf->chksum = calc_chksum((uint8*)romconf, (uint8*)&romconf->chksum); +#endif + // write new config sector + SPIEraseSector(BOOT_CONFIG_SECTOR); + SPIWrite(BOOT_CONFIG_SECTOR * SECTOR_SIZE, buffer, SECTOR_SIZE); + } + + // if gpio mode enabled check status of the gpio + if ((romconf->mode & MODE_GPIO_ROM) && (get_gpio16() == 0)) { + ets_printf("Booting GPIO-selected.\r\n"); + romToBoot = romconf->previous_rom; + /* + * Modified by Cesanta + * Make FD current + */ + updateConfig = TRUE; + romconf->fw_updated = 0; + romconf->is_first_boot = 0; + gpio_boot = TRUE; + } else if (romconf->current_rom >= romconf->count) { + // if invalid rom selected try rom 0 + ets_printf("Invalid rom selected, defaulting.\r\n"); + romToBoot = 0; + romconf->current_rom = 0; + romconf->fw_updated = 0; + romconf->is_first_boot = 0; + updateConfig = TRUE; + } else { + /* Modified by Cesanta */ + if (romconf->is_first_boot != 0) { + ets_printf("First boot, attempt %d\n", romconf->boot_attempts); + /* boot is unconfirmed */ + if (romconf->boot_attempts == 0) { + /* haven't try to load yes */ + ets_printf("Boot is unconfirmed\r\n"); + romconf->boot_attempts++; + } else { + ets_printf("Boot failed, fallback to fw #%d\r\n", + romconf->previous_rom); + romconf->current_rom = romconf->previous_rom; + /* clear fw update flag, to avoid post-update acttions */ + romconf->fw_updated = 0; + romconf->boot_attempts = 0; + } + + updateConfig = TRUE; + } + /* End of Cesanta modifications */ + // try rom selected in the config + romToBoot = romconf->current_rom; + } + + // try to find a good rom + do { + runAddr = check_image(romconf->roms[romToBoot]); + if (runAddr == 0) { + ets_printf("Rom %d is bad.\r\n", romToBoot); + if (gpio_boot) { + // don't switch to backup for gpio-selected rom + ets_printf("GPIO boot failed.\r\n"); + return 0; + } else { + // for normal mode try each previous rom + // until we find a good one or run out + updateConfig = TRUE; + romToBoot--; + if (romToBoot < 0) romToBoot = romconf->count - 1; + if (romToBoot == romconf->current_rom) { + // tried them all and all are bad! + ets_printf("No good rom available.\r\n"); + return 0; + } + } + } + } while (runAddr == 0); + + // re-write config, if required + if (updateConfig) { + romconf->current_rom = romToBoot; +#ifdef BOOT_CONFIG_CHKSUM + romconf->chksum = calc_chksum((uint8*)romconf, (uint8*)&romconf->chksum); +#endif + SPIEraseSector(BOOT_CONFIG_SECTOR); + SPIWrite(BOOT_CONFIG_SECTOR * SECTOR_SIZE, buffer, SECTOR_SIZE); + } + + ets_printf("Booting rom %d (0x%x).\r\n", romToBoot, romconf->roms[romToBoot]); + // copy the loader to top of iram + ets_memcpy((void*)_text_addr, _text_data, _text_len); + // return address to load from + return runAddr; + +} + +#ifdef BOOT_NO_ASM + +// small stub method to ensure minimum stack space used +void call_user_start(void) { + uint32 addr; + stage2a *loader; + + addr = find_image(); + if (addr != 0) { + loader = (stage2a*)entry_addr; + loader(addr); + } +} + +#else + +// assembler stub uses no stack space +// works with gcc +void call_user_start(void) { + __asm volatile ( + "mov a15, a0\n" // store return addr, hope nobody wanted a15! + "call0 find_image\n" // find a good rom to boot + "mov a0, a15\n" // restore return addr + "bnez a2, 1f\n" // ?success + "ret\n" // no, return + "1:\n" // yes... + "movi a3, entry_addr\n" // actually gives us a pointer to entry_addr + "l32i a3, a3, 0\n" // now really load entry_addr + "jx a3\n" // now jump to it + ); +} + +#endif diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.h b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.h new file mode 100644 index 0000000..3949d74 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.h @@ -0,0 +1,82 @@ +#ifndef CS_COMMON_PLATFORMS_ESP8266_RBOOT_RBOOT_RBOOT_H_ +#define CS_COMMON_PLATFORMS_ESP8266_RBOOT_RBOOT_RBOOT_H_ + +////////////////////////////////////////////////// +// rBoot open source boot loader for ESP8266. +// Copyright 2015 Richard A Burton +// richardaburton@gmail.com +// See license.txt for license terms. +////////////////////////////////////////////////// + +#ifdef __cplusplus +extern "C" { +#endif + +// uncomment to use only c code +// if you aren't using gcc you may need to do this +//#define BOOT_NO_ASM + +// uncomment to have a checksum on the boot config +//#define BOOT_CONFIG_CHKSUM + +// uncomment to enable big flash support (>1MB) +//#define BOOT_BIG_FLASH + +// uncomment to include .irom0.text section in the checksum +// roms must be built with esptool2 using -iromchksum option +//#define BOOT_IROM_CHKSUM + +// increase if required +#define MAX_ROMS 4 + +#define CHKSUM_INIT 0xef + +#define SECTOR_SIZE 0x1000 + +#ifndef BOOT_CONFIG_ADDR +#define BOOT_CONFIG_ADDR 0x1000 +#endif + +#define BOOT_CONFIG_SECTOR (BOOT_CONFIG_ADDR / SECTOR_SIZE) + +#define BOOT_CONFIG_MAGIC 0xe1 +#define BOOT_CONFIG_VERSION 0x01 + +#define MODE_STANDARD 0x00 +#define MODE_GPIO_ROM 0x01 + +// boot config structure +// rom addresses must be multiples of 0x1000 (flash sector aligned) +// without BOOT_BIG_FLASH only the first 8Mbit of the chip will be memory mapped +// so rom slots containing .irom0.text sections must remain below 0x100000 +// slots beyond this will only be accessible via spi read calls, so +// use these for stored resources, not code +// with BOOT_BIG_FLASH the flash will be mapped in chunks of 8MBit, so roms can +// be anywhere, but must not straddle two 8MBit blocks +typedef struct { + uint8 magic; // our magic + uint8 version; // config struct version + uint8 mode; // boot loader mode + uint8 current_rom; // currently selected rom + uint8 gpio_rom; // rom to use for gpio boot + uint8 count; // number of roms in use + uint8 previous_rom; // previously selected rom + uint8 is_first_boot; + uint8 boot_attempts; + uint8 fw_updated; + uint8 padding[2]; + uint32 roms[MAX_ROMS]; // flash addresses of the roms + uint32 roms_sizes[MAX_ROMS]; // sizes of the roms + uint32 fs_addresses[MAX_ROMS]; // file system addresses + uint32 fs_sizes[MAX_ROMS]; // file system sizes + uint32 user_flags; +#ifdef BOOT_CONFIG_CHKSUM + uint8 chksum; // config chksum +#endif +} rboot_config; + +#ifdef __cplusplus +} +#endif + +#endif /* CS_COMMON_PLATFORMS_ESP8266_RBOOT_RBOOT_RBOOT_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.ld b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.ld new file mode 100644 index 0000000..5e25740 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.ld @@ -0,0 +1,2 @@ +INCLUDE "eagle.app.v6.ld" +INCLUDE "rboot_rom.ld" diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.mk b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.mk new file mode 100644 index 0000000..f7f3987 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.mk @@ -0,0 +1,89 @@ +# +# Makefile for rBoot +# https://github.com/raburton/esp8266 +# + +ESPTOOL2 ?= ../../build/esptool2 +SDK_BASE ?= /opt/Espressif/ESP8266_SDK +SPI_SIZE ?= +RBOOT_INTEGRATION ?= +RBOOT_EXTRA_INCDIR ?= + +# RBOOT_BUILD_BASE and RBOOT_GEN_BASE should be provided via makefile parameters +RBOOT_BUILD_BASE ?= +# RBOOT_GEN_BASE is the directory for generated inputs +RBOOT_GEN_BASE ?= + +ifndef XTENSA_BINDIR +CC := xtensa-lx106-elf-gcc +LD := xtensa-lx106-elf-gcc +else +CC := $(addprefix $(XTENSA_BINDIR)/,xtensa-lx106-elf-gcc) +LD := $(addprefix $(XTENSA_BINDIR)/,xtensa-lx106-elf-gcc) +endif +CC_WRAPPER ?= + +CFLAGS = -Os -O3 -Wpointer-arith -Wundef -Werror -Wl,-EL \ + -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals \ + -D__ets__ -DIRAM='__attribute__((section(".fast.text")))' \ + -DNOINSTR='__attribute__((no_instrument_function))' \ + -DICACHE_FLASH $(CFLAGS_EXTRA) + +LDFLAGS = -nostdlib -Wl,--no-check-sections -u call_user_start -Wl,-static -L $(SDK_BASE)/ld/ + +LD_SCRIPT = rboot.ld + +E2_OPTS = -quiet -bin -boot0 + +ifeq ($(RBOOT_BIG_FLASH),1) + CFLAGS += -DBOOT_BIG_FLASH +endif +ifeq ($(RBOOT_INTEGRATION),1) + CFLAGS += -DRBOOT_INTEGRATION +endif +ifeq ($(SPI_SIZE), 256K) + E2_OPTS += -256 +else ifeq ($(SPI_SIZE), 512K) + E2_OPTS += -512 +else ifeq ($(SPI_SIZE), 1M) + E2_OPTS += -1024 +else ifeq ($(SPI_SIZE), 2M) + E2_OPTS += -2048 +else ifeq ($(SPI_SIZE), 4M) + E2_OPTS += -4096 +endif + +RBOOT_EXTRA_INCDIR := $(addprefix -I,$(RBOOT_EXTRA_INCDIR)) + +.SECONDARY: + +all: $(RBOOT_BUILD_BASE)/rboot.bin + +$(RBOOT_BUILD_BASE)/rboot-stage2a.o: rboot-stage2a.c rboot-private.h rboot.h + @echo "CC $<" + @$(CC_WRAPPER) $(CC) $(CFLAGS) $(RBOOT_EXTRA_INCDIR) -c $< -o $@ + +$(RBOOT_BUILD_BASE)/rboot-stage2a.elf: $(RBOOT_BUILD_BASE)/rboot-stage2a.o + @echo "LD $@" + @$(CC_WRAPPER) $(LD) -Trboot-stage2a.ld $(LDFLAGS) -Wl,--start-group $^ -Wl,--end-group -o $@ + +$(RBOOT_GEN_BASE)/rboot-hex2a.h: $(RBOOT_BUILD_BASE)/rboot-stage2a.elf + @echo "E2 $@" + @$(ESPTOOL2) -quiet -header $< $@ .text + +$(RBOOT_BUILD_BASE)/rboot.o: rboot.c rboot-private.h rboot.h $(RBOOT_GEN_BASE)/rboot-hex2a.h + @echo "CC $<" + @$(CC) $(CFLAGS) -I$(RBOOT_GEN_BASE) $(RBOOT_EXTRA_INCDIR) -c $< -o $@ + +$(RBOOT_BUILD_BASE)/%.o: %.c %.h + @echo "CC $<" + @$(CC) $(CFLAGS) $(RBOOT_EXTRA_INCDIR) -c $< -o $@ + +$(RBOOT_BUILD_BASE)/%.elf: $(RBOOT_BUILD_BASE)/%.o + @echo "LD $@" + @$(LD) -T$(LD_SCRIPT) $(LDFLAGS) -Wl,--start-group $^ -Wl,--end-group -o $@ + +$(RBOOT_BUILD_BASE)/%.bin: $(RBOOT_BUILD_BASE)/%.elf + @echo "E2 $@" + @$(ESPTOOL2) $(E2_OPTS) $< $@ .text .rodata + diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot_rom.ld b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot_rom.ld new file mode 100644 index 0000000..69e862c --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot_rom.ld @@ -0,0 +1,8 @@ +PROVIDE ( ets_delay_us = 0x40002ecc ); +PROVIDE ( ets_memcpy = 0x400018b4 ); +PROVIDE ( ets_memset = 0x400018a4 ); +PROVIDE ( ets_printf = 0x400024cc ); +PROVIDE ( SPIEraseSector = 0x40004a00 ); +PROVIDE ( SPIRead = 0x40004b1c ); +PROVIDE ( SPIWrite = 0x40004a4c ); +PROVIDE ( uart_div_modify = 0x400039d8 ); diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/readme-api.txt b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/readme-api.txt new file mode 100644 index 0000000..2304748 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/readme-api.txt @@ -0,0 +1,40 @@ +rBoot - User API for rBoot on the ESP8266 +------------------------------------------ +by Richard A Burton, richardaburton@gmail.com +http://richard.burtons.org/ + +This provides a few simple APIs for getting & setting rBoot config and for +writing data from OTA updates. API source files are in the appcode directory. + +Actual OTA network code is implementation specific and no longer included in +rBoot itself, see the rBoot sample projects for this code (which you can then +use in your own projects). + + rboot_config rboot_get_config(); + Returns rboot_config (defined in rboot.h) allowing you to modify any values + in it, including the rom layout. + + bool rboot_set_config(rboot_config *conf); + Saves the rboot_config structure back to sector 2 of the flash, while + maintaining the contents of the rest of the sector. You can use the rest of + this sector for your app settings, as long as you protect this structure + when you do so. + + uint8 rboot_get_current_rom(); + Get the currently selected boot rom (the currently running rom, as long as + you haven't changed it since boot). + + bool rboot_set_current_rom(uint8 rom); + Set the current boot rom, which will be used when next restarted. + + rboot_write_status rboot_write_init(uint32 start_addr); + Call once before starting to pass data to write to the flash. start_addr is + the address on the SPI flash to write from. Returns a status structure which + must be passed back on each write. The contents of the structure should not + be modified by the calling code. + + bool rboot_write_flash(rboot_write_status *status, uint8 *data, uint16 len); + Call repeatedly to write data to the flash, starting at the address + specified on the prior call to rboot_write_init. Current write position is + tracked automatically. This method is likely to be called each time a packet + of OTA data is received over the network. diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/readme.txt b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/readme.txt new file mode 100644 index 0000000..944e1f2 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/readme.txt @@ -0,0 +1,218 @@ +rBoot - An open source boot loader for the ESP8266 +-------------------------------------------------- +by Richard A Burton, richardaburton@gmail.com +http://richard.burtons.org/ + + +rBoot is designed to be a flexible open source boot loader, a replacement for +the binary blob supplied with the SDK. It has the following advantages over the +Espressif loader: + + - Open source (written in C). + - Supports up to 256 roms. + - Roms can be variable size. + - Able to test multiple roms to find a valid backup (without resetting). + - Flash layout can be changed on the fly (with care and appropriately linked + rom images). + - GPIO support for rom selection. + - Wastes no stack space (SDK boot loader uses 144 bytes). + - Documented config structure to allow easy editing from user code. + - Can validate .irom0.text section with checksum. + +Limitations +----------- +The ESP8266 can only map 8Mbits (1MB) of flash to memory, but which 8Mbits to +map is selectable. This allows individual roms to be up to 1MB in size, so long +as they do not straddle an 8Mbit boundary on the flash. This means you could +have four 1MB roms or 8 512KB roms on a 32Mbit flash (such as on the ESP-12), or +a combination. Note, however, that you could not have, for example, a 512KB rom +followed immediately by a 1MB rom because the 2nd rom would then straddle an +8MBit boundary. By default support for using more than the first 8Mbit of the +flash is disabled, because it requires several steps to get it working. See +below for instructions. + +Building +-------- +A Makefile is included, which should work with the gcc xtensa cross compiler. +There are two source files, the first is compiled and included as data in the +second. When run this code is copied to memory and executed (there is a good +reason for this, see my blog for an explanation). The make file will handle this +for you, but you'll need my esptool2 (see github). + +To use the Makefile set SDK_BASE to point to the root of the Espressif SDK and +either set XTENSA_BINDIR to the gcc xtensa bin directory or include it in your +PATH. These can be set as environment variables or by editing the Makefile. + +Two small assembler stub functions allow the bootloader to launch the user code +without reserving any space on the stack (while the SDK boot loader uses 144 +bytes). This compiles fine with GCC, but if you use another compiler and it +will not compile/work for you then uncomment the #define BOOT_NO_ASM in rboot.h +to use a C version of these functions (this uses 32 bytes). + +Tested with SDK v1.3 and GCC v4.8.2. + +Installation +------------ +Simply write rboot.bin to the first sector of the flash. Remember to set your +flash size correctly with your chosen flash tool (e.g. for esptool.py use the +-fs option). When run rBoot will create it's own config at the start of sector +two for a simple two rom system. You can can then write your two roms to flash +addresses 0x2000 and (half chip size + 0x2000). E.g. for 8Mbit flash: + esptool.py -fs 8m 0x0000 rboot.bin 0x2000 user1.bin 0x82000 user2.bin + +Note: your device may need other options specified. E.g. The nodemcu devkit v1.0 +(commonly, but incorrectly, sold as v2) also needs the "-fm dio" option. + +For more interesting rom layouts you'll need to write an rBoot config sector +manually, see next step. + +The two testload bin files can be flashed in place of normal user roms for +testing rBoot. You do not need these for normal use. + +rBoot Config +------------ +typedef struct { + uint8 magic; // our magic + uint8 version; // config struct version + uint8 mode; // boot loader mode + uint8 current_rom; // currently selected rom + uint8 gpio_rom; // rom to use for gpio boot + uint8 count; // number of roms in use + uint8 unused[2]; // padding + uint32 roms[MAX_ROMS]; // flash addresses of the roms +#ifdef BOOT_CONFIG_CHKSUM + uint8 chksum; // boot config chksum +#endif +} rboot_config; + +Write a config structure as above to address 0x1000 on the flash. If you want +more than 4 roms (default) just increase MAX_ROMS when you compile rBoot. +Think about how you intend to layout your flash before you start! +Rom addresses must be sector aligned i.e start on a multiple of 4096. + + - magic should have value 0xe1 (defined as BOOT_CONFIG_MAGIC). + - version is used in case the config structure changes after deployment. It is + defined as 0x01 (BOOT_CONFIG_VERSION). I don't intend to increase this, but + you should if you choose to reflash the bootloader after deployment and + the config structure has changed. + - mode can be 0x00 (MODE_STANDARD) or 0x01 (MODE_GPIO_ROM). If you set GPIO + you will need to set gpio_rom as well. The sample GPIO code uses GPIO 16 on + a nodemcu dev board, if you want to use a different GPIO you'll need to + adapt the code in rBoot slightly. + - current_rom is the rom to boot, numbered 0 to count-1. + - gpio_rom is the rom to boot when the GPIO is triggered at boot. + - count is the number of roms available (may be less than MAX_ROMS, but not + more). + - unused[2] is padding so the uint32 rom addresses are 4 bytes aligned. + - roms is the array of flash address for the roms. The default generated + config will contain two entries: 0x00002000 and 0x00082000. + - chksum (if enabled, not by deafult) should be the xor of 0xef followed by + each of the bytes of the config structure up to (but obviously not + including) the chksum byte itself. + +Linking user code +----------------- +Each rom will need to be linked with an appropriate linker file, specifying +where it will reside on the flash. If you are only flashing one rom to multiple +places on the flash it must be linked multiple times to produce the set of rom +images. This is the same as with the SDK loader. + +Because there are endless possibilities for layout with this loader I don't +supply sample linker files. Instead I'll tell you how to make them. + +For each rom slot on the flash take a copy of the eagle.app.v6.ld linker script +from the sdk. You then need to modify just one line in it for each rom: + irom0_0_seg : org = 0x40240000, len = 0x3C000 + +Change the org address to be 0x40200000 (base memory mapped location of the +flash) + flash address + 0x10 (offset of data after the header). +The logical place for your first rom is the third sector, address 0x2000. + 0x40200000 + 0x2000 + 0x10 = 0x40202010 +If you use the default generated config the loader will expect to find the +second rom at flash address half-chip-size + 0x2000 (e.g. 0x82000 on an 8MBit +flash) so the irom0_0_seg should be: + 0x40200000 + 0x82000 + 0x10 = 0x40282010 +Due to the limitation of mapped flash (max 8MBit) if you use a larger chip and +do not have big flash support enabled the second rom in the default config will +still be placed at 0x082000, not truly half-chip-size + 0x2000. +Ideally you should also adjust the len to help detect over sized sections at +link time, but more important is the overall size of the rom which you need to +ensure fits in the space you have allocated for it in your flash layout plan. + +Then simply compile and link as you would normally for OTA updates with the SDK +boot loader, except using the linker scripts you've just prepared rather than +the ones supplied with the SDK. Remember when building roms to create them as +'new' type roms (for use with SDK boot loader v1.2+). Or if using my esptool2 +use the -boot2 option. Note: the test loads included with rBoot are built with +-boot0 because they do not contain a .irom0.text section (and so the value of +irom0_0_seg in the linker file is irrelevant to them) but 'normal' user apps +always do. + +irom checksum +------------- +The SDK boot loader checksum only covers sections loaded into ram (data and some +code). Most of the SDK and user code remains on the flash and that is not +included in the checksum. This means you could attempt to boot a corrupt rom +and, because it looks ok to the boot loader, there will be no attempt to switch +to a backup rom. rBoot improves on this by allowing the .irom0.text section to +be included in the checksum. To enable this uncomment #define BOOT_IROM_CHKSUM +in rboot.h and build your roms with esptool2 using the -iromchksum option. + +Big flash support +----------------- +This only needs to be enabled if you wish to be able to memory map more than the +first 8MBit of the flash. Note you can still only map 8Mbit at a time. Use this +if you want to have multiple 1MB roms, or more smaller roms than will fit in +8Mbits. If you have a large flash but only need, for example, two 512KB roms you +do not need to enable this mode. + +Support in rBoot is enabled by uncommenting the #define BOOT_BIG_FLASH in +rboot.h. + +Thinking about your linker files is either simpler or more complicated, +depending on your usage of the flash. If you intend to use multiple 1MB roms you +will only need one linker file and you only need to link once for OTA updates. +Although when you perform an OTA update the rom will be written to a different +position on the flash, each 8Mbit of flash is mapped (separately) to 0x40200000. +So when any given rom is run the code will appear at the same place in memory +regardless of where it is on the flash. Your base address for the linker would +be 0x40202010. (Actually all but the first rom could base at 0x40200010 (because +they don't need to leave space for rBoot and config) but then you're just making +it more complicated again!) +If you wanted eight 512KB roms you would need two linker files - one for the +first half of any given 8Mbits of flash and another for the second half. Just +remember you are really laying out within a single 8MBit area, which can then be +replicated multiple times on the flash. + +Now the clever bit - rBoot needs to hijack the memory mapping code to select +which 8Mbits gets mapped. There is no API for this, but we can override the SDK +function. First we need to slightly modify the SDK library libmain.a, like so: + + xtensa-lx106-elf-objcopy -W Cache_Read_Enable_New libmain.a libmain2.a + +This produces a version of libmain with a 'weakened' Cache_Read_Enable_New +function, which we can then override with our own. Modify your Makefile to link +against the library main2 instead of main. +Next add rboot-bigflash.c (from the appcode directory) & rboot.h to your project +- this adds the replacement Cache_Read_Enable_New to your code. + +Getting gcc to apply the override correctly can be slightly tricky (I'm not sure +why, it shouldn't be). One option is to add "-u Cache_Read_Enable_New" to your +LD_FLAGS and change the order of objects on the LD command so your objects/.a +file is before the libraries. Another way that seems easier was to #include +rboot-bigflash.c into the main .c file, rather than compiling it to a separate +object file. I can't make any sense of that, but I suggest you uncomment the +message in the Cache_Read_Enable_New function when you first build with it, to +make sure you are getting your version into the rom. + +Now when rBoot starts your rom, the SDK code linked in it that normally performs +the memory mapping will delegate part of that task to rBoot code (linked in your +rom, not in rBoot itself) to choose which part of the flash to map. + +Integration into other frameworks +--------------------------------- +If you wish to integrate rBoot into a development framework (e.g. Sming) you +can set the define RBOOT_INTEGRATION and at compile time the file +rboot-integration.h will be included into the source. This should allow you to +set some platform specific options without having to modify the source of rBoot +which makes it easier to integrate and maintain. diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rom/.gitattributes b/src/mongoose-6.11/src/common/platforms/esp8266/rom/.gitattributes new file mode 100644 index 0000000..b2dd225 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rom/.gitattributes @@ -0,0 +1,2 @@ +rom.bin -nodiff +rom.elf -nodiff diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rom/disasm.sh b/src/mongoose-6.11/src/common/platforms/esp8266/rom/disasm.sh new file mode 100755 index 0000000..50c06f2 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rom/disasm.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +xtensa-lx106-elf-gcc -Wl,-N,-Ttext,0x40000000 -nostdlib rom.S -o rom.elf && \ + xtensa-lx106-elf-objdump -d rom.elf > ESP8266_ROM.txt diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rom/rom.S b/src/mongoose-6.11/src/common/platforms/esp8266/rom/rom.S new file mode 100644 index 0000000..ac27596 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rom/rom.S @@ -0,0 +1,101 @@ +.text +.org 0 +.globl _start + +// xtensa-esp108-elf-gcc -Wl,-N,-Ttext,0x40000000 -nostdlib rom.S -o rom.elf + +here = . +#define PROVIDE(name, addr) name = here + addr - 0x40000000 + +#include "rom_functions.S" + +PROVIDE(SPI_chip_erase, 0x40004080) +PROVIDE(SPIFlashModeConfig, 0x40004568) + +PROVIDE(_c_0x80000000, 0x400003a4) +PROVIDE(_c_0x00400000, 0x40000414) +PROVIDE(_c_0x00ffffff, 0x40000418) +PROVIDE(_p_user_start, 0x40000fb8) // 0x3fffdcd0 +PROVIDE(_c_0x60000200, 0x40000fc4) +PROVIDE(_s_ets_build, 0x40000fc8) // Jan 8 2013 +PROVIDE(_s_ets_banner, 0x40000fcc) // ets %s,rst cause:%d, boot mode:(%d,%d) +PROVIDE(_s_format_s_s, 0x40000fd0) // %s %s +PROVIDE(_s_ets_main_c, 0x40000fd4) // ets_main.c +PROVIDE(_s_user_code_done, 0x40000fe0) // user code done +PROVIDE(_s_wdt_reset, 0x40000fe4) // wdt reset +PROVIDE(_s_unk_reset, 0x40000fe8) // unknown reset +PROVIDE(_l_boot_mode_6, 0x40001044) +PROVIDE(_l_boot_mode_0_1_2_3, 0x4000104a) +PROVIDE(_l_rst_cause_ge_3, 0x40001075) +PROVIDE(_l_rst_cause_ge_3_lt_7, 0x40001084) +PROVIDE(_l_rst_cause_3_soft_wdt, 0x4000108a) +PROVIDE(_l_load_from_flash, 0x400010a8) +PROVIDE(_l_run_user_code, 0x400010be) +PROVIDE(_l_wdt_reset, 0x40001118) +PROVIDE(_l_rst_cause_1_2, 0x40001121) +PROVIDE(_l_rst_cause_unknown, 0x40001130) +PROVIDE(_l_boot_mode_7, 0x40001148) +PROVIDE(_l_boot_mode_4, 0x40001150) +PROVIDE(_c_0x60000600, 0x4000115c) +PROVIDE(_s_waiting_for_host, 0x4000119c) // waiting for host +PROVIDE(_x_unk1160, 0x40001160) +PROVIDE(_c_0x3fffa000_uart_buf, 0x400011a0) +PROVIDE(_c_0x2000, 0x400011a4) +PROVIDE(_c_0x40100000, 0x400011a8) // default user_start +PROVIDE(_x_boot2, 0x400011ac) // arg: 0x3fffdcd0 (&user_start) +PROVIDE(_l_strapping_2_eq_0, 0x400011d2) +PROVIDE(_l_124d, 0x4000124d) +PROVIDE(_l_boot_mode2_eq_2, 0x40001265) +PROVIDE(_l_boot_mode_eq_2, 0x40001268) +PROVIDE(_l_boot_mode_eq_1, 0x4000127a) +PROVIDE(_l_setup_uart, 0x40001291) +PROVIDE(_l_boot_mode_eq_3, 0x400012b0) +PROVIDE(_x_load_from_flash, 0x40001308) +PROVIDE(_c_0x00001000, 0x40001994) +PROVIDE(_c_0xffdfffff, 0x400025dc) +PROVIDE(_l_rr_not_dsleep, 0x40002624) +PROVIDE(_c_100000, 0x40002664) +PROVIDE(_c_0x3feffe00, 0x40002e5c) +PROVIDE(_p_cpu_freq, 0x40002ec8) +PROVIDE(_x_wdt_interval, 0x40002f14) // arg: 3 -> 11, 6 -> 12, 12 -> 13 +PROVIDE(_p_wdt_cfg, 0x40002f30) +PROVIDE(_wdt_soft_timer_fn2, 0x40002f3c) +PROVIDE(_l_wdt_soft_timer_fn2_exit, 0x40002f59) +PROVIDE(_l_wdt_soft_timer_fn2_feed, 0x40002f60) +PROVIDE(_l_wdt_soft_timer_fn2_mode2_stage1_feed, 0x40002f74) +PROVIDE(_wdt_soft_timer_fn, 0x40002f88) +PROVIDE(_p_wdt_soft_timer_fn, 0x40002f98) +PROVIDE(_p_wdt_soft_timer, 0x40002f9c) // 0x3fffdde0 +PROVIDE(_l_skip_wdt_imask, 0x40002fc4) +PROVIDE(_l_wdt_enable_and_exit, 0x40002fea) +PROVIDE(_l_wdt_mode_1, 0x4000309c) +PROVIDE(_l_wdt_mode_2_4, 0x4000300a) +PROVIDE(_l_wdt_mode_3, 0x40003060) +PROVIDE(_l_recv_req, 0x400033a1) +PROVIDE(_l_send_response, 0x400033be) +PROVIDE(_l_send_resp_pkt, 0x400033da) +PROVIDE(_l_process_req, 0x40003424) +PROVIDE(_l_34a3, 0x400034a3) +PROVIDE(_l_34b9, 0x400034b9) +PROVIDE(_c_0x00001800, 0x40003534) +PROVIDE(_c_0x60000a00, 0x40003f54) +PROVIDE(_c_0x01000000, 0x400040bc) +PROVIDE(_x_SPI_erase_sector, 0x400040c0) +PROVIDE(_l_SPI_erase_sector_align_ok, 0x400040dc) +PROVIDE(_c_0x00800000, 0x4000411c) +PROVIDE(_x_SPI_erase_block, 0x40004120) +PROVIDE(_c_0x20000000, 0x4000416c) +PROVIDE(_c_0x08000000, 0x400043c4) +PROVIDE(_c_0x04000000, 0x400043fc) +PROVIDE(_c_0x40000000, 0x40004438) +PROVIDE(_p_flashchip, 0x40004874) +PROVIDE(_s_bootup, 0x400054cc) // bootup , addr 0x%08x +PROVIDE(_p_sip_ctx, 0x40005130) +PROVIDE(_l_sip_cmd_out, 0x4000550a) +PROVIDE(_c_0x00010000, 0x40005de0) + +.text + +_start: +.incbin "rom.bin" +_end: diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rom/rom.bin b/src/mongoose-6.11/src/common/platforms/esp8266/rom/rom.bin new file mode 100644 index 0000000..fe4d195 Binary files /dev/null and b/src/mongoose-6.11/src/common/platforms/esp8266/rom/rom.bin differ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rom/rom.elf b/src/mongoose-6.11/src/common/platforms/esp8266/rom/rom.elf new file mode 100755 index 0000000..6a21116 Binary files /dev/null and b/src/mongoose-6.11/src/common/platforms/esp8266/rom/rom.elf differ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rom/rom_functions.S b/src/mongoose-6.11/src/common/platforms/esp8266/rom/rom_functions.S new file mode 100644 index 0000000..bf86dad --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rom/rom_functions.S @@ -0,0 +1,347 @@ +PROVIDE ( Cache_Read_Disable , 0x400047f0 ); +PROVIDE ( Cache_Read_Enable , 0x40004678 ); +PROVIDE ( FilePacketSendReqMsgProc , 0x400035a0 ); +PROVIDE ( FlashDwnLdParamCfgMsgProc , 0x4000368c ); +PROVIDE ( FlashDwnLdStartMsgProc , 0x40003538 ); +PROVIDE ( FlashDwnLdStopReqMsgProc , 0x40003658 ); +PROVIDE ( GetUartDevice , 0x40003f4c ); +PROVIDE ( MD5Final , 0x40009900 ); +PROVIDE ( MD5Init , 0x40009818 ); +PROVIDE ( MD5Update , 0x40009834 ); +PROVIDE ( MemDwnLdStartMsgProc , 0x400036c4 ); +PROVIDE ( MemDwnLdStopReqMsgProc , 0x4000377c ); +PROVIDE ( MemPacketSendReqMsgProc , 0x400036f0 ); +PROVIDE ( RcvMsg , 0x40003eac ); +PROVIDE ( SHA1Final , 0x4000b648 ); +PROVIDE ( SHA1Init , 0x4000b584 ); +PROVIDE ( SHA1Transform , 0x4000a364 ); +PROVIDE ( SHA1Update , 0x4000b5a8 ); +PROVIDE ( SPI_read_status , 0x400043c8 ); +PROVIDE ( SPI_write_status , 0x40004400 ); +PROVIDE ( SPI_write_enable , 0x4000443c ); +PROVIDE ( Wait_SPI_Idle , 0x4000448c ); +PROVIDE ( SPIEraseArea , 0x40004b44 ); +PROVIDE ( SPIEraseBlock , 0x400049b4 ); +PROVIDE ( SPIEraseChip , 0x40004984 ); +PROVIDE ( SPIEraseSector , 0x40004a00 ); +PROVIDE ( SPILock , 0x400048a8 ); +PROVIDE ( SPIParamCfg , 0x40004c2c ); +PROVIDE ( SPIRead , 0x40004b1c ); +PROVIDE ( SPIReadModeCnfig , 0x400048ec ); +PROVIDE ( SPIUnlock , 0x40004878 ); +PROVIDE ( SPIWrite , 0x40004a4c ); +PROVIDE ( SelectSpiFunction , 0x40003f58 ); +PROVIDE ( SendMsg , 0x40003cf4 ); +PROVIDE ( UartConnCheck , 0x40003230 ); +PROVIDE ( UartConnectProc , 0x400037a0 ); +PROVIDE ( UartDwnLdProc , 0x40003368 ); +PROVIDE ( UartGetCmdLn , 0x40003ef4 ); +PROVIDE ( UartRegReadProc , 0x4000381c ); +PROVIDE ( UartRegWriteProc , 0x400037ac ); +PROVIDE ( UartRxString , 0x40003c30 ); +PROVIDE ( Uart_Init , 0x40003a14 ); +PROVIDE ( _DebugExceptionVector , 0x40000010 ); +PROVIDE ( _DoubleExceptionVector , 0x40000070 ); +PROVIDE ( _KernelExceptionVector , 0x40000030 ); +PROVIDE ( _NMIExceptionVector , 0x40000020 ); +PROVIDE ( _ResetHandler , 0x400000a4 ); +PROVIDE ( _ResetVector , 0x40000080 ); +PROVIDE ( _UserExceptionVector , 0x40000050 ); +PROVIDE ( __adddf3 , 0x4000c538 ); +PROVIDE ( __addsf3 , 0x4000c180 ); +PROVIDE ( __divdf3 , 0x4000cb94 ); +PROVIDE ( __divdi3 , 0x4000ce60 ); +PROVIDE ( __divsi3 , 0x4000dc88 ); +PROVIDE ( __extendsfdf2 , 0x4000cdfc ); +PROVIDE ( __fixdfsi , 0x4000ccb8 ); +PROVIDE ( __fixunsdfsi , 0x4000cd00 ); +PROVIDE ( __fixunssfsi , 0x4000c4c4 ); +PROVIDE ( __floatsidf , 0x4000e2f0 ); +PROVIDE ( __floatsisf , 0x4000e2ac ); +PROVIDE ( __floatunsidf , 0x4000e2e8 ); +PROVIDE ( __floatunsisf , 0x4000e2a4 ); +PROVIDE ( __muldf3 , 0x4000c8f0 ); +PROVIDE ( __muldi3 , 0x40000650 ); +PROVIDE ( __mulsf3 , 0x4000c3dc ); +PROVIDE ( __subdf3 , 0x4000c688 ); +PROVIDE ( __subsf3 , 0x4000c268 ); +PROVIDE ( __truncdfsf2 , 0x4000cd5c ); +PROVIDE ( __udivdi3 , 0x4000d310 ); +PROVIDE ( __udivsi3 , 0x4000e21c ); +PROVIDE ( __umoddi3 , 0x4000d770 ); +PROVIDE ( __umodsi3 , 0x4000e268 ); +PROVIDE ( __umulsidi3 , 0x4000dcf0 ); +PROVIDE ( _rom_store , 0x4000e388 ); +PROVIDE ( _rom_store_table , 0x4000e328 ); +PROVIDE ( _start , 0x4000042c ); +PROVIDE ( _xtos_alloca_handler , 0x4000dbe0 ); +PROVIDE ( _xtos_c_wrapper_handler , 0x40000598 ); +PROVIDE ( _xtos_cause3_handler , 0x40000590 ); +PROVIDE ( _xtos_ints_off , 0x4000bda4 ); +PROVIDE ( _xtos_ints_on , 0x4000bd84 ); +PROVIDE ( _xtos_l1int_handler , 0x4000048c ); +PROVIDE ( _xtos_p_none , 0x4000dbf8 ); +PROVIDE ( _xtos_restore_intlevel , 0x4000056c ); +PROVIDE ( _xtos_return_from_exc , 0x4000dc54 ); +PROVIDE ( _xtos_set_exception_handler , 0x40000454 ); +PROVIDE ( _xtos_set_interrupt_handler , 0x4000bd70 ); +PROVIDE ( _xtos_set_interrupt_handler_arg , 0x4000bd28 ); +PROVIDE ( _xtos_set_intlevel , 0x4000dbfc ); +PROVIDE ( _xtos_set_min_intlevel , 0x4000dc18 ); +PROVIDE ( _xtos_set_vpri , 0x40000574 ); +PROVIDE ( _xtos_syscall_handler , 0x4000dbe4 ); +PROVIDE ( _xtos_unhandled_exception , 0x4000dc44 ); +PROVIDE ( _xtos_unhandled_interrupt , 0x4000dc3c ); +PROVIDE ( aes_decrypt , 0x400092d4 ); +PROVIDE ( aes_decrypt_deinit , 0x400092e4 ); +PROVIDE ( aes_decrypt_init , 0x40008ea4 ); +PROVIDE ( aes_unwrap , 0x40009410 ); +PROVIDE ( base64_decode , 0x40009648 ); +PROVIDE ( base64_encode , 0x400094fc ); +PROVIDE ( bzero , 0x4000de84 ); +PROVIDE ( cmd_parse , 0x40000814 ); +PROVIDE ( conv_str_decimal , 0x40000b24 ); +PROVIDE ( conv_str_hex , 0x40000cb8 ); +PROVIDE ( convert_para_str , 0x40000a60 ); +PROVIDE ( dtm_get_intr_mask , 0x400026d0 ); +PROVIDE ( dtm_params_init , 0x4000269c ); +PROVIDE ( dtm_set_intr_mask , 0x400026c8 ); +PROVIDE ( dtm_set_params , 0x400026dc ); +PROVIDE ( eprintf , 0x40001d14 ); +PROVIDE ( eprintf_init_buf , 0x40001cb8 ); +PROVIDE ( eprintf_to_host , 0x40001d48 ); +PROVIDE ( est_get_printf_buf_remain_len , 0x40002494 ); +PROVIDE ( est_reset_printf_buf_len , 0x4000249c ); +PROVIDE ( ets_bzero , 0x40002ae8 ); +PROVIDE ( ets_char2xdigit , 0x40002b74 ); +PROVIDE ( ets_delay_us , 0x40002ecc ); +PROVIDE ( ets_enter_sleep , 0x400027b8 ); +PROVIDE ( ets_external_printf , 0x40002578 ); +PROVIDE ( ets_get_cpu_frequency , 0x40002f0c ); +PROVIDE ( ets_getc , 0x40002bcc ); +PROVIDE ( ets_install_external_printf , 0x40002450 ); +PROVIDE ( ets_install_putc1 , 0x4000242c ); +PROVIDE ( ets_install_putc2 , 0x4000248c ); +PROVIDE ( ets_install_uart_printf , 0x40002438 ); +PROVIDE ( ets_intr_lock , 0x40000f74 ); +PROVIDE ( ets_intr_unlock , 0x40000f80 ); +PROVIDE ( ets_isr_attach , 0x40000f88 ); +PROVIDE ( ets_isr_mask , 0x40000f98 ); +PROVIDE ( ets_isr_unmask , 0x40000fa8 ); +PROVIDE ( ets_memcmp , 0x400018d4 ); +PROVIDE ( ets_memcpy , 0x400018b4 ); +PROVIDE ( ets_memmove , 0x400018c4 ); +PROVIDE ( ets_memset , 0x400018a4 ); +PROVIDE ( ets_post , 0x40000e24 ); +PROVIDE ( ets_printf , 0x400024cc ); +PROVIDE ( ets_putc , 0x40002be8 ); +PROVIDE ( ets_rtc_int_register , 0x40002a40 ); +PROVIDE ( ets_run , 0x40000e04 ); +PROVIDE ( ets_set_idle_cb , 0x40000dc0 ); +PROVIDE ( ets_set_user_start , 0x40000fbc ); +PROVIDE ( ets_str2macaddr , 0x40002af8 ); +PROVIDE ( ets_strcmp , 0x40002aa8 ); +PROVIDE ( ets_strcpy , 0x40002a88 ); +PROVIDE ( ets_strlen , 0x40002ac8 ); +PROVIDE ( ets_strncmp , 0x40002ab8 ); +PROVIDE ( ets_strncpy , 0x40002a98 ); +PROVIDE ( ets_strstr , 0x40002ad8 ); +PROVIDE ( ets_task , 0x40000dd0 ); +PROVIDE ( ets_timer_arm , 0x40002cc4 ); +PROVIDE ( ets_timer_disarm , 0x40002d40 ); +PROVIDE ( ets_timer_done , 0x40002d80 ); +PROVIDE ( ets_timer_handler_isr , 0x40002da8 ); +PROVIDE ( ets_timer_init , 0x40002e68 ); +PROVIDE ( ets_timer_setfn , 0x40002c48 ); +PROVIDE ( ets_uart_printf , 0x40002544 ); +PROVIDE ( ets_update_cpu_frequency , 0x40002f04 ); +PROVIDE ( ets_vprintf , 0x40001f00 ); +PROVIDE ( ets_wdt_disable , 0x400030f0 ); +PROVIDE ( ets_wdt_enable , 0x40002fa0 ); +PROVIDE ( ets_wdt_get_mode , 0x40002f34 ); +PROVIDE ( ets_wdt_init , 0x40003170 ); +PROVIDE ( ets_wdt_restore , 0x40003158 ); +PROVIDE ( ets_write_char , 0x40001da0 ); +PROVIDE ( get_first_seg , 0x4000091c ); +PROVIDE ( gpio_init , 0x40004c50 ); +PROVIDE ( gpio_input_get , 0x40004cf0 ); +PROVIDE ( gpio_intr_ack , 0x40004dcc ); +PROVIDE ( gpio_intr_handler_register , 0x40004e28 ); +PROVIDE ( gpio_intr_pending , 0x40004d88 ); +PROVIDE ( gpio_intr_test , 0x40004efc ); +PROVIDE ( gpio_output_set , 0x40004cd0 ); +PROVIDE ( gpio_pin_intr_state_set , 0x40004d90 ); +PROVIDE ( gpio_pin_wakeup_disable , 0x40004ed4 ); +PROVIDE ( gpio_pin_wakeup_enable , 0x40004e90 ); +PROVIDE ( gpio_register_get , 0x40004d5c ); +PROVIDE ( gpio_register_set , 0x40004d04 ); +PROVIDE ( hmac_md5 , 0x4000a2cc ); +PROVIDE ( hmac_md5_vector , 0x4000a160 ); +PROVIDE ( hmac_sha1 , 0x4000ba28 ); +PROVIDE ( hmac_sha1_vector , 0x4000b8b4 ); +PROVIDE ( lldesc_build_chain , 0x40004f40 ); +PROVIDE ( lldesc_num2link , 0x40005050 ); +PROVIDE ( lldesc_set_owner , 0x4000507c ); +PROVIDE ( main , 0x40000fec ); +PROVIDE ( md5_vector , 0x400097ac ); +PROVIDE ( mem_calloc , 0x40001c2c ); +PROVIDE ( mem_free , 0x400019e0 ); +PROVIDE ( mem_init , 0x40001998 ); +PROVIDE ( mem_malloc , 0x40001b40 ); +PROVIDE ( mem_realloc , 0x40001c6c ); +PROVIDE ( mem_trim , 0x40001a14 ); +PROVIDE ( mem_zalloc , 0x40001c58 ); +PROVIDE ( memcmp , 0x4000dea8 ); +PROVIDE ( memcpy , 0x4000df48 ); +PROVIDE ( memmove , 0x4000e04c ); +PROVIDE ( memset , 0x4000e190 ); +PROVIDE ( multofup , 0x400031c0 ); +PROVIDE ( pbkdf2_sha1 , 0x4000b840 ); +PROVIDE ( phy_get_romfuncs , 0x40006b08 ); +PROVIDE ( rand , 0x40000600 ); +PROVIDE ( rc4_skip , 0x4000dd68 ); +PROVIDE ( recv_packet , 0x40003d08 ); +PROVIDE ( remove_head_space , 0x40000a04 ); +PROVIDE ( rijndaelKeySetupDec , 0x40008dd0 ); +PROVIDE ( rijndaelKeySetupEnc , 0x40009300 ); +PROVIDE ( rom_abs_temp , 0x400060c0 ); +PROVIDE ( rom_ana_inf_gating_en , 0x40006b10 ); +PROVIDE ( rom_cal_tos_v50 , 0x40007a28 ); +PROVIDE ( rom_chip_50_set_channel , 0x40006f84 ); +PROVIDE ( rom_chip_v5_disable_cca , 0x400060d0 ); +PROVIDE ( rom_chip_v5_enable_cca , 0x400060ec ); +PROVIDE ( rom_chip_v5_rx_init , 0x4000711c ); +PROVIDE ( rom_chip_v5_sense_backoff , 0x4000610c ); +PROVIDE ( rom_chip_v5_tx_init , 0x4000718c ); +PROVIDE ( rom_dc_iq_est , 0x4000615c ); +PROVIDE ( rom_en_pwdet , 0x400061b8 ); +PROVIDE ( rom_get_bb_atten , 0x40006238 ); +PROVIDE ( rom_get_corr_power , 0x40006260 ); +PROVIDE ( rom_get_fm_sar_dout , 0x400062dc ); +PROVIDE ( rom_get_noisefloor , 0x40006394 ); +PROVIDE ( rom_get_power_db , 0x400063b0 ); +PROVIDE ( rom_i2c_readReg , 0x40007268 ); +PROVIDE ( rom_i2c_readReg_Mask , 0x4000729c ); +PROVIDE ( rom_i2c_writeReg , 0x400072d8 ); +PROVIDE ( rom_i2c_writeReg_Mask , 0x4000730c ); +PROVIDE ( rom_iq_est_disable , 0x40006400 ); +PROVIDE ( rom_iq_est_enable , 0x40006430 ); +PROVIDE ( rom_linear_to_db , 0x40006484 ); +PROVIDE ( rom_mhz2ieee , 0x400065a4 ); +PROVIDE ( rom_pbus_dco___SA2 , 0x40007bf0 ); +PROVIDE ( rom_pbus_debugmode , 0x4000737c ); +PROVIDE ( rom_pbus_enter_debugmode , 0x40007410 ); +PROVIDE ( rom_pbus_exit_debugmode , 0x40007448 ); +PROVIDE ( rom_pbus_force_test , 0x4000747c ); +PROVIDE ( rom_pbus_rd , 0x400074d8 ); +PROVIDE ( rom_pbus_set_rxgain , 0x4000754c ); +PROVIDE ( rom_pbus_set_txgain , 0x40007610 ); +PROVIDE ( rom_pbus_workmode , 0x40007648 ); +PROVIDE ( rom_pbus_xpd_rx_off , 0x40007688 ); +PROVIDE ( rom_pbus_xpd_rx_on , 0x400076cc ); +PROVIDE ( rom_pbus_xpd_tx_off , 0x400076fc ); +PROVIDE ( rom_pbus_xpd_tx_on , 0x40007740 ); +PROVIDE ( rom_pbus_xpd_tx_on__low_gain , 0x400077a0 ); +PROVIDE ( rom_phy_reset_req , 0x40007804 ); +PROVIDE ( rom_restart_cal , 0x4000781c ); +PROVIDE ( rom_rfcal_pwrctrl , 0x40007eb4 ); +PROVIDE ( rom_rfcal_rxiq , 0x4000804c ); +PROVIDE ( rom_rfcal_rxiq_set_reg , 0x40008264 ); +PROVIDE ( rom_rfcal_txcap , 0x40008388 ); +PROVIDE ( rom_rfcal_txiq , 0x40008610 ); +PROVIDE ( rom_rfcal_txiq_cover , 0x400088b8 ); +PROVIDE ( rom_rfcal_txiq_set_reg , 0x40008a70 ); +PROVIDE ( rom_rfpll_reset , 0x40007868 ); +PROVIDE ( rom_rfpll_set_freq , 0x40007968 ); +PROVIDE ( rom_rxiq_cover_mg_mp , 0x40008b6c ); +PROVIDE ( rom_rxiq_get_mis , 0x40006628 ); +PROVIDE ( rom_sar_init , 0x40006738 ); +PROVIDE ( rom_set_ana_inf_tx_scale , 0x4000678c ); +PROVIDE ( rom_set_channel_freq , 0x40006c50 ); +PROVIDE ( rom_set_loopback_gain , 0x400067c8 ); +PROVIDE ( rom_set_noise_floor , 0x40006830 ); +PROVIDE ( rom_set_rxclk_en , 0x40006550 ); +PROVIDE ( rom_set_txbb_atten , 0x40008c6c ); +PROVIDE ( rom_set_txclk_en , 0x4000650c ); +PROVIDE ( rom_set_txiq_cal , 0x40008d34 ); +PROVIDE ( rom_start_noisefloor , 0x40006874 ); +PROVIDE ( rom_start_tx_tone , 0x400068b4 ); +PROVIDE ( rom_stop_tx_tone , 0x4000698c ); +PROVIDE ( rom_tx_mac_disable , 0x40006a98 ); +PROVIDE ( rom_tx_mac_enable , 0x40006ad4 ); +PROVIDE ( rom_txtone_linear_pwr , 0x40006a1c ); +PROVIDE ( rom_write_rfpll_sdm , 0x400078dc ); +PROVIDE ( roundup2 , 0x400031b4 ); +PROVIDE ( rtc_enter_sleep , 0x40002870 ); +PROVIDE ( rtc_get_reset_reason , 0x400025e0 ); +PROVIDE ( rtc_intr_handler , 0x400029ec ); +PROVIDE ( rtc_set_sleep_mode , 0x40002668 ); +PROVIDE ( save_rxbcn_mactime , 0x400027a4 ); +PROVIDE ( save_tsf_us , 0x400027ac ); +PROVIDE ( send_packet , 0x40003c80 ); +PROVIDE ( sha1_prf , 0x4000ba48 ); +PROVIDE ( sha1_vector , 0x4000a2ec ); +PROVIDE ( sip_alloc_to_host_evt , 0x40005180 ); +PROVIDE ( sip_get_ptr , 0x400058a8 ); +PROVIDE ( sip_get_state , 0x40005668 ); +PROVIDE ( sip_init_attach , 0x4000567c ); +PROVIDE ( sip_install_rx_ctrl_cb , 0x4000544c ); +PROVIDE ( sip_install_rx_data_cb , 0x4000545c ); +PROVIDE ( sip_post , 0x400050fc ); +PROVIDE ( sip_post_init , 0x400056c4 ); +PROVIDE ( sip_reclaim_from_host_cmd , 0x4000534c ); +PROVIDE ( sip_reclaim_tx_data_pkt , 0x400052c0 ); +PROVIDE ( sip_send , 0x40005808 ); +PROVIDE ( sip_to_host_chain_append , 0x40005864 ); +PROVIDE ( sip_to_host_evt_send_done , 0x40005234 ); +PROVIDE ( slc_add_credits , 0x400060ac ); +PROVIDE ( slc_enable , 0x40005d90 ); +PROVIDE ( slc_from_host_chain_fetch , 0x40005f24 ); +PROVIDE ( slc_from_host_chain_recycle , 0x40005e94 ); +PROVIDE ( slc_init_attach , 0x40005c50 ); +PROVIDE ( slc_init_credit , 0x4000608c ); +PROVIDE ( slc_pause_from_host , 0x40006014 ); +PROVIDE ( slc_reattach , 0x40005c1c ); +PROVIDE ( slc_resume_from_host , 0x4000603c ); +PROVIDE ( slc_select_tohost_gpio , 0x40005dc0 ); +PROVIDE ( slc_select_tohost_gpio_mode , 0x40005db8 ); +PROVIDE ( slc_send_to_host_chain , 0x40005de4 ); +PROVIDE ( slc_set_host_io_max_window , 0x40006068 ); +PROVIDE ( slc_to_host_chain_recycle , 0x40005f10 ); +PROVIDE ( software_reset , 0x4000264c ); +PROVIDE ( spi_flash_attach , 0x40004644 ); +PROVIDE ( srand , 0x400005f0 ); +PROVIDE ( strcmp , 0x4000bdc8 ); +PROVIDE ( strcpy , 0x4000bec8 ); +PROVIDE ( strlen , 0x4000bf4c ); +PROVIDE ( strncmp , 0x4000bfa8 ); +PROVIDE ( strncpy , 0x4000c0a0 ); +PROVIDE ( strstr , 0x4000e1e0 ); +PROVIDE ( timer_insert , 0x40002c64 ); +PROVIDE ( uartAttach , 0x4000383c ); +PROVIDE ( uart_baudrate_detect , 0x40003924 ); +PROVIDE ( uart_buff_switch , 0x400038a4 ); +PROVIDE ( uart_div_modify , 0x400039d8 ); +PROVIDE ( uart_rx_intr_handler , 0x40003bbc ); +PROVIDE ( uart_rx_one_char , 0x40003b8c ); +PROVIDE ( uart_rx_one_char_block , 0x40003b64 ); +PROVIDE ( uart_rx_readbuff , 0x40003ec8 ); +PROVIDE ( uart_tx_one_char , 0x40003b30 ); +PROVIDE ( wepkey_128 , 0x4000bc40 ); +PROVIDE ( wepkey_64 , 0x4000bb3c ); +PROVIDE ( xthal_bcopy , 0x40000688 ); +PROVIDE ( xthal_copy123 , 0x4000074c ); +PROVIDE ( xthal_get_ccompare , 0x4000dd4c ); +PROVIDE ( xthal_get_ccount , 0x4000dd38 ); +PROVIDE ( xthal_get_interrupt , 0x4000dd58 ); +PROVIDE ( xthal_get_intread , 0x4000dd58 ); +PROVIDE ( xthal_memcpy , 0x400006c4 ); +PROVIDE ( xthal_set_ccompare , 0x4000dd40 ); +PROVIDE ( xthal_set_intclear , 0x4000dd60 ); +PROVIDE ( xthal_spill_registers_into_stack_nw , 0x4000e320 ); +PROVIDE ( xthal_window_spill , 0x4000e324 ); +PROVIDE ( xthal_window_spill_nw , 0x4000e320 ); + +PROVIDE ( Te0 , 0x3fffccf0 ); +PROVIDE ( UartDev , 0x3fffde10 ); +PROVIDE ( flashchip , 0x3fffc714); diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/stubs/Makefile b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/Makefile new file mode 100644 index 0000000..7aee561 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/Makefile @@ -0,0 +1,50 @@ +# +# Copyright (c) 2015 Cesanta Software Limited +# All rights reserved +# + +STUB = stub_hello.c +LIBS = +PARAMS = +CFLAGS = +PORT = /dev/ttyUSB0 + +BUILD_DIR = .build +COMMON_STUB_DIR = ../../esp +STUB_ELF = $(BUILD_DIR)/$(patsubst %.c,%.elf,$(notdir $(STUB))) +STUB_JSON ?= $(BUILD_DIR)/$(patsubst %.c,%.json,$(notdir $(STUB))) +SDK = $(shell cat ../../../../fw/platforms/esp8266/sdk.version) +XT_CC = xtensa-lx106-elf-gcc + +.PHONY: all clean run wrap + +all: $(STUB_ELF) + +$(STUB_ELF): $(STUB) $(LIBS) + @echo " CC $^ -> $@" + @[ -d $(BUILD_DIR) ] || mkdir $(BUILD_DIR) + @docker run --rm -i -v $(CURDIR)/../../../..:/src $(SDK) //bin/bash -c \ + "cd /src/common/platforms/esp8266/stubs && \ + $(XT_CC) -std=c99 -Wall -Werror -Os -DESP8266 \ + -mtext-section-literals -mlongcalls -nostdlib -fno-builtin \ + -I. -I/src/common/platforms/esp \ + -I/opt/Espressif/ESP8266_SDK \ + -Wl,-static -ffunction-sections -Wl,--gc-sections \ + -Tstub.ld $(CFLAGS) -o $@ $^" + +wrap: $(STUB_JSON) + +$(STUB_JSON): $(STUB_ELF) $(COMMON_STUB_DIR)/esptool.py + @echo " WRAP $< -> $@" + @docker run --rm -i -v $(CURDIR)/../../../..:/src $(SDK) //bin/bash -c \ + "cd /src/common/platforms/esp8266/stubs && \ + $(COMMON_STUB_DIR)/esptool.py wrap_stub $<" > $@ + +run: $(STUB_JSON) + @echo " RUN $< $(PARAMS) -> $(PORT)" + @docker run --rm -i --privileged -v $(CURDIR)/../../../..:/src $(SDK) //bin/bash -c \ + "cd /src/common/platforms/esp8266/stubs && \ + $(COMMON_STUB_DIR)/esptool.py --port $(PORT) run_stub $< $(PARAMS)" + +clean: + @rm -rf $(BUILD_DIR) diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/stubs/README.md b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/README.md new file mode 100644 index 0000000..9265771 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/README.md @@ -0,0 +1,13 @@ +This is a ESP boot loader stub development environment. + +Code produced in this environment can be loaded and executed +in the bootloader environment. Usually it is used to implement +functionality not found in the bootloader. + +Stubs can be executed using the `run_stub` command of the modified esptool.py provided. +`wrap_stub` produces a JSON represenattion of the stub that can later be reused +or built into other tools. + +Example usage: + $ make run STUB=stub_flash_size.c PORT=/dev/ttyUSB0 + $ make run STUB=stub_md5.c PORT=/dev/ttyUSB0 PARAMS="0x11000 10000 1" diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/stubs/chop80.py b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/chop80.py new file mode 100755 index 0000000..a488ba2 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/chop80.py @@ -0,0 +1,14 @@ +#!/usr/bin/python + +import sys + +for line in open(sys.argv[1]): + i = 0 + while i < len(line): + n = len(line) - i + if n > 80: n = 80 + l = line[i:i+n] + if n == 80: + l += '\\' + print l + i += n diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/stubs/rom_functions.h b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/rom_functions.h new file mode 100644 index 0000000..82ea746 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/rom_functions.h @@ -0,0 +1,91 @@ +#ifndef CS_COMMON_PLATFORMS_ESP8266_STUBS_ROM_FUNCTIONS_H_ +#define CS_COMMON_PLATFORMS_ESP8266_STUBS_ROM_FUNCTIONS_H_ + +#include + +#include "/opt/Espressif/ESP8266_NONOS_SDK/include/c_types.h" +#include "/opt/Espressif/ESP8266_NONOS_SDK/include/spi_flash.h" + +int uart_rx_one_char(uint8_t *ch); +uint8_t uart_rx_one_char_block(); +int uart_tx_one_char(char ch); +void uart_div_modify(uint32_t uart_no, uint32_t baud_div); + +int SendMsg(uint8_t *msg, uint8_t size); +int send_packet(uint8_t *packet, uint32_t size); +// recv_packet depends on global UartDev, better to avoid it. +// uint32_t recv_packet(void *packet, uint32_t len, uint8_t no_sync); + +void _putc1(char *ch); + +void ets_delay_us(uint32_t us); + +uint32_t SPILock(); +uint32_t SPIUnlock(); +uint32_t SPIRead(uint32_t addr, void *dst, uint32_t size); +uint32_t SPIWrite(uint32_t addr, const uint32_t *src, uint32_t size); +uint32_t SPIEraseChip(); +uint32_t SPIEraseBlock(uint32_t block_num); +uint32_t SPIEraseSector(uint32_t sector_num); + +extern SpiFlashChip *flashchip; +uint32_t Wait_SPI_Idle(SpiFlashChip *spi); +uint32_t SPI_chip_erase(SpiFlashChip *spi); +uint32_t SPI_read_status(SpiFlashChip *spi); +uint32_t SPI_write_enable(SpiFlashChip *spi); + +void spi_flash_attach(); + +/* ESP32 API compatibility */ +#define esp_rom_spiflash_unlock SPIUnlock +#define esp_rom_spiflash_erase_sector SPIEraseSector +#define esp_rom_spiflash_erase_block SPIEraseBlock +#define esp_rom_spiflash_erase_chip SPIEraseChip +#define esp_rom_spiflash_read SPIRead +#define esp_rom_spiflash_write SPIWrite +#define esp_rom_spiflash_config_param SPIParamCfg + +void SelectSpiFunction(); +void SPIFlashModeConfig(uint32_t a, uint32_t b); +void SPIReadModeCnfig(uint32_t a); +uint32_t SPIParamCfg(uint32_t deviceId, uint32_t chip_size, uint32_t block_size, + uint32_t sector_size, uint32_t page_size, + uint32_t status_mask); + +void Cache_Read_Disable(); + +void ets_delay_us(uint32_t delay_micros); + +void ets_isr_mask(uint32_t ints); +void ets_isr_unmask(uint32_t ints); +typedef void (*int_handler_t)(void *arg); + +void ets_intr_lock(); +void ets_intr_unlock(); +void ets_set_user_start(void (*user_start_fn)()); + +uint32_t rtc_get_reset_reason(); +void software_reset(); +void rom_phy_reset_req(); + +void uart_rx_intr_handler(void *arg); + +void _ResetVector(); + +/* Crypto functions are from wpa_supplicant. */ +int md5_vector(uint32_t num_msgs, const uint8_t *msgs[], + const uint32_t *msg_lens, uint8_t *digest); +int sha1_vector(uint32_t num_msgs, const uint8_t *msgs[], + const uint32_t *msg_lens, uint8_t *digest); + +struct MD5Context { + uint32_t buf[4]; + uint32_t bits[2]; + uint8_t in[64]; +}; + +void MD5Init(struct MD5Context *ctx); +void MD5Update(struct MD5Context *ctx, void *buf, uint32_t len); +void MD5Final(uint8_t digest[16], struct MD5Context *ctx); + +#endif /* CS_COMMON_PLATFORMS_ESP8266_STUBS_ROM_FUNCTIONS_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/stubs/stub.ld b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/stub.ld new file mode 100644 index 0000000..f54a4f8 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/stub.ld @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015 Cesanta Software Limited + * All rights reserved + */ + +MEMORY { + iram : org = 0x40100000, len = 0x8000 + dram : org = 0x3FFE8000, len = 0x14000 +} + +ENTRY(stub_main) + +SECTIONS { + .params 0x40100000 : { + _params_start = ABSOLUTE(.); + *(.params) + _params_end = ABSOLUTE(.); + } > iram + + .text : ALIGN(4) { + _code_start = ABSOLUTE(.); + *(.literal) + *(.text .text.*) + } > iram + + .bss : ALIGN(4) { + _bss_start = ABSOLUTE(.); + *(.bss) + _bss_end = ABSOLUTE(.); + } > dram + + .data : ALIGN(4) { + _data_start = ABSOLUTE(.); + *(.data) + *(.rodata .rodata.*) + } > dram +} + +INCLUDE "eagle.rom.addr.v6.ld" + +PROVIDE(SPIFlashModeConfig = 0x40004568); +PROVIDE(SPI_erase_sector = 0x400040c0); +PROVIDE(SPI_erase_block = 0x40004120); diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/stubs/stub_hello.c b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/stub_hello.c new file mode 100644 index 0000000..10a827d --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/stub_hello.c @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2015 Cesanta Software Limited + * All rights reserved + * + * + * Stub template. + */ + +#include +#include "rom_functions.h" + +/* Define the args vector and put it into the ".params" section. */ +uint32_t params[3] __attribute__((section(".params"))); + +/* Define a function called stub_main. Do not return or reboot. + * Use send_packet to communicate to the host. */ + +const char *hello = "Hello"; + +void stub_main(void) { + send_packet(hello, 5); + _ResetVector(); +} diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/stubs/uart.c b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/uart.c new file mode 100644 index 0000000..5651bd8 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/uart.c @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#include "uart.h" +#include "ets_sys.h" + +#define UART_CLKDIV_26MHZ(B) (52000000 + B / 2) / B + +void set_baud_rate(uint32_t uart_no, uint32_t baud_rate) { + uint32_t div = UART_CLKDIV_26MHZ(baud_rate); + WRITE_PERI_REG(UART_CLKDIV_REG(uart_no), div & 0xfffff); +} diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/stubs/uart.h b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/uart.h new file mode 100644 index 0000000..fc321c2 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/uart.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_ESP8266_STUBS_UART_H_ +#define CS_COMMON_PLATFORMS_ESP8266_STUBS_UART_H_ + +#include + +void set_baud_rate(uint32_t uart_no, uint32_t baud_rate); + +#define REG_UART_BASE(i) (0x60000000 + (i) *0xf00) +#define UART_FIFO_REG(i) (REG_UART_BASE(i) + 0x0) +#define UART_CONF1_REG(i) (REG_UART_BASE(i) + 0x24) +#define UART_RX_TOUT_EN (BIT(31)) +#define UART_RX_TOUT_THRHD_S 24 +#define UART_RXFIFO_FULL_THRHD_S 0 +#define UART_INT_ST_REG(i) (REG_UART_BASE(i) + 0x8) +#define UART_INT_ENA_REG(i) (REG_UART_BASE(i) + 0xC) +#define UART_RXFIFO_TOUT_INT_ENA (BIT(8)) +#define UART_RXFIFO_FULL_INT_ENA (BIT(0)) + +#define UART_INT_CLR_REG(i) (REG_UART_BASE(i) + 0x10) + +#define UART_CLKDIV_REG(i) (REG_UART_BASE(i) + 0x14) + +#define UART_STATUS_REG(i) (REG_UART_BASE(i) + 0x1C) + +#define UART_CONF0_REG(i) (REG_UART_BASE(i) + 0x20) +#define UART_RXFIFO_RST (BIT(17)) + +#define ETS_UART0_INUM ETS_UART_INUM + +#endif /* CS_COMMON_PLATFORMS_ESP8266_STUBS_UART_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/tools/malloc_analyzer.py b/src/mongoose-6.11/src/common/platforms/esp8266/tools/malloc_analyzer.py new file mode 100644 index 0000000..91d4d0c --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/tools/malloc_analyzer.py @@ -0,0 +1,23 @@ +#!/usr/bin/python + +# Analyzers output of firmware compiler with -DESP_ENABLE_MALLOC_TRACES + +import re +import sys + +allocs = {} + +for l in sys.stdin: + m = re.match(r'(ca|ma|re|fr) (\S+)\s*(\S*)\s*(\S*)', l) + if not m: continue + op = m.group(1) + if op in ('ca', 'ma'): + allocs[m.group(2)] = long(m.group(3)) + else: + if m.group(2) in allocs: + del allocs[m.group(2)] + if op == 're': + allocs[m.group(3)] = long(m.group(4)) + +for k, v in sorted(allocs.iteritems()): + print k, v diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/uart_register.h b/src/mongoose-6.11/src/common/platforms/esp8266/uart_register.h new file mode 100644 index 0000000..e8fcda7 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/uart_register.h @@ -0,0 +1,136 @@ +/* clang-format off */ +/* + * Copyright (c) 2010 - 2011 Espressif System + * + */ + +#ifndef CS_COMMON_PLATFORMS_ESP8266_UART_REGISTER_H_ +#define CS_COMMON_PLATFORMS_ESP8266_UART_REGISTER_H_ + +#define REG_UART_BASE(i) (0x60000000 + (i)*0xf00) +//version value:32'h062000 + +#define UART_FIFO(i) (REG_UART_BASE(i) + 0x0) +#define UART_RXFIFO_RD_BYTE 0x000000FF +#define UART_RXFIFO_RD_BYTE_S 0 + +#define UART_INT_RAW(i) (REG_UART_BASE(i) + 0x4) +#define UART_RXFIFO_TOUT_INT_RAW (BIT(8)) +#define UART_BRK_DET_INT_RAW (BIT(7)) +#define UART_CTS_CHG_INT_RAW (BIT(6)) +#define UART_DSR_CHG_INT_RAW (BIT(5)) +#define UART_RXFIFO_OVF_INT_RAW (BIT(4)) +#define UART_FRM_ERR_INT_RAW (BIT(3)) +#define UART_PARITY_ERR_INT_RAW (BIT(2)) +#define UART_TXFIFO_EMPTY_INT_RAW (BIT(1)) +#define UART_RXFIFO_FULL_INT_RAW (BIT(0)) + +#define UART_INT_ST(i) (REG_UART_BASE(i) + 0x8) +#define UART_RXFIFO_TOUT_INT_ST (BIT(8)) +#define UART_BRK_DET_INT_ST (BIT(7)) +#define UART_CTS_CHG_INT_ST (BIT(6)) +#define UART_DSR_CHG_INT_ST (BIT(5)) +#define UART_RXFIFO_OVF_INT_ST (BIT(4)) +#define UART_FRM_ERR_INT_ST (BIT(3)) +#define UART_PARITY_ERR_INT_ST (BIT(2)) +#define UART_TXFIFO_EMPTY_INT_ST (BIT(1)) +#define UART_RXFIFO_FULL_INT_ST (BIT(0)) + +#define UART_INT_ENA(i) (REG_UART_BASE(i) + 0xC) +#define UART_RXFIFO_TOUT_INT_ENA (BIT(8)) +#define UART_BRK_DET_INT_ENA (BIT(7)) +#define UART_CTS_CHG_INT_ENA (BIT(6)) +#define UART_DSR_CHG_INT_ENA (BIT(5)) +#define UART_RXFIFO_OVF_INT_ENA (BIT(4)) +#define UART_FRM_ERR_INT_ENA (BIT(3)) +#define UART_PARITY_ERR_INT_ENA (BIT(2)) +#define UART_TXFIFO_EMPTY_INT_ENA (BIT(1)) +#define UART_RXFIFO_FULL_INT_ENA (BIT(0)) + +#define UART_INT_CLR(i) (REG_UART_BASE(i) + 0x10) +#define UART_RXFIFO_TOUT_INT_CLR (BIT(8)) +#define UART_BRK_DET_INT_CLR (BIT(7)) +#define UART_CTS_CHG_INT_CLR (BIT(6)) +#define UART_DSR_CHG_INT_CLR (BIT(5)) +#define UART_RXFIFO_OVF_INT_CLR (BIT(4)) +#define UART_FRM_ERR_INT_CLR (BIT(3)) +#define UART_PARITY_ERR_INT_CLR (BIT(2)) +#define UART_TXFIFO_EMPTY_INT_CLR (BIT(1)) +#define UART_RXFIFO_FULL_INT_CLR (BIT(0)) + +#define UART_CLKDIV(i) (REG_UART_BASE(i) + 0x14) +#define UART_CLKDIV_CNT 0x000FFFFF +#define UART_CLKDIV_S 0 + +#define UART_AUTOBAUD(i) (REG_UART_BASE(i) + 0x18) +#define UART_GLITCH_FILT 0x000000FF +#define UART_GLITCH_FILT_S 8 +#define UART_AUTOBAUD_EN (BIT(0)) + +#define UART_STATUS(i) (REG_UART_BASE(i) + 0x1C) +#define UART_TXD (BIT(31)) +#define UART_RTSN (BIT(30)) +#define UART_DTRN (BIT(29)) +#define UART_TXFIFO_CNT 0x000000FF +#define UART_TXFIFO_CNT_S 16 +#define UART_RXD (BIT(15)) +#define UART_CTSN (BIT(14)) +#define UART_DSRN (BIT(13)) +#define UART_RXFIFO_CNT 0x000000FF +#define UART_RXFIFO_CNT_S 0 + +#define UART_CONF0(i) (REG_UART_BASE(i) + 0x20) +#define UART_DTR_INV (BIT(24)) +#define UART_RTS_INV (BIT(23)) +#define UART_TXD_INV (BIT(22)) +#define UART_DSR_INV (BIT(21)) +#define UART_CTS_INV (BIT(20)) +#define UART_RXD_INV (BIT(19)) +#define UART_TXFIFO_RST (BIT(18)) +#define UART_RXFIFO_RST (BIT(17)) +#define UART_IRDA_EN (BIT(16)) +#define UART_TX_FLOW_EN (BIT(15)) +#define UART_LOOPBACK (BIT(14)) +#define UART_IRDA_RX_INV (BIT(13)) +#define UART_IRDA_TX_INV (BIT(12)) +#define UART_IRDA_WCTL (BIT(11)) +#define UART_IRDA_TX_EN (BIT(10)) +#define UART_IRDA_DPLX (BIT(9)) +#define UART_TXD_BRK (BIT(8)) +#define UART_SW_DTR (BIT(7)) +#define UART_SW_RTS (BIT(6)) +#define UART_STOP_BIT_NUM 0x00000003 +#define UART_STOP_BIT_NUM_S 4 +#define UART_BIT_NUM 0x00000003 +#define UART_BIT_NUM_S 2 +#define UART_PARITY_EN (BIT(1)) +#define UART_PARITY (BIT(0)) + +#define UART_CONF1(i) (REG_UART_BASE(i) + 0x24) +#define UART_RX_TOUT_EN (BIT(31)) +#define UART_RX_TOUT_THRHD 0x0000007F +#define UART_RX_TOUT_THRHD_S 24 +#define UART_RX_FLOW_EN (BIT(23)) +#define UART_RX_FLOW_THRHD 0x0000007F +#define UART_RX_FLOW_THRHD_S 16 +#define UART_TXFIFO_EMPTY_THRHD 0x0000007F +#define UART_TXFIFO_EMPTY_THRHD_S 8 +#define UART_RXFIFO_FULL_THRHD 0x0000007F +#define UART_RXFIFO_FULL_THRHD_S 0 + +#define UART_LOWPULSE(i) (REG_UART_BASE(i) + 0x28) +#define UART_LOWPULSE_MIN_CNT 0x000FFFFF +#define UART_LOWPULSE_MIN_CNT_S 0 + +#define UART_HIGHPULSE(i) (REG_UART_BASE(i) + 0x2C) +#define UART_HIGHPULSE_MIN_CNT 0x000FFFFF +#define UART_HIGHPULSE_MIN_CNT_S 0 + +#define UART_PULSE_NUM(i) (REG_UART_BASE(i) + 0x30) +#define UART_PULSE_NUM_CNT 0x0003FF +#define UART_PULSE_NUM_CNT_S 0 + +#define UART_DATE(i) (REG_UART_BASE(i) + 0x78) +#define UART_ID(i) (REG_UART_BASE(i) + 0x7C) + +#endif /* CS_COMMON_PLATFORMS_ESP8266_UART_REGISTER_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip.h b/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip.h new file mode 100644 index 0000000..1a772aa --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_LWIP_MG_LWIP_H_ +#define CS_COMMON_PLATFORMS_LWIP_MG_LWIP_H_ + +#ifndef MG_LWIP +#define MG_LWIP 0 +#endif + +#if MG_LWIP + +/* + * When compiling for nRF5x chips with arm-none-eabi-gcc, it has BYTE_ORDER + * already defined, so in order to avoid warnings in lwip, we have to undefine + * it. + * + * TODO: Check if in the future versions of nRF5 SDK that changes. + * Current version of nRF51 SDK: 0.8.0 + * nRF5 SDK: 0.9.0 + */ +#if CS_PLATFORM == CS_P_NRF51 || CS_PLATFORM == CS_P_NRF52 +#undef BYTE_ORDER +#endif + +#include +#include +#include +#include +#include +#include + +#ifndef LWIP_PROVIDE_ERRNO +#include +#endif + +#if LWIP_SOCKET +#include +#else +/* We really need the definitions from sockets.h. */ +#undef LWIP_SOCKET +#define LWIP_SOCKET 1 +#include +#undef LWIP_SOCKET +#define LWIP_SOCKET 0 +#endif + +#define INVALID_SOCKET (-1) +#define SOMAXCONN 10 +typedef int sock_t; + +#if MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL +struct mg_mgr; +struct mg_connection; +uint32_t mg_lwip_get_poll_delay_ms(struct mg_mgr *mgr); +void mg_lwip_set_keepalive_params(struct mg_connection *nc, int idle, + int interval, int count); +#endif + +/* For older version of LWIP */ +#ifndef ipX_2_ip +#define ipX_2_ip(x) (x) +#endif + +#endif /* MG_LWIP */ + +#endif /* CS_COMMON_PLATFORMS_LWIP_MG_LWIP_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_ev_mgr.c b/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_ev_mgr.c new file mode 100644 index 0000000..5eea26f --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_ev_mgr.c @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL + +#ifndef MG_SIG_QUEUE_LEN +#define MG_SIG_QUEUE_LEN 32 +#endif + +struct mg_ev_mgr_lwip_signal { + int sig; + struct mg_connection *nc; +}; + +struct mg_ev_mgr_lwip_data { + struct mg_ev_mgr_lwip_signal sig_queue[MG_SIG_QUEUE_LEN]; + int sig_queue_len; + int start_index; +}; + +void mg_lwip_post_signal(enum mg_sig_type sig, struct mg_connection *nc) { + struct mg_ev_mgr_lwip_data *md = + (struct mg_ev_mgr_lwip_data *) nc->iface->data; + mgos_lock(); + if (md->sig_queue_len >= MG_SIG_QUEUE_LEN) { + mgos_unlock(); + return; + } + int end_index = (md->start_index + md->sig_queue_len) % MG_SIG_QUEUE_LEN; + md->sig_queue[end_index].sig = sig; + md->sig_queue[end_index].nc = nc; + md->sig_queue_len++; + mg_lwip_mgr_schedule_poll(nc->mgr); + mgos_unlock(); +} + +void mg_ev_mgr_lwip_process_signals(struct mg_mgr *mgr) { + struct mg_ev_mgr_lwip_data *md = + (struct mg_ev_mgr_lwip_data *) mgr->ifaces[MG_MAIN_IFACE]->data; + while (md->sig_queue_len > 0) { + mgos_lock(); + int sig = md->sig_queue[md->start_index].sig; + struct mg_connection *nc = md->sig_queue[md->start_index].nc; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + md->start_index = (md->start_index + 1) % MG_SIG_QUEUE_LEN; + md->sig_queue_len--; + mgos_unlock(); + if (nc->iface == NULL || nc->mgr == NULL) continue; + switch (sig) { + case MG_SIG_CONNECT_RESULT: { +#if MG_ENABLE_SSL + if (cs->err == 0 && (nc->flags & MG_F_SSL) && + !(nc->flags & MG_F_SSL_HANDSHAKE_DONE)) { + mg_lwip_ssl_do_hs(nc); + } else +#endif + { + mg_if_connect_cb(nc, cs->err); + } + break; + } + case MG_SIG_CLOSE_CONN: { + nc->flags |= MG_F_SEND_AND_CLOSE; + mg_close_conn(nc); + break; + } + case MG_SIG_RECV: { + cs->recv_pending = 0; + if (nc->flags & MG_F_UDP) { + mg_lwip_handle_recv_udp(nc); + } else { + mg_lwip_handle_recv_tcp(nc); + } + break; + } + case MG_SIG_TOMBSTONE: { + break; + } + case MG_SIG_ACCEPT: { + mg_lwip_handle_accept(nc); + break; + } + } + } +} + +void mg_lwip_if_init(struct mg_iface *iface) { + LOG(LL_INFO, ("%p Mongoose init", iface)); + iface->data = MG_CALLOC(1, sizeof(struct mg_ev_mgr_lwip_data)); +} + +void mg_lwip_if_free(struct mg_iface *iface) { + MG_FREE(iface->data); + iface->data = NULL; +} + +void mg_lwip_if_add_conn(struct mg_connection *nc) { + (void) nc; +} + +void mg_lwip_if_remove_conn(struct mg_connection *nc) { + struct mg_ev_mgr_lwip_data *md = + (struct mg_ev_mgr_lwip_data *) nc->iface->data; + /* Walk the queue and null-out further signals for this conn. */ + for (int i = 0; i < MG_SIG_QUEUE_LEN; i++) { + if (md->sig_queue[i].nc == nc) { + md->sig_queue[i].sig = MG_SIG_TOMBSTONE; + } + } +} + +time_t mg_lwip_if_poll(struct mg_iface *iface, int timeout_ms) { + struct mg_mgr *mgr = iface->mgr; + int n = 0; + double now = mg_time(); + struct mg_connection *nc, *tmp; + double min_timer = 0; + int num_timers = 0; +#if 0 + DBG(("begin poll @%u", (unsigned int) (now * 1000))); +#endif + mg_ev_mgr_lwip_process_signals(mgr); + for (nc = mgr->active_connections; nc != NULL; nc = tmp) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + tmp = nc->next; + n++; + if ((nc->flags & MG_F_CLOSE_IMMEDIATELY) || + ((nc->flags & MG_F_SEND_AND_CLOSE) && (nc->flags & MG_F_UDP) && + (nc->send_mbuf.len == 0))) { + mg_close_conn(nc); + continue; + } + mg_if_poll(nc, now); + mg_if_timer(nc, now); +#if MG_ENABLE_SSL + if ((nc->flags & MG_F_SSL) && cs != NULL && cs->pcb.tcp != NULL && + cs->pcb.tcp->state == ESTABLISHED) { + if (((nc->flags & MG_F_WANT_WRITE) || + ((nc->send_mbuf.len > 0) && + (nc->flags & MG_F_SSL_HANDSHAKE_DONE))) && + cs->pcb.tcp->snd_buf > 0) { + /* Can write more. */ + if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) { + if (!(nc->flags & MG_F_CONNECTING)) mg_lwip_ssl_send(nc); + } else { + mg_lwip_ssl_do_hs(nc); + } + } + if (cs->rx_chain != NULL || (nc->flags & MG_F_WANT_READ)) { + if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) { + if (!(nc->flags & MG_F_CONNECTING)) mg_lwip_ssl_recv(nc); + } else { + mg_lwip_ssl_do_hs(nc); + } + } + } else +#endif /* MG_ENABLE_SSL */ + { + if (nc->send_mbuf.len > 0 && !(nc->flags & MG_F_CONNECTING)) { + mg_lwip_send_more(nc); + } + } + if (nc->sock != INVALID_SOCKET && + !(nc->flags & (MG_F_UDP | MG_F_LISTENING)) && cs->pcb.tcp != NULL && + cs->pcb.tcp->unsent != NULL) { + tcpip_callback(tcp_output_tcpip, cs->pcb.tcp); + } + if (nc->ev_timer_time > 0) { + if (num_timers == 0 || nc->ev_timer_time < min_timer) { + min_timer = nc->ev_timer_time; + } + num_timers++; + } + + if (nc->sock != INVALID_SOCKET) { + /* Try to consume data from cs->rx_chain */ + mg_lwip_consume_rx_chain_tcp(nc); + + /* + * If the connection is about to close, and rx_chain is finally empty, + * send the MG_SIG_CLOSE_CONN signal + */ + if (cs->draining_rx_chain && cs->rx_chain == NULL) { + mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); + } + } + } +#if 0 + DBG(("end poll @%u, %d conns, %d timers (min %u), next in %d ms", + (unsigned int) (now * 1000), n, num_timers, + (unsigned int) (min_timer * 1000), timeout_ms)); +#endif + (void) timeout_ms; + return now; +} + +uint32_t mg_lwip_get_poll_delay_ms(struct mg_mgr *mgr) { + struct mg_connection *nc; + double now; + double min_timer = 0; + int num_timers = 0; + mg_ev_mgr_lwip_process_signals(mgr); + for (nc = mg_next(mgr, NULL); nc != NULL; nc = mg_next(mgr, nc)) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + if (nc->ev_timer_time > 0) { + if (num_timers == 0 || nc->ev_timer_time < min_timer) { + min_timer = nc->ev_timer_time; + } + num_timers++; + } + if (nc->send_mbuf.len > 0 +#if MG_ENABLE_SSL + || (nc->flags & MG_F_WANT_WRITE) +#endif + ) { + int can_send = 0; + /* We have stuff to send, but can we? */ + if (nc->flags & MG_F_UDP) { + /* UDP is always ready for sending. */ + can_send = (cs->pcb.udp != NULL); + } else { + can_send = (cs->pcb.tcp != NULL && cs->pcb.tcp->snd_buf > 0); + } + /* We want and can send, request a poll immediately. */ + if (can_send) return 0; + } + } + uint32_t timeout_ms = ~0; + now = mg_time(); + if (num_timers > 0) { + /* If we have a timer that is past due, do a poll ASAP. */ + if (min_timer < now) return 0; + double timer_timeout_ms = (min_timer - now) * 1000 + 1 /* rounding */; + if (timer_timeout_ms < timeout_ms) { + timeout_ms = timer_timeout_ms; + } + } + return timeout_ms; +} + +#endif /* MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL */ diff --git a/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_net_if.c b/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_net_if.c new file mode 100644 index 0000000..460f8f5 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_net_if.c @@ -0,0 +1,779 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_NET_IF_LWIP_LOW_LEVEL + +#include "common/mg_mem.h" + +#include +#include +#include +#include +#if ((LWIP_VERSION_MAJOR << 8) | LWIP_VERSION_MINOR) >= 0x0105 +#include /* For tcp_seg */ +#else +#include +#endif +#include + +#include "common/cs_dbg.h" + +/* + * Newest versions of LWIP have ip_2_ip4, older have ipX_2_ip, + * even older have nothing. + */ +#ifndef ip_2_ip4 +#ifdef ipX_2_ip +#define ip_2_ip4(addr) ipX_2_ip(addr) +#else +#define ip_2_ip4(addr) (addr) +#endif +#endif + +/* + * Depending on whether Mongoose is compiled with ipv6 support, use right + * lwip functions + */ +#if MG_ENABLE_IPV6 +#define TCP_NEW tcp_new_ip6 +#define TCP_BIND tcp_bind_ip6 +#define UDP_BIND udp_bind_ip6 +#define IPADDR_NTOA(x) ip6addr_ntoa((const ip6_addr_t *)(x)) +#define SET_ADDR(dst, src) \ + memcpy((dst)->sin6.sin6_addr.s6_addr, (src)->ip6.addr, \ + sizeof((dst)->sin6.sin6_addr.s6_addr)) +#else +#define TCP_NEW tcp_new +#define TCP_BIND tcp_bind +#define UDP_BIND udp_bind +#define IPADDR_NTOA ipaddr_ntoa +#define SET_ADDR(dst, src) (dst)->sin.sin_addr.s_addr = ip_2_ip4(src)->addr +#endif + +#if NO_SYS +#define tcpip_callback(fn, arg) (fn)(arg) +typedef void (*tcpip_callback_fn)(void *arg); +#endif + +void mg_lwip_ssl_do_hs(struct mg_connection *nc); +void mg_lwip_ssl_send(struct mg_connection *nc); +void mg_lwip_ssl_recv(struct mg_connection *nc); + +void mg_lwip_if_init(struct mg_iface *iface); +void mg_lwip_if_free(struct mg_iface *iface); +void mg_lwip_if_add_conn(struct mg_connection *nc); +void mg_lwip_if_remove_conn(struct mg_connection *nc); +time_t mg_lwip_if_poll(struct mg_iface *iface, int timeout_ms); + +#if defined(RTOS_SDK) || defined(ESP_PLATFORM) +extern void mgos_lock(); +extern void mgos_unlock(); +#else +#define mgos_lock() +#define mgos_unlock() +#endif + +static void mg_lwip_recv_common(struct mg_connection *nc, struct pbuf *p); + +#if LWIP_TCP_KEEPALIVE +void mg_lwip_set_keepalive_params(struct mg_connection *nc, int idle, + int interval, int count) { + if (nc->sock == INVALID_SOCKET || nc->flags & MG_F_UDP) { + return; + } + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + struct tcp_pcb *tpcb = cs->pcb.tcp; + if (idle > 0 && interval > 0 && count > 0) { + tpcb->keep_idle = idle * 1000; + tpcb->keep_intvl = interval * 1000; + tpcb->keep_cnt = count; + tpcb->so_options |= SOF_KEEPALIVE; + } else { + tpcb->so_options &= ~SOF_KEEPALIVE; + } +} +#elif !defined(MG_NO_LWIP_TCP_KEEPALIVE) +#warning LWIP TCP keepalive is disabled. Please consider enabling it. +#endif /* LWIP_TCP_KEEPALIVE */ + +static err_t mg_lwip_tcp_conn_cb(void *arg, struct tcp_pcb *tpcb, err_t err) { + struct mg_connection *nc = (struct mg_connection *) arg; + DBG(("%p connect to %s:%u = %d", nc, IPADDR_NTOA(ipX_2_ip(&tpcb->remote_ip)), + tpcb->remote_port, err)); + if (nc == NULL) { + tcp_abort(tpcb); + return ERR_ARG; + } + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + cs->err = err; +#if LWIP_TCP_KEEPALIVE + if (err == 0) mg_lwip_set_keepalive_params(nc, 60, 10, 6); +#endif + mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); + return ERR_OK; +} + +static void mg_lwip_tcp_error_cb(void *arg, err_t err) { + struct mg_connection *nc = (struct mg_connection *) arg; + DBG(("%p conn error %d", nc, err)); + if (nc == NULL) return; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + cs->pcb.tcp = NULL; /* Has already been deallocated */ + if (nc->flags & MG_F_CONNECTING) { + cs->err = err; + mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); + } else { + mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); + } +} + +static err_t mg_lwip_tcp_recv_cb(void *arg, struct tcp_pcb *tpcb, + struct pbuf *p, err_t err) { + struct mg_connection *nc = (struct mg_connection *) arg; + DBG(("%p %p %u %d", nc, tpcb, (p != NULL ? p->tot_len : 0), err)); + if (p == NULL) { + if (nc != NULL && !(nc->flags & MG_F_CLOSE_IMMEDIATELY)) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + if (cs->rx_chain != NULL) { + /* + * rx_chain still contains non-consumed data, don't close the + * connection + */ + cs->draining_rx_chain = 1; + } else { + mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); + } + } else { + /* Tombstoned connection, do nothing. */ + } + return ERR_OK; + } else if (nc == NULL) { + tcp_abort(tpcb); + return ERR_ARG; + } + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + /* + * If we get a chain of more than one segment at once, we need to bump + * refcount on the subsequent bufs to make them independent. + */ + if (p->next != NULL) { + struct pbuf *q = p->next; + for (; q != NULL; q = q->next) pbuf_ref(q); + } + mgos_lock(); + if (cs->rx_chain == NULL) { + cs->rx_offset = 0; + } else if (pbuf_clen(cs->rx_chain) >= 4) { + /* ESP SDK has a limited pool of 5 pbufs. We must not hog them all or RX + * will be completely blocked. We already have at least 4 in the chain, + * this one is, so we have to make a copy and release this one. */ + struct pbuf *np = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); + if (np != NULL) { + pbuf_copy(np, p); + pbuf_free(p); + p = np; + } + } + mgos_unlock(); + mg_lwip_recv_common(nc, p); + return ERR_OK; +} + +static void mg_lwip_consume_rx_chain_tcp(struct mg_connection *nc) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + if (cs->rx_chain == NULL) return; +#if MG_ENABLE_SSL + if (nc->flags & MG_F_SSL) { + if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) { + mg_lwip_ssl_recv(nc); + } else { + mg_lwip_ssl_do_hs(nc); + } + return; + } +#endif + mgos_lock(); + while (cs->rx_chain != NULL && nc->recv_mbuf.len < nc->recv_mbuf_limit) { + struct pbuf *seg = cs->rx_chain; + + size_t seg_len = (seg->len - cs->rx_offset); + size_t buf_avail = (nc->recv_mbuf_limit - nc->recv_mbuf.len); + size_t len = MIN(seg_len, buf_avail); + + char *data = (char *) MG_MALLOC(len); + if (data == NULL) { + mgos_unlock(); + DBG(("OOM")); + return; + } + pbuf_copy_partial(seg, data, len, cs->rx_offset); + cs->rx_offset += len; + if (cs->rx_offset == cs->rx_chain->len) { + cs->rx_chain = pbuf_dechain(cs->rx_chain); + pbuf_free(seg); + cs->rx_offset = 0; + } + mgos_unlock(); + mg_if_recv_tcp_cb(nc, data, len, 1 /* own */); + mgos_lock(); + } + mgos_unlock(); +} + +static void mg_lwip_handle_recv_tcp(struct mg_connection *nc) { + mg_lwip_consume_rx_chain_tcp(nc); + + if (nc->send_mbuf.len > 0) { + mg_lwip_mgr_schedule_poll(nc->mgr); + } +} + +static err_t mg_lwip_tcp_sent_cb(void *arg, struct tcp_pcb *tpcb, + u16_t num_sent) { + struct mg_connection *nc = (struct mg_connection *) arg; + DBG(("%p %p %u %p %p", nc, tpcb, num_sent, tpcb->unsent, tpcb->unacked)); + if (nc == NULL) return ERR_OK; + if ((nc->flags & MG_F_SEND_AND_CLOSE) && !(nc->flags & MG_F_WANT_WRITE) && + nc->send_mbuf.len == 0 && tpcb->unsent == NULL && tpcb->unacked == NULL) { + mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); + } + return ERR_OK; +} + +struct mg_lwip_if_connect_tcp_ctx { + struct mg_connection *nc; + const union socket_address *sa; +}; + +static void mg_lwip_if_connect_tcp_tcpip(void *arg) { + struct mg_lwip_if_connect_tcp_ctx *ctx = + (struct mg_lwip_if_connect_tcp_ctx *) arg; + struct mg_connection *nc = ctx->nc; + const union socket_address *sa = ctx->sa; + + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + struct tcp_pcb *tpcb = TCP_NEW(); + cs->pcb.tcp = tpcb; + ip_addr_t *ip = (ip_addr_t *) &sa->sin.sin_addr.s_addr; + u16_t port = ntohs(sa->sin.sin_port); + tcp_arg(tpcb, nc); + tcp_err(tpcb, mg_lwip_tcp_error_cb); + tcp_sent(tpcb, mg_lwip_tcp_sent_cb); + tcp_recv(tpcb, mg_lwip_tcp_recv_cb); + cs->err = TCP_BIND(tpcb, IP_ADDR_ANY, 0 /* any port */); + DBG(("%p tcp_bind = %d", nc, cs->err)); + if (cs->err != ERR_OK) { + mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); + return; + } + cs->err = tcp_connect(tpcb, ip, port, mg_lwip_tcp_conn_cb); + DBG(("%p tcp_connect %p = %d", nc, tpcb, cs->err)); + if (cs->err != ERR_OK) { + mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); + return; + } +} + +void mg_lwip_if_connect_tcp(struct mg_connection *nc, + const union socket_address *sa) { + struct mg_lwip_if_connect_tcp_ctx ctx = {.nc = nc, .sa = sa}; + tcpip_callback(mg_lwip_if_connect_tcp_tcpip, &ctx); +} + +/* + * Lwip included in the SDKs for nRF5x chips has different type for the + * callback of `udp_recv()` + */ +#if ((LWIP_VERSION_MAJOR << 8) | LWIP_VERSION_MINOR) >= 0x0105 +static void mg_lwip_udp_recv_cb(void *arg, struct udp_pcb *pcb, struct pbuf *p, + const ip_addr_t *addr, u16_t port) +#else +static void mg_lwip_udp_recv_cb(void *arg, struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *addr, u16_t port) +#endif +{ + struct mg_connection *nc = (struct mg_connection *) arg; + DBG(("%p %s:%u %p %u %u", nc, IPADDR_NTOA(addr), port, p, p->ref, p->len)); + /* Put address in a separate pbuf and tack it onto the packet. */ + struct pbuf *sap = + pbuf_alloc(PBUF_RAW, sizeof(union socket_address), PBUF_RAM); + if (sap == NULL) { + pbuf_free(p); + return; + } + union socket_address *sa = (union socket_address *) sap->payload; +#if ((LWIP_VERSION_MAJOR << 8) | LWIP_VERSION_MINOR) >= 0x0105 + sa->sin.sin_addr.s_addr = ip_2_ip4(addr)->addr; +#else + sa->sin.sin_addr.s_addr = addr->addr; +#endif + sa->sin.sin_port = htons(port); + /* Logic in the recv handler requires that there be exactly one data pbuf. */ + p = pbuf_coalesce(p, PBUF_RAW); + pbuf_chain(sap, p); + mg_lwip_recv_common(nc, sap); + (void) pcb; +} + +static void mg_lwip_recv_common(struct mg_connection *nc, struct pbuf *p) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + mgos_lock(); + if (cs->rx_chain == NULL) { + cs->rx_chain = p; + } else { + pbuf_chain(cs->rx_chain, p); + } + if (!cs->recv_pending) { + cs->recv_pending = 1; + mg_lwip_post_signal(MG_SIG_RECV, nc); + } + mgos_unlock(); +} + +static void mg_lwip_handle_recv_udp(struct mg_connection *nc) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + /* + * For UDP, RX chain consists of interleaved address and packet bufs: + * Address pbuf followed by exactly one data pbuf (recv_cb took care of that). + */ + while (cs->rx_chain != NULL) { + struct pbuf *sap = cs->rx_chain; + struct pbuf *p = sap->next; + cs->rx_chain = pbuf_dechain(p); + size_t data_len = p->len; + char *data = (char *) MG_MALLOC(data_len); + if (data != NULL) { + pbuf_copy_partial(p, data, data_len, 0); + pbuf_free(p); + mg_if_recv_udp_cb(nc, data, data_len, + (union socket_address *) sap->payload, sap->len); + pbuf_free(sap); + } else { + pbuf_free(p); + pbuf_free(sap); + } + } +} + +static void mg_lwip_if_connect_udp_tcpip(void *arg) { + struct mg_connection *nc = (struct mg_connection *) arg; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + struct udp_pcb *upcb = udp_new(); + cs->err = UDP_BIND(upcb, IP_ADDR_ANY, 0 /* any port */); + DBG(("%p udp_bind %p = %d", nc, upcb, cs->err)); + if (cs->err == ERR_OK) { + udp_recv(upcb, mg_lwip_udp_recv_cb, nc); + cs->pcb.udp = upcb; + } else { + udp_remove(upcb); + } + mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); +} + +void mg_lwip_if_connect_udp(struct mg_connection *nc) { + tcpip_callback(mg_lwip_if_connect_udp_tcpip, nc); +} + +void mg_lwip_accept_conn(struct mg_connection *nc, struct tcp_pcb *tpcb) { + union socket_address sa; + SET_ADDR(&sa, &tpcb->remote_ip); + sa.sin.sin_port = htons(tpcb->remote_port); + mg_if_accept_tcp_cb(nc, &sa, sizeof(sa.sin)); +} + +static void tcp_close_tcpip(void *arg) { + tcp_close((struct tcp_pcb *) arg); +} + +void mg_lwip_handle_accept(struct mg_connection *nc) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + if (cs->pcb.tcp == NULL) return; +#if MG_ENABLE_SSL + if (cs->lc->flags & MG_F_SSL) { + if (mg_ssl_if_conn_accept(nc, cs->lc) != MG_SSL_OK) { + LOG(LL_ERROR, ("SSL error")); + tcpip_callback(tcp_close_tcpip, cs->pcb.tcp); + } + } else +#endif + { + mg_lwip_accept_conn(nc, cs->pcb.tcp); + } +} + +static err_t mg_lwip_accept_cb(void *arg, struct tcp_pcb *newtpcb, err_t err) { + struct mg_connection *lc = (struct mg_connection *) arg, *nc; + struct mg_lwip_conn_state *lcs, *cs; + struct tcp_pcb_listen *lpcb; + LOG(LL_DEBUG, + ("%p conn %p from %s:%u", lc, newtpcb, + IPADDR_NTOA(ipX_2_ip(&newtpcb->remote_ip)), newtpcb->remote_port)); + if (lc == NULL) { + tcp_abort(newtpcb); + return ERR_ABRT; + } + lcs = (struct mg_lwip_conn_state *) lc->sock; + lpcb = (struct tcp_pcb_listen *) lcs->pcb.tcp; +#if TCP_LISTEN_BACKLOG + tcp_accepted(lpcb); +#endif + nc = mg_if_accept_new_conn(lc); + if (nc == NULL) { + tcp_abort(newtpcb); + return ERR_ABRT; + } + cs = (struct mg_lwip_conn_state *) nc->sock; + cs->lc = lc; + cs->pcb.tcp = newtpcb; + /* We need to set up callbacks before returning because data may start + * arriving immediately. */ + tcp_arg(newtpcb, nc); + tcp_err(newtpcb, mg_lwip_tcp_error_cb); + tcp_sent(newtpcb, mg_lwip_tcp_sent_cb); + tcp_recv(newtpcb, mg_lwip_tcp_recv_cb); +#if LWIP_TCP_KEEPALIVE + mg_lwip_set_keepalive_params(nc, 60, 10, 6); +#endif + mg_lwip_post_signal(MG_SIG_ACCEPT, nc); + (void) err; + (void) lpcb; + return ERR_OK; +} + +struct mg_lwip_if_listen_ctx { + struct mg_connection *nc; + union socket_address *sa; + int ret; +}; + +static void mg_lwip_if_listen_tcp_tcpip(void *arg) { + struct mg_lwip_if_listen_ctx *ctx = (struct mg_lwip_if_listen_ctx *) arg; + struct mg_connection *nc = ctx->nc; + union socket_address *sa = ctx->sa; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + struct tcp_pcb *tpcb = TCP_NEW(); + ip_addr_t *ip = (ip_addr_t *) &sa->sin.sin_addr.s_addr; + u16_t port = ntohs(sa->sin.sin_port); + cs->err = TCP_BIND(tpcb, ip, port); + DBG(("%p tcp_bind(%s:%u) = %d", nc, IPADDR_NTOA(ip), port, cs->err)); + if (cs->err != ERR_OK) { + tcp_close(tpcb); + ctx->ret = -1; + return; + } + tcp_arg(tpcb, nc); + tpcb = tcp_listen(tpcb); + cs->pcb.tcp = tpcb; + tcp_accept(tpcb, mg_lwip_accept_cb); + ctx->ret = 0; +} + +int mg_lwip_if_listen_tcp(struct mg_connection *nc, union socket_address *sa) { + struct mg_lwip_if_listen_ctx ctx = {.nc = nc, .sa = sa}; + tcpip_callback(mg_lwip_if_listen_tcp_tcpip, &ctx); + return ctx.ret; +} + +static void mg_lwip_if_listen_udp_tcpip(void *arg) { + struct mg_lwip_if_listen_ctx *ctx = (struct mg_lwip_if_listen_ctx *) arg; + struct mg_connection *nc = ctx->nc; + union socket_address *sa = ctx->sa; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + struct udp_pcb *upcb = udp_new(); + ip_addr_t *ip = (ip_addr_t *) &sa->sin.sin_addr.s_addr; + u16_t port = ntohs(sa->sin.sin_port); + cs->err = UDP_BIND(upcb, ip, port); + DBG(("%p udb_bind(%s:%u) = %d", nc, IPADDR_NTOA(ip), port, cs->err)); + if (cs->err != ERR_OK) { + udp_remove(upcb); + ctx->ret = -1; + } else { + udp_recv(upcb, mg_lwip_udp_recv_cb, nc); + cs->pcb.udp = upcb; + ctx->ret = 0; + } +} + +int mg_lwip_if_listen_udp(struct mg_connection *nc, union socket_address *sa) { + struct mg_lwip_if_listen_ctx ctx = {.nc = nc, .sa = sa}; + tcpip_callback(mg_lwip_if_listen_udp_tcpip, &ctx); + return ctx.ret; +} + +struct mg_lwip_tcp_write_ctx { + struct mg_connection *nc; + const void *data; + uint16_t len; + int ret; +}; + +static void tcp_output_tcpip(void *arg) { + tcp_output((struct tcp_pcb *) arg); +} + +static void mg_lwip_tcp_write_tcpip(void *arg) { + struct mg_lwip_tcp_write_ctx *ctx = (struct mg_lwip_tcp_write_ctx *) arg; + struct mg_connection *nc = ctx->nc; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + struct tcp_pcb *tpcb = cs->pcb.tcp; + size_t len = MIN(tpcb->mss, MIN(ctx->len, tpcb->snd_buf)); + size_t unsent, unacked; + if (len == 0) { + DBG(("%p no buf avail %u %u %p %p", tpcb, tpcb->snd_buf, tpcb->snd_queuelen, + tpcb->unsent, tpcb->unacked)); + tcpip_callback(tcp_output_tcpip, tpcb); + ctx->ret = 0; + return; + } + unsent = (tpcb->unsent != NULL ? tpcb->unsent->len : 0); + unacked = (tpcb->unacked != NULL ? tpcb->unacked->len : 0); +/* + * On ESP8266 we only allow one TCP segment in flight at any given time. + * This may increase latency and reduce efficiency of tcp windowing, + * but memory is scarce and precious on that platform so we do this to + * reduce footprint. + */ +#if CS_PLATFORM == CS_P_ESP8266 + if (unacked > 0) { + ctx->ret = 0; + return; + } + len = MIN(len, (TCP_MSS - unsent)); +#endif + cs->err = tcp_write(tpcb, ctx->data, len, TCP_WRITE_FLAG_COPY); + unsent = (tpcb->unsent != NULL ? tpcb->unsent->len : 0); + unacked = (tpcb->unacked != NULL ? tpcb->unacked->len : 0); + DBG(("%p tcp_write %u = %d, %u %u", tpcb, len, cs->err, unsent, unacked)); + if (cs->err != ERR_OK) { + /* + * We ignore ERR_MEM because memory will be freed up when the data is sent + * and we'll retry. + */ + ctx->ret = (cs->err == ERR_MEM ? 0 : -1); + return; + } + ctx->ret = len; +} + +static int mg_lwip_tcp_write(struct mg_connection *nc, const void *data, + uint16_t len) { + struct mg_lwip_tcp_write_ctx ctx = {.nc = nc, .data = data, .len = len}; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + struct tcp_pcb *tpcb = cs->pcb.tcp; + if (tpcb == NULL) { + return -1; + } + tcpip_callback(mg_lwip_tcp_write_tcpip, &ctx); + return ctx.ret; +} + +struct udp_sendto_ctx { + struct udp_pcb *upcb; + struct pbuf *p; + ip_addr_t *ip; + uint16_t port; + int ret; +}; + +static void udp_sendto_tcpip(void *arg) { + struct udp_sendto_ctx *ctx = (struct udp_sendto_ctx *) arg; + ctx->ret = udp_sendto(ctx->upcb, ctx->p, ctx->ip, ctx->port); +} + +static int mg_lwip_udp_send(struct mg_connection *nc, const void *data, + uint16_t len) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + if (cs->pcb.udp == NULL) { + /* + * In case of UDP, this usually means, what + * async DNS resolve is still in progress and connection + * is not ready yet + */ + DBG(("%p socket is not connected", nc)); + return -1; + } + struct udp_pcb *upcb = cs->pcb.udp; + struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM); +#if defined(LWIP_IPV4) && LWIP_IPV4 && defined(LWIP_IPV6) && LWIP_IPV6 + ip_addr_t ip = {.u_addr.ip4.addr = nc->sa.sin.sin_addr.s_addr, .type = 0}; +#else + ip_addr_t ip = {.addr = nc->sa.sin.sin_addr.s_addr}; +#endif + u16_t port = ntohs(nc->sa.sin.sin_port); + if (p == NULL) { + DBG(("OOM")); + return 0; + } + memcpy(p->payload, data, len); + struct udp_sendto_ctx ctx = {.upcb = upcb, .p = p, .ip = &ip, .port = port}; + tcpip_callback(udp_sendto_tcpip, &ctx); + cs->err = ctx.ret; + pbuf_free(p); + return (cs->err == ERR_OK ? len : -1); +} + +static void mg_lwip_send_more(struct mg_connection *nc) { + int num_sent = 0; + if (nc->sock == INVALID_SOCKET) return; + if (nc->flags & MG_F_UDP) { + num_sent = mg_lwip_udp_send(nc, nc->send_mbuf.buf, nc->send_mbuf.len); + DBG(("%p mg_lwip_udp_send %u = %d", nc, nc->send_mbuf.len, num_sent)); + } else { + num_sent = mg_lwip_tcp_write(nc, nc->send_mbuf.buf, nc->send_mbuf.len); + DBG(("%p mg_lwip_tcp_write %u = %d", nc, nc->send_mbuf.len, num_sent)); + } + if (num_sent == 0) return; + if (num_sent > 0) { + mg_if_sent_cb(nc, num_sent); + } else { + mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); + } +} + +void mg_lwip_if_tcp_send(struct mg_connection *nc, const void *buf, + size_t len) { + mbuf_append(&nc->send_mbuf, buf, len); + mg_lwip_mgr_schedule_poll(nc->mgr); +} + +void mg_lwip_if_udp_send(struct mg_connection *nc, const void *buf, + size_t len) { + mbuf_append(&nc->send_mbuf, buf, len); + mg_lwip_mgr_schedule_poll(nc->mgr); +} + +struct tcp_recved_ctx { + struct tcp_pcb *tpcb; + size_t len; +}; + +void tcp_recved_tcpip(void *arg) { + struct tcp_recved_ctx *ctx = (struct tcp_recved_ctx *) arg; + tcp_recved(ctx->tpcb, ctx->len); +} + +void mg_lwip_if_recved(struct mg_connection *nc, size_t len) { + if (nc->flags & MG_F_UDP) return; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + if (nc->sock == INVALID_SOCKET || cs->pcb.tcp == NULL) { + DBG(("%p invalid socket", nc)); + return; + } + DBG(("%p %p %u %u", nc, cs->pcb.tcp, len, + (cs->rx_chain ? cs->rx_chain->tot_len : 0))); + struct tcp_recved_ctx ctx = {.tpcb = cs->pcb.tcp, .len = len}; +#if MG_ENABLE_SSL + if (!(nc->flags & MG_F_SSL)) { + tcpip_callback(tcp_recved_tcpip, &ctx); + } else { + /* Currently SSL acknowledges data immediately. + * TODO(rojer): Find a way to propagate mg_lwip_if_recved. */ + } +#else + tcpip_callback(tcp_recved_tcpip, &ctx); +#endif + mbuf_trim(&nc->recv_mbuf); +} + +int mg_lwip_if_create_conn(struct mg_connection *nc) { + struct mg_lwip_conn_state *cs = + (struct mg_lwip_conn_state *) MG_CALLOC(1, sizeof(*cs)); + if (cs == NULL) return 0; + cs->nc = nc; + nc->sock = (intptr_t) cs; + return 1; +} + +static void udp_remove_tcpip(void *arg) { + udp_remove((struct udp_pcb *) arg); +} + +void mg_lwip_if_destroy_conn(struct mg_connection *nc) { + if (nc->sock == INVALID_SOCKET) return; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + if (!(nc->flags & MG_F_UDP)) { + struct tcp_pcb *tpcb = cs->pcb.tcp; + if (tpcb != NULL) { + tcp_arg(tpcb, NULL); + DBG(("%p tcp_close %p", nc, tpcb)); + tcp_arg(tpcb, NULL); + tcpip_callback(tcp_close_tcpip, tpcb); + } + while (cs->rx_chain != NULL) { + struct pbuf *seg = cs->rx_chain; + cs->rx_chain = pbuf_dechain(cs->rx_chain); + pbuf_free(seg); + } + memset(cs, 0, sizeof(*cs)); + MG_FREE(cs); + } else if (nc->listener == NULL) { + /* Only close outgoing UDP pcb or listeners. */ + struct udp_pcb *upcb = cs->pcb.udp; + if (upcb != NULL) { + DBG(("%p udp_remove %p", nc, upcb)); + tcpip_callback(udp_remove_tcpip, upcb); + } + memset(cs, 0, sizeof(*cs)); + MG_FREE(cs); + } + nc->sock = INVALID_SOCKET; +} + +void mg_lwip_if_get_conn_addr(struct mg_connection *nc, int remote, + union socket_address *sa) { + memset(sa, 0, sizeof(*sa)); + if (nc == NULL || nc->sock == INVALID_SOCKET) return; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + if (nc->flags & MG_F_UDP) { + struct udp_pcb *upcb = cs->pcb.udp; + if (remote) { + memcpy(sa, &nc->sa, sizeof(*sa)); + } else if (upcb != NULL) { + sa->sin.sin_port = htons(upcb->local_port); + SET_ADDR(sa, &upcb->local_ip); + } + } else { + struct tcp_pcb *tpcb = cs->pcb.tcp; + if (remote) { + memcpy(sa, &nc->sa, sizeof(*sa)); + } else if (tpcb != NULL) { + sa->sin.sin_port = htons(tpcb->local_port); + SET_ADDR(sa, &tpcb->local_ip); + } + } +} + +void mg_lwip_if_sock_set(struct mg_connection *nc, sock_t sock) { + nc->sock = sock; +} + +/* clang-format off */ +#define MG_LWIP_IFACE_VTABLE \ + { \ + mg_lwip_if_init, \ + mg_lwip_if_free, \ + mg_lwip_if_add_conn, \ + mg_lwip_if_remove_conn, \ + mg_lwip_if_poll, \ + mg_lwip_if_listen_tcp, \ + mg_lwip_if_listen_udp, \ + mg_lwip_if_connect_tcp, \ + mg_lwip_if_connect_udp, \ + mg_lwip_if_tcp_send, \ + mg_lwip_if_udp_send, \ + mg_lwip_if_recved, \ + mg_lwip_if_create_conn, \ + mg_lwip_if_destroy_conn, \ + mg_lwip_if_sock_set, \ + mg_lwip_if_get_conn_addr, \ + } +/* clang-format on */ + +const struct mg_iface_vtable mg_lwip_iface_vtable = MG_LWIP_IFACE_VTABLE; +#if MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL +const struct mg_iface_vtable mg_default_iface_vtable = MG_LWIP_IFACE_VTABLE; +#endif + +#endif /* MG_ENABLE_NET_IF_LWIP_LOW_LEVEL */ diff --git a/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_net_if.h b/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_net_if.h new file mode 100644 index 0000000..d00fd2f --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_net_if.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_LWIP_MG_NET_IF_LWIP_H_ +#define CS_COMMON_PLATFORMS_LWIP_MG_NET_IF_LWIP_H_ + +#ifndef MG_ENABLE_NET_IF_LWIP_LOW_LEVEL +#define MG_ENABLE_NET_IF_LWIP_LOW_LEVEL MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL +#endif + +#if MG_ENABLE_NET_IF_LWIP_LOW_LEVEL + +#include + +extern const struct mg_iface_vtable mg_lwip_iface_vtable; + +struct mg_lwip_conn_state { + struct mg_connection *nc; + struct mg_connection *lc; + union { + struct tcp_pcb *tcp; + struct udp_pcb *udp; + } pcb; + err_t err; + size_t num_sent; /* Number of acknowledged bytes to be reported to the core */ + struct pbuf *rx_chain; /* Chain of incoming data segments. */ + size_t rx_offset; /* Offset within the first pbuf (if partially consumed) */ + /* Last SSL write size, for retries. */ + int last_ssl_write_size; + /* Whether MG_SIG_RECV is already pending for this connection */ + int recv_pending : 1; + /* Whether the connection is about to close, just `rx_chain` needs to drain */ + int draining_rx_chain : 1; +}; + +enum mg_sig_type { + MG_SIG_CONNECT_RESULT = 1, + MG_SIG_RECV = 2, + MG_SIG_CLOSE_CONN = 3, + MG_SIG_TOMBSTONE = 4, + MG_SIG_ACCEPT = 5, +}; + +void mg_lwip_post_signal(enum mg_sig_type sig, struct mg_connection *nc); + +/* To be implemented by the platform. */ +void mg_lwip_mgr_schedule_poll(struct mg_mgr *mgr); + +#endif /* MG_ENABLE_NET_IF_LWIP_LOW_LEVEL */ + +#endif /* CS_COMMON_PLATFORMS_LWIP_MG_NET_IF_LWIP_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_ssl_if.c b/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_ssl_if.c new file mode 100644 index 0000000..04ed778 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_ssl_if.c @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_SSL && MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL + +#include "common/mg_mem.h" +#include "common/cs_dbg.h" + +#include +#include + +#ifndef MG_LWIP_SSL_IO_SIZE +#define MG_LWIP_SSL_IO_SIZE 1024 +#endif + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +void mg_lwip_ssl_do_hs(struct mg_connection *nc) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + int server_side = (nc->listener != NULL); + enum mg_ssl_if_result res; + if (nc->flags & MG_F_CLOSE_IMMEDIATELY) return; + res = mg_ssl_if_handshake(nc); + DBG(("%p %lu %d %d", nc, nc->flags, server_side, res)); + if (res != MG_SSL_OK) { + if (res == MG_SSL_WANT_WRITE) { + nc->flags |= MG_F_WANT_WRITE; + cs->err = 0; + } else if (res == MG_SSL_WANT_READ) { + /* + * Nothing to do in particular, we are callback-driven. + * What we definitely do not need anymore is SSL reading (nothing left). + */ + nc->flags &= ~MG_F_WANT_READ; + cs->err = 0; + } else { + cs->err = res; + if (server_side) { + mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); + } else { + mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); + } + } + } else { + cs->err = 0; + nc->flags &= ~MG_F_WANT_WRITE; + /* + * Handshake is done. Schedule a read immediately to consume app data + * which may already be waiting. + */ + nc->flags |= (MG_F_SSL_HANDSHAKE_DONE | MG_F_WANT_READ); + if (server_side) { + mg_lwip_accept_conn(nc, cs->pcb.tcp); + } else { + mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); + } + } +} + +void mg_lwip_ssl_send(struct mg_connection *nc) { + if (nc->sock == INVALID_SOCKET) { + DBG(("%p invalid socket", nc)); + return; + } + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + /* It's ok if the buffer is empty. Return value of 0 may also be valid. */ + int len = cs->last_ssl_write_size; + if (len == 0) { + len = MIN(MG_LWIP_SSL_IO_SIZE, nc->send_mbuf.len); + } + int ret = mg_ssl_if_write(nc, nc->send_mbuf.buf, len); + DBG(("%p SSL_write %u = %d", nc, len, ret)); + if (ret > 0) { + mg_if_sent_cb(nc, ret); + cs->last_ssl_write_size = 0; + } else if (ret < 0) { + /* This is tricky. We must remember the exact data we were sending to retry + * exactly the same send next time. */ + cs->last_ssl_write_size = len; + } + if (ret == len) { + nc->flags &= ~MG_F_WANT_WRITE; + } else if (ret == MG_SSL_WANT_WRITE) { + nc->flags |= MG_F_WANT_WRITE; + } else { + mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); + } +} + +void mg_lwip_ssl_recv(struct mg_connection *nc) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + /* Don't deliver data before connect callback */ + if (nc->flags & MG_F_CONNECTING) return; + while (nc->recv_mbuf.len < nc->recv_mbuf_limit) { + char *buf = (char *) MG_MALLOC(MG_LWIP_SSL_IO_SIZE); + if (buf == NULL) return; + int ret = mg_ssl_if_read(nc, buf, MG_LWIP_SSL_IO_SIZE); + DBG(("%p %p SSL_read %u = %d", nc, cs->rx_chain, MG_LWIP_SSL_IO_SIZE, ret)); + if (ret <= 0) { + MG_FREE(buf); + if (ret == MG_SSL_WANT_WRITE) { + nc->flags |= MG_F_WANT_WRITE; + return; + } else if (ret == MG_SSL_WANT_READ) { + /* + * Nothing to do in particular, we are callback-driven. + * What we definitely do not need anymore is SSL reading (nothing left). + */ + nc->flags &= ~MG_F_WANT_READ; + cs->err = 0; + return; + } else { + mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); + return; + } + } else { + mg_if_recv_tcp_cb(nc, buf, ret, 1 /* own */); + } + } +} + +#ifdef KR_VERSION + +ssize_t kr_send(int fd, const void *buf, size_t len) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) fd; + int ret = mg_lwip_tcp_write(cs->nc, buf, len); + DBG(("%p mg_lwip_tcp_write %u = %d", cs->nc, len, ret)); + if (ret == 0) ret = KR_IO_WOULDBLOCK; + return ret; +} + +ssize_t kr_recv(int fd, void *buf, size_t len) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) fd; + struct pbuf *seg = cs->rx_chain; + if (seg == NULL) { + DBG(("%u - nothing to read", len)); + return KR_IO_WOULDBLOCK; + } + size_t seg_len = (seg->len - cs->rx_offset); + DBG(("%u %u %u %u", len, cs->rx_chain->len, seg_len, cs->rx_chain->tot_len)); + len = MIN(len, seg_len); + pbuf_copy_partial(seg, buf, len, cs->rx_offset); + cs->rx_offset += len; + tcp_recved(cs->pcb.tcp, len); + if (cs->rx_offset == cs->rx_chain->len) { + cs->rx_chain = pbuf_dechain(cs->rx_chain); + pbuf_free(seg); + cs->rx_offset = 0; + } + return len; +} + +#elif MG_SSL_IF == MG_SSL_IF_MBEDTLS + +int ssl_socket_send(void *ctx, const unsigned char *buf, size_t len) { + struct mg_connection *nc = (struct mg_connection *) ctx; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + int ret = mg_lwip_tcp_write(cs->nc, buf, len); + if (ret == 0) ret = MBEDTLS_ERR_SSL_WANT_WRITE; + LOG(LL_DEBUG, ("%p %d -> %d", nc, len, ret)); + return ret; +} + +int ssl_socket_recv(void *ctx, unsigned char *buf, size_t len) { + struct mg_connection *nc = (struct mg_connection *) ctx; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + struct pbuf *seg = cs->rx_chain; + if (seg == NULL) { + DBG(("%u - nothing to read", len)); + return MBEDTLS_ERR_SSL_WANT_READ; + } + size_t seg_len = (seg->len - cs->rx_offset); + DBG(("%u %u %u %u", len, cs->rx_chain->len, seg_len, cs->rx_chain->tot_len)); + mgos_lock(); + len = MIN(len, seg_len); + pbuf_copy_partial(seg, buf, len, cs->rx_offset); + cs->rx_offset += len; + /* TCP PCB may be NULL if connection has already been closed + * but we still have data to deliver to SSL. */ + if (cs->pcb.tcp != NULL) tcp_recved(cs->pcb.tcp, len); + if (cs->rx_offset == cs->rx_chain->len) { + cs->rx_chain = pbuf_dechain(cs->rx_chain); + pbuf_free(seg); + cs->rx_offset = 0; + } + mgos_unlock(); + LOG(LL_DEBUG, ("%p <- %d", nc, (int) len)); + return len; +} + +#endif + +#endif /* MG_ENABLE_SSL && MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL */ diff --git a/src/mongoose-6.11/src/common/platforms/mbed/mbed_libc.c b/src/mongoose-6.11/src/common/platforms/mbed/mbed_libc.c new file mode 100644 index 0000000..a5b35b1 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/mbed/mbed_libc.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#include "common/platform.h" + +#if CS_PLATFORM == CS_P_MBED + +long timezone; + +/* + * The GCC ARM toolchain for implements a weak + * gettimeofday stub that should be implemented + * to hook the OS time source. But mbed OS doesn't do it; + * the mbed doc only talks about C date and time functions: + * + * https://docs.mbed.com/docs/mbed-os-api-reference/en/5.1/APIs/tasks/Time/ + * + * gettimeof day is a BSD API. + */ +int _gettimeofday(struct timeval *tv, void *tzvp) { + tv->tv_sec = time(NULL); + tv->tv_usec = 0; + return 0; +} + +int inet_aton(const char *cp, struct in_addr *inp) { + /* We don't have aton, but have pton in mbed */ + return inet_pton(AF_INET, cp, inp); +} + +in_addr_t inet_addr(const char *cp) { + in_addr_t ret; + if (inet_pton(AF_INET, cp, &ret) != 1) { + return 0; + } + + return ret; +} + +#endif /* CS_PLATFORM == CS_P_MBED */ diff --git a/src/mongoose-6.11/src/common/platforms/msp432/msp432_libc.c b/src/mongoose-6.11/src/common/platforms/msp432/msp432_libc.c new file mode 100644 index 0000000..faedadc --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/msp432/msp432_libc.c @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if CS_PLATFORM == CS_P_MSP432 + +#include +#include + +int gettimeofday(struct timeval *tp, void *tzp) { + uint32_t ticks = Clock_getTicks(); + tp->tv_sec = ticks / 1000; + tp->tv_usec = (ticks % 1000) * 1000; + return 0; +} + +#endif /* CS_PLATFORM == CS_P_MSP432 */ diff --git a/src/mongoose-6.11/src/common/platforms/nrf5/nrf5_libc.c b/src/mongoose-6.11/src/common/platforms/nrf5/nrf5_libc.c new file mode 100644 index 0000000..9cf66dc --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/nrf5/nrf5_libc.c @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if (CS_PLATFORM == CS_P_NRF51 || CS_PLATFORM == CS_P_NRF52) && \ + defined(__ARMCC_VERSION) +int gettimeofday(struct timeval *tp, void *tzp) { + /* TODO */ + tp->tv_sec = 0; + tp->tv_usec = 0; + return 0; +} +#endif diff --git a/src/mongoose-6.11/src/common/platforms/pic32/pic32_net_if.c b/src/mongoose-6.11/src/common/platforms/pic32/pic32_net_if.c new file mode 100644 index 0000000..d0e67e9 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/pic32/pic32_net_if.c @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_NET_IF_PIC32 + +int mg_pic32_if_create_conn(struct mg_connection *nc) { + (void) nc; + return 1; +} + +void mg_pic32_if_recved(struct mg_connection *nc, size_t len) { + (void) nc; + (void) len; +} + +void mg_pic32_if_add_conn(struct mg_connection *nc) { + (void) nc; +} + +void mg_pic32_if_init(struct mg_iface *iface) { + (void) iface; + (void) mg_get_errno(); /* Shutup compiler */ +} + +void mg_pic32_if_free(struct mg_iface *iface) { + (void) iface; +} + +void mg_pic32_if_remove_conn(struct mg_connection *nc) { + (void) nc; +} + +void mg_pic32_if_destroy_conn(struct mg_connection *nc) { + if (nc->sock == INVALID_SOCKET) return; + /* For UDP, only close outgoing sockets or listeners. */ + if (!(nc->flags & MG_F_UDP)) { + /* Close TCP */ + TCPIP_TCP_Close((TCP_SOCKET) nc->sock); + } else if (nc->listener == NULL) { + /* Only close outgoing UDP or listeners. */ + TCPIP_UDP_Close((UDP_SOCKET) nc->sock); + } + + nc->sock = INVALID_SOCKET; +} + +int mg_pic32_if_listen_udp(struct mg_connection *nc, union socket_address *sa) { + nc->sock = TCPIP_UDP_ServerOpen( + sa->sin.sin_family == AF_INET ? IP_ADDRESS_TYPE_IPV4 + : IP_ADDRESS_TYPE_IPV6, + ntohs(sa->sin.sin_port), + sa->sin.sin_addr.s_addr == 0 ? 0 : (IP_MULTI_ADDRESS *) &sa->sin); + if (nc->sock == INVALID_SOCKET) { + return -1; + } + return 0; +} + +void mg_pic32_if_udp_send(struct mg_connection *nc, const void *buf, + size_t len) { + mbuf_append(&nc->send_mbuf, buf, len); +} + +void mg_pic32_if_tcp_send(struct mg_connection *nc, const void *buf, + size_t len) { + mbuf_append(&nc->send_mbuf, buf, len); +} + +int mg_pic32_if_listen_tcp(struct mg_connection *nc, union socket_address *sa) { + nc->sock = TCPIP_TCP_ServerOpen( + sa->sin.sin_family == AF_INET ? IP_ADDRESS_TYPE_IPV4 + : IP_ADDRESS_TYPE_IPV6, + ntohs(sa->sin.sin_port), + sa->sin.sin_addr.s_addr == 0 ? 0 : (IP_MULTI_ADDRESS *) &sa->sin); + memcpy(&nc->sa, sa, sizeof(*sa)); + if (nc->sock == INVALID_SOCKET) { + return -1; + } + return 0; +} + +static int mg_accept_conn(struct mg_connection *lc) { + struct mg_connection *nc; + TCP_SOCKET_INFO si; + union socket_address sa; + + nc = mg_if_accept_new_conn(lc); + + if (nc == NULL) { + return 0; + } + + nc->sock = lc->sock; + nc->flags &= ~MG_F_LISTENING; + + if (!TCPIP_TCP_SocketInfoGet((TCP_SOCKET) nc->sock, &si)) { + return 0; + } + + if (si.addressType == IP_ADDRESS_TYPE_IPV4) { + sa.sin.sin_family = AF_INET; + sa.sin.sin_port = htons(si.remotePort); + sa.sin.sin_addr.s_addr = si.remoteIPaddress.v4Add.Val; + } else { + /* TODO(alashkin): do something with _potential_ IPv6 */ + memset(&sa, 0, sizeof(sa)); + } + + mg_if_accept_tcp_cb(nc, (union socket_address *) &sa, sizeof(sa)); + + return mg_pic32_if_listen_tcp(lc, &lc->sa) >= 0; +} + +char *inet_ntoa(struct in_addr in) { + static char addr[17]; + snprintf(addr, sizeof(addr), "%d.%d.%d.%d", (int) in.S_un.S_un_b.s_b1, + (int) in.S_un.S_un_b.s_b2, (int) in.S_un.S_un_b.s_b3, + (int) in.S_un.S_un_b.s_b4); + return addr; +} + +static void mg_handle_send(struct mg_connection *nc) { + uint16_t bytes_written = 0; + if (nc->flags & MG_F_UDP) { + if (!TCPIP_UDP_RemoteBind( + (UDP_SOCKET) nc->sock, + nc->sa.sin.sin_family == AF_INET ? IP_ADDRESS_TYPE_IPV4 + : IP_ADDRESS_TYPE_IPV6, + ntohs(nc->sa.sin.sin_port), (IP_MULTI_ADDRESS *) &nc->sa.sin)) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + return; + } + bytes_written = TCPIP_UDP_TxPutIsReady((UDP_SOCKET) nc->sock, 0); + if (bytes_written >= nc->send_mbuf.len) { + if (TCPIP_UDP_ArrayPut((UDP_SOCKET) nc->sock, + (uint8_t *) nc->send_mbuf.buf, + nc->send_mbuf.len) != nc->send_mbuf.len) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + bytes_written = 0; + } + } + } else { + bytes_written = TCPIP_TCP_FifoTxFreeGet((TCP_SOCKET) nc->sock); + if (bytes_written != 0) { + if (bytes_written > nc->send_mbuf.len) { + bytes_written = nc->send_mbuf.len; + } + if (TCPIP_TCP_ArrayPut((TCP_SOCKET) nc->sock, + (uint8_t *) nc->send_mbuf.buf, + bytes_written) != bytes_written) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + bytes_written = 0; + } + } + } + + mg_if_sent_cb(nc, bytes_written); +} + +static void mg_handle_recv(struct mg_connection *nc) { + uint16_t bytes_read = 0; + uint8_t *buf = NULL; + if (nc->flags & MG_F_UDP) { + bytes_read = TCPIP_UDP_GetIsReady((UDP_SOCKET) nc->sock); + if (bytes_read != 0 && + (nc->recv_mbuf_limit == -1 || + nc->recv_mbuf.len + bytes_read < nc->recv_mbuf_limit)) { + buf = (uint8_t *) MG_MALLOC(bytes_read); + if (TCPIP_UDP_ArrayGet((UDP_SOCKET) nc->sock, buf, bytes_read) != + bytes_read) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + bytes_read = 0; + MG_FREE(buf); + } + } + } else { + bytes_read = TCPIP_TCP_GetIsReady((TCP_SOCKET) nc->sock); + if (bytes_read != 0) { + if (nc->recv_mbuf_limit != -1 && + nc->recv_mbuf_limit - nc->recv_mbuf.len > bytes_read) { + bytes_read = nc->recv_mbuf_limit - nc->recv_mbuf.len; + } + buf = (uint8_t *) MG_MALLOC(bytes_read); + if (TCPIP_TCP_ArrayGet((TCP_SOCKET) nc->sock, buf, bytes_read) != + bytes_read) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + MG_FREE(buf); + bytes_read = 0; + } + } + } + + if (bytes_read != 0) { + mg_if_recv_tcp_cb(nc, buf, bytes_read, 1 /* own */); + } +} + +time_t mg_pic32_if_poll(struct mg_iface *iface, int timeout_ms) { + struct mg_mgr *mgr = iface->mgr; + double now = mg_time(); + struct mg_connection *nc, *tmp; + + for (nc = mgr->active_connections; nc != NULL; nc = tmp) { + tmp = nc->next; + + if (nc->flags & MG_F_CONNECTING) { + /* processing connections */ + if (nc->flags & MG_F_UDP || + TCPIP_TCP_IsConnected((TCP_SOCKET) nc->sock)) { + mg_if_connect_cb(nc, 0); + } + } else if (nc->flags & MG_F_LISTENING) { + if (TCPIP_TCP_IsConnected((TCP_SOCKET) nc->sock)) { + /* accept new connections */ + mg_accept_conn(nc); + } + } else { + if (nc->send_mbuf.len != 0) { + mg_handle_send(nc); + } + + if (nc->recv_mbuf_limit == -1 || + nc->recv_mbuf.len < nc->recv_mbuf_limit) { + mg_handle_recv(nc); + } + } + } + + for (nc = mgr->active_connections; nc != NULL; nc = tmp) { + tmp = nc->next; + if ((nc->flags & MG_F_CLOSE_IMMEDIATELY) || + (nc->send_mbuf.len == 0 && (nc->flags & MG_F_SEND_AND_CLOSE))) { + mg_close_conn(nc); + } + } + + return now; +} + +void mg_pic32_if_sock_set(struct mg_connection *nc, sock_t sock) { + nc->sock = sock; +} + +void mg_pic32_if_get_conn_addr(struct mg_connection *nc, int remote, + union socket_address *sa) { + /* TODO(alaskin): not implemented yet */ +} + +void mg_pic32_if_connect_tcp(struct mg_connection *nc, + const union socket_address *sa) { + nc->sock = TCPIP_TCP_ClientOpen( + sa->sin.sin_family == AF_INET ? IP_ADDRESS_TYPE_IPV4 + : IP_ADDRESS_TYPE_IPV6, + ntohs(sa->sin.sin_port), (IP_MULTI_ADDRESS *) &sa->sin); + nc->err = (nc->sock == INVALID_SOCKET) ? -1 : 0; +} + +void mg_pic32_if_connect_udp(struct mg_connection *nc) { + nc->sock = TCPIP_UDP_ClientOpen(IP_ADDRESS_TYPE_ANY, 0, NULL); + nc->err = (nc->sock == INVALID_SOCKET) ? -1 : 0; +} + +/* clang-format off */ +#define MG_PIC32_IFACE_VTABLE \ + { \ + mg_pic32_if_init, \ + mg_pic32_if_free, \ + mg_pic32_if_add_conn, \ + mg_pic32_if_remove_conn, \ + mg_pic32_if_poll, \ + mg_pic32_if_listen_tcp, \ + mg_pic32_if_listen_udp, \ + mg_pic32_if_connect_tcp, \ + mg_pic32_if_connect_udp, \ + mg_pic32_if_tcp_send, \ + mg_pic32_if_udp_send, \ + mg_pic32_if_recved, \ + mg_pic32_if_create_conn, \ + mg_pic32_if_destroy_conn, \ + mg_pic32_if_sock_set, \ + mg_pic32_if_get_conn_addr, \ + } +/* clang-format on */ + +const struct mg_iface_vtable mg_pic32_iface_vtable = MG_PIC32_IFACE_VTABLE; +#if MG_NET_IF == MG_NET_IF_PIC32 +const struct mg_iface_vtable mg_default_iface_vtable = MG_PIC32_IFACE_VTABLE; +#endif + +#endif /* MG_ENABLE_NET_IF_PIC32 */ diff --git a/src/mongoose-6.11/src/common/platforms/pic32/pic32_net_if.h b/src/mongoose-6.11/src/common/platforms/pic32/pic32_net_if.h new file mode 100644 index 0000000..2bf66ef --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/pic32/pic32_net_if.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PIC32_NET_IF_H_ +#define CS_COMMON_PLATFORMS_PIC32_NET_IF_H_ + +#include "mongoose/src/net_if.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef MG_ENABLE_NET_IF_PIC32 +#define MG_ENABLE_NET_IF_PIC32 MG_NET_IF == MG_NET_IF_PIC32 +#endif + +extern const struct mg_iface_vtable mg_pic32_iface_vtable; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CS_COMMON_PLATFORMS_PIC32_NET_IF_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/pic32/xc32.mk b/src/mongoose-6.11/src/common/platforms/pic32/xc32.mk new file mode 100644 index 0000000..aff6b4e --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/pic32/xc32.mk @@ -0,0 +1,51 @@ +APP_LDFLAGS ?= +CC_WRAPPER ?= +GENFILES_LIST ?= +CC = $(XC32_PATH)/bin/xc32-gcc +AR = $(XC32_PATH)/bin/xc32-ar +LD = $(XC32_PATH)/bin/xc32-gcc +BIN2HEX = $(XC32_PATH)/bin/xc32-bin2hex + +CFLAGS = -fframe-base-loclist -mprocessor=32MZ2048EFM064 \ + -ffunction-sections -Wall -g -no-legacy-libc \ + -O1 + +CFLAGS += -DCS_ENABLE_DEBUG + +LDFLAGS = -mprocessor=32MZ2048EFM064 + +# TODO(dfrank) enable that conditionally +CFLAGS += -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 +LDFLAGS += -mdebugger -D__MPLAB_DEBUGGER_ICD3=1 + +OBJCOPY = $(XC32_PATH)/bin/xc32-objcopy + +# cc flags,file +define cc + $(vecho) "XC32 $2 -> $@" + $(Q) $(CC_WRAPPER) $(CC) -c $1 -o $@ $2 +endef + +# cc flags,file +define asm + $(vecho) "XC32 ASM $2 -> $@" + $(Q) $(CC) -c $1 -o $@ $2 +endef + +# ar files +define ar + $(vecho) "AR $@" + $(Q) $(AR) cru $@ $1 +endef + +# link script,flags,objs +define link + $(vecho) "LD $@ (ignoring linker script $1)" + $(Q) $(CC_WRAPPER) $(LD) -o $@ $2 $3 -no-legacy-libc \ + -Wl,--defsym=__MPLAB_BUILD=1,--defsym=__MPLAB_DEBUG=1,--defsym=__DEBUG=1,--defsym=__MPLAB_DEBUGGER_ICD3=1,--defsym=_min_heap_size=44960,--gc-sections +endef + +define bin2hex + $(vecho) "HEX $1 -> $@" + $(Q) $(BIN2HEX) $1 +endef diff --git a/src/mongoose-6.11/src/common/platforms/platform_cc3100.h b/src/mongoose-6.11/src/common/platforms/platform_cc3100.h new file mode 100644 index 0000000..ba07f22 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/platform_cc3100.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PLATFORM_CC3100_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_CC3100_H_ +#if CS_PLATFORM == CS_P_CC3100 + +#include +#include +#include +#include +#include +#include +#include + +#define MG_NET_IF MG_NET_IF_SIMPLELINK +#define MG_SSL_IF MG_SSL_IF_SIMPLELINK + +/* + * CC3100 SDK and STM32 SDK include headers w/out path, just like + * #include "simplelink.h". As result, we have to add all required directories + * into Makefile IPATH and do the same thing (include w/out path) + */ + +#include +#include +#undef timeval + +typedef int sock_t; +#define INVALID_SOCKET (-1) + +#define to64(x) strtoll(x, NULL, 10) +#define INT64_FMT PRId64 +#define INT64_X_FMT PRIx64 +#define SIZE_T_FMT "u" + +#define SOMAXCONN 8 + +const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); +char *inet_ntoa(struct in_addr in); +int inet_pton(int af, const char *src, void *dst); + +#endif /* CS_PLATFORM == CS_P_CC3100 */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_CC3100_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_cc3200.h b/src/mongoose-6.11/src/common/platforms/platform_cc3200.h new file mode 100644 index 0000000..1c2033a --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/platform_cc3200.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PLATFORM_CC3200_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_CC3200_H_ +#if CS_PLATFORM == CS_P_CC3200 + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef __TI_COMPILER_VERSION__ +#include +#include +#endif + +#define MG_NET_IF MG_NET_IF_SIMPLELINK +#define MG_SSL_IF MG_SSL_IF_SIMPLELINK + +/* Only SPIFFS supports directories, SLFS does not. */ +#if defined(CC3200_FS_SPIFFS) && !defined(MG_ENABLE_DIRECTORY_LISTING) +#define MG_ENABLE_DIRECTORY_LISTING 1 +#endif + +#include "common/platforms/simplelink/cs_simplelink.h" + +typedef int sock_t; +#define INVALID_SOCKET (-1) +#define SIZE_T_FMT "u" +typedef struct stat cs_stat_t; +#define DIRSEP '/' +#define to64(x) strtoll(x, NULL, 10) +#define INT64_FMT PRId64 +#define INT64_X_FMT PRIx64 +#define __cdecl + +#define fileno(x) -1 + +/* Some functions we implement for Mongoose. */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __TI_COMPILER_VERSION__ +struct SlTimeval_t; +#define timeval SlTimeval_t +int gettimeofday(struct timeval *t, void *tz); +int settimeofday(const struct timeval *tv, const void *tz); + +int asprintf(char **strp, const char *fmt, ...); + +#endif + +/* TI's libc does not have stat & friends, add them. */ +#ifdef __TI_COMPILER_VERSION__ + +#include + +typedef unsigned int mode_t; +typedef size_t _off_t; +typedef long ssize_t; + +struct stat { + int st_ino; + mode_t st_mode; + int st_nlink; + time_t st_mtime; + off_t st_size; +}; + +int _stat(const char *pathname, struct stat *st); +int stat(const char *pathname, struct stat *st); + +#define __S_IFMT 0170000 + +#define __S_IFDIR 0040000 +#define __S_IFCHR 0020000 +#define __S_IFREG 0100000 + +#define __S_ISTYPE(mode, mask) (((mode) &__S_IFMT) == (mask)) + +#define S_IFDIR __S_IFDIR +#define S_IFCHR __S_IFCHR +#define S_IFREG __S_IFREG +#define S_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR) +#define S_ISREG(mode) __S_ISTYPE((mode), __S_IFREG) + +/* 5.x series compilers don't have va_copy, 16.x do. */ +#if __TI_COMPILER_VERSION__ < 16000000 +#define va_copy(apc, ap) ((apc) = (ap)) +#endif + +#endif /* __TI_COMPILER_VERSION__ */ + +#ifdef CC3200_FS_SLFS +#define MG_FS_SLFS +#endif + +#if (defined(CC3200_FS_SPIFFS) || defined(CC3200_FS_SLFS)) && \ + !defined(MG_ENABLE_FILESYSTEM) +#define MG_ENABLE_FILESYSTEM 1 +#define CS_DEFINE_DIRENT +#endif + +#ifndef CS_ENABLE_STDIO +#define CS_ENABLE_STDIO 1 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* CS_PLATFORM == CS_P_CC3200 */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_CC3200_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_cc3220.h b/src/mongoose-6.11/src/common/platforms/platform_cc3220.h new file mode 100644 index 0000000..a32264a --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/platform_cc3220.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PLATFORM_CC3220_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_CC3220_H_ +#if CS_PLATFORM == CS_P_CC3220 + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef __TI_COMPILER_VERSION__ +#include +#include +#endif + +#define MG_NET_IF MG_NET_IF_SIMPLELINK +#define MG_SSL_IF MG_SSL_IF_SIMPLELINK + +/* Only SPIFFS supports directories, SLFS does not. */ +#if defined(CC3220_FS_SPIFFS) && !defined(MG_ENABLE_DIRECTORY_LISTING) +#define MG_ENABLE_DIRECTORY_LISTING 1 +#endif + +#include "common/platforms/simplelink/cs_simplelink.h" + +typedef int sock_t; +#define INVALID_SOCKET (-1) +#define SIZE_T_FMT "u" +typedef struct stat cs_stat_t; +#define DIRSEP '/' +#define to64(x) strtoll(x, NULL, 10) +#define INT64_FMT PRId64 +#define INT64_X_FMT PRIx64 +#define __cdecl + +#define fileno(x) -1 + +/* Some functions we implement for Mongoose. */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __TI_COMPILER_VERSION__ +struct SlTimeval_t; +#define timeval SlTimeval_t +int gettimeofday(struct timeval *t, void *tz); +int settimeofday(const struct timeval *tv, const void *tz); + +int asprintf(char **strp, const char *fmt, ...); + +#endif + +/* TI's libc does not have stat & friends, add them. */ +#ifdef __TI_COMPILER_VERSION__ + +#include + +typedef unsigned int mode_t; +typedef size_t _off_t; +typedef long ssize_t; + +struct stat { + int st_ino; + mode_t st_mode; + int st_nlink; + time_t st_mtime; + off_t st_size; +}; + +int _stat(const char *pathname, struct stat *st); +int stat(const char *pathname, struct stat *st); + +#define __S_IFMT 0170000 + +#define __S_IFDIR 0040000 +#define __S_IFCHR 0020000 +#define __S_IFREG 0100000 + +#define __S_ISTYPE(mode, mask) (((mode) &__S_IFMT) == (mask)) + +#define S_IFDIR __S_IFDIR +#define S_IFCHR __S_IFCHR +#define S_IFREG __S_IFREG +#define S_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR) +#define S_ISREG(mode) __S_ISTYPE((mode), __S_IFREG) + +#endif /* __TI_COMPILER_VERSION__ */ + +#ifndef CS_ENABLE_STDIO +#define CS_ENABLE_STDIO 1 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* CS_PLATFORM == CS_P_CC3220 */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_CC3200_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_esp32.h b/src/mongoose-6.11/src/common/platforms/platform_esp32.h new file mode 100644 index 0000000..4580f75 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/platform_esp32.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PLATFORM_ESP32_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_ESP32_H_ +#if CS_PLATFORM == CS_P_ESP32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SIZE_T_FMT "u" +typedef struct stat cs_stat_t; +#define DIRSEP '/' +#define to64(x) strtoll(x, NULL, 10) +#define INT64_FMT PRId64 +#define INT64_X_FMT PRIx64 +#define __cdecl +#define _FILE_OFFSET_BITS 32 + +#define MG_LWIP 1 + +#ifndef MG_NET_IF +#define MG_NET_IF MG_NET_IF_SOCKET +#endif + +#ifndef CS_ENABLE_STDIO +#define CS_ENABLE_STDIO 1 +#endif + +#endif /* CS_PLATFORM == CS_P_ESP32 */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_ESP32_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_esp8266.h b/src/mongoose-6.11/src/common/platforms/platform_esp8266.h new file mode 100644 index 0000000..b397101 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/platform_esp8266.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PLATFORM_ESP8266_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_ESP8266_H_ +#if CS_PLATFORM == CS_P_ESP8266 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SIZE_T_FMT "u" +typedef struct stat cs_stat_t; +#define DIRSEP '/' +#if !defined(MGOS_VFS_DEFINE_DIRENT) +#define CS_DEFINE_DIRENT +#endif + +#define to64(x) strtoll(x, NULL, 10) +#define INT64_FMT PRId64 +#define INT64_X_FMT PRIx64 +#define __cdecl +#define _FILE_OFFSET_BITS 32 + +#if !defined(RTOS_SDK) && !defined(__cplusplus) +#define fileno(x) -1 +#endif + +#define MG_LWIP 1 + +/* struct timeval is defined in sys/time.h. */ +#define LWIP_TIMEVAL_PRIVATE 0 + +#ifndef MG_NET_IF +#include +#if LWIP_SOCKET /* RTOS SDK has LWIP sockets */ +#define MG_NET_IF MG_NET_IF_SOCKET +#else +#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL +#endif +#endif + +#ifndef CS_ENABLE_STDIO +#define CS_ENABLE_STDIO 1 +#endif + +#define inet_ntop(af, src, dst, size) \ + (((af) == AF_INET) ? ipaddr_ntoa_r((const ip_addr_t *) (src), (dst), (size)) \ + : NULL) +#define inet_pton(af, src, dst) \ + (((af) == AF_INET) ? ipaddr_aton((src), (ip_addr_t *) (dst)) : 0) + +#endif /* CS_PLATFORM == CS_P_ESP8266 */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_ESP8266_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_mbed.h b/src/mongoose-6.11/src/common/platforms/platform_mbed.h new file mode 100644 index 0000000..ab3f563 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/platform_mbed.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PLATFORM_MBED_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_MBED_H_ +#if CS_PLATFORM == CS_P_MBED + +/* + * mbed.h contains C++ code (e.g. templates), thus, it should be processed + * only if included directly to startup file (ex: main.cpp) + */ +#ifdef __cplusplus +#include "mbed.h" +#endif /* __cplusplus */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct stat cs_stat_t; +#define DIRSEP '/' + +#ifndef CS_ENABLE_STDIO +#define CS_ENABLE_STDIO 1 +#endif + +/* + * mbed can be compiled with the ARM compiler which + * just doesn't come with a gettimeofday shim + * because it's a BSD API and ARM targets embedded + * non-unix platforms. + */ +#if defined(__ARMCC_VERSION) || defined(__ICCARM__) +#define _TIMEVAL_DEFINED +#define gettimeofday _gettimeofday + +/* copied from GCC on ARM; for some reason useconds are signed */ +typedef long suseconds_t; /* microseconds (signed) */ +struct timeval { + time_t tv_sec; /* seconds */ + suseconds_t tv_usec; /* and microseconds */ +}; + +#endif + +#if MG_NET_IF == MG_NET_IF_SIMPLELINK + +#define MG_SIMPLELINK_NO_OSI 1 + +#include + +typedef int sock_t; +#define INVALID_SOCKET (-1) + +#define to64(x) strtoll(x, NULL, 10) +#define INT64_FMT PRId64 +#define INT64_X_FMT PRIx64 +#define SIZE_T_FMT "u" + +#define SOMAXCONN 8 + +const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); +char *inet_ntoa(struct in_addr in); +int inet_pton(int af, const char *src, void *dst); +int inet_aton(const char *cp, struct in_addr *inp); +in_addr_t inet_addr(const char *cp); + +#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK */ + +#endif /* CS_PLATFORM == CS_P_MBED */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_MBED_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_msp432.h b/src/mongoose-6.11/src/common/platforms/platform_msp432.h new file mode 100644 index 0000000..d8cf16a --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/platform_msp432.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PLATFORM_MSP432_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_MSP432_H_ +#if CS_PLATFORM == CS_P_MSP432 + +#include +#include +#include +#include +#include +#include +#include + +#ifndef __TI_COMPILER_VERSION__ +#include +#include +#endif + +#define MG_NET_IF MG_NET_IF_SIMPLELINK +#define MG_SSL_IF MG_SSL_IF_SIMPLELINK + +#include "common/platforms/simplelink/cs_simplelink.h" + +typedef int sock_t; +#define INVALID_SOCKET (-1) +#define SIZE_T_FMT "u" +typedef struct stat cs_stat_t; +#define DIRSEP '/' +#define to64(x) strtoll(x, NULL, 10) +#define INT64_FMT PRId64 +#define INT64_X_FMT PRIx64 +#define __cdecl + +#define fileno(x) -1 + +/* Some functions we implement for Mongoose. */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __TI_COMPILER_VERSION__ +struct SlTimeval_t; +#define timeval SlTimeval_t +int gettimeofday(struct timeval *t, void *tz); +#endif + +/* TI's libc does not have stat & friends, add them. */ +#ifdef __TI_COMPILER_VERSION__ + +#include + +typedef unsigned int mode_t; +typedef size_t _off_t; +typedef long ssize_t; + +struct stat { + int st_ino; + mode_t st_mode; + int st_nlink; + time_t st_mtime; + off_t st_size; +}; + +int _stat(const char *pathname, struct stat *st); +#define stat(a, b) _stat(a, b) + +#define __S_IFMT 0170000 + +#define __S_IFDIR 0040000 +#define __S_IFCHR 0020000 +#define __S_IFREG 0100000 + +#define __S_ISTYPE(mode, mask) (((mode) &__S_IFMT) == (mask)) + +#define S_IFDIR __S_IFDIR +#define S_IFCHR __S_IFCHR +#define S_IFREG __S_IFREG +#define S_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR) +#define S_ISREG(mode) __S_ISTYPE((mode), __S_IFREG) + +/* As of 5.2.7, TI compiler does not support va_copy() yet. */ +#define va_copy(apc, ap) ((apc) = (ap)) + +#endif /* __TI_COMPILER_VERSION__ */ + +#ifndef CS_ENABLE_STDIO +#define CS_ENABLE_STDIO 1 +#endif + +#if (defined(CC3200_FS_SPIFFS) || defined(CC3200_FS_SLFS)) && \ + !defined(MG_ENABLE_FILESYSTEM) +#define MG_ENABLE_FILESYSTEM 1 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* CS_PLATFORM == CS_P_MSP432 */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_MSP432_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_nrf51.h b/src/mongoose-6.11/src/common/platforms/platform_nrf51.h new file mode 100644 index 0000000..55f89dd --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/platform_nrf51.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ +#ifndef CS_COMMON_PLATFORMS_PLATFORM_NRF51_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_NRF51_H_ +#if CS_PLATFORM == CS_P_NRF51 + +#include +#include +#include +#include +#include +#include + +#define to64(x) strtoll(x, NULL, 10) + +#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL +#define MG_LWIP 1 +#define MG_ENABLE_IPV6 1 + +/* + * For ARM C Compiler, make lwip to export `struct timeval`; for other + * compilers, suppress it. + */ +#if !defined(__ARMCC_VERSION) +#define LWIP_TIMEVAL_PRIVATE 0 +#else +struct timeval; +int gettimeofday(struct timeval *tp, void *tzp); +#endif + +#define INT64_FMT PRId64 +#define SIZE_T_FMT "u" + +/* + * ARM C Compiler doesn't have strdup, so we provide it + */ +#define CS_ENABLE_STRDUP defined(__ARMCC_VERSION) + +#endif /* CS_PLATFORM == CS_P_NRF51 */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_NRF51_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_nrf52.h b/src/mongoose-6.11/src/common/platforms/platform_nrf52.h new file mode 100644 index 0000000..3516cf9 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/platform_nrf52.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ +#ifndef CS_COMMON_PLATFORMS_PLATFORM_NRF52_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_NRF52_H_ +#if CS_PLATFORM == CS_P_NRF52 + +#include +#include +#include +#include +#include +#include +#include +#include + +#define to64(x) strtoll(x, NULL, 10) + +#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL +#define MG_LWIP 1 +#define MG_ENABLE_IPV6 1 + +#if !defined(ENOSPC) +#define ENOSPC 28 /* No space left on device */ +#endif + +/* + * For ARM C Compiler, make lwip to export `struct timeval`; for other + * compilers, suppress it. + */ +#if !defined(__ARMCC_VERSION) +#define LWIP_TIMEVAL_PRIVATE 0 +#endif + +#define INT64_FMT PRId64 +#define SIZE_T_FMT "u" + +/* + * ARM C Compiler doesn't have strdup, so we provide it + */ +#define CS_ENABLE_STRDUP defined(__ARMCC_VERSION) + +#endif /* CS_PLATFORM == CS_P_NRF52 */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_NRF52_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_nxp_kinetis.h b/src/mongoose-6.11/src/common/platforms/platform_nxp_kinetis.h new file mode 100644 index 0000000..b4ffd53 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/platform_nxp_kinetis.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PLATFORM_NXP_KINETIS_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_NXP_KINETIS_H_ + +#if CS_PLATFORM == CS_P_NXP_KINETIS + +#include +#include +#include +#include + +#define SIZE_T_FMT "u" +typedef struct stat cs_stat_t; +#define to64(x) strtoll(x, NULL, 10) +#define INT64_FMT "lld" +#define INT64_X_FMT "llx" +#define __cdecl + +#define MG_LWIP 1 + +#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL + +/* struct timeval is defined in sys/time.h. */ +#define LWIP_TIMEVAL_PRIVATE 0 + +#endif /* CS_PLATFORM == CS_P_NXP_KINETIS */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_NXP_KINETIS_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_nxp_lpc.h b/src/mongoose-6.11/src/common/platforms/platform_nxp_lpc.h new file mode 100644 index 0000000..cf9b7e7 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/platform_nxp_lpc.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PLATFORM_NXP_LPC_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_NXP_LPC_H_ + +#if CS_PLATFORM == CS_P_NXP_LPC + +#include +#include +#include + +#define SIZE_T_FMT "u" +typedef struct stat cs_stat_t; +#define INT64_FMT "lld" +#define INT64_X_FMT "llx" +#define __cdecl + +#define MG_LWIP 1 + +#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL + +/* + * LPCXpress comes with 3 C library implementations: Newlib, NewlibNano and + *Redlib. + * See https://community.nxp.com/message/630860 for more details. + * + * Redlib is the default and lacks certain things, so we provide them. + */ +#ifdef __REDLIB_INTERFACE_VERSION__ + +/* Let LWIP define timeval for us. */ +#define LWIP_TIMEVAL_PRIVATE 1 + +#define va_copy(d, s) __builtin_va_copy(d, s) + +#define CS_ENABLE_TO64 1 +#define to64(x) cs_to64(x) + +#define CS_ENABLE_STRDUP 1 + +#else + +#include +#define LWIP_TIMEVAL_PRIVATE 0 +#define to64(x) strtoll(x, NULL, 10) + +#endif + +#endif /* CS_PLATFORM == CS_P_NXP_LPC */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_NXP_LPC_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_pic32.h b/src/mongoose-6.11/src/common/platforms/platform_pic32.h new file mode 100644 index 0000000..b568025 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/platform_pic32.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PLATFORM_PIC32_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_PIC32_H_ + +#if CS_PLATFORM == CS_P_PIC32 + +#define MG_NET_IF MG_NET_IF_PIC32 + +#include +#include +#include +#include + +#include +#include + +#include + +typedef TCP_SOCKET sock_t; +#define to64(x) strtoll(x, NULL, 10) + +#define SIZE_T_FMT "lu" +#define INT64_FMT "lld" + +#ifndef CS_ENABLE_STDIO +#define CS_ENABLE_STDIO 1 +#endif + +char *inet_ntoa(struct in_addr in); + +#endif /* CS_PLATFORM == CS_P_PIC32 */ + +#endif /* CS_COMMON_PLATFORMS_PLATFORM_PIC32_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_stm32.h b/src/mongoose-6.11/src/common/platforms/platform_stm32.h new file mode 100644 index 0000000..ece78c8 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/platform_stm32.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PLATFORM_STM32_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_STM32_H_ +#if CS_PLATFORM == CS_P_STM32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define to64(x) strtoll(x, NULL, 10) +#define INT64_FMT PRId64 +#define SIZE_T_FMT "u" +typedef struct stat cs_stat_t; +#define DIRSEP '/' + +#ifndef CS_ENABLE_STDIO +#define CS_ENABLE_STDIO 1 +#endif + +#ifndef MG_ENABLE_FILESYSTEM +#define MG_ENABLE_FILESYSTEM 1 +#endif + +#endif /* CS_PLATFORM == CS_P_STM32 */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_STM32_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_tm4c129.h b/src/mongoose-6.11/src/common/platforms/platform_tm4c129.h new file mode 100644 index 0000000..5e87b94 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/platform_tm4c129.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PLATFORM_TM4C129_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_TM4C129_H_ +#if CS_PLATFORM == CS_P_TM4C129 + +#include +#include +#include +#include +#include +#include +#include + +#ifndef __TI_COMPILER_VERSION__ +#include +#include +#endif + +#define SIZE_T_FMT "u" +typedef struct stat cs_stat_t; +#define DIRSEP '/' +#define to64(x) strtoll(x, NULL, 10) +#define INT64_FMT PRId64 +#define INT64_X_FMT PRIx64 +#define __cdecl + +#ifndef MG_NET_IF +#include +#if LWIP_SOCKET +#define MG_NET_IF MG_NET_IF_SOCKET +#else +#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL +#endif +#define MG_LWIP 1 +#elif MG_NET_IF == MG_NET_IF_SIMPLELINK +#include "common/platforms/simplelink/cs_simplelink.h" +#endif + +#ifndef CS_ENABLE_STDIO +#define CS_ENABLE_STDIO 1 +#endif + +#ifdef __TI_COMPILER_VERSION__ +/* As of 5.2.8, TI compiler does not support va_copy() yet. */ +#define va_copy(apc, ap) ((apc) = (ap)) +#endif /* __TI_COMPILER_VERSION__ */ + +#ifdef __cplusplus +} +#endif + +#endif /* CS_PLATFORM == CS_P_TM4C129 */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_TM4C129_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_unix.h b/src/mongoose-6.11/src/common/platforms/platform_unix.h new file mode 100644 index 0000000..59a04e3 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/platform_unix.h @@ -0,0 +1,145 @@ +#ifndef CS_COMMON_PLATFORMS_PLATFORM_UNIX_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_UNIX_H_ +#if CS_PLATFORM == CS_P_UNIX + +#ifndef _XOPEN_SOURCE +#define _XOPEN_SOURCE 600 +#endif + +/* wants this for C++ */ +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif + +/* C++ wants that for INT64_MAX */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS +#endif + +/* Enable fseeko() and ftello() functions */ +#ifndef _LARGEFILE_SOURCE +#define _LARGEFILE_SOURCE +#endif + +/* Enable 64-bit file offsets */ +#ifndef _FILE_OFFSET_BITS +#define _FILE_OFFSET_BITS 64 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __APPLE__ +#include +#ifndef BYTE_ORDER +#define LITTLE_ENDIAN __DARWIN_LITTLE_ENDIAN +#define BIG_ENDIAN __DARWIN_BIG_ENDIAN +#define PDP_ENDIAN __DARWIN_PDP_ENDIAN +#define BYTE_ORDER __DARWIN_BYTE_ORDER +#endif +#endif + +/* + * osx correctly avoids defining strtoll when compiling in strict ansi mode. + * c++ 11 standard defines strtoll as well. + * We require strtoll, and if your embedded pre-c99 compiler lacks one, please + * implement a shim. + */ +#if !(defined(__cplusplus) && __cplusplus >= 201103L) && \ + !(defined(__DARWIN_C_LEVEL) && __DARWIN_C_LEVEL >= 200809L) +long long strtoll(const char *, char **, int); +#endif + +typedef int sock_t; +#define INVALID_SOCKET (-1) +#define SIZE_T_FMT "zu" +typedef struct stat cs_stat_t; +#define DIRSEP '/' +#define to64(x) strtoll(x, NULL, 10) +#define INT64_FMT PRId64 +#define INT64_X_FMT PRIx64 + +#ifndef __cdecl +#define __cdecl +#endif + +#ifndef va_copy +#ifdef __va_copy +#define va_copy __va_copy +#else +#define va_copy(x, y) (x) = (y) +#endif +#endif + +#define closesocket(x) close(x) + +#ifndef MG_MAX_HTTP_REQUEST_SIZE +#define MG_MAX_HTTP_REQUEST_SIZE 8192 +#endif + +#ifndef MG_MAX_HTTP_SEND_MBUF +#define MG_MAX_HTTP_SEND_MBUF 4096 +#endif + +#ifndef MG_MAX_HTTP_HEADERS +#define MG_MAX_HTTP_HEADERS 40 +#endif + +#ifndef CS_ENABLE_STDIO +#define CS_ENABLE_STDIO 1 +#endif + +#ifndef MG_ENABLE_BROADCAST +#define MG_ENABLE_BROADCAST 1 +#endif + +#ifndef MG_ENABLE_DIRECTORY_LISTING +#define MG_ENABLE_DIRECTORY_LISTING 1 +#endif + +#ifndef MG_ENABLE_FILESYSTEM +#define MG_ENABLE_FILESYSTEM 1 +#endif + +#ifndef MG_ENABLE_HTTP_CGI +#define MG_ENABLE_HTTP_CGI MG_ENABLE_FILESYSTEM +#endif + +#ifndef MG_NET_IF +#define MG_NET_IF MG_NET_IF_SOCKET +#endif + +#ifndef MG_HOSTS_FILE_NAME +#define MG_HOSTS_FILE_NAME "/etc/hosts" +#endif + +#ifndef MG_RESOLV_CONF_FILE_NAME +#define MG_RESOLV_CONF_FILE_NAME "/etc/resolv.conf" +#endif + +#endif /* CS_PLATFORM == CS_P_UNIX */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_UNIX_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_wince.h b/src/mongoose-6.11/src/common/platforms/platform_wince.h new file mode 100644 index 0000000..48c51f6 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/platform_wince.h @@ -0,0 +1,201 @@ +#ifndef CS_COMMON_PLATFORMS_PLATFORM_WINCE_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_WINCE_H_ + +#if CS_PLATFORM == CS_P_WINCE + +/* + * MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015) + * MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013) + * MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012) + * MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010) + * MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008) + * MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005) + * MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio 2003) + * MSVC++ 7.0 _MSC_VER == 1300 + * MSVC++ 6.0 _MSC_VER == 1200 + * MSVC++ 5.0 _MSC_VER == 1100 + */ +#pragma warning(disable : 4127) /* FD_SET() emits warning, disable it */ +#pragma warning(disable : 4204) /* missing c99 support */ + +#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS +#define _WINSOCK_DEPRECATED_NO_WARNINGS 1 +#endif + +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include +#include +#include +#include + +#pragma comment(lib, "ws2.lib") /* Linking with WinCE winsock library */ + +#include +#include +#include + +#define strdup _strdup + +#ifndef EINPROGRESS +#define EINPROGRESS WSAEINPROGRESS +#endif + +#ifndef EWOULDBLOCK +#define EWOULDBLOCK WSAEWOULDBLOCK +#endif + +#ifndef EAGAIN +#define EAGAIN EWOULDBLOCK +#endif + +#ifndef __func__ +#define STRX(x) #x +#define STR(x) STRX(x) +#define __func__ __FILE__ ":" STR(__LINE__) +#endif + +#define snprintf _snprintf +#define fileno _fileno +#define vsnprintf _vsnprintf +#define sleep(x) Sleep((x) *1000) +#define to64(x) _atoi64(x) +#define rmdir _rmdir + +#if defined(_MSC_VER) && _MSC_VER >= 1400 +#define fseeko(x, y, z) _fseeki64((x), (y), (z)) +#else +#define fseeko(x, y, z) fseek((x), (y), (z)) +#endif + +typedef int socklen_t; + +#if _MSC_VER >= 1700 +#include +#else +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef int int32_t; +typedef unsigned int uint32_t; +typedef short int16_t; +typedef unsigned short uint16_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#endif + +typedef SOCKET sock_t; +typedef uint32_t in_addr_t; + +#ifndef UINT16_MAX +#define UINT16_MAX 65535 +#endif + +#ifndef UINT32_MAX +#define UINT32_MAX 4294967295 +#endif + +#ifndef pid_t +#define pid_t HANDLE +#endif + +#define INT64_FMT "I64d" +#define INT64_X_FMT "I64x" +/* TODO(alashkin): check if this is correct */ +#define SIZE_T_FMT "u" + +#define DIRSEP '\\' +#define CS_DEFINE_DIRENT + +#ifndef va_copy +#ifdef __va_copy +#define va_copy __va_copy +#else +#define va_copy(x, y) (x) = (y) +#endif +#endif + +#ifndef MG_MAX_HTTP_REQUEST_SIZE +#define MG_MAX_HTTP_REQUEST_SIZE 8192 +#endif + +#ifndef MG_MAX_HTTP_SEND_MBUF +#define MG_MAX_HTTP_SEND_MBUF 4096 +#endif + +#ifndef MG_MAX_HTTP_HEADERS +#define MG_MAX_HTTP_HEADERS 40 +#endif + +#ifndef CS_ENABLE_STDIO +#define CS_ENABLE_STDIO 1 +#endif + +#define abort() DebugBreak(); + +#ifndef BUFSIZ +#define BUFSIZ 4096 +#endif +/* + * Explicitly disabling MG_ENABLE_THREADS for WinCE + * because they are enabled for _WIN32 by default + */ +#ifndef MG_ENABLE_THREADS +#define MG_ENABLE_THREADS 0 +#endif + +#ifndef MG_ENABLE_FILESYSTEM +#define MG_ENABLE_FILESYSTEM 1 +#endif + +#ifndef MG_NET_IF +#define MG_NET_IF MG_NET_IF_SOCKET +#endif + +typedef struct _stati64 { + uint32_t st_mtime; + uint32_t st_size; + uint32_t st_mode; +} cs_stat_t; + +/* + * WinCE 6.0 has a lot of useful definitions in ATL (not windows.h) headers + * use #ifdefs to avoid conflicts + */ + +#ifndef ENOENT +#define ENOENT ERROR_PATH_NOT_FOUND +#endif + +#ifndef EACCES +#define EACCES ERROR_ACCESS_DENIED +#endif + +#ifndef ENOMEM +#define ENOMEM ERROR_NOT_ENOUGH_MEMORY +#endif + +#ifndef _UINTPTR_T_DEFINED +typedef unsigned int *uintptr_t; +#endif + +#define _S_IFREG 2 +#define _S_IFDIR 4 + +#ifndef S_ISDIR +#define S_ISDIR(x) (((x) &_S_IFDIR) != 0) +#endif + +#ifndef S_ISREG +#define S_ISREG(x) (((x) &_S_IFREG) != 0) +#endif + +int open(const char *filename, int oflag, int pmode); +int _wstati64(const wchar_t *path, cs_stat_t *st); +const char *strerror(); + +#endif /* CS_PLATFORM == CS_P_WINCE */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_WINCE_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_windows.h b/src/mongoose-6.11/src/common/platforms/platform_windows.h new file mode 100644 index 0000000..3b300bd --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/platform_windows.h @@ -0,0 +1,183 @@ +#ifndef CS_COMMON_PLATFORMS_PLATFORM_WINDOWS_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_WINDOWS_H_ +#if CS_PLATFORM == CS_P_WINDOWS + +/* + * MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015) + * MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013) + * MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012) + * MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010) + * MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008) + * MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005) + * MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio 2003) + * MSVC++ 7.0 _MSC_VER == 1300 + * MSVC++ 6.0 _MSC_VER == 1200 + * MSVC++ 5.0 _MSC_VER == 1100 + */ +#ifdef _MSC_VER +#pragma warning(disable : 4127) /* FD_SET() emits warning, disable it */ +#pragma warning(disable : 4204) /* missing c99 support */ +#endif + +#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS +#define _WINSOCK_DEPRECATED_NO_WARNINGS 1 +#endif + +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") /* Linking with winsock library */ +#endif + +#include +#include +#include +#include + +#if _MSC_VER < 1700 +typedef int bool; +#else +#include +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1800 +#define strdup _strdup +#endif + +#ifndef EINPROGRESS +#define EINPROGRESS WSAEINPROGRESS +#endif +#ifndef EWOULDBLOCK +#define EWOULDBLOCK WSAEWOULDBLOCK +#endif +#ifndef __func__ +#define STRX(x) #x +#define STR(x) STRX(x) +#define __func__ __FILE__ ":" STR(__LINE__) +#endif +#define snprintf _snprintf +#define vsnprintf _vsnprintf +#define to64(x) _atoi64(x) +#if !defined(__MINGW32__) && !defined(__MINGW64__) +#define popen(x, y) _popen((x), (y)) +#define pclose(x) _pclose(x) +#define fileno _fileno +#endif +#if defined(_MSC_VER) && _MSC_VER >= 1400 +#define fseeko(x, y, z) _fseeki64((x), (y), (z)) +#else +#define fseeko(x, y, z) fseek((x), (y), (z)) +#endif +#if defined(_MSC_VER) && _MSC_VER <= 1200 +typedef unsigned long uintptr_t; +typedef long intptr_t; +#endif +typedef int socklen_t; +#if _MSC_VER >= 1700 +#include +#else +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef int int32_t; +typedef unsigned int uint32_t; +typedef short int16_t; +typedef unsigned short uint16_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#endif +typedef SOCKET sock_t; +typedef uint32_t in_addr_t; +#ifndef UINT16_MAX +#define UINT16_MAX 65535 +#endif +#ifndef UINT32_MAX +#define UINT32_MAX 4294967295 +#endif +#ifndef pid_t +#define pid_t HANDLE +#endif +#define INT64_FMT "I64d" +#define INT64_X_FMT "I64x" +#define SIZE_T_FMT "Iu" +typedef struct _stati64 cs_stat_t; +#ifndef S_ISDIR +#define S_ISDIR(x) (((x) &_S_IFMT) == _S_IFDIR) +#endif +#ifndef S_ISREG +#define S_ISREG(x) (((x) &_S_IFMT) == _S_IFREG) +#endif +#define DIRSEP '\\' +#define CS_DEFINE_DIRENT + +#ifndef va_copy +#ifdef __va_copy +#define va_copy __va_copy +#else +#define va_copy(x, y) (x) = (y) +#endif +#endif + +#ifndef MG_MAX_HTTP_REQUEST_SIZE +#define MG_MAX_HTTP_REQUEST_SIZE 8192 +#endif + +#ifndef MG_MAX_HTTP_SEND_MBUF +#define MG_MAX_HTTP_SEND_MBUF 4096 +#endif + +#ifndef MG_MAX_HTTP_HEADERS +#define MG_MAX_HTTP_HEADERS 40 +#endif + +#ifndef CS_ENABLE_STDIO +#define CS_ENABLE_STDIO 1 +#endif + +#ifndef MG_ENABLE_BROADCAST +#define MG_ENABLE_BROADCAST 1 +#endif + +#ifndef MG_ENABLE_DIRECTORY_LISTING +#define MG_ENABLE_DIRECTORY_LISTING 1 +#endif + +#ifndef MG_ENABLE_FILESYSTEM +#define MG_ENABLE_FILESYSTEM 1 +#endif + +#ifndef MG_ENABLE_HTTP_CGI +#define MG_ENABLE_HTTP_CGI MG_ENABLE_FILESYSTEM +#endif + +#ifndef MG_NET_IF +#define MG_NET_IF MG_NET_IF_SOCKET +#endif + +unsigned int sleep(unsigned int seconds); + +/* https://stackoverflow.com/questions/16647819/timegm-cross-platform */ +#define timegm _mkgmtime + +#define gmtime_r(a, b) \ + do { \ + *(b) = *gmtime(a); \ + } while (0) + +#endif /* CS_PLATFORM == CS_P_WINDOWS */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_WINDOWS_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/simplelink/cs_simplelink.h b/src/mongoose-6.11/src/common/platforms/simplelink/cs_simplelink.h new file mode 100644 index 0000000..ed6afa2 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/simplelink/cs_simplelink.h @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_SIMPLELINK_CS_SIMPLELINK_H_ +#define CS_COMMON_PLATFORMS_SIMPLELINK_CS_SIMPLELINK_H_ + +#if defined(MG_NET_IF) && MG_NET_IF == MG_NET_IF_SIMPLELINK + +/* If simplelink.h is already included, all bets are off. */ +#if !defined(__SIMPLELINK_H__) + +#include + +#ifndef __TI_COMPILER_VERSION__ +#undef __CONCAT +#undef FD_CLR +#undef FD_ISSET +#undef FD_SET +#undef FD_SETSIZE +#undef FD_ZERO +#undef fd_set +#endif + +#if CS_PLATFORM == CS_P_CC3220 +#include +#include +#include +#include +#else +/* We want to disable SL_INC_STD_BSD_API_NAMING, so we include user.h ourselves + * and undef it. */ +#define PROVISIONING_API_H_ +#include +#undef PROVISIONING_API_H_ +#undef SL_INC_STD_BSD_API_NAMING + +#include +#include +#endif /* CS_PLATFORM == CS_P_CC3220 */ + +/* Now define only the subset of the BSD API that we use. + * Notably, close(), read() and write() are not defined. */ +#define AF_INET SL_AF_INET + +#define socklen_t SlSocklen_t +#define sockaddr SlSockAddr_t +#define sockaddr_in SlSockAddrIn_t +#define in_addr SlInAddr_t + +#define SOCK_STREAM SL_SOCK_STREAM +#define SOCK_DGRAM SL_SOCK_DGRAM + +#define htonl sl_Htonl +#define ntohl sl_Ntohl +#define htons sl_Htons +#define ntohs sl_Ntohs + +#ifndef EACCES +#define EACCES SL_EACCES +#endif +#ifndef EAFNOSUPPORT +#define EAFNOSUPPORT SL_EAFNOSUPPORT +#endif +#ifndef EAGAIN +#define EAGAIN SL_EAGAIN +#endif +#ifndef EBADF +#define EBADF SL_EBADF +#endif +#ifndef EINVAL +#define EINVAL SL_EINVAL +#endif +#ifndef ENOMEM +#define ENOMEM SL_ENOMEM +#endif +#ifndef EWOULDBLOCK +#define EWOULDBLOCK SL_EWOULDBLOCK +#endif + +#define SOMAXCONN 8 + +#ifdef __cplusplus +extern "C" { +#endif + +const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); +char *inet_ntoa(struct in_addr in); +int inet_pton(int af, const char *src, void *dst); + +struct mg_mgr; +struct mg_connection; + +typedef void (*mg_init_cb)(struct mg_mgr *mgr); +bool mg_start_task(int priority, int stack_size, mg_init_cb mg_init); + +void mg_run_in_task(void (*cb)(struct mg_mgr *mgr, void *arg), void *cb_arg); + +int sl_fs_init(void); + +void sl_restart_cb(struct mg_mgr *mgr); + +int sl_set_ssl_opts(int sock, struct mg_connection *nc); + +#ifdef __cplusplus +} +#endif + +#endif /* !defined(__SIMPLELINK_H__) */ + +/* Compatibility with older versions of SimpleLink */ +#if SL_MAJOR_VERSION_NUM < 2 + +#define SL_ERROR_BSD_EAGAIN SL_EAGAIN +#define SL_ERROR_BSD_EALREADY SL_EALREADY +#define SL_ERROR_BSD_ENOPROTOOPT SL_ENOPROTOOPT +#define SL_ERROR_BSD_ESECDATEERROR SL_ESECDATEERROR +#define SL_ERROR_BSD_ESECSNOVERIFY SL_ESECSNOVERIFY +#define SL_ERROR_FS_FAILED_TO_ALLOCATE_MEM SL_FS_ERR_FAILED_TO_ALLOCATE_MEM +#define SL_ERROR_FS_FILE_HAS_NOT_BEEN_CLOSE_CORRECTLY \ + SL_FS_FILE_HAS_NOT_BEEN_CLOSE_CORRECTLY +#define SL_ERROR_FS_FILE_NAME_EXIST SL_FS_FILE_NAME_EXIST +#define SL_ERROR_FS_FILE_NOT_EXISTS SL_FS_ERR_FILE_NOT_EXISTS +#define SL_ERROR_FS_NO_AVAILABLE_NV_INDEX SL_FS_ERR_NO_AVAILABLE_NV_INDEX +#define SL_ERROR_FS_NOT_ENOUGH_STORAGE_SPACE SL_FS_ERR_NO_AVAILABLE_BLOCKS +#define SL_ERROR_FS_NOT_SUPPORTED SL_FS_ERR_NOT_SUPPORTED +#define SL_ERROR_FS_WRONG_FILE_NAME SL_FS_WRONG_FILE_NAME +#define SL_ERROR_FS_INVALID_HANDLE SL_FS_ERR_INVALID_HANDLE +#define SL_NETCFG_MAC_ADDRESS_GET SL_MAC_ADDRESS_GET +#define SL_SOCKET_FD_ZERO SL_FD_ZERO +#define SL_SOCKET_FD_SET SL_FD_SET +#define SL_SOCKET_FD_ISSET SL_FD_ISSET +#define SL_SO_SECURE_DOMAIN_NAME_VERIFICATION SO_SECURE_DOMAIN_NAME_VERIFICATION + +#define SL_FS_READ FS_MODE_OPEN_READ +#define SL_FS_WRITE FS_MODE_OPEN_WRITE + +#define SL_FI_FILE_SIZE(fi) ((fi).FileLen) +#define SL_FI_FILE_MAX_SIZE(fi) ((fi).AllocatedLen) + +#define SlDeviceVersion_t SlVersionFull +#define sl_DeviceGet sl_DevGet +#define SL_DEVICE_GENERAL SL_DEVICE_GENERAL_CONFIGURATION +#define SL_LEN_TYPE _u8 +#define SL_OPT_TYPE _u8 + +#else /* SL_MAJOR_VERSION_NUM >= 2 */ + +#define FS_MODE_OPEN_CREATE(max_size, flag) \ + (SL_FS_CREATE | SL_FS_CREATE_MAX_SIZE(max_size)) +#define SL_FI_FILE_SIZE(fi) ((fi).Len) +#define SL_FI_FILE_MAX_SIZE(fi) ((fi).MaxSize) + +#define SL_LEN_TYPE _u16 +#define SL_OPT_TYPE _u16 + +#endif /* SL_MAJOR_VERSION_NUM < 2 */ + +int slfs_open(const unsigned char *fname, uint32_t flags); + +#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK */ + +#endif /* CS_COMMON_PLATFORMS_SIMPLELINK_CS_SIMPLELINK_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/simplelink/sl_fs.c b/src/mongoose-6.11/src/common/platforms/simplelink/sl_fs.c new file mode 100644 index 0000000..fc09342 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/simplelink/sl_fs.c @@ -0,0 +1,411 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_NET_IF == MG_NET_IF_SIMPLELINK && \ + (defined(MG_FS_SLFS) || defined(MG_FS_SPIFFS)) + +int set_errno(int e) { + errno = e; + return (e == 0 ? 0 : -1); +} + +const char *drop_dir(const char *fname, bool *is_slfs) { + if (is_slfs != NULL) { + *is_slfs = (strncmp(fname, "SL:", 3) == 0); + if (*is_slfs) fname += 3; + } + /* Drop "./", if any */ + if (fname[0] == '.' && fname[1] == '/') { + fname += 2; + } + /* + * Drop / if it is the only one in the path. + * This allows use of /pretend/directories but serves /file.txt as normal. + */ + if (fname[0] == '/' && strchr(fname + 1, '/') == NULL) { + fname++; + } + return fname; +} + +#if !defined(MG_FS_NO_VFS) + +#include +#include +#include +#include +#include +#ifdef __TI_COMPILER_VERSION__ +#include +#endif + +#include "common/cs_dbg.h" +#include "common/platform.h" + +#ifdef CC3200_FS_SPIFFS +#include "cc3200_fs_spiffs.h" +#endif + +#ifdef MG_FS_SLFS +#include "sl_fs_slfs.h" +#endif + +#define NUM_SYS_FDS 3 +#define SPIFFS_FD_BASE 10 +#define SLFS_FD_BASE 100 + +#if !defined(MG_UART_CHAR_PUT) && !defined(MG_UART_WRITE) +#if CS_PLATFORM == CS_P_CC3200 +#include +#include +#include +#include +#include +#define MG_UART_CHAR_PUT(fd, c) MAP_UARTCharPut(UARTA0_BASE, c); +#else +#define MG_UART_WRITE(fd, buf, len) +#endif /* CS_PLATFORM == CS_P_CC3200 */ +#endif /* !MG_UART_CHAR_PUT */ + +enum fd_type { + FD_INVALID, + FD_SYS, +#ifdef CC3200_FS_SPIFFS + FD_SPIFFS, +#endif +#ifdef MG_FS_SLFS + FD_SLFS +#endif +}; +static int fd_type(int fd) { + if (fd >= 0 && fd < NUM_SYS_FDS) return FD_SYS; +#ifdef CC3200_FS_SPIFFS + if (fd >= SPIFFS_FD_BASE && fd < SPIFFS_FD_BASE + MAX_OPEN_SPIFFS_FILES) { + return FD_SPIFFS; + } +#endif +#ifdef MG_FS_SLFS + if (fd >= SLFS_FD_BASE && fd < SLFS_FD_BASE + MAX_OPEN_SLFS_FILES) { + return FD_SLFS; + } +#endif + return FD_INVALID; +} + +#if MG_TI_NO_HOST_INTERFACE +int open(const char *pathname, unsigned flags, int mode) { +#else +int _open(const char *pathname, int flags, mode_t mode) { +#endif + int fd = -1; + bool is_sl; + const char *fname = drop_dir(pathname, &is_sl); + if (is_sl) { +#ifdef MG_FS_SLFS + fd = fs_slfs_open(fname, flags, mode); + if (fd >= 0) fd += SLFS_FD_BASE; +#endif + } else { +#ifdef CC3200_FS_SPIFFS + fd = fs_spiffs_open(fname, flags, mode); + if (fd >= 0) fd += SPIFFS_FD_BASE; +#endif + } + LOG(LL_DEBUG, + ("open(%s, 0x%x) = %d, fname = %s", pathname, flags, fd, fname)); + return fd; +} + +int _stat(const char *pathname, struct stat *st) { + int res = -1; + bool is_sl; + const char *fname = drop_dir(pathname, &is_sl); + memset(st, 0, sizeof(*st)); + /* Simulate statting the root directory. */ + if (fname[0] == '\0' || strcmp(fname, ".") == 0) { + st->st_ino = 0; + st->st_mode = S_IFDIR | 0777; + st->st_nlink = 1; + st->st_size = 0; + return 0; + } + if (is_sl) { +#ifdef MG_FS_SLFS + res = fs_slfs_stat(fname, st); +#endif + } else { +#ifdef CC3200_FS_SPIFFS + res = fs_spiffs_stat(fname, st); +#endif + } + LOG(LL_DEBUG, ("stat(%s) = %d; fname = %s", pathname, res, fname)); + return res; +} + +#if MG_TI_NO_HOST_INTERFACE +int close(int fd) { +#else +int _close(int fd) { +#endif + int r = -1; + switch (fd_type(fd)) { + case FD_INVALID: + r = set_errno(EBADF); + break; + case FD_SYS: + r = set_errno(EACCES); + break; +#ifdef CC3200_FS_SPIFFS + case FD_SPIFFS: + r = fs_spiffs_close(fd - SPIFFS_FD_BASE); + break; +#endif +#ifdef MG_FS_SLFS + case FD_SLFS: + r = fs_slfs_close(fd - SLFS_FD_BASE); + break; +#endif + } + DBG(("close(%d) = %d", fd, r)); + return r; +} + +#if MG_TI_NO_HOST_INTERFACE +off_t lseek(int fd, off_t offset, int whence) { +#else +off_t _lseek(int fd, off_t offset, int whence) { +#endif + int r = -1; + switch (fd_type(fd)) { + case FD_INVALID: + r = set_errno(EBADF); + break; + case FD_SYS: + r = set_errno(ESPIPE); + break; +#ifdef CC3200_FS_SPIFFS + case FD_SPIFFS: + r = fs_spiffs_lseek(fd - SPIFFS_FD_BASE, offset, whence); + break; +#endif +#ifdef MG_FS_SLFS + case FD_SLFS: + r = fs_slfs_lseek(fd - SLFS_FD_BASE, offset, whence); + break; +#endif + } + DBG(("lseek(%d, %d, %d) = %d", fd, (int) offset, whence, r)); + return r; +} + +int _fstat(int fd, struct stat *s) { + int r = -1; + memset(s, 0, sizeof(*s)); + switch (fd_type(fd)) { + case FD_INVALID: + r = set_errno(EBADF); + break; + case FD_SYS: { + /* Create barely passable stats for STD{IN,OUT,ERR}. */ + memset(s, 0, sizeof(*s)); + s->st_ino = fd; + s->st_mode = S_IFCHR | 0666; + r = 0; + break; + } +#ifdef CC3200_FS_SPIFFS + case FD_SPIFFS: + r = fs_spiffs_fstat(fd - SPIFFS_FD_BASE, s); + break; +#endif +#ifdef MG_FS_SLFS + case FD_SLFS: + r = fs_slfs_fstat(fd - SLFS_FD_BASE, s); + break; +#endif + } + DBG(("fstat(%d) = %d", fd, r)); + return r; +} + +#if MG_TI_NO_HOST_INTERFACE +int read(int fd, char *buf, unsigned count) { +#else +ssize_t _read(int fd, void *buf, size_t count) { +#endif + int r = -1; + switch (fd_type(fd)) { + case FD_INVALID: + r = set_errno(EBADF); + break; + case FD_SYS: { + if (fd != 0) { + r = set_errno(EACCES); + break; + } + /* Should we allow reading from stdin = uart? */ + r = set_errno(ENOTSUP); + break; + } +#ifdef CC3200_FS_SPIFFS + case FD_SPIFFS: + r = fs_spiffs_read(fd - SPIFFS_FD_BASE, buf, count); + break; +#endif +#ifdef MG_FS_SLFS + case FD_SLFS: + r = fs_slfs_read(fd - SLFS_FD_BASE, buf, count); + break; +#endif + } + DBG(("read(%d, %u) = %d", fd, count, r)); + return r; +} + +#if MG_TI_NO_HOST_INTERFACE +int write(int fd, const char *buf, unsigned count) { +#else +ssize_t _write(int fd, const void *buf, size_t count) { +#endif + int r = -1; + switch (fd_type(fd)) { + case FD_INVALID: + r = set_errno(EBADF); + break; + case FD_SYS: { + if (fd == 0) { + r = set_errno(EACCES); + break; + } +#ifdef MG_UART_WRITE + MG_UART_WRITE(fd, buf, count); +#elif defined(MG_UART_CHAR_PUT) + { + size_t i; + for (i = 0; i < count; i++) { + const char c = ((const char *) buf)[i]; + if (c == '\n') MG_UART_CHAR_PUT(fd, '\r'); + MG_UART_CHAR_PUT(fd, c); + } + } +#endif + r = count; + break; + } +#ifdef CC3200_FS_SPIFFS + case FD_SPIFFS: + r = fs_spiffs_write(fd - SPIFFS_FD_BASE, buf, count); + break; +#endif +#ifdef MG_FS_SLFS + case FD_SLFS: + r = fs_slfs_write(fd - SLFS_FD_BASE, buf, count); + break; +#endif + } + return r; +} + +/* + * On Newlib we override rename directly too, because the default + * implementation using _link and _unlink doesn't work for us. + */ +#if MG_TI_NO_HOST_INTERFACE || defined(_NEWLIB_VERSION) +int rename(const char *frompath, const char *topath) { + int r = -1; + bool is_sl_from, is_sl_to; + const char *from = drop_dir(frompath, &is_sl_from); + const char *to = drop_dir(topath, &is_sl_to); + if (is_sl_from || is_sl_to) { + set_errno(ENOTSUP); + } else { +#ifdef CC3200_FS_SPIFFS + r = fs_spiffs_rename(from, to); +#endif + } + DBG(("rename(%s, %s) = %d", from, to, r)); + return r; +} +#endif /* MG_TI_NO_HOST_INTERFACE || defined(_NEWLIB_VERSION) */ + +#if MG_TI_NO_HOST_INTERFACE +int unlink(const char *pathname) { +#else +int _unlink(const char *pathname) { +#endif + int r = -1; + bool is_sl; + const char *fname = drop_dir(pathname, &is_sl); + if (is_sl) { +#ifdef MG_FS_SLFS + r = fs_slfs_unlink(fname); +#endif + } else { +#ifdef CC3200_FS_SPIFFS + r = fs_spiffs_unlink(fname); +#endif + } + DBG(("unlink(%s) = %d, fname = %s", pathname, r, fname)); + return r; +} + +#ifdef CC3200_FS_SPIFFS /* FailFS does not support listing files. */ +DIR *opendir(const char *dir_name) { + DIR *r = NULL; + bool is_sl; + drop_dir(dir_name, &is_sl); + if (is_sl) { + r = NULL; + set_errno(ENOTSUP); + } else { + r = fs_spiffs_opendir(dir_name); + } + DBG(("opendir(%s) = %p", dir_name, r)); + return r; +} + +struct dirent *readdir(DIR *dir) { + struct dirent *res = fs_spiffs_readdir(dir); + DBG(("readdir(%p) = %p", dir, res)); + return res; +} + +int closedir(DIR *dir) { + int res = fs_spiffs_closedir(dir); + DBG(("closedir(%p) = %d", dir, res)); + return res; +} + +int rmdir(const char *path) { + return fs_spiffs_rmdir(path); +} + +int mkdir(const char *path, mode_t mode) { + (void) path; + (void) mode; + /* for spiffs supports only root dir, which comes from mongoose as '.' */ + return (strlen(path) == 1 && *path == '.') ? 0 : ENOTDIR; +} +#endif + +int sl_fs_init(void) { + int ret = 1; +#ifdef __TI_COMPILER_VERSION__ +#ifdef MG_FS_SLFS +#pragma diag_push +#pragma diag_suppress 169 /* Nothing we can do about the prototype mismatch. \ + */ + ret = (add_device("SL", _MSA, fs_slfs_open, fs_slfs_close, fs_slfs_read, + fs_slfs_write, fs_slfs_lseek, fs_slfs_unlink, + fs_slfs_rename) == 0); +#pragma diag_pop +#endif +#endif + return ret; +} + +#endif /* !defined(MG_FS_NO_VFS) */ +#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK && (defined(MG_FS_SLFS) || \ + defined(MG_FS_SPIFFS)) */ diff --git a/src/mongoose-6.11/src/common/platforms/simplelink/sl_fs_slfs.c b/src/mongoose-6.11/src/common/platforms/simplelink/sl_fs_slfs.c new file mode 100644 index 0000000..2754b6b --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/simplelink/sl_fs_slfs.c @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +/* Standard libc interface to TI SimpleLink FS. */ + +#if defined(MG_FS_SLFS) || defined(CC3200_FS_SLFS) + +#include "common/platforms/simplelink/sl_fs_slfs.h" + +#include + +#if CS_PLATFORM == CS_P_CC3200 +#include +#endif + +#include "common/cs_dbg.h" +#include "common/mg_mem.h" + +#if SL_MAJOR_VERSION_NUM < 2 +int slfs_open(const unsigned char *fname, uint32_t flags) { + _i32 fh; + _i32 r = sl_FsOpen(fname, flags, NULL /* token */, &fh); + return (r < 0 ? r : fh); +} +#else /* SL_MAJOR_VERSION_NUM >= 2 */ +int slfs_open(const unsigned char *fname, uint32_t flags) { + return sl_FsOpen(fname, flags, NULL /* token */); +} +#endif + +/* From sl_fs.c */ +int set_errno(int e); +const char *drop_dir(const char *fname, bool *is_slfs); + +/* + * With SLFS, you have to pre-declare max file size. Yes. Really. + * 64K should be enough for everyone. Right? + */ +#ifndef FS_SLFS_MAX_FILE_SIZE +#define FS_SLFS_MAX_FILE_SIZE (64 * 1024) +#endif + +struct sl_file_size_hint { + char *name; + size_t size; +}; + +struct sl_fd_info { + _i32 fh; + _off_t pos; + size_t size; +}; + +static struct sl_fd_info s_sl_fds[MAX_OPEN_SLFS_FILES]; +static struct sl_file_size_hint s_sl_file_size_hints[MAX_OPEN_SLFS_FILES]; + +static int sl_fs_to_errno(_i32 r) { + DBG(("SL error: %d", (int) r)); + switch (r) { + case SL_FS_OK: + return 0; + case SL_ERROR_FS_FILE_NAME_EXIST: + return EEXIST; + case SL_ERROR_FS_WRONG_FILE_NAME: + return EINVAL; + case SL_ERROR_FS_NO_AVAILABLE_NV_INDEX: + case SL_ERROR_FS_NOT_ENOUGH_STORAGE_SPACE: + return ENOSPC; + case SL_ERROR_FS_FAILED_TO_ALLOCATE_MEM: + return ENOMEM; + case SL_ERROR_FS_FILE_NOT_EXISTS: + return ENOENT; + case SL_ERROR_FS_NOT_SUPPORTED: + return ENOTSUP; + } + return ENXIO; +} + +int fs_slfs_open(const char *pathname, int flags, mode_t mode) { + int fd; + for (fd = 0; fd < MAX_OPEN_SLFS_FILES; fd++) { + if (s_sl_fds[fd].fh <= 0) break; + } + if (fd >= MAX_OPEN_SLFS_FILES) return set_errno(ENOMEM); + struct sl_fd_info *fi = &s_sl_fds[fd]; + + /* + * Apply path manipulations again, in case we got here directly + * (via TI libc's "add_device"). + */ + pathname = drop_dir(pathname, NULL); + + _u32 am = 0; + fi->size = (size_t) -1; + int rw = (flags & 3); + size_t new_size = FS_SLFS_MAX_FILE_SIZE; + if (rw == O_RDONLY) { + SlFsFileInfo_t sl_fi; + _i32 r = sl_FsGetInfo((const _u8 *) pathname, 0, &sl_fi); + if (r == SL_FS_OK) { + fi->size = SL_FI_FILE_SIZE(sl_fi); + } + am = SL_FS_READ; + } else { + if (!(flags & O_TRUNC) || (flags & O_APPEND)) { + // FailFS files cannot be opened for append and will be truncated + // when opened for write. + return set_errno(ENOTSUP); + } + if (flags & O_CREAT) { + size_t i; + for (i = 0; i < MAX_OPEN_SLFS_FILES; i++) { + if (s_sl_file_size_hints[i].name != NULL && + strcmp(s_sl_file_size_hints[i].name, pathname) == 0) { + new_size = s_sl_file_size_hints[i].size; + MG_FREE(s_sl_file_size_hints[i].name); + s_sl_file_size_hints[i].name = NULL; + break; + } + } + am = FS_MODE_OPEN_CREATE(new_size, 0); + } else { + am = SL_FS_WRITE; + } + } + fi->fh = slfs_open((_u8 *) pathname, am); + LOG(LL_DEBUG, ("sl_FsOpen(%s, 0x%x) sz %u = %d", pathname, (int) am, + (unsigned int) new_size, (int) fi->fh)); + int r; + if (fi->fh >= 0) { + fi->pos = 0; + r = fd; + } else { + r = set_errno(sl_fs_to_errno(fi->fh)); + } + return r; +} + +int fs_slfs_close(int fd) { + struct sl_fd_info *fi = &s_sl_fds[fd]; + if (fi->fh <= 0) return set_errno(EBADF); + _i32 r = sl_FsClose(fi->fh, NULL, NULL, 0); + LOG(LL_DEBUG, ("sl_FsClose(%d) = %d", (int) fi->fh, (int) r)); + s_sl_fds[fd].fh = -1; + return set_errno(sl_fs_to_errno(r)); +} + +ssize_t fs_slfs_read(int fd, void *buf, size_t count) { + struct sl_fd_info *fi = &s_sl_fds[fd]; + if (fi->fh <= 0) return set_errno(EBADF); + /* Simulate EOF. sl_FsRead @ file_size return SL_FS_ERR_OFFSET_OUT_OF_RANGE. + */ + if (fi->pos == fi->size) return 0; + _i32 r = sl_FsRead(fi->fh, fi->pos, buf, count); + DBG(("sl_FsRead(%d, %d, %d) = %d", (int) fi->fh, (int) fi->pos, (int) count, + (int) r)); + if (r >= 0) { + fi->pos += r; + return r; + } + return set_errno(sl_fs_to_errno(r)); +} + +ssize_t fs_slfs_write(int fd, const void *buf, size_t count) { + struct sl_fd_info *fi = &s_sl_fds[fd]; + if (fi->fh <= 0) return set_errno(EBADF); + _i32 r = sl_FsWrite(fi->fh, fi->pos, (_u8 *) buf, count); + DBG(("sl_FsWrite(%d, %d, %d) = %d", (int) fi->fh, (int) fi->pos, (int) count, + (int) r)); + if (r >= 0) { + fi->pos += r; + return r; + } + return set_errno(sl_fs_to_errno(r)); +} + +int fs_slfs_stat(const char *pathname, struct stat *s) { + SlFsFileInfo_t sl_fi; + /* + * Apply path manipulations again, in case we got here directly + * (via TI libc's "add_device"). + */ + pathname = drop_dir(pathname, NULL); + _i32 r = sl_FsGetInfo((const _u8 *) pathname, 0, &sl_fi); + if (r == SL_FS_OK) { + s->st_mode = S_IFREG | 0666; + s->st_nlink = 1; + s->st_size = SL_FI_FILE_SIZE(sl_fi); + return 0; + } + return set_errno(sl_fs_to_errno(r)); +} + +int fs_slfs_fstat(int fd, struct stat *s) { + struct sl_fd_info *fi = &s_sl_fds[fd]; + if (fi->fh <= 0) return set_errno(EBADF); + s->st_mode = 0666; + s->st_mode = S_IFREG | 0666; + s->st_nlink = 1; + s->st_size = fi->size; + return 0; +} + +off_t fs_slfs_lseek(int fd, off_t offset, int whence) { + if (s_sl_fds[fd].fh <= 0) return set_errno(EBADF); + switch (whence) { + case SEEK_SET: + s_sl_fds[fd].pos = offset; + break; + case SEEK_CUR: + s_sl_fds[fd].pos += offset; + break; + case SEEK_END: + return set_errno(ENOTSUP); + } + return 0; +} + +int fs_slfs_unlink(const char *pathname) { + /* + * Apply path manipulations again, in case we got here directly + * (via TI libc's "add_device"). + */ + pathname = drop_dir(pathname, NULL); + return set_errno(sl_fs_to_errno(sl_FsDel((const _u8 *) pathname, 0))); +} + +int fs_slfs_rename(const char *from, const char *to) { + return set_errno(ENOTSUP); +} + +void fs_slfs_set_new_file_size(const char *name, size_t size) { + int i; + for (i = 0; i < MAX_OPEN_SLFS_FILES; i++) { + if (s_sl_file_size_hints[i].name == NULL) { + DBG(("File size hint: %s %d", name, (int) size)); + s_sl_file_size_hints[i].name = strdup(name); + s_sl_file_size_hints[i].size = size; + break; + } + } +} + +#endif /* defined(MG_FS_SLFS) || defined(CC3200_FS_SLFS) */ diff --git a/src/mongoose-6.11/src/common/platforms/simplelink/sl_fs_slfs.h b/src/mongoose-6.11/src/common/platforms/simplelink/sl_fs_slfs.h new file mode 100644 index 0000000..0d8a4b1 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/simplelink/sl_fs_slfs.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_SIMPLELINK_SL_FS_SLFS_H_ +#define CS_COMMON_PLATFORMS_SIMPLELINK_SL_FS_SLFS_H_ + +#if defined(MG_FS_SLFS) + +#include +#ifndef __TI_COMPILER_VERSION__ +#include +#include +#endif + +#define MAX_OPEN_SLFS_FILES 8 + +/* Indirect libc interface - same functions, different names. */ +int fs_slfs_open(const char *pathname, int flags, mode_t mode); +int fs_slfs_close(int fd); +ssize_t fs_slfs_read(int fd, void *buf, size_t count); +ssize_t fs_slfs_write(int fd, const void *buf, size_t count); +int fs_slfs_stat(const char *pathname, struct stat *s); +int fs_slfs_fstat(int fd, struct stat *s); +off_t fs_slfs_lseek(int fd, off_t offset, int whence); +int fs_slfs_unlink(const char *filename); +int fs_slfs_rename(const char *from, const char *to); + +void fs_slfs_set_new_file_size(const char *name, size_t size); + +#endif /* defined(MG_FS_SLFS) */ + +#endif /* CS_COMMON_PLATFORMS_SIMPLELINK_SL_FS_SLFS_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/simplelink/sl_mg_task.c b/src/mongoose-6.11/src/common/platforms/simplelink/sl_mg_task.c new file mode 100644 index 0000000..f9c7671 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/simplelink/sl_mg_task.c @@ -0,0 +1,52 @@ +#if MG_NET_IF == MG_NET_IF_SIMPLELINK && !defined(MG_SIMPLELINK_NO_OSI) + +#include "mg_task.h" + +#include + +enum mg_q_msg_type { + MG_Q_MSG_CB, +}; +struct mg_q_msg { + enum mg_q_msg_type type; + void (*cb)(struct mg_mgr *mgr, void *arg); + void *arg; +}; +static OsiMsgQ_t s_mg_q; +static void mg_task(void *arg); + +bool mg_start_task(int priority, int stack_size, mg_init_cb mg_init) { + if (osi_MsgQCreate(&s_mg_q, "MG", sizeof(struct mg_q_msg), 16) != OSI_OK) { + return false; + } + if (osi_TaskCreate(mg_task, (const signed char *) "MG", stack_size, + (void *) mg_init, priority, NULL) != OSI_OK) { + return false; + } + return true; +} + +static void mg_task(void *arg) { + struct mg_mgr mgr; + mg_init_cb mg_init = (mg_init_cb) arg; + mg_mgr_init(&mgr, NULL); + mg_init(&mgr); + while (1) { + struct mg_q_msg msg; + mg_mgr_poll(&mgr, 1); + if (osi_MsgQRead(&s_mg_q, &msg, 1) != OSI_OK) continue; + switch (msg.type) { + case MG_Q_MSG_CB: { + msg.cb(&mgr, msg.arg); + } + } + } +} + +void mg_run_in_task(void (*cb)(struct mg_mgr *mgr, void *arg), void *cb_arg) { + struct mg_q_msg msg = {MG_Q_MSG_CB, cb, cb_arg}; + osi_MsgQWrite(&s_mg_q, &msg, OSI_NO_WAIT); +} + +#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK && !defined(MG_SIMPLELINK_NO_OSI) \ + */ diff --git a/src/mongoose-6.11/src/common/platforms/simplelink/sl_net_if.c b/src/mongoose-6.11/src/common/platforms/simplelink/sl_net_if.c new file mode 100644 index 0000000..2a39d14 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/simplelink/sl_net_if.c @@ -0,0 +1,480 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#include "common/platforms/simplelink/sl_net_if.h" + +#if MG_ENABLE_NET_IF_SIMPLELINK + +#include "mongoose/src/internal.h" +#include "mongoose/src/util.h" + +#define MG_TCP_RECV_BUFFER_SIZE 1024 +#define MG_UDP_RECV_BUFFER_SIZE 1500 + +static sock_t mg_open_listening_socket(struct mg_connection *nc, + union socket_address *sa, int type, + int proto); + +void mg_set_non_blocking_mode(sock_t sock) { + SlSockNonblocking_t opt; +#if SL_MAJOR_VERSION_NUM < 2 + opt.NonblockingEnabled = 1; +#else + opt.NonBlockingEnabled = 1; +#endif + sl_SetSockOpt(sock, SL_SOL_SOCKET, SL_SO_NONBLOCKING, &opt, sizeof(opt)); +} + +static int mg_is_error(int n) { + return (n < 0 && n != SL_ERROR_BSD_EALREADY && n != SL_ERROR_BSD_EAGAIN); +} + +void mg_sl_if_connect_tcp(struct mg_connection *nc, + const union socket_address *sa) { + int proto = 0; + if (nc->flags & MG_F_SSL) proto = SL_SEC_SOCKET; + sock_t sock = sl_Socket(AF_INET, SOCK_STREAM, proto); + if (sock < 0) { + nc->err = sock; + goto out; + } + mg_sock_set(nc, sock); +#if MG_ENABLE_SSL + nc->err = sl_set_ssl_opts(sock, nc); + if (nc->err != 0) goto out; +#endif + nc->err = sl_Connect(sock, &sa->sa, sizeof(sa->sin)); +out: + DBG(("%p to %s:%d sock %d %d err %d", nc, inet_ntoa(sa->sin.sin_addr), + ntohs(sa->sin.sin_port), nc->sock, proto, nc->err)); +} + +void mg_sl_if_connect_udp(struct mg_connection *nc) { + sock_t sock = sl_Socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) { + nc->err = sock; + return; + } + mg_sock_set(nc, sock); + nc->err = 0; +} + +int mg_sl_if_listen_tcp(struct mg_connection *nc, union socket_address *sa) { + int proto = 0; + if (nc->flags & MG_F_SSL) proto = SL_SEC_SOCKET; + sock_t sock = mg_open_listening_socket(nc, sa, SOCK_STREAM, proto); + if (sock < 0) return sock; + mg_sock_set(nc, sock); + return 0; +} + +int mg_sl_if_listen_udp(struct mg_connection *nc, union socket_address *sa) { + sock_t sock = mg_open_listening_socket(nc, sa, SOCK_DGRAM, 0); + if (sock == INVALID_SOCKET) return (errno ? errno : 1); + mg_sock_set(nc, sock); + return 0; +} + +void mg_sl_if_tcp_send(struct mg_connection *nc, const void *buf, size_t len) { + mbuf_append(&nc->send_mbuf, buf, len); +} + +void mg_sl_if_udp_send(struct mg_connection *nc, const void *buf, size_t len) { + mbuf_append(&nc->send_mbuf, buf, len); +} + +void mg_sl_if_recved(struct mg_connection *nc, size_t len) { + (void) nc; + (void) len; +} + +int mg_sl_if_create_conn(struct mg_connection *nc) { + (void) nc; + return 1; +} + +void mg_sl_if_destroy_conn(struct mg_connection *nc) { + if (nc->sock == INVALID_SOCKET) return; + /* For UDP, only close outgoing sockets or listeners. */ + if (!(nc->flags & MG_F_UDP) || nc->listener == NULL) { + sl_Close(nc->sock); + } + nc->sock = INVALID_SOCKET; +} + +static int mg_accept_conn(struct mg_connection *lc) { + struct mg_connection *nc; + union socket_address sa; + socklen_t sa_len = sizeof(sa); + sock_t sock = sl_Accept(lc->sock, &sa.sa, &sa_len); + if (sock < 0) { + DBG(("%p: failed to accept: %d", lc, sock)); + return 0; + } + nc = mg_if_accept_new_conn(lc); + if (nc == NULL) { + sl_Close(sock); + return 0; + } + DBG(("%p conn from %s:%d", nc, inet_ntoa(sa.sin.sin_addr), + ntohs(sa.sin.sin_port))); + mg_sock_set(nc, sock); + if (nc->flags & MG_F_SSL) nc->flags |= MG_F_SSL_HANDSHAKE_DONE; + mg_if_accept_tcp_cb(nc, &sa, sa_len); + return 1; +} + +/* 'sa' must be an initialized address to bind to */ +static sock_t mg_open_listening_socket(struct mg_connection *nc, + union socket_address *sa, int type, + int proto) { + int r; + socklen_t sa_len = + (sa->sa.sa_family == AF_INET) ? sizeof(sa->sin) : sizeof(sa->sin6); + sock_t sock = sl_Socket(sa->sa.sa_family, type, proto); + if (sock < 0) return sock; + if ((r = sl_Bind(sock, &sa->sa, sa_len)) < 0) goto clean; + if (type != SOCK_DGRAM) { +#if MG_ENABLE_SSL + if ((r = sl_set_ssl_opts(sock, nc)) < 0) goto clean; +#endif + if ((r = sl_Listen(sock, SOMAXCONN)) < 0) goto clean; + } + mg_set_non_blocking_mode(sock); +clean: + if (r < 0) { + sl_Close(sock); + sock = r; + } + return sock; +} + +static void mg_write_to_socket(struct mg_connection *nc) { + struct mbuf *io = &nc->send_mbuf; + int n = 0; + + if (nc->flags & MG_F_UDP) { + n = sl_SendTo(nc->sock, io->buf, io->len, 0, &nc->sa.sa, + sizeof(nc->sa.sin)); + DBG(("%p %d %d %d %s:%hu", nc, nc->sock, n, errno, + inet_ntoa(nc->sa.sin.sin_addr), ntohs(nc->sa.sin.sin_port))); + } else { + n = (int) sl_Send(nc->sock, io->buf, io->len, 0); + DBG(("%p %d bytes -> %d", nc, n, nc->sock)); + } + + if (n > 0) { + mg_if_sent_cb(nc, n); + } else if (n < 0 && mg_is_error(n)) { + /* Something went wrong, drop the connection. */ + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } +} + +MG_INTERNAL size_t recv_avail_size(struct mg_connection *conn, size_t max) { + size_t avail; + if (conn->recv_mbuf_limit < conn->recv_mbuf.len) return 0; + avail = conn->recv_mbuf_limit - conn->recv_mbuf.len; + return avail > max ? max : avail; +} + +static void mg_handle_tcp_read(struct mg_connection *conn) { + int n = 0; + char *buf = (char *) MG_MALLOC(MG_TCP_RECV_BUFFER_SIZE); + + if (buf == NULL) { + DBG(("OOM")); + return; + } + + n = (int) sl_Recv(conn->sock, buf, + recv_avail_size(conn, MG_TCP_RECV_BUFFER_SIZE), 0); + DBG(("%p %d bytes <- %d", conn, n, conn->sock)); + if (n > 0) { + mg_if_recv_tcp_cb(conn, buf, n, 1 /* own */); + } else { + MG_FREE(buf); + } + if (n == 0) { + /* Orderly shutdown of the socket, try flushing output. */ + conn->flags |= MG_F_SEND_AND_CLOSE; + } else if (mg_is_error(n)) { + conn->flags |= MG_F_CLOSE_IMMEDIATELY; + } +} + +static void mg_handle_udp_read(struct mg_connection *nc) { + char *buf = (char *) MG_MALLOC(MG_UDP_RECV_BUFFER_SIZE); + if (buf == NULL) return; + union socket_address sa; + socklen_t sa_len = sizeof(sa); + int n = sl_RecvFrom(nc->sock, buf, MG_UDP_RECV_BUFFER_SIZE, 0, + (SlSockAddr_t *) &sa, &sa_len); + DBG(("%p %d bytes from %s:%d", nc, n, inet_ntoa(nc->sa.sin.sin_addr), + ntohs(nc->sa.sin.sin_port))); + if (n > 0) { + mg_if_recv_udp_cb(nc, buf, n, &sa, sa_len); + } else { + MG_FREE(buf); + } +} + +#define _MG_F_FD_CAN_READ 1 +#define _MG_F_FD_CAN_WRITE 1 << 1 +#define _MG_F_FD_ERROR 1 << 2 + +void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) { + DBG(("%p fd=%d fd_flags=%d nc_flags=%lu rmbl=%d smbl=%d", nc, nc->sock, + fd_flags, nc->flags, (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); + + if (nc->flags & MG_F_CONNECTING) { + if (nc->flags & MG_F_UDP || nc->err != SL_ERROR_BSD_EALREADY) { + mg_if_connect_cb(nc, nc->err); + } else { + /* In SimpleLink, to get status of non-blocking connect() we need to wait + * until socket is writable and repeat the call to sl_Connect again, + * which will now return the real status. */ + if (fd_flags & _MG_F_FD_CAN_WRITE) { + nc->err = sl_Connect(nc->sock, &nc->sa.sa, sizeof(nc->sa.sin)); + DBG(("%p conn res=%d", nc, nc->err)); + if (nc->err == SL_ERROR_BSD_ESECSNOVERIFY || + /* TODO(rojer): Provide API to set the date for verification. */ + nc->err == SL_ERROR_BSD_ESECDATEERROR +#if SL_MAJOR_VERSION_NUM >= 2 + /* Per SWRU455, this error does not mean verification failed, + * it only means that the cert used is not present in the trusted + * root CA catalog. Which is perfectly fine. */ + || + nc->err == SL_ERROR_BSD_ESECUNKNOWNROOTCA +#endif + ) { + nc->err = 0; + } + if (nc->flags & MG_F_SSL && nc->err == 0) { + nc->flags |= MG_F_SSL_HANDSHAKE_DONE; + } + mg_if_connect_cb(nc, nc->err); + } + } + /* Ignore read/write in further processing, we've handled it. */ + fd_flags &= ~(_MG_F_FD_CAN_READ | _MG_F_FD_CAN_WRITE); + } + + if (fd_flags & _MG_F_FD_CAN_READ) { + if (nc->flags & MG_F_UDP) { + mg_handle_udp_read(nc); + } else { + if (nc->flags & MG_F_LISTENING) { + mg_accept_conn(nc); + } else { + mg_handle_tcp_read(nc); + } + } + } + + if (!(nc->flags & MG_F_CLOSE_IMMEDIATELY)) { + if ((fd_flags & _MG_F_FD_CAN_WRITE) && nc->send_mbuf.len > 0) { + mg_write_to_socket(nc); + } + + if (!(fd_flags & (_MG_F_FD_CAN_READ | _MG_F_FD_CAN_WRITE))) { + mg_if_poll(nc, now); + } + mg_if_timer(nc, now); + } + + DBG(("%p after fd=%d nc_flags=%lu rmbl=%d smbl=%d", nc, nc->sock, nc->flags, + (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); +} + +/* Associate a socket to a connection. */ +void mg_sl_if_sock_set(struct mg_connection *nc, sock_t sock) { + mg_set_non_blocking_mode(sock); + nc->sock = sock; + DBG(("%p %d", nc, sock)); +} + +void mg_sl_if_init(struct mg_iface *iface) { + (void) iface; + DBG(("%p using sl_Select()", iface->mgr)); +} + +void mg_sl_if_free(struct mg_iface *iface) { + (void) iface; +} + +void mg_sl_if_add_conn(struct mg_connection *nc) { + (void) nc; +} + +void mg_sl_if_remove_conn(struct mg_connection *nc) { + (void) nc; +} + +time_t mg_sl_if_poll(struct mg_iface *iface, int timeout_ms) { + struct mg_mgr *mgr = iface->mgr; + double now = mg_time(); + double min_timer; + struct mg_connection *nc, *tmp; + struct SlTimeval_t tv; + SlFdSet_t read_set, write_set, err_set; + sock_t max_fd = INVALID_SOCKET; + int num_fds, num_ev = 0, num_timers = 0; + + SL_SOCKET_FD_ZERO(&read_set); + SL_SOCKET_FD_ZERO(&write_set); + SL_SOCKET_FD_ZERO(&err_set); + + /* + * Note: it is ok to have connections with sock == INVALID_SOCKET in the list, + * e.g. timer-only "connections". + */ + min_timer = 0; + for (nc = mgr->active_connections, num_fds = 0; nc != NULL; nc = tmp) { + tmp = nc->next; + + if (nc->sock != INVALID_SOCKET) { + num_fds++; + + if (!(nc->flags & MG_F_WANT_WRITE) && + nc->recv_mbuf.len < nc->recv_mbuf_limit && + (!(nc->flags & MG_F_UDP) || nc->listener == NULL)) { + SL_SOCKET_FD_SET(nc->sock, &read_set); + if (max_fd == INVALID_SOCKET || nc->sock > max_fd) max_fd = nc->sock; + } + + if (((nc->flags & MG_F_CONNECTING) && !(nc->flags & MG_F_WANT_READ)) || + (nc->send_mbuf.len > 0 && !(nc->flags & MG_F_CONNECTING))) { + SL_SOCKET_FD_SET(nc->sock, &write_set); + SL_SOCKET_FD_SET(nc->sock, &err_set); + if (max_fd == INVALID_SOCKET || nc->sock > max_fd) max_fd = nc->sock; + } + } + + if (nc->ev_timer_time > 0) { + if (num_timers == 0 || nc->ev_timer_time < min_timer) { + min_timer = nc->ev_timer_time; + } + num_timers++; + } + } + + /* + * If there is a timer to be fired earlier than the requested timeout, + * adjust the timeout. + */ + if (num_timers > 0) { + double timer_timeout_ms = (min_timer - mg_time()) * 1000 + 1 /* rounding */; + if (timer_timeout_ms < timeout_ms) { + timeout_ms = timer_timeout_ms; + } + } + if (timeout_ms < 0) timeout_ms = 0; + + tv.tv_sec = timeout_ms / 1000; + tv.tv_usec = (timeout_ms % 1000) * 1000; + + if (num_fds > 0) { + num_ev = sl_Select((int) max_fd + 1, &read_set, &write_set, &err_set, &tv); + } + + now = mg_time(); + DBG(("sl_Select @ %ld num_ev=%d of %d, timeout=%d", (long) now, num_ev, + num_fds, timeout_ms)); + + for (nc = mgr->active_connections; nc != NULL; nc = tmp) { + int fd_flags = 0; + if (nc->sock != INVALID_SOCKET) { + if (num_ev > 0) { + fd_flags = + (SL_SOCKET_FD_ISSET(nc->sock, &read_set) && + (!(nc->flags & MG_F_UDP) || nc->listener == NULL) + ? _MG_F_FD_CAN_READ + : 0) | + (SL_SOCKET_FD_ISSET(nc->sock, &write_set) ? _MG_F_FD_CAN_WRITE + : 0) | + (SL_SOCKET_FD_ISSET(nc->sock, &err_set) ? _MG_F_FD_ERROR : 0); + } + /* SimpleLink does not report UDP sockets as writable. */ + if (nc->flags & MG_F_UDP && nc->send_mbuf.len > 0) { + fd_flags |= _MG_F_FD_CAN_WRITE; + } + } + tmp = nc->next; + mg_mgr_handle_conn(nc, fd_flags, now); + } + + for (nc = mgr->active_connections; nc != NULL; nc = tmp) { + tmp = nc->next; + if ((nc->flags & MG_F_CLOSE_IMMEDIATELY) || + (nc->send_mbuf.len == 0 && (nc->flags & MG_F_SEND_AND_CLOSE))) { + mg_close_conn(nc); + } + } + + return now; +} + +void mg_sl_if_get_conn_addr(struct mg_connection *nc, int remote, + union socket_address *sa) { + /* SimpleLink does not provide a way to get socket's peer address after + * accept or connect. Address should have been preserved in the connection, + * so we do our best here by using it. */ + if (remote) memcpy(sa, &nc->sa, sizeof(*sa)); +} + +void sl_restart_cb(struct mg_mgr *mgr) { + /* + * SimpleLink has been restarted, meaning all sockets have been invalidated. + * We try our best - we'll restart the listeners, but for outgoing + * connections we have no option but to terminate. + */ + struct mg_connection *nc; + for (nc = mg_next(mgr, NULL); nc != NULL; nc = mg_next(mgr, nc)) { + if (nc->sock == INVALID_SOCKET) continue; /* Could be a timer */ + if (nc->flags & MG_F_LISTENING) { + DBG(("restarting %p %s:%d", nc, inet_ntoa(nc->sa.sin.sin_addr), + ntohs(nc->sa.sin.sin_port))); + int res = (nc->flags & MG_F_UDP ? mg_sl_if_listen_udp(nc, &nc->sa) + : mg_sl_if_listen_tcp(nc, &nc->sa)); + if (res == 0) continue; + /* Well, we tried and failed. Fall through to closing. */ + } + nc->sock = INVALID_SOCKET; + DBG(("terminating %p %s:%d", nc, inet_ntoa(nc->sa.sin.sin_addr), + ntohs(nc->sa.sin.sin_port))); + /* TODO(rojer): Outgoing UDP? */ + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } +} + +/* clang-format off */ +#define MG_SL_IFACE_VTABLE \ + { \ + mg_sl_if_init, \ + mg_sl_if_free, \ + mg_sl_if_add_conn, \ + mg_sl_if_remove_conn, \ + mg_sl_if_poll, \ + mg_sl_if_listen_tcp, \ + mg_sl_if_listen_udp, \ + mg_sl_if_connect_tcp, \ + mg_sl_if_connect_udp, \ + mg_sl_if_tcp_send, \ + mg_sl_if_udp_send, \ + mg_sl_if_recved, \ + mg_sl_if_create_conn, \ + mg_sl_if_destroy_conn, \ + mg_sl_if_sock_set, \ + mg_sl_if_get_conn_addr, \ + } +/* clang-format on */ + +const struct mg_iface_vtable mg_simplelink_iface_vtable = MG_SL_IFACE_VTABLE; +#if MG_NET_IF == MG_NET_IF_SIMPLELINK +const struct mg_iface_vtable mg_default_iface_vtable = MG_SL_IFACE_VTABLE; +#endif + +#endif /* MG_ENABLE_NET_IF_SIMPLELINK */ diff --git a/src/mongoose-6.11/src/common/platforms/simplelink/sl_net_if.h b/src/mongoose-6.11/src/common/platforms/simplelink/sl_net_if.h new file mode 100644 index 0000000..aa49d5d --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/simplelink/sl_net_if.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_SIMPLELINK_SL_NET_IF_H_ +#define CS_COMMON_PLATFORMS_SIMPLELINK_SL_NET_IF_H_ + +#include "mongoose/src/net_if.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef MG_ENABLE_NET_IF_SIMPLELINK +#define MG_ENABLE_NET_IF_SIMPLELINK MG_NET_IF == MG_NET_IF_SIMPLELINK +#endif + +extern const struct mg_iface_vtable mg_simplelink_iface_vtable; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CS_COMMON_PLATFORMS_SIMPLELINK_SL_NET_IF_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/simplelink/sl_socket.c b/src/mongoose-6.11/src/common/platforms/simplelink/sl_socket.c new file mode 100644 index 0000000..e4a0c26 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/simplelink/sl_socket.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_NET_IF == MG_NET_IF_SIMPLELINK + +#include +#include + +#include "common/platform.h" + +const char *inet_ntop(int af, const void *src, char *dst, socklen_t size) { + int res; + struct in_addr *in = (struct in_addr *) src; + if (af != AF_INET) { + errno = ENOTSUP; + return NULL; + } + res = snprintf(dst, size, "%lu.%lu.%lu.%lu", SL_IPV4_BYTE(in->s_addr, 0), + SL_IPV4_BYTE(in->s_addr, 1), SL_IPV4_BYTE(in->s_addr, 2), + SL_IPV4_BYTE(in->s_addr, 3)); + return res > 0 ? dst : NULL; +} + +char *inet_ntoa(struct in_addr n) { + static char a[16]; + return (char *) inet_ntop(AF_INET, &n, a, sizeof(a)); +} + +int inet_pton(int af, const char *src, void *dst) { + uint32_t a0, a1, a2, a3; + uint8_t *db = (uint8_t *) dst; + if (af != AF_INET) { + errno = ENOTSUP; + return 0; + } + if (sscanf(src, "%lu.%lu.%lu.%lu", &a0, &a1, &a2, &a3) != 4) { + return 0; + } + *db = a3; + *(db + 1) = a2; + *(db + 2) = a1; + *(db + 3) = a0; + return 1; +} + +#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK */ diff --git a/src/mongoose-6.11/src/common/platforms/simplelink/sl_ssl_if.c b/src/mongoose-6.11/src/common/platforms/simplelink/sl_ssl_if.c new file mode 100644 index 0000000..99642b4 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/simplelink/sl_ssl_if.c @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_SIMPLELINK + +#include "common/mg_mem.h" + +#ifndef MG_SSL_IF_SIMPLELINK_SLFS_PREFIX +#define MG_SSL_IF_SIMPLELINK_SLFS_PREFIX "SL:" +#endif + +#define MG_SSL_IF_SIMPLELINK_SLFS_PREFIX_LEN \ + (sizeof(MG_SSL_IF_SIMPLELINK_SLFS_PREFIX) - 1) + +struct mg_ssl_if_ctx { + char *ssl_cert; + char *ssl_key; + char *ssl_ca_cert; + char *ssl_server_name; +}; + +void mg_ssl_if_init() { +} + +enum mg_ssl_if_result mg_ssl_if_conn_init( + struct mg_connection *nc, const struct mg_ssl_if_conn_params *params, + const char **err_msg) { + struct mg_ssl_if_ctx *ctx = + (struct mg_ssl_if_ctx *) MG_CALLOC(1, sizeof(*ctx)); + if (ctx == NULL) { + MG_SET_PTRPTR(err_msg, "Out of memory"); + return MG_SSL_ERROR; + } + nc->ssl_if_data = ctx; + + if (params->cert != NULL || params->key != NULL) { + if (params->cert != NULL && params->key != NULL) { + ctx->ssl_cert = strdup(params->cert); + ctx->ssl_key = strdup(params->key); + } else { + MG_SET_PTRPTR(err_msg, "Both cert and key are required."); + return MG_SSL_ERROR; + } + } + if (params->ca_cert != NULL && strcmp(params->ca_cert, "*") != 0) { + ctx->ssl_ca_cert = strdup(params->ca_cert); + } + /* TODO(rojer): cipher_suites. */ + if (params->server_name != NULL) { + ctx->ssl_server_name = strdup(params->server_name); + } + return MG_SSL_OK; +} + +void mg_ssl_if_conn_close_notify(struct mg_connection *nc) { + /* Nothing to do */ + (void) nc; +} + +void mg_ssl_if_conn_free(struct mg_connection *nc) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + if (ctx == NULL) return; + nc->ssl_if_data = NULL; + MG_FREE(ctx->ssl_cert); + MG_FREE(ctx->ssl_key); + MG_FREE(ctx->ssl_ca_cert); + MG_FREE(ctx->ssl_server_name); + memset(ctx, 0, sizeof(*ctx)); + MG_FREE(ctx); +} + +bool pem_to_der(const char *pem_file, const char *der_file) { + bool ret = false; + FILE *pf = NULL, *df = NULL; + bool writing = false; + pf = fopen(pem_file, "r"); + if (pf == NULL) goto clean; + remove(der_file); + fs_slfs_set_new_file_size(der_file + MG_SSL_IF_SIMPLELINK_SLFS_PREFIX_LEN, + 2048); + df = fopen(der_file, "w"); + if (df == NULL) goto clean; + while (1) { + char pem_buf[70]; + char der_buf[48]; + if (!fgets(pem_buf, sizeof(pem_buf), pf)) break; + if (writing) { + if (strstr(pem_buf, "-----END ") != NULL) { + ret = true; + break; + } + int l = 0; + while (!isspace((unsigned int) pem_buf[l])) l++; + int der_len = 0; + cs_base64_decode((const unsigned char *) pem_buf, sizeof(pem_buf), + der_buf, &der_len); + if (der_len <= 0) break; + if (fwrite(der_buf, 1, der_len, df) != der_len) break; + } else if (strstr(pem_buf, "-----BEGIN ") != NULL) { + writing = true; + } + } + +clean: + if (pf != NULL) fclose(pf); + if (df != NULL) { + fclose(df); + if (!ret) remove(der_file); + } + return ret; +} + +#if MG_ENABLE_FILESYSTEM && defined(MG_FS_SLFS) +/* If the file's extension is .pem, convert it to DER format and put on SLFS. */ +static char *sl_pem2der(const char *pem_file) { + const char *pem_ext = strstr(pem_file, ".pem"); + if (pem_ext == NULL || *(pem_ext + 4) != '\0') { + return strdup(pem_file); + } + char *der_file = NULL; + /* DER file must be located on SLFS, add prefix. */ + int l = mg_asprintf(&der_file, 0, MG_SSL_IF_SIMPLELINK_SLFS_PREFIX "%.*s.der", + (int) (pem_ext - pem_file), pem_file); + if (der_file == NULL) return NULL; + bool result = false; + cs_stat_t st; + if (mg_stat(der_file, &st) != 0) { + result = pem_to_der(pem_file, der_file); + LOG(LL_DEBUG, ("%s -> %s = %d", pem_file, der_file, result)); + } else { + /* File exists, assume it's already been converted. */ + result = true; + } + if (result) { + /* Strip the SL: prefix we added since NWP does not expect it. */ + memmove(der_file, der_file + MG_SSL_IF_SIMPLELINK_SLFS_PREFIX_LEN, + l - 2 /* including \0 */); + } else { + MG_FREE(der_file); + der_file = NULL; + } + return der_file; +} +#else +static char *sl_pem2der(const char *pem_file) { + return strdup(pem_file); +} +#endif + +int sl_set_ssl_opts(int sock, struct mg_connection *nc) { + int err; + const struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + DBG(("%p ssl ctx: %p", nc, ctx)); + + if (ctx != NULL) { + DBG(("%p %s,%s,%s,%s", nc, (ctx->ssl_cert ? ctx->ssl_cert : "-"), + (ctx->ssl_key ? ctx->ssl_cert : "-"), + (ctx->ssl_ca_cert ? ctx->ssl_ca_cert : "-"), + (ctx->ssl_server_name ? ctx->ssl_server_name : "-"))); + if (ctx->ssl_cert != NULL && ctx->ssl_key != NULL) { + char *ssl_cert = sl_pem2der(ctx->ssl_cert); + char *ssl_key = sl_pem2der(ctx->ssl_key); + if (ssl_cert != NULL && ssl_key != NULL) { + err = sl_SetSockOpt(sock, SL_SOL_SOCKET, + SL_SO_SECURE_FILES_CERTIFICATE_FILE_NAME, ssl_cert, + strlen(ssl_cert)); + LOG(LL_INFO, ("CERTIFICATE_FILE_NAME %s -> %d", ssl_cert, err)); + err = sl_SetSockOpt(sock, SL_SOL_SOCKET, + SL_SO_SECURE_FILES_PRIVATE_KEY_FILE_NAME, ssl_key, + strlen(ssl_key)); + LOG(LL_INFO, ("PRIVATE_KEY_FILE_NAME %s -> %d", ssl_key, err)); + } else { + err = -1; + } + MG_FREE(ssl_cert); + MG_FREE(ssl_key); + if (err != 0) return err; + } + if (ctx->ssl_ca_cert != NULL) { + if (ctx->ssl_ca_cert[0] != '\0') { + char *ssl_ca_cert = sl_pem2der(ctx->ssl_ca_cert); + if (ssl_ca_cert != NULL) { + err = sl_SetSockOpt(sock, SL_SOL_SOCKET, + SL_SO_SECURE_FILES_CA_FILE_NAME, ssl_ca_cert, + strlen(ssl_ca_cert)); + LOG(LL_INFO, ("CA_FILE_NAME %s -> %d", ssl_ca_cert, err)); + } else { + err = -1; + } + MG_FREE(ssl_ca_cert); + if (err != 0) return err; + } + } + if (ctx->ssl_server_name != NULL) { + err = sl_SetSockOpt(sock, SL_SOL_SOCKET, + SL_SO_SECURE_DOMAIN_NAME_VERIFICATION, + ctx->ssl_server_name, strlen(ctx->ssl_server_name)); + DBG(("DOMAIN_NAME_VERIFICATION %s -> %d", ctx->ssl_server_name, err)); + /* Domain name verificationw as added in a NWP service pack, older + * versions return SL_ERROR_BSD_ENOPROTOOPT. There isn't much we can do + * about it, + * so we ignore the error. */ + if (err != 0 && err != SL_ERROR_BSD_ENOPROTOOPT) return err; + } + } + return 0; +} + +#endif /* MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_SIMPLELINK */ diff --git a/src/mongoose-6.11/src/common/platforms/stm32/LICENSE b/src/mongoose-6.11/src/common/platforms/stm32/LICENSE new file mode 100644 index 0000000..3d464ca --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/stm32/LICENSE @@ -0,0 +1,29 @@ +Copyright (c) 2011 The stlink project (github.com/texane/stlink) contributors. + All rights reserved. +Copyright (c) 2011 The "Capt'ns Missing Link" Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of Martin Capitanio nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"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 COPYRIGHT +OWNER OR CONTRIBUTORS 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. diff --git a/src/mongoose-6.11/src/common/platforms/stm32/common.c b/src/mongoose-6.11/src/common/platforms/stm32/common.c new file mode 100644 index 0000000..9a201ca --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/stm32/common.c @@ -0,0 +1,869 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "stlink.h" +#include "usb.h" + +#ifndef _WIN32 +#define O_BINARY 0 //! @todo get rid of this OH MY (@xor-gate) +#endif + +/* todo: stm32l15xxx flash memory, pm0062 manual */ + +/* stm32f FPEC flash controller interface, pm0063 manual */ +// TODO - all of this needs to be abstracted out.... +// STM32F05x is identical, based on RM0091 (DM00031936, Doc ID 018940 Rev 2, +// August 2012) +#define FLASH_REGS_ADDR 0x40022000 +#define FLASH_REGS_SIZE 0x28 + +#define FLASH_ACR (FLASH_REGS_ADDR + 0x00) +#define FLASH_KEYR (FLASH_REGS_ADDR + 0x04) +#define FLASH_SR (FLASH_REGS_ADDR + 0x0c) +#define FLASH_CR (FLASH_REGS_ADDR + 0x10) +#define FLASH_AR (FLASH_REGS_ADDR + 0x14) +#define FLASH_OBR (FLASH_REGS_ADDR + 0x1c) +#define FLASH_WRPR (FLASH_REGS_ADDR + 0x20) + +// For STM32F05x, the RDPTR_KEY may be wrong, but as it is not used anywhere... +#define FLASH_RDPTR_KEY 0x00a5 +#define FLASH_KEY1 0x45670123 +#define FLASH_KEY2 0xcdef89ab + +#define FLASH_SR_BSY 0 +#define FLASH_SR_EOP 5 + +#define FLASH_CR_PG 0 +#define FLASH_CR_PER 1 +#define FLASH_CR_MER 2 +#define FLASH_CR_STRT 6 +#define FLASH_CR_LOCK 7 + +// 32L = 32F1 same CoreID as 32F4! +#define STM32L_FLASH_REGS_ADDR ((uint32_t) 0x40023c00) +#define STM32L_FLASH_ACR (STM32L_FLASH_REGS_ADDR + 0x00) +#define STM32L_FLASH_PECR (STM32L_FLASH_REGS_ADDR + 0x04) +#define STM32L_FLASH_PDKEYR (STM32L_FLASH_REGS_ADDR + 0x08) +#define STM32L_FLASH_PEKEYR (STM32L_FLASH_REGS_ADDR + 0x0c) +#define STM32L_FLASH_PRGKEYR (STM32L_FLASH_REGS_ADDR + 0x10) +#define STM32L_FLASH_OPTKEYR (STM32L_FLASH_REGS_ADDR + 0x14) +#define STM32L_FLASH_SR (STM32L_FLASH_REGS_ADDR + 0x18) +#define STM32L_FLASH_OBR (STM32L_FLASH_REGS_ADDR + 0x1c) +#define STM32L_FLASH_WRPR (STM32L_FLASH_REGS_ADDR + 0x20) +#define FLASH_L1_FPRG 10 +#define FLASH_L1_PROG 3 + +// 32L4 register base is at FLASH_REGS_ADDR (0x40022000) +#define STM32L4_FLASH_KEYR (FLASH_REGS_ADDR + 0x08) +#define STM32L4_FLASH_SR (FLASH_REGS_ADDR + 0x10) +#define STM32L4_FLASH_CR (FLASH_REGS_ADDR + 0x14) +#define STM32L4_FLASH_OPTR (FLASH_REGS_ADDR + 0x20) + +#define STM32L4_FLASH_SR_BSY 16 +#define STM32L4_FLASH_SR_ERRMASK 0x3f8 /* SR [9:3] */ + +#define STM32L4_FLASH_CR_LOCK 31 /* Lock control register */ +#define STM32L4_FLASH_CR_PG 0 /* Program */ +#define STM32L4_FLASH_CR_PER 1 /* Page erase */ +#define STM32L4_FLASH_CR_MER1 2 /* Bank 1 erase */ +#define STM32L4_FLASH_CR_MER2 15 /* Bank 2 erase */ +#define STM32L4_FLASH_CR_STRT 16 /* Start command */ +#define STM32L4_FLASH_CR_BKER 11 /* Bank select for page erase */ +#define STM32L4_FLASH_CR_PNB 3 /* Page number (8 bits) */ +// Bits requesting flash operations (useful when we want to clear them) +#define STM32L4_FLASH_CR_OPBITS \ + ((1lu << STM32L4_FLASH_CR_PG) | (1lu << STM32L4_FLASH_CR_PER) | \ + (1lu << STM32L4_FLASH_CR_MER1) | (1lu << STM32L4_FLASH_CR_MER1)) +// Page is fully specified by BKER and PNB +#define STM32L4_FLASH_CR_PAGEMASK (0x1fflu << STM32L4_FLASH_CR_PNB) + +#define STM32L4_FLASH_OPTR_DUALBANK 21 + +// STM32L0x flash register base and offsets +// same as 32L1 above +// RM0090 - DM00031020.pdf +#define STM32L0_FLASH_REGS_ADDR ((uint32_t) 0x40022000) +#define FLASH_ACR_OFF ((uint32_t) 0x00) +#define FLASH_PECR_OFF ((uint32_t) 0x04) +#define FLASH_PDKEYR_OFF ((uint32_t) 0x08) +#define FLASH_PEKEYR_OFF ((uint32_t) 0x0c) +#define FLASH_PRGKEYR_OFF ((uint32_t) 0x10) +#define FLASH_OPTKEYR_OFF ((uint32_t) 0x14) +#define FLASH_SR_OFF ((uint32_t) 0x18) +#define FLASH_OBR_OFF ((uint32_t) 0x1c) +#define FLASH_WRPR_OFF ((uint32_t) 0x20) + +// STM32F4 +#define FLASH_F4_REGS_ADDR ((uint32_t) 0x40023c00) +#define FLASH_F4_KEYR (FLASH_F4_REGS_ADDR + 0x04) +#define FLASH_F4_OPT_KEYR (FLASH_F4_REGS_ADDR + 0x08) +#define FLASH_F4_SR (FLASH_F4_REGS_ADDR + 0x0c) +#define FLASH_F4_CR (FLASH_F4_REGS_ADDR + 0x10) +#define FLASH_F4_OPT_CR (FLASH_F4_REGS_ADDR + 0x14) +#define FLASH_F4_CR_STRT 16 +#define FLASH_F4_CR_LOCK 31 +#define FLASH_F4_CR_SER 1 +#define FLASH_F4_CR_SNB 3 +#define FLASH_F4_CR_SNB_MASK 0xf8 +#define FLASH_F4_SR_BSY 16 + +#define L1_WRITE_BLOCK_SIZE 0x80 +#define L0_WRITE_BLOCK_SIZE 0x40 + +#define STLINK_REG_CM3_CPUID 0xE000ED00 +#define STLINK_REG_CM3_FP_CTRL 0xE0002000 +#define STLINK_REG_CM3_FP_COMP0 0xE0002008 + +/* Cortexâ„¢-M3 Technical Reference Manual */ +/* Debug Halting Control and Status Register */ +#define STLINK_REG_DHCSR 0xe000edf0 +#define STLINK_REG_DHCSR_DBGKEY 0xa05f0000 +#define STLINK_REG_DCRSR 0xe000edf4 +#define STLINK_REG_DCRDR 0xe000edf8 + +struct stlink_chipid_params { + uint32_t chip_id; + char *description; + uint32_t flash_size_reg; + uint32_t flash_pagesize; + uint32_t sram_size; + uint32_t bootrom_base; + uint32_t bootrom_size; +}; + +void write_uint32(unsigned char *buf, uint32_t ui) { + if (!is_bigendian()) { // le -> le (don't swap) + buf[0] = ((unsigned char *) &ui)[0]; + buf[1] = ((unsigned char *) &ui)[1]; + buf[2] = ((unsigned char *) &ui)[2]; + buf[3] = ((unsigned char *) &ui)[3]; + } else { + buf[0] = ((unsigned char *) &ui)[3]; + buf[1] = ((unsigned char *) &ui)[2]; + buf[2] = ((unsigned char *) &ui)[1]; + buf[3] = ((unsigned char *) &ui)[0]; + } +} + +void write_uint16(unsigned char *buf, uint16_t ui) { + if (!is_bigendian()) { // le -> le (don't swap) + buf[0] = ((unsigned char *) &ui)[0]; + buf[1] = ((unsigned char *) &ui)[1]; + } else { + buf[0] = ((unsigned char *) &ui)[1]; + buf[1] = ((unsigned char *) &ui)[0]; + } +} + +uint32_t read_uint32(const unsigned char *c, const int pt) { + uint32_t ui; + char *p = (char *) &ui; + + if (!is_bigendian()) { // le -> le (don't swap) + p[0] = c[pt + 0]; + p[1] = c[pt + 1]; + p[2] = c[pt + 2]; + p[3] = c[pt + 3]; + } else { + p[0] = c[pt + 3]; + p[1] = c[pt + 2]; + p[2] = c[pt + 1]; + p[3] = c[pt + 0]; + } + return ui; +} + +static uint32_t __attribute__((unused)) read_flash_rdp(stlink_t *sl) { + uint32_t rdp; + stlink_read_debug32(sl, FLASH_WRPR, &rdp); + return rdp & 0xff; +} + +static inline uint32_t read_flash_cr(stlink_t *sl) { + uint32_t res; + stlink_read_debug32(sl, FLASH_F4_CR, &res); + return res; +} + +static inline unsigned int is_flash_locked(stlink_t *sl) { + /* return non zero for true */ + uint32_t cr_lock_shift = FLASH_F4_CR_LOCK, cr = read_flash_cr(sl); + return cr & (1 << cr_lock_shift); +} + +static void unlock_flash(stlink_t *sl) { + uint32_t key_reg = FLASH_F4_KEYR; + stlink_write_debug32(sl, key_reg, FLASH_KEY1); + stlink_write_debug32(sl, key_reg, FLASH_KEY2); +} + +static int unlock_flash_if(stlink_t *sl) { + /* unlock flash if already locked */ + + if (is_flash_locked(sl)) { + unlock_flash(sl); + if (is_flash_locked(sl)) { + printf("Failed to unlock flash!\n"); + return -1; + } + } + return 0; +} + +static void lock_flash(stlink_t *sl) { + uint32_t n; + uint32_t cr_reg = FLASH_F4_CR; + uint32_t cr_lock_shift = FLASH_F4_CR_LOCK; + + n = read_flash_cr(sl) | (1 << cr_lock_shift); + stlink_write_debug32(sl, cr_reg, n); +} + +static void set_flash_cr_pg(stlink_t *sl) { + uint32_t cr_reg, x; + + x = read_flash_cr(sl); + + cr_reg = FLASH_F4_CR; + x |= 1 << FLASH_CR_PG; + + stlink_write_debug32(sl, cr_reg, x); +} + +static void __attribute__((unused)) clear_flash_cr_pg(stlink_t *sl) { + uint32_t cr_reg, n; + + cr_reg = FLASH_F4_CR; + + n = read_flash_cr(sl) & ~(1 << FLASH_CR_PG); + stlink_write_debug32(sl, cr_reg, n); +} + +static void set_flash_cr_per(stlink_t *sl) { + const uint32_t n = 1 << FLASH_CR_PER; + stlink_write_debug32(sl, FLASH_CR, n); +} + +static void __attribute__((unused)) clear_flash_cr_per(stlink_t *sl) { + const uint32_t n = read_flash_cr(sl) & ~(1 << FLASH_CR_PER); + stlink_write_debug32(sl, FLASH_CR, n); +} + +static void set_flash_cr_mer(stlink_t *sl, bool v) { + uint32_t val, cr_reg, cr_mer, cr_pg; + + cr_reg = FLASH_F4_CR; + cr_mer = 1 << FLASH_CR_MER; + cr_pg = 1 << FLASH_CR_PG; + + stlink_read_debug32(sl, cr_reg, &val); + if (val & cr_pg) { + /* STM32F030 will drop MER bit if PG was set */ + val &= ~cr_pg; + stlink_write_debug32(sl, cr_reg, val); + } + + if (v) + val |= cr_mer; + else + val &= ~cr_mer; + stlink_write_debug32(sl, cr_reg, val); +} + +static void __attribute__((unused)) clear_flash_cr_mer(stlink_t *sl) { + uint32_t val, cr_reg, cr_mer; + + cr_reg = FLASH_F4_CR; + cr_mer = 1 << FLASH_CR_MER; + + stlink_read_debug32(sl, cr_reg, &val); + val &= ~cr_mer; + stlink_write_debug32(sl, cr_reg, val); +} + +static void set_flash_cr_strt(stlink_t *sl) { + uint32_t val, cr_reg, cr_strt; + + cr_reg = FLASH_F4_CR; + cr_strt = 1 << FLASH_F4_CR_STRT; + + stlink_read_debug32(sl, cr_reg, &val); + val |= cr_strt; + stlink_write_debug32(sl, cr_reg, val); +} + +static inline uint32_t read_flash_sr(stlink_t *sl) { + uint32_t res, sr_reg; + sr_reg = FLASH_F4_SR; + stlink_read_debug32(sl, sr_reg, &res); + return res; +} + +static inline unsigned int is_flash_busy(stlink_t *sl) { + uint32_t sr_busy_shift; + sr_busy_shift = FLASH_F4_SR_BSY; + return read_flash_sr(sl) & (1 << sr_busy_shift); +} + +static void wait_flash_busy(stlink_t *sl) { + /* todo: add some delays here */ + while (is_flash_busy(sl)) + ; +} + +static void wait_flash_busy_progress(stlink_t *sl) { + while (is_flash_busy(sl)) { + usleep(10000); + } +} + +static inline unsigned int is_flash_eop(stlink_t *sl) { + return read_flash_sr(sl) & (1 << FLASH_SR_EOP); +} + +static void __attribute__((unused)) clear_flash_sr_eop(stlink_t *sl) { + const uint32_t n = read_flash_sr(sl) & ~(1 << FLASH_SR_EOP); + stlink_write_debug32(sl, FLASH_SR, n); +} + +static void __attribute__((unused)) wait_flash_eop(stlink_t *sl) { + /* todo: add some delays here */ + while (is_flash_eop(sl) == 0) + ; +} + +static inline void write_flash_ar(stlink_t *sl, uint32_t n) { + stlink_write_debug32(sl, FLASH_AR, n); +} + +static inline void write_flash_cr_psiz(stlink_t *sl, uint32_t n) { + uint32_t x = read_flash_cr(sl); + x &= ~(0x03 << 8); + x |= (n << 8); + stlink_write_debug32(sl, FLASH_F4_CR, x); +} + +static inline void write_flash_cr_snb(stlink_t *sl, uint32_t n) { + uint32_t x = read_flash_cr(sl); + x &= ~FLASH_F4_CR_SNB_MASK; + x |= (n << FLASH_F4_CR_SNB); + x |= (1 << FLASH_F4_CR_SER); + stlink_write_debug32(sl, FLASH_F4_CR, x); +} + +static inline void write_flash_cr_bker_pnb(stlink_t *sl, uint32_t n) { + stlink_write_debug32(sl, STM32L4_FLASH_SR, + 0xFFFFFFFF & ~(1 << STM32L4_FLASH_SR_BSY)); + uint32_t x = read_flash_cr(sl); + x &= ~STM32L4_FLASH_CR_OPBITS; + x &= ~STM32L4_FLASH_CR_PAGEMASK; + x &= ~(1 << STM32L4_FLASH_CR_MER1); + x &= ~(1 << STM32L4_FLASH_CR_MER2); + x |= (n << STM32L4_FLASH_CR_PNB); + x |= (1lu << STM32L4_FLASH_CR_PER); + stlink_write_debug32(sl, STM32L4_FLASH_CR, x); +} + +void stlink_close(stlink_t *sl) { + if (!sl) return; + _stlink_usb_close(sl); + free(sl); +} + +int stlink_exit_debug_mode(stlink_t *sl) { + int ret; + + ret = stlink_write_debug32(sl, STLINK_REG_DHCSR, STLINK_REG_DHCSR_DBGKEY); + if (ret == -1) return ret; + + return _stlink_usb_exit_debug_mode(sl); +} + +int stlink_enter_swd_mode(stlink_t *sl) { + return _stlink_usb_enter_swd_mode(sl); +} + +// Force the core into the debug mode -> halted state. +int stlink_force_debug(stlink_t *sl) { + return _stlink_usb_force_debug(sl); +} + +int stlink_exit_dfu_mode(stlink_t *sl) { + return _stlink_usb_exit_dfu_mode(sl); +} + +int stlink_core_id(stlink_t *sl) { + int ret; + + ret = _stlink_usb_core_id(sl); + if (ret == -1) { + printf("Failed to read core_id\n"); + return ret; + } + return ret; +} + +int stlink_chip_id(stlink_t *sl, uint32_t *chip_id) { + int ret; + + ret = stlink_read_debug32(sl, 0xE0042000, chip_id); + if (ret == -1) return ret; + + if (*chip_id == 0) + ret = stlink_read_debug32( + sl, 0x40015800, + chip_id); // Try Corex M0 DBGMCU_IDCODE register address + + return ret; +} + +/** + * reads and decodes the flash parameters, as dynamically as possible + * @param sl + * @return 0 for success, or -1 for unsupported core type. + */ +static const struct stlink_chipid_params F7_params = { + // RM0385 and DS10916 document was used to find these paramaters + .chip_id = STLINK_CHIPID_STM32_F7, + .description = "F7 device", + .flash_size_reg = 0x1ff0f442, // section 41.2 + .flash_pagesize = 0x800, // No flash pages + .sram_size = 0x50000, // "SRAM" byte size in hex from DS Fig 18 + .bootrom_base = + 0x00100000, // "System memory" starting address from DS Fig 18 + .bootrom_size = 0xEDC0 // "System memory" byte size in hex from DS Fig 18 +}; + +int stlink_load_device_params(stlink_t *sl) { + const struct stlink_chipid_params *params = &F7_params; + stlink_core_id(sl); + uint32_t chip_id; + uint32_t flash_size; + + stlink_chip_id(sl, &chip_id); + sl->chip_id = chip_id & 0xfff; + /* Fix chip_id for F4 rev A errata , Read CPU ID, as CoreID is the same for + * F2/F4*/ + if (sl->chip_id == 0x411) { + uint32_t cpuid; + stlink_read_debug32(sl, 0xE000ED00, &cpuid); + if ((cpuid & 0xfff0) == 0xc240) sl->chip_id = 0x413; + } + + if (sl->chip_id != STLINK_CHIPID_STM32_F7) { + printf("unsupported chip id! %d\n", chip_id); + return -1; + } + + // These are fixed... + sl->flash_base = STM32_FLASH_BASE; + sl->sram_base = STM32_SRAM_BASE; + stlink_read_debug32(sl, (params->flash_size_reg) & ~3, &flash_size); + if (params->flash_size_reg & 2) flash_size = flash_size >> 16; + flash_size = flash_size & 0xffff; + + sl->flash_size = flash_size * 1024; + sl->flash_pgsz = params->flash_pagesize; + sl->sram_size = params->sram_size; + sl->sys_base = params->bootrom_base; + sl->sys_size = params->bootrom_size; + + return 0; +} + +int stlink_reset(stlink_t *sl) { + return _stlink_usb_reset(sl); +} + +int stlink_jtag_reset(stlink_t *sl, int value) { + return _stlink_usb_jtag_reset(sl, value); +} + +int stlink_run(stlink_t *sl) { + return _stlink_usb_run(sl); +} + +int stlink_set_swdclk(stlink_t *sl, uint16_t divisor) { + return _stlink_usb_set_swdclk(sl, divisor); +} + +int stlink_status(stlink_t *sl) { + int ret; + + ret = _stlink_usb_status(sl); + stlink_core_stat(sl); + + return ret; +} + +int stlink_target_voltage(stlink_t *sl) { + return _stlink_usb_target_voltage(sl); +} + +int stlink_read_debug32(stlink_t *sl, uint32_t addr, uint32_t *data) { + return _stlink_usb_read_debug32(sl, addr, data); +} + +int stlink_write_debug32(stlink_t *sl, uint32_t addr, uint32_t data) { + return _stlink_usb_write_debug32(sl, addr, data); +} + +int stlink_write_mem32(stlink_t *sl, uint32_t addr, uint16_t len) { + if (len % 4 != 0) { + fprintf(stderr, + "Error: Data length doesn't have a 32 bit alignment: +%d byte.\n", + len % 4); + abort(); + } + return _stlink_usb_write_mem32(sl, addr, len); +} + +int stlink_read_mem32(stlink_t *sl, uint32_t addr, uint16_t len) { + if (len % 4 != 0) { // !!! never ever: fw gives just wrong values + fprintf(stderr, + "Error: Data length doesn't have a 32 bit alignment: +%d byte.\n", + len % 4); + abort(); + } + return _stlink_usb_read_mem32(sl, addr, len); +} + +int stlink_write_mem8(stlink_t *sl, uint32_t addr, uint16_t len) { + if (len > 0x40) { // !!! never ever: Writing more then 0x40 bytes gives + // unexpected behaviour + fprintf(stderr, "Error: Data length > 64: +%d byte.\n", len); + abort(); + } + return _stlink_usb_write_mem8(sl, addr, len); +} + +int stlink_write_reg(stlink_t *sl, uint32_t reg, int idx) { + return _stlink_usb_write_reg(sl, reg, idx); +} + +int stlink_read_reg(stlink_t *sl, int r_idx, struct stlink_reg *regp) { + if (r_idx > 20 || r_idx < 0) { + fprintf(stderr, "Error: register index must be in [0..20]\n"); + return -1; + } + + return _stlink_usb_read_reg(sl, r_idx, regp); +} + +bool stlink_is_core_halted(stlink_t *sl) { + bool ret = false; + + stlink_status(sl); + if (sl->q_buf[0] == STLINK_CORE_HALTED) ret = true; + + return ret; +} + +int stlink_step(stlink_t *sl) { + return _stlink_usb_step(sl); +} + +int stlink_current_mode(stlink_t *sl) { + return _stlink_usb_current_mode(sl); +} + +// End of delegates.... Common code below here... + +// Endianness +// http://www.ibm.com/developerworks/aix/library/au-endianc/index.html +// const int i = 1; +// #define is_bigendian() ( (*(char*)&i) == 0 ) + +unsigned int is_bigendian(void) { + static volatile const unsigned int i = 1; + return *(volatile const char *) &i == 0; +} + +uint16_t read_uint16(const unsigned char *c, const int pt) { + uint32_t ui; + char *p = (char *) &ui; + + if (!is_bigendian()) { // le -> le (don't swap) + p[0] = c[pt + 0]; + p[1] = c[pt + 1]; + } else { + p[0] = c[pt + 1]; + p[1] = c[pt + 0]; + } + return ui; +} + +// same as above with entrypoint. + +void stlink_run_at(stlink_t *sl, stm32_addr_t addr) { + stlink_write_reg(sl, addr, 15); /* pc register */ + + stlink_run(sl); + + while (stlink_is_core_halted(sl)) usleep(3000000); +} + +void stlink_core_stat(stlink_t *sl) { + if (sl->q_len <= 0) return; + + switch (sl->q_buf[0]) { + case STLINK_CORE_RUNNING: + sl->core_stat = STLINK_CORE_RUNNING; + return; + case STLINK_CORE_HALTED: + sl->core_stat = STLINK_CORE_HALTED; + return; + default: + sl->core_stat = STLINK_CORE_STAT_UNKNOWN; + fprintf(stderr, " core status: unknown\n"); + } +} + +void stlink_fwrite_finalize(stlink_t *sl, stm32_addr_t addr) { + unsigned int val; + /* set stack*/ + stlink_read_debug32(sl, addr, &val); + stlink_write_reg(sl, val, 13); + /* Set PC to the reset routine*/ + stlink_read_debug32(sl, addr + 4, &val); + stlink_write_reg(sl, val, 15); + stlink_run(sl); +} + +typedef bool (*save_block_fn)(void *arg, uint8_t *block, ssize_t len); + +static int stlink_read(stlink_t *sl, stm32_addr_t addr, size_t size, + save_block_fn fn, void *fn_arg) { + int error = -1; + + if (size < 1) size = sl->flash_size; + + if (size > sl->flash_size) size = sl->flash_size; + + size_t cmp_size = (sl->flash_pgsz > 0x1800) ? 0x1800 : sl->flash_pgsz; + for (size_t off = 0; off < size; off += cmp_size) { + size_t aligned_size; + + /* adjust last page size */ + if ((off + cmp_size) > size) cmp_size = size - off; + + aligned_size = cmp_size; + if (aligned_size & (4 - 1)) aligned_size = (cmp_size + 4) & ~(4 - 1); + + stlink_read_mem32(sl, addr + (uint32_t) off, aligned_size); + + if (!fn(fn_arg, sl->q_buf, aligned_size)) { + goto on_error; + } + } + + /* success */ + error = 0; + +on_error: + return error; +} + +int write_buffer_to_sram(stlink_t *sl, flash_loader_t *fl, const uint8_t *buf, + size_t size) { + /* write the buffer right after the loader */ + size_t chunk = size & ~0x3; + size_t rem = size & 0x3; + if (chunk) { + memcpy(sl->q_buf, buf, chunk); + stlink_write_mem32(sl, fl->buf_addr, chunk); + } + if (rem) { + memcpy(sl->q_buf, buf + chunk, rem); + stlink_write_mem8(sl, (fl->buf_addr) + (uint32_t) chunk, rem); + } + return 0; +} + +uint32_t calculate_F7_sectornum(uint32_t flashaddr) { + flashaddr &= ~STM32_FLASH_BASE; // Page now holding the actual flash address + if (flashaddr < 0x20000) + return (flashaddr / 0x8000); + else if (flashaddr < 0x40000) + return (4); + else + return (flashaddr / 0x40000) + 4; +} + +uint32_t stlink_calculate_pagesize(stlink_t *sl, uint32_t flashaddr) { + if (sl->chip_id == STLINK_CHIPID_STM32_F7) { + uint32_t sector = calculate_F7_sectornum(flashaddr); + if (sector < 4) + sl->flash_pgsz = 0x8000; + else if (sector < 5) + sl->flash_pgsz = 0x20000; + else + sl->flash_pgsz = 0x40000; + } + return (uint32_t) sl->flash_pgsz; +} + +/** + * Erase a page of flash, assumes sl is fully populated with things like + * chip/core ids + * @param sl stlink context + * @param flashaddr an address in the flash page to erase + * @return 0 on success -ve on failure + */ +int stlink_erase_flash_page(stlink_t *sl, stm32_addr_t flashaddr) { + /* wait for ongoing op to finish */ + wait_flash_busy(sl); + + /* unlock if locked */ + unlock_flash_if(sl); + + /* select the page to erase */ + // calculate the actual page from the address + uint32_t sector = calculate_F7_sectornum(flashaddr); + + write_flash_cr_snb(sl, sector); + + /* start erase operation */ + set_flash_cr_strt(sl); + + /* wait for completion */ + wait_flash_busy(sl); + + /* relock the flash */ + // todo: fails to program if this is in + lock_flash(sl); + + return 0; +} + +/** + * Verify addr..addr+len is binary identical to base...base+len + * @param sl stlink context + * @param address stm device address + * @param data host side buffer to check against + * @param length how much + * @return 0 for success, -ve for failure + */ +int stlink_verify_write_flash(stlink_t *sl, stm32_addr_t address, uint8_t *data, + unsigned length) { + size_t off; + size_t cmp_size = (sl->flash_pgsz > 0x1800) ? 0x1800 : sl->flash_pgsz; + printf("Verifying flash...\n"); + for (off = 0; off < length; off += cmp_size) { + size_t aligned_size; + + /* adjust last page size */ + if ((off + cmp_size) > length) cmp_size = length - off; + + aligned_size = cmp_size; + if (aligned_size & (4 - 1)) aligned_size = (cmp_size + 4) & ~(4 - 1); + + stlink_read_mem32(sl, address + (uint32_t) off, aligned_size); + + if (memcmp(sl->q_buf, data + off, cmp_size)) { + printf("Verification of flash failed at offset: %u\n", + (unsigned int) off); + return -1; + } + } + return 0; +} + +int stlink_write_flash(stlink_t *sl, stm32_addr_t addr, uint8_t *base, + uint32_t len, uint8_t eraseonly) { + size_t off; + flash_loader_t fl; + printf("Writing %d (%#x) bytes to stm32 address: %u (%#x)\n", len, len, addr, + addr); + /* check addr range is inside the flash */ + stlink_calculate_pagesize(sl, addr); + if (addr < sl->flash_base) { + printf("addr too low %#x < %#x\n", addr, sl->flash_base); + return -1; + } else if ((addr + len) < addr) { + printf("addr overruns\n"); + return -1; + } else if ((addr + len) > (sl->flash_base + sl->flash_size)) { + printf("addr too high\n"); + return -1; + } else if (addr & 1) { + printf("unaligned addr 0x%x\n", addr); + return -1; + } else if (len & 1) { + printf("unaligned len 0x%x -- padding with zero\n", len); + len += 1; + } else if (addr & (sl->flash_pgsz - 1)) { + printf("addr not a multiple of pagesize, not supported\n"); + return -1; + } + + // Make sure we've loaded the context with the chip details + stlink_core_id(sl); + /* erase each page */ + int page_count = 0; + for (off = 0; off < len; + off += stlink_calculate_pagesize(sl, addr + (uint32_t) off)) { + fprintf(stdout, "Erasing flash page at addr 0x%08X\n", (int) (addr + off)); + /* addr must be an addr inside the page */ + if (stlink_erase_flash_page(sl, addr + (uint32_t) off) == -1) { + printf("Failed to erase_flash_page(%#zx) == -1\n", addr + off); + return -1; + } + page_count++; + } + + if (eraseonly) return 0; + + /* flash loader initialization */ + if (stlink_flash_loader_init(sl, &fl) == -1) { + printf("stlink_flash_loader_init() == -1\n"); + return -1; + } + + /* First unlock the cr */ + unlock_flash_if(sl); + + /* TODO: Check that Voltage range is 2.7 - 3.6 V */ + /* set parallelisim to 32 bit*/ + int voltage = stlink_target_voltage(sl); + if (voltage == -1) { + printf("Failed to read Target voltage\n"); + return voltage; + } else if (voltage > 2700) { + write_flash_cr_psiz(sl, 2); + } else { + printf( + "Target voltage (%d mV) too low for 32-bit flash, using 8-bit " + "flash writes\n", + voltage); + write_flash_cr_psiz(sl, 0); + } + + /* set programming mode */ + set_flash_cr_pg(sl); + + for (off = 0; off < len;) { + size_t size = len - off > 0x8000 ? 0x8000 : len - off; + printf("Writing %d bytes at 0x%08X\n", (int) size, + (int) (addr + (uint32_t) off)); + if (stlink_flash_loader_run(sl, &fl, addr + (uint32_t) off, base + off, + size) == -1) { + printf("stlink_flash_loader_run(%#zx) failed! == -1\n", addr + off); + return -1; + } + off += size; + } + + /* Relock flash */ + lock_flash(sl); + + return stlink_verify_write_flash(sl, addr, base, len); +} diff --git a/src/mongoose-6.11/src/common/platforms/stm32/flash.c b/src/mongoose-6.11/src/common/platforms/stm32/flash.c new file mode 100644 index 0000000..371c6eb --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/stm32/flash.c @@ -0,0 +1,93 @@ +/* simple wrapper around the stlink_flash_write function */ + +#include +#include +#include +#include +#include + +#include "stlink.h" +#include "usb.h" + +#define FLASH_ADDRESS 0x8000000 + +int stm32_flash(const char *device_name, void *data, int len) { + stlink_t *sl = NULL; + int err = -1; + uint8_t serial[16]; + + int j = (int) strlen(device_name); + int length = j / 2; // the length of the destination-array + if (j % 2 != 0) return -1; + + for (size_t k = 0; j >= 0 && k < sizeof(serial); ++k, j -= 2) { + char buffer[3] = {0}; + memcpy(buffer, device_name + j, 2); + serial[length - k] = (uint8_t) strtol(buffer, NULL, 16); + } + + sl = stlink_open_usb(1, (char *) serial); + + if (sl == NULL) return -1; + + if (stlink_current_mode(sl) == STLINK_DEV_DFU_MODE) { + if (stlink_exit_dfu_mode(sl)) { + printf("Failed to exit DFU mode\n"); + goto on_error; + } + } + + if (stlink_current_mode(sl) != STLINK_DEV_DEBUG_MODE) { + if (stlink_enter_swd_mode(sl)) { + printf("Failed to enter SWD mode\n"); + goto on_error; + } + } + + if (stlink_jtag_reset(sl, 2)) { + printf("Failed to reset JTAG\n"); + goto on_error; + } + + if (stlink_reset(sl)) { + printf("Failed to reset device\n"); + goto on_error; + } + + // Core must be halted to use RAM based flashloaders + if (stlink_force_debug(sl)) { + printf("Failed to halt the core\n"); + goto on_error; + } + + if (stlink_status(sl)) { + printf("Failed to get Core's status\n"); + goto on_error; + } + + size_t size = 0; + + if ((FLASH_ADDRESS > sl->flash_base + sl->flash_size)) { + printf("Unknown memory region\n"); + goto on_error; + } + + err = stlink_write_flash(sl, FLASH_ADDRESS, (uint8_t *) data, len, 0); + stlink_fwrite_finalize(sl, FLASH_ADDRESS); + + if (err == -1) { + printf("stlink_fwrite_flash() == -1\n"); + goto on_error; + } + + stlink_jtag_reset(sl, 2); + stlink_reset(sl); + + err = 0; + +on_error: + stlink_exit_debug_mode(sl); + stlink_close(sl); + + return err; +} diff --git a/src/mongoose-6.11/src/common/platforms/stm32/flash_loader.c b/src/mongoose-6.11/src/common/platforms/stm32/flash_loader.c new file mode 100644 index 0000000..b508e8e --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/stm32/flash_loader.c @@ -0,0 +1,93 @@ +#include "stlink.h" + +#include +#include +#include + +static const uint8_t loader_code_stm32f7[] = { + 0x08, 0x4b, 0x72, 0xb1, 0x04, 0x68, 0x0c, 0x60, + 0xbf, 0xf3, 0x4f, 0x8f, // DSB Memory barrier for in order flash write + 0xdc, 0x89, 0x14, 0xf0, 0x01, 0x0f, 0xfb, 0xd1, + 0x00, 0xf1, 0x04, 0x00, 0x01, 0xf1, 0x04, 0x01, + 0xa2, 0xf1, 0x01, 0x02, 0xef, 0xe7, 0x00, 0xbe, // bkpt #0x00 + 0x00, 0x3c, 0x02, 0x40, +}; + +#define WAIT_ROUNDS 10000 + +int stlink_flash_loader_init(stlink_t *sl, flash_loader_t *fl) { + size_t size; + + /* allocate the loader in sram */ + if (stlink_flash_loader_write_to_sram(sl, &fl->loader_addr, &size) == -1) { + printf("Failed to write flash loader to sram!\n"); + return -1; + } + + /* allocate a one page buffer in sram right after loader */ + fl->buf_addr = fl->loader_addr + (uint32_t) size; + + return 0; +} + +int stlink_flash_loader_write_to_sram(stlink_t *sl, stm32_addr_t *addr, + size_t *size) { + memcpy(sl->q_buf, loader_code_stm32f7, sizeof(loader_code_stm32f7)); + stlink_write_mem32(sl, sl->sram_base, sizeof(loader_code_stm32f7)); + + *addr = sl->sram_base; + *size = sizeof(loader_code_stm32f7); + + /* success */ + return 0; +} + +int stlink_flash_loader_run(stlink_t *sl, flash_loader_t *fl, + stm32_addr_t target, const uint8_t *buf, + size_t size) { + struct stlink_reg rr; + int i = 0; + size_t count = 0; + + // FIXME This can never return -1 + if (write_buffer_to_sram(sl, fl, buf, size) == -1) { + // IMPOSSIBLE! + printf("write_buffer_to_sram() == -1\n"); + return -1; + } + + count = size / sizeof(uint32_t); + if (size % sizeof(uint32_t)) ++count; + + /* setup core */ + stlink_write_reg(sl, fl->buf_addr, 0); /* source */ + stlink_write_reg(sl, target, 1); /* target */ + stlink_write_reg(sl, (uint32_t) count, 2); /* count */ + stlink_write_reg( + sl, 0, + 3); /* flash bank 0 (input), only used on F0, but armless fopr others */ + stlink_write_reg(sl, fl->loader_addr, 15); /* pc register */ + + /* run loader */ + stlink_run(sl); + + /* wait until done (reaches breakpoint) */ + for (i = 0; i < WAIT_ROUNDS; i++) { + usleep(10); + if (stlink_is_core_halted(sl)) break; + } + + if (i >= WAIT_ROUNDS) { + printf("flash loader run error\n"); + return -1; + } + + /* check written byte count */ + stlink_read_reg(sl, 2, &rr); + if (rr.r[2] != 0) { + printf("write error, count == %u\n", rr.r[2]); + return -1; + } + + return 0; +} diff --git a/src/mongoose-6.11/src/common/platforms/stm32/flash_loader.h b/src/mongoose-6.11/src/common/platforms/stm32/flash_loader.h new file mode 100644 index 0000000..a3f072d --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/stm32/flash_loader.h @@ -0,0 +1,30 @@ +/* + * File: stlink.h + * + * This should contain all the common top level stlink interfaces, regardless + * of how the backend does the work.... + */ +#ifndef STLINK_FLASH_LOADER_H_ +#define STLINK_FLASH_LOADER_H_ + +#include +#include + +#include "stlink.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int stlink_flash_loader_init(stlink_t *sl, flash_loader_t *fl); +int stlink_flash_loader_write_to_sram(stlink_t *sl, stm32_addr_t *addr, + size_t *size); +int stlink_flash_loader_run(stlink_t *sl, flash_loader_t *fl, + stm32_addr_t target, const uint8_t *buf, + size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* STLINK_FLASH_LOADER_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/stm32/flasher.go b/src/mongoose-6.11/src/common/platforms/stm32/flasher.go new file mode 100644 index 0000000..8ddde03 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/stm32/flasher.go @@ -0,0 +1,38 @@ +// +build linux + +package stm32 + +// #cgo CFLAGS: -I/usr/include/libusb-1.0/ +// #cgo LDFLAGS: -lusb-1.0 +// #include "stm32_flash.h" +import "C" + +import ( + "time" + "unsafe" + + "cesanta.com/mos/flash/common" + "github.com/cesanta/errors" +) + +type FlashOpts struct { + ShareName string + Timeout time.Duration +} + +func Flash(fw *common.FirmwareBundle, opts *FlashOpts) error { + data, err := fw.GetPartData("boot") + if err != nil { + return errors.Annotatef(err, "invalid manifest") + } + + flash_res := C.stm32_flash(C.CString(opts.ShareName), + unsafe.Pointer(&data[0]), + C.int(len(data))) + + if flash_res != 0 { + return errors.Errorf("flash failed") + } + + return nil +} diff --git a/src/mongoose-6.11/src/common/platforms/stm32/stlink.h b/src/mongoose-6.11/src/common/platforms/stm32/stlink.h new file mode 100644 index 0000000..ac34dd0 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/stm32/stlink.h @@ -0,0 +1,198 @@ +/* + * File: stlink.h + * + * This should contain all the common top level stlink interfaces, regardless + * of how the backend does the work.... + */ +#ifndef STLINK_H +#define STLINK_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define STLINK_ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) + +// Max data transfer size. +// 6kB = max mem32_read block, 8kB sram +//#define Q_BUF_LEN 96 +#define Q_BUF_LEN (1024 * 100) + +// STLINK_DEBUG_RESETSYS, etc: +#define STLINK_CORE_RUNNING 0x80 +#define STLINK_CORE_HALTED 0x81 +#define STLINK_CORE_STAT_UNKNOWN -1 + +#define STLINK_GET_VERSION 0xf1 +#define STLINK_GET_CURRENT_MODE 0xf5 +#define STLINK_GET_TARGET_VOLTAGE 0xF7 + +#define STLINK_DEBUG_COMMAND 0xF2 +#define STLINK_DFU_COMMAND 0xF3 +#define STLINK_DFU_EXIT 0x07 + +// STLINK_GET_CURRENT_MODE +#define STLINK_DEV_DFU_MODE 0x00 +#define STLINK_DEV_MASS_MODE 0x01 +#define STLINK_DEV_DEBUG_MODE 0x02 +#define STLINK_DEV_UNKNOWN_MODE -1 + +// TODO - possible poor names... +#define STLINK_SWD_ENTER 0x30 +#define STLINK_SWD_READCOREID 0x32 // TBD +#define STLINK_JTAG_WRITEDEBUG_32BIT 0x35 +#define STLINK_JTAG_READDEBUG_32BIT 0x36 +#define STLINK_JTAG_DRIVE_NRST 0x3c + +#define STLINK_DEBUG_APIV2_SWD_SET_FREQ 0x43 + +/* cortex core ids */ +// TODO clean this up... +#define STM32VL_CORE_ID 0x1ba01477 +#define STM32F7_CORE_ID 0x5ba02477 + +// Constant STM32 memory map figures +#define STM32_FLASH_BASE 0x08000000 +#define STM32_SRAM_BASE 0x20000000 + +// Baud rate divisors for SWDCLK +#define STLINK_SWDCLK_4MHZ_DIVISOR 0 +#define STLINK_SWDCLK_1P8MHZ_DIVISOR 1 +#define STLINK_SWDCLK_1P2MHZ_DIVISOR 2 +#define STLINK_SWDCLK_950KHZ_DIVISOR 3 +#define STLINK_SWDCLK_480KHZ_DIVISOR 7 +#define STLINK_SWDCLK_240KHZ_DIVISOR 15 +#define STLINK_SWDCLK_125KHZ_DIVISOR 31 +#define STLINK_SWDCLK_100KHZ_DIVISOR 40 +#define STLINK_SWDCLK_50KHZ_DIVISOR 79 +#define STLINK_SWDCLK_25KHZ_DIVISOR 158 +#define STLINK_SWDCLK_15KHZ_DIVISOR 265 +#define STLINK_SWDCLK_5KHZ_DIVISOR 798 + +#define STLINK_CHIPID_STM32_F7 0x449 + +/* Enough space to hold both a V2 command or a V1 command packaged as generic + * scsi*/ +#define C_BUF_LEN 32 + +struct stlink_reg { + uint32_t r[16]; + uint32_t s[32]; + uint32_t xpsr; + uint32_t main_sp; + uint32_t process_sp; + uint32_t rw; + uint32_t rw2; + uint8_t control; + uint8_t faultmask; + uint8_t basepri; + uint8_t primask; + uint32_t fpscr; +}; + +typedef uint32_t stm32_addr_t; + +typedef struct flash_loader { + stm32_addr_t loader_addr; /* loader sram adddr */ + stm32_addr_t buf_addr; /* buffer sram address */ +} flash_loader_t; + +typedef struct stlink_version_ { + uint32_t stlink_v; + uint32_t jtag_v; + uint32_t swim_v; + uint32_t st_vid; + uint32_t stlink_pid; +} stlink_version_t; + +typedef struct _stlink stlink_t; + +struct _stlink { + void *backend_data; + + // Room for the command header + unsigned char c_buf[C_BUF_LEN]; + // Data transferred from or to device + unsigned char q_buf[Q_BUF_LEN]; + int q_len; + + // transport layer verboseness: 0 for no debug info, 10 for lots + uint32_t core_id; + uint32_t chip_id; + int core_stat; + + char serial[16]; + int serial_size; + + stm32_addr_t flash_base; + size_t flash_size; + size_t flash_pgsz; + + /* sram settings */ + stm32_addr_t sram_base; + size_t sram_size; + + // bootloader + stm32_addr_t sys_base; + size_t sys_size; + + struct stlink_version_ version; +}; + +int stlink_enter_swd_mode(stlink_t *sl); +int stlink_enter_jtag_mode(stlink_t *sl); +int stlink_exit_debug_mode(stlink_t *sl); +int stlink_exit_dfu_mode(stlink_t *sl); +void stlink_close(stlink_t *sl); +int stlink_core_id(stlink_t *sl); +int stlink_reset(stlink_t *sl); +int stlink_jtag_reset(stlink_t *sl, int value); +int stlink_run(stlink_t *sl); +int stlink_status(stlink_t *sl); +int stlink_version(stlink_t *sl); +int stlink_read_debug32(stlink_t *sl, uint32_t addr, uint32_t *data); +int stlink_read_mem32(stlink_t *sl, uint32_t addr, uint16_t len); +int stlink_write_debug32(stlink_t *sl, uint32_t addr, uint32_t data); +int stlink_write_mem32(stlink_t *sl, uint32_t addr, uint16_t len); +int stlink_write_mem8(stlink_t *sl, uint32_t addr, uint16_t len); +int stlink_read_all_regs(stlink_t *sl, struct stlink_reg *regp); +int stlink_read_reg(stlink_t *sl, int r_idx, struct stlink_reg *regp); +int stlink_write_reg(stlink_t *sl, uint32_t reg, int idx); +int stlink_step(stlink_t *sl); +int stlink_current_mode(stlink_t *sl); +int stlink_force_debug(stlink_t *sl); +int stlink_target_voltage(stlink_t *sl); +int stlink_set_swdclk(stlink_t *sl, uint16_t divisor); +int stlink_write_flash(stlink_t *sl, stm32_addr_t address, uint8_t *data, + uint32_t length, uint8_t eraseonly); +int stlink_fwrite_flash(stlink_t *sl, const char *path, stm32_addr_t addr); +int stlink_verify_write_flash(stlink_t *sl, stm32_addr_t address, uint8_t *data, + uint32_t length); +int stlink_chip_id(stlink_t *sl, uint32_t *chip_id); +int stlink_erase_flash_page(stlink_t *sl, stm32_addr_t flashaddr); +uint32_t stlink_calculate_pagesize(stlink_t *sl, uint32_t flashaddr); +uint16_t read_uint16(const unsigned char *c, const int pt); +void stlink_core_stat(stlink_t *sl); +unsigned int is_bigendian(void); +uint32_t read_uint32(const unsigned char *c, const int pt); +void write_uint32(unsigned char *buf, uint32_t ui); +void write_uint16(unsigned char *buf, uint16_t ui); +bool stlink_is_core_halted(stlink_t *sl); +int write_buffer_to_sram(stlink_t *sl, flash_loader_t *fl, const uint8_t *buf, + size_t size); +int write_loader_to_sram(stlink_t *sl, stm32_addr_t *addr, size_t *size); +int stlink_load_device_params(stlink_t *sl); +void stlink_fwrite_finalize(stlink_t *sl, stm32_addr_t addr); + +#include "usb.h" +#include "flash_loader.h" + +#ifdef __cplusplus +} +#endif + +#endif /* STLINK_H */ diff --git a/src/mongoose-6.11/src/common/platforms/stm32/stm32_flash.h b/src/mongoose-6.11/src/common/platforms/stm32/stm32_flash.h new file mode 100644 index 0000000..2846ab7 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/stm32/stm32_flash.h @@ -0,0 +1,6 @@ +#ifndef STM32_FLASH_H_ +#define STM32_FLASH_H_ + +int stm32_flash(const char *device_name, void *data, int len); + +#endif diff --git a/src/mongoose-6.11/src/common/platforms/stm32/usb.c b/src/mongoose-6.11/src/common/platforms/stm32/usb.c new file mode 100644 index 0000000..a60e99f --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/stm32/usb.c @@ -0,0 +1,773 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "stlink.h" + +enum SCSI_Generic_Direction { SG_DXFER_TO_DEV = 0, SG_DXFER_FROM_DEV = 0x80 }; + +enum stlink_debug_commands { + STLINK_DEBUG_ENTER_JTAG = 0x00, + STLINK_DEBUG_GETSTATUS = 0x01, + STLINK_DEBUG_FORCEDEBUG = 0x02, + STLINK_DEBUG_RESETSYS = 0x03, + STLINK_DEBUG_READALLREGS = 0x04, + STLINK_DEBUG_READREG = 0x05, + STLINK_DEBUG_WRITEREG = 0x06, + STLINK_DEBUG_READMEM_32BIT = 0x07, + STLINK_DEBUG_WRITEMEM_32BIT = 0x08, + STLINK_DEBUG_RUNCORE = 0x09, + STLINK_DEBUG_STEPCORE = 0x0a, + STLINK_DEBUG_SETFP = 0x0b, + STLINK_DEBUG_WRITEMEM_8BIT = 0x0d, + STLINK_DEBUG_CLEARFP = 0x0e, + STLINK_DEBUG_WRITEDEBUGREG = 0x0f, + STLINK_DEBUG_ENTER = 0x20, + STLINK_DEBUG_EXIT = 0x21, + STLINK_DEBUG_READCOREID = 0x22, + STLINK_DEBUG_ENTER_SWD = 0xa3 +}; + +void _stlink_usb_close(stlink_t *sl) { + if (!sl) return; + + struct stlink_libusb *const handle = sl->backend_data; + // maybe we couldn't even get the usb device? + if (handle != NULL) { + if (handle->usb_handle != NULL) { + libusb_close(handle->usb_handle); + } + + libusb_exit(handle->libusb_ctx); + free(handle); + } +} + +ssize_t send_recv(struct stlink_libusb *handle, int terminate, + unsigned char *txbuf, size_t txsize, unsigned char *rxbuf, + size_t rxsize) { + /* note: txbuf and rxbuf can point to the same area */ + int res = 0; + int t; + + t = libusb_bulk_transfer(handle->usb_handle, handle->ep_req, txbuf, + (int) txsize, &res, 3000); + if (t) { + printf("[!] send_recv send request failed: %s\n", libusb_error_name(t)); + return -1; + } else if ((size_t) res != txsize) { + printf("[!] send_recv send request wrote %u bytes (instead of %u).\n", + (unsigned int) res, (unsigned int) txsize); + } + + if (rxsize != 0) { + t = libusb_bulk_transfer(handle->usb_handle, handle->ep_rep, rxbuf, + (int) rxsize, &res, 3000); + if (t) { + printf("[!] send_recv read reply failed: %s\n", libusb_error_name(t)); + return -1; + } + } + + if ((handle->protocoll == 1) && terminate) { + /* Read the SG reply */ + unsigned char sg_buf[13]; + t = libusb_bulk_transfer(handle->usb_handle, handle->ep_rep, sg_buf, 13, + &res, 3000); + if (t) { + printf("[!] send_recv read storage failed: %s\n", libusb_error_name(t)); + return -1; + } + /* The STLink doesn't seem to evaluate the sequence number */ + handle->sg_transfer_idx++; + } + + return res; +} + +static inline int send_only(struct stlink_libusb *handle, int terminate, + unsigned char *txbuf, size_t txsize) { + return (int) send_recv(handle, terminate, txbuf, txsize, NULL, 0); +} + +static int fill_command(stlink_t *sl, enum SCSI_Generic_Direction dir, + uint32_t len) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const cmd = sl->c_buf; + int i = 0; + memset(cmd, 0, sizeof(sl->c_buf)); + if (slu->protocoll == 1) { + cmd[i++] = 'U'; + cmd[i++] = 'S'; + cmd[i++] = 'B'; + cmd[i++] = 'C'; + write_uint32(&cmd[i], slu->sg_transfer_idx); + write_uint32(&cmd[i + 4], len); + i += 8; + cmd[i++] = (dir == SG_DXFER_FROM_DEV) ? 0x80 : 0; + cmd[i++] = 0; /* Logical unit */ + cmd[i++] = 0xa; /* Command length */ + } + return i; +} + +int _stlink_usb_version(stlink_t *sl) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const data = sl->q_buf; + unsigned char *const cmd = sl->c_buf; + ssize_t size; + uint32_t rep_len = 6; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_GET_VERSION; + + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv STLINK_GET_VERSION\n"); + return (int) size; + } + + return 0; +} + +int32_t _stlink_usb_target_voltage(stlink_t *sl) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const rdata = sl->q_buf; + unsigned char *const cmd = sl->c_buf; + ssize_t size; + uint32_t rep_len = 8; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + uint32_t factor, reading; + int voltage; + + cmd[i++] = STLINK_GET_TARGET_VOLTAGE; + + size = send_recv(slu, 1, cmd, slu->cmd_len, rdata, rep_len); + if (size == -1) { + printf("[!] send_recv STLINK_GET_TARGET_VOLTAGE\n"); + return -1; + } else if (size != 8) { + printf("[!] wrong length STLINK_GET_TARGET_VOLTAGE\n"); + return -1; + } + + factor = + (rdata[3] << 24) | (rdata[2] << 16) | (rdata[1] << 8) | (rdata[0] << 0); + reading = + (rdata[7] << 24) | (rdata[6] << 16) | (rdata[5] << 8) | (rdata[4] << 0); + voltage = 2400 * reading / factor; + + return voltage; +} + +int _stlink_usb_read_debug32(stlink_t *sl, uint32_t addr, uint32_t *data) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const rdata = sl->q_buf; + unsigned char *const cmd = sl->c_buf; + ssize_t size; + const int rep_len = 8; + + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_JTAG_READDEBUG_32BIT; + write_uint32(&cmd[i], addr); + size = send_recv(slu, 1, cmd, slu->cmd_len, rdata, rep_len); + if (size == -1) { + printf("[!] send_recv STLINK_JTAG_READDEBUG_32BIT\n"); + return (int) size; + } + *data = read_uint32(rdata, 4); + return 0; +} + +int _stlink_usb_write_debug32(stlink_t *sl, uint32_t addr, uint32_t data) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const rdata = sl->q_buf; + unsigned char *const cmd = sl->c_buf; + ssize_t size; + const int rep_len = 2; + + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_JTAG_WRITEDEBUG_32BIT; + write_uint32(&cmd[i], addr); + write_uint32(&cmd[i + 4], data); + size = send_recv(slu, 1, cmd, slu->cmd_len, rdata, rep_len); + if (size == -1) { + printf("[!] send_recv STLINK_JTAG_WRITEDEBUG_32BIT\n"); + return (int) size; + } + + return 0; +} + +int _stlink_usb_write_mem32(stlink_t *sl, uint32_t addr, uint16_t len) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const data = sl->q_buf; + unsigned char *const cmd = sl->c_buf; + int i, ret; + + i = fill_command(sl, SG_DXFER_TO_DEV, len); + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_WRITEMEM_32BIT; + write_uint32(&cmd[i], addr); + write_uint16(&cmd[i + 4], len); + ret = send_only(slu, 0, cmd, slu->cmd_len); + if (ret == -1) return ret; + + ret = send_only(slu, 1, data, len); + if (ret == -1) return ret; + + return 0; +} + +int _stlink_usb_write_mem8(stlink_t *sl, uint32_t addr, uint16_t len) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const data = sl->q_buf; + unsigned char *const cmd = sl->c_buf; + int i, ret; + + i = fill_command(sl, SG_DXFER_TO_DEV, 0); + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_WRITEMEM_8BIT; + write_uint32(&cmd[i], addr); + write_uint16(&cmd[i + 4], len); + ret = send_only(slu, 0, cmd, slu->cmd_len); + if (ret == -1) return ret; + + ret = send_only(slu, 1, data, len); + if (ret == -1) return ret; + + return 0; +} + +int _stlink_usb_current_mode(stlink_t *sl) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const cmd = sl->c_buf; + unsigned char *const data = sl->q_buf; + ssize_t size; + int rep_len = 2; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_GET_CURRENT_MODE; + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv STLINK_GET_CURRENT_MODE\n"); + return -1; + } + return sl->q_buf[0]; +} + +int _stlink_usb_core_id(stlink_t *sl) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const cmd = sl->c_buf; + unsigned char *const data = sl->q_buf; + ssize_t size; + int rep_len = 4; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_READCOREID; + + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv STLINK_DEBUG_READCOREID\n"); + return -1; + } + + sl->core_id = read_uint32(data, 0); + return 0; +} + +int _stlink_usb_status(stlink_t *sl) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const data = sl->q_buf; + unsigned char *const cmd = sl->c_buf; + ssize_t size; + int rep_len = 2; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_GETSTATUS; + + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv STLINK_DEBUG_GETSTATUS\n"); + return (int) size; + } + sl->q_len = (int) size; + + return 0; +} + +int _stlink_usb_force_debug(stlink_t *sl) { + struct stlink_libusb *slu = sl->backend_data; + unsigned char *const data = sl->q_buf; + unsigned char *const cmd = sl->c_buf; + ssize_t size; + int rep_len = 2; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_FORCEDEBUG; + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv STLINK_DEBUG_FORCEDEBUG\n"); + return (int) size; + } + + return 0; +} + +int _stlink_usb_enter_swd_mode(stlink_t *sl) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const cmd = sl->c_buf; + ssize_t size; + const int rep_len = 0; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_ENTER; + cmd[i++] = STLINK_DEBUG_ENTER_SWD; + + size = send_only(slu, 1, cmd, slu->cmd_len); + if (size == -1) { + printf("[!] send_recv STLINK_DEBUG_ENTER\n"); + return (int) size; + } + + return 0; +} + +int _stlink_usb_exit_dfu_mode(stlink_t *sl) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const cmd = sl->c_buf; + ssize_t size; + int i = fill_command(sl, SG_DXFER_FROM_DEV, 0); + + cmd[i++] = STLINK_DFU_COMMAND; + cmd[i++] = STLINK_DFU_EXIT; + + size = send_only(slu, 1, cmd, slu->cmd_len); + if (size == -1) { + printf("[!] send_recv STLINK_DFU_EXIT\n"); + return (int) size; + } + + return 0; +} + +/** + * TODO - not convinced this does anything... + * @param sl + */ +int _stlink_usb_reset(stlink_t *sl) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const data = sl->q_buf; + unsigned char *const cmd = sl->c_buf; + ssize_t size; + int rep_len = 2; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_RESETSYS; + + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv STLINK_DEBUG_RESETSYS\n"); + return (int) size; + } + + return 0; +} + +int _stlink_usb_jtag_reset(stlink_t *sl, int value) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const data = sl->q_buf; + unsigned char *const cmd = sl->c_buf; + ssize_t size; + int rep_len = 2; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_JTAG_DRIVE_NRST; + cmd[i++] = value; + + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv STLINK_JTAG_DRIVE_NRST\n"); + return (int) size; + } + + return 0; +} + +int _stlink_usb_step(stlink_t *sl) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const data = sl->q_buf; + unsigned char *const cmd = sl->c_buf; + ssize_t size; + int rep_len = 2; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_STEPCORE; + + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv STLINK_DEBUG_STEPCORE\n"); + return (int) size; + } + + return 0; +} + +/** + * This seems to do a good job of restarting things from the beginning? + * @param sl + */ +int _stlink_usb_run(stlink_t *sl) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const data = sl->q_buf; + unsigned char *const cmd = sl->c_buf; + ssize_t size; + int rep_len = 2; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_RUNCORE; + + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv STLINK_DEBUG_RUNCORE\n"); + return (int) size; + } + + return 0; +} + +int _stlink_usb_set_swdclk(stlink_t *sl, uint16_t clk_divisor) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const data = sl->q_buf; + unsigned char *const cmd = sl->c_buf; + ssize_t size; + int rep_len = 2; + int i; + + // clock speed only supported by stlink/v2 and for firmware >= 22 + if (sl->version.stlink_v >= 2 && sl->version.jtag_v >= 22) { + i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_APIV2_SWD_SET_FREQ; + cmd[i++] = clk_divisor & 0xFF; + cmd[i++] = (clk_divisor >> 8) & 0xFF; + + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv STLINK_DEBUG_APIV2_SWD_SET_FREQ\n"); + return (int) size; + } + + return 0; + } else { + return -1; + } +} + +int _stlink_usb_exit_debug_mode(stlink_t *sl) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const cmd = sl->c_buf; + ssize_t size; + int i = fill_command(sl, SG_DXFER_FROM_DEV, 0); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_EXIT; + + size = send_only(slu, 1, cmd, slu->cmd_len); + if (size == -1) { + printf("[!] send_only STLINK_DEBUG_EXIT\n"); + return (int) size; + } + + return 0; +} + +int _stlink_usb_read_mem32(stlink_t *sl, uint32_t addr, uint16_t len) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const data = sl->q_buf; + unsigned char *const cmd = sl->c_buf; + ssize_t size; + int i = fill_command(sl, SG_DXFER_FROM_DEV, len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_READMEM_32BIT; + write_uint32(&cmd[i], addr); + write_uint16(&cmd[i + 4], len); + + size = send_recv(slu, 1, cmd, slu->cmd_len, data, len); + if (size == -1) { + printf("[!] send_recv STLINK_DEBUG_READMEM_32BIT\n"); + return (int) size; + } + + sl->q_len = (int) size; + + return 0; +} + +int _stlink_usb_read_reg(stlink_t *sl, int r_idx, struct stlink_reg *regp) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const data = sl->q_buf; + unsigned char *const cmd = sl->c_buf; + ssize_t size; + uint32_t r; + uint32_t rep_len = 4; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_READREG; + cmd[i++] = (uint8_t) r_idx; + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv STLINK_DEBUG_READREG\n"); + return (int) size; + } + sl->q_len = (int) size; + r = read_uint32(sl->q_buf, 0); + + switch (r_idx) { + case 16: + regp->xpsr = r; + break; + case 17: + regp->main_sp = r; + break; + case 18: + regp->process_sp = r; + break; + case 19: + regp->rw = r; /* XXX ?(primask, basemask etc.) */ + break; + case 20: + regp->rw2 = r; /* XXX ?(primask, basemask etc.) */ + break; + default: + regp->r[r_idx] = r; + } + + return 0; +} + +int _stlink_usb_write_reg(stlink_t *sl, uint32_t reg, int idx) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const data = sl->q_buf; + unsigned char *const cmd = sl->c_buf; + ssize_t size; + uint32_t rep_len = 2; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_WRITEREG; + cmd[i++] = idx; + write_uint32(&cmd[i], reg); + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv STLINK_DEBUG_WRITEREG\n"); + return (int) size; + } + sl->q_len = (int) size; + + return 0; +} + +stlink_t *stlink_open_usb(bool reset, char serial[16]) { + stlink_t *sl = NULL; + struct stlink_libusb *slu = NULL; + int ret = -1; + int config; + + sl = calloc(1, sizeof(stlink_t)); + slu = calloc(1, sizeof(struct stlink_libusb)); + if (sl == NULL) goto on_malloc_error; + if (slu == NULL) goto on_malloc_error; + + sl->backend_data = slu; + + sl->core_stat = STLINK_CORE_STAT_UNKNOWN; + if (libusb_init(&(slu->libusb_ctx))) { + printf("failed to init libusb context, wrong version of libraries?\n"); + goto on_error; + } + + libusb_device **list; + /** @todo We should use ssize_t and use it as a counter if > 0. As per libusb + * API: ssize_t libusb_get_device_list (libusb_context *ctx, libusb_device + * ***list) */ + int cnt = (int) libusb_get_device_list(slu->libusb_ctx, &list); + struct libusb_device_descriptor desc; + int devBus = 0; + int devAddr = 0; + + /* @TODO: Reading a environment variable in a usb open function is not very + nice, this + should be refactored and moved into the CLI tools, and instead of giving + USB_BUS:USB_ADDR a real stlink + serial string should be passed to this function. Probably people are using + this but this is very odd because + as programmer can change to multiple busses and it is better to detect them + based on serial. */ + char *device = getenv("STLINK_DEVICE"); + if (device) { + char *c = strchr(device, ':'); + if (c == NULL) { + printf("STLINK_DEVICE must be : format\n"); + goto on_error; + } + devBus = atoi(device); + *c++ = 0; + devAddr = atoi(c); + printf("bus %03d dev %03d\n", devBus, devAddr); + } + + while (cnt--) { + libusb_get_device_descriptor(list[cnt], &desc); + if (desc.idVendor != STLINK_USB_VID_ST) continue; + + if (devBus && devAddr) { + if ((libusb_get_bus_number(list[cnt]) != devBus) || + (libusb_get_device_address(list[cnt]) != devAddr)) { + continue; + } + } + + if ((desc.idProduct == STLINK_USB_PID_STLINK_32L) || + (desc.idProduct == STLINK_USB_PID_STLINK_NUCLEO)) { + struct libusb_device_handle *handle; + + ret = libusb_open(list[cnt], &handle); + if (ret) continue; + + sl->serial_size = libusb_get_string_descriptor_ascii( + handle, desc.iSerialNumber, (unsigned char *) sl->serial, + sizeof(sl->serial)); + libusb_close(handle); + + if ((serial == NULL) || (*serial == 0)) break; + + if (sl->serial_size < 0) continue; + + if (memcmp(serial, &sl->serial, sl->serial_size) == 0) break; + + continue; + } + + if (desc.idProduct == STLINK_USB_PID_STLINK) { + slu->protocoll = 1; + break; + } + } + + if (cnt < 0) { + printf("Couldn't find %s ST-Link/V2 devices\n", + (devBus && devAddr) ? "matched" : "any"); + goto on_error; + } else { + ret = libusb_open(list[cnt], &slu->usb_handle); + if (ret != 0) { + printf("Error %d (%s) opening ST-Link/V2 device %03d:%03d\n", ret, + strerror(errno), libusb_get_bus_number(list[cnt]), + libusb_get_device_address(list[cnt])); + goto on_error; + } + } + + libusb_free_device_list(list, 1); + + if (libusb_kernel_driver_active(slu->usb_handle, 0) == 1) { + ret = libusb_detach_kernel_driver(slu->usb_handle, 0); + if (ret < 0) { + printf("libusb_detach_kernel_driver(() error %s\n", strerror(-ret)); + goto on_libusb_error; + } + } + + if (libusb_get_configuration(slu->usb_handle, &config)) { + /* this may fail for a previous configured device */ + printf("libusb_get_configuration()\n"); + goto on_libusb_error; + } + + if (config != 1) { + printf("setting new configuration (%d -> 1)\n", config); + if (libusb_set_configuration(slu->usb_handle, 1)) { + /* this may fail for a previous configured device */ + printf("libusb_set_configuration() failed\n"); + goto on_libusb_error; + } + } + + if (libusb_claim_interface(slu->usb_handle, 0)) { + printf( + "Stlink usb device found, but unable to claim (probably already in " + "use?)\n"); + goto on_libusb_error; + } + + // TODO - could use the scanning techniq from stm8 code here... + slu->ep_rep = 1 /* ep rep */ | LIBUSB_ENDPOINT_IN; + if (desc.idProduct == STLINK_USB_PID_STLINK_NUCLEO) { + slu->ep_req = 1 /* ep req */ | LIBUSB_ENDPOINT_OUT; + } else { + slu->ep_req = 2 /* ep req */ | LIBUSB_ENDPOINT_OUT; + } + + slu->sg_transfer_idx = 0; + // TODO - never used at the moment, always CMD_SIZE + slu->cmd_len = (slu->protocoll == 1) ? STLINK_SG_SIZE : STLINK_CMD_SIZE; + + if (stlink_current_mode(sl) == STLINK_DEV_DFU_MODE) { + printf("-- exit_dfu_mode\n"); + stlink_exit_dfu_mode(sl); + } + + if (stlink_current_mode(sl) != STLINK_DEV_DEBUG_MODE) { + stlink_enter_swd_mode(sl); + } + + sl->version.stlink_v = 2; + + if (reset) { + if (sl->version.stlink_v > 1) stlink_jtag_reset(sl, 2); + stlink_reset(sl); + usleep(10000); + } + + ret = stlink_load_device_params(sl); + + // Set the stlink clock speed (default is 1800kHz) + stlink_set_swdclk(sl, STLINK_SWDCLK_1P8MHZ_DIVISOR); + +on_libusb_error: + if (ret == -1) { + stlink_close(sl); + return NULL; + } + + return sl; + +on_error: + if (slu->libusb_ctx) libusb_exit(slu->libusb_ctx); + +on_malloc_error: + if (sl != NULL) free(sl); + if (slu != NULL) free(slu); + + return NULL; +} diff --git a/src/mongoose-6.11/src/common/platforms/stm32/usb.h b/src/mongoose-6.11/src/common/platforms/stm32/usb.h new file mode 100644 index 0000000..6ee4ff3 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/stm32/usb.h @@ -0,0 +1,76 @@ +/* + * File: stlink/usb.h + * Author: karl + * + * Created on October 1, 2011, 11:29 PM + */ + +#ifndef STLINK_USB_H +#define STLINK_USB_H + +#include +#include + +#include "stlink.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define STLINK_USB_VID_ST 0x0483 +#define STLINK_USB_PID_STLINK 0x3744 +#define STLINK_USB_PID_STLINK_32L 0x3748 +#define STLINK_USB_PID_STLINK_NUCLEO 0x374b + +#define STLINK_SG_SIZE 31 +#define STLINK_CMD_SIZE 16 + +struct stlink_libusb { + libusb_context *libusb_ctx; + libusb_device_handle *usb_handle; + unsigned int ep_req; + unsigned int ep_rep; + int protocoll; + unsigned int sg_transfer_idx; + unsigned int cmd_len; +}; + +/** + * Open a stlink + * @param verbose Verbosity loglevel + * @param reset Reset stlink programmer + * @param serial Serial number to search for, when NULL the first stlink found + * is opened (binary format) + * @retval NULL Error while opening the stlink + * @retval !NULL Stlink found and ready to use + */ +stlink_t *stlink_open_usb(bool reset, char serial[16]); + +void _stlink_usb_close(stlink_t *sl); +int _stlink_usb_version(stlink_t *sl); +int32_t _stlink_usb_target_voltage(stlink_t *sl); +int _stlink_usb_read_debug32(stlink_t *sl, uint32_t addr, uint32_t *data); +int _stlink_usb_write_debug32(stlink_t *sl, uint32_t addr, uint32_t data); +int _stlink_usb_write_mem32(stlink_t *sl, uint32_t addr, uint16_t len); +int _stlink_usb_write_mem8(stlink_t *sl, uint32_t addr, uint16_t len); +int _stlink_usb_current_mode(stlink_t *sl); +int _stlink_usb_core_id(stlink_t *sl); +int _stlink_usb_status(stlink_t *sl); +int _stlink_usb_force_debug(stlink_t *sl); +int _stlink_usb_enter_swd_mode(stlink_t *sl); +int _stlink_usb_exit_dfu_mode(stlink_t *sl); +int _stlink_usb_reset(stlink_t *sl); +int _stlink_usb_jtag_reset(stlink_t *sl, int value); +int _stlink_usb_step(stlink_t *sl); +int _stlink_usb_run(stlink_t *sl); +int _stlink_usb_set_swdclk(stlink_t *sl, uint16_t clk_divisor); +int _stlink_usb_exit_debug_mode(stlink_t *sl); +int _stlink_usb_read_mem32(stlink_t *sl, uint32_t addr, uint16_t len); +int _stlink_usb_read_reg(stlink_t *sl, int r_idx, struct stlink_reg *regp); +int _stlink_usb_write_reg(stlink_t *sl, uint32_t reg, int idx); + +#ifdef __cplusplus +} +#endif + +#endif /* STLINK_USB_H */ diff --git a/src/mongoose-6.11/src/common/platforms/wince/wince_libc.c b/src/mongoose-6.11/src/common/platforms/wince/wince_libc.c new file mode 100644 index 0000000..b385fde --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/wince/wince_libc.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2016 Cesanta Software Limited + * All rights reserved + */ + +#ifdef WINCE + +const char *strerror(int err) { + /* + * TODO(alashkin): there is no strerror on WinCE; + * look for similar wce_xxxx function + */ + static char buf[10]; + snprintf(buf, sizeof(buf), "%d", err); + return buf; +} + +int open(const char *filename, int oflag, int pmode) { + /* + * TODO(alashkin): mg_open function is not used in mongoose + * but exists in documentation as utility function + * Shall we delete it at all or implement for WinCE as well? + */ + DebugBreak(); + return 0; /* for compiler */ +} + +int _wstati64(const wchar_t *path, cs_stat_t *st) { + DWORD fa = GetFileAttributesW(path); + if (fa == INVALID_FILE_ATTRIBUTES) { + return -1; + } + memset(st, 0, sizeof(*st)); + if ((fa & FILE_ATTRIBUTE_DIRECTORY) == 0) { + HANDLE h; + FILETIME ftime; + st->st_mode |= _S_IFREG; + h = CreateFileW(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + if (h == INVALID_HANDLE_VALUE) { + return -1; + } + st->st_size = GetFileSize(h, NULL); + GetFileTime(h, NULL, NULL, &ftime); + st->st_mtime = (uint32_t)((((uint64_t) ftime.dwLowDateTime + + ((uint64_t) ftime.dwHighDateTime << 32)) / + 10000000.0) - + 11644473600); + CloseHandle(h); + } else { + st->st_mode |= _S_IFDIR; + } + return 0; +} + +/* Windows CE doesn't have neither gmtime nor strftime */ +static void mg_gmt_time_string(char *buf, size_t buf_len, time_t *t) { + FILETIME ft; + SYSTEMTIME systime; + if (t != NULL) { + uint64_t filetime = (*t + 11644473600) * 10000000; + ft.dwLowDateTime = filetime & 0xFFFFFFFF; + ft.dwHighDateTime = (filetime & 0xFFFFFFFF00000000) >> 32; + FileTimeToSystemTime(&ft, &systime); + } else { + GetSystemTime(&systime); + } + /* There is no PRIu16 in WinCE SDK */ + snprintf(buf, buf_len, "%d.%d.%d %d:%d:%d GMT", (int) systime.wYear, + (int) systime.wMonth, (int) systime.wDay, (int) systime.wHour, + (int) systime.wMinute, (int) systime.wSecond); +} + +#endif diff --git a/src/mongoose-6.11/src/common/platforms/windows/windows_direct.c b/src/mongoose-6.11/src/common/platforms/windows/windows_direct.c new file mode 100644 index 0000000..8932899 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/windows/windows_direct.c @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2017 Cesanta Software Limited + * All rights reserved + */ + +#ifdef _WIN32 + +int rmdir(const char *dirname) { + return _rmdir(dirname); +} + +unsigned int sleep(unsigned int seconds) { + Sleep(seconds * 1000); + return 0; +} + +#endif /* _WIN32 */ diff --git a/src/mongoose-6.11/src/common/test_util.c b/src/mongoose-6.11/src/common/test_util.c new file mode 100644 index 0000000..705a12f --- /dev/null +++ b/src/mongoose-6.11/src/common/test_util.c @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#include "common/test_util.h" + +#include +#include +#include +#include +#ifndef _WIN32 +#include +#else +#include +#endif + +int num_tests = 0; + +static char *_escape(const char *s, size_t n) { + size_t i, j; + char *res = (char *) malloc(n * 4 + 1); + for (i = j = 0; s[i] != '\0' && i < n; i++) { + if (!iscntrl((int) s[i])) { + res[j++] = s[i]; + } else { + j += sprintf(res + j, "\\x%02x", s[i]); + } + } + res[j] = '\0'; + return res; +} + +void _strfail(const char *a, const char *e, int len) { + char *ae, *ee; + if (len < 0) { + len = strlen(a); + if (strlen(e) > (size_t) len) len = strlen(e); + } + ae = _escape(a, len); + ee = _escape(e, len); + printf("Expected: %s\nActual : %s\n", ee, ae); + free(ae); + free(ee); +} + +int _assert_streq(const char *actual, const char *expected) { + if (strcmp(actual, expected) != 0) { + _strfail(actual, expected, -1); + return 0; + } + return 1; +} + +int _assert_streq_nz(const char *actual, const char *expected) { + size_t n = strlen(expected); + if (strncmp(actual, expected, n) != 0) { + _strfail(actual, expected, n); + return 0; + } + return 1; +} + +#if MG_ENABLE_POLL_UNTIL +int c_str_ne(void *a, void *b) { + int r = strcmp((const char *) a, (const char *) b); + return r; +} + +int c_int_ne(void *a, void *b) { + return *((int *) a) != (intptr_t) b; +} + +int c_int_eq(void *a, void *b) { + return *((int *) a) == (intptr_t) b; +} + +void poll_until(struct mg_mgr *mgr, double timeout, int (*cond)(void *, void *), + void *cond_arg1, void *cond_arg2) { + int i; + double start = cs_time(); + while (cs_time() - start < timeout) { + mg_mgr_poll(mgr, 2); + if (cond != NULL && cond(cond_arg1, cond_arg2)) { + /* A few more cycles to test for overshoots. */ + for (i = 0; i < 5; i++) { + mg_mgr_poll(mgr, 2); + } + return; + } + } +} +#endif diff --git a/src/mongoose-6.11/src/common/test_util.h b/src/mongoose-6.11/src/common/test_util.h new file mode 100644 index 0000000..6110387 --- /dev/null +++ b/src/mongoose-6.11/src/common/test_util.h @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2015 Cesanta Software Limited + * All rights reserved + * This software is dual-licensed: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. For the terms of this + * license, see . + * + * You are free to use this software under the terms of the GNU General + * Public License, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * Alternatively, you can license this software under a commercial + * license, as set out in . + */ + +#ifndef CS_COMMON_TEST_UTIL_H_ +#define CS_COMMON_TEST_UTIL_H_ + +#include +#include + +#include "common/cs_time.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern int g_num_tests; + +#ifdef MG_TEST_ABORT_ON_FAIL +#define MG_TEST_ABORT abort() +#else +#define MG_TEST_ABORT +#endif + +int _assert_streq(const char *actual, const char *expected); +int _assert_streq_nz(const char *actual, const char *expected); +void _strfail(const char *a, const char *e, int len); + +#define FAIL(str, line) \ + do { \ + printf("%s:%d:1 [%s] (in %s)\n", __FILE__, line, str, __func__); \ + MG_TEST_ABORT; \ + return str; \ + } while (0) + +#define ASSERT(expr) \ + do { \ + g_num_tests++; \ + if (!(expr)) FAIL(#expr, __LINE__); \ + } while (0) +#define ASSERT_TRUE(expr) ASSERT(expr) +#define ASSERT_FALSE(expr) ASSERT(!(expr)) + +/* + * Run test function, use its name as the test name to print + */ +#define RUN_TEST(test) RUN_TEST_WNAME(test, #test) + +/* + * Run test function, use the provided name as the test name to print + */ +#define RUN_TEST_WNAME(test, test_name) \ + do { \ + const char *msg = NULL; \ + if (strstr(test_name, filter)) { \ + double elapsed = cs_time(); \ + msg = test(); \ + elapsed = cs_time() - elapsed; \ + printf(" [%.3f] %s\n", elapsed, test_name); \ + fflush(stdout); \ + } \ + if (msg) return msg; \ + } while (0) + +/* VC6 doesn't know how to cast an unsigned 64-bit int to double */ +#if (defined(_MSC_VER) && _MSC_VER <= 1200) +#define AS_DOUBLE(d) (double)(int64_t)(d) +#else +#define AS_DOUBLE(d) (double)(d) +#endif + +/* + * Numeric equality assertion. Comparison is made in native types but for + * printing both are convetrted to double. + */ +#define ASSERT_EQ(actual, expected) \ + do { \ + g_num_tests++; \ + if (!((actual) == (expected))) { \ + printf("%f != %f\n", AS_DOUBLE(actual), AS_DOUBLE(expected)); \ + FAIL(#actual " == " #expected, __LINE__); \ + } \ + } while (0) + +/* "Less than" assertion. */ +#define ASSERT_LT(a, b) \ + do { \ + g_num_tests++; \ + if (!((a) < (b))) { \ + printf("%f >= %f\n", AS_DOUBLE(a), AS_DOUBLE(b)); \ + FAIL(#a " < " #b, __LINE__); \ + } \ + } while (0) + +/* "Greater than" assertion. */ +#define ASSERT_GT(a, b) \ + do { \ + g_num_tests++; \ + if (!((a) > (b))) { \ + printf("%f <= %f\n", AS_DOUBLE(a), AS_DOUBLE(b)); \ + FAIL(#a " > " #b, __LINE__); \ + } \ + } while (0) + +/* Assert that actual == expected, where both are NUL-terminated. */ +#define ASSERT_STREQ(actual, expected) \ + do { \ + g_num_tests++; \ + if (!_assert_streq(actual, expected)) { \ + FAIL("ASSERT_STREQ(" #actual ", " #expected ")", __LINE__); \ + } \ + } while (0) + +/* Assert that actual == expected, where both are pointers */ +#define ASSERT_PTREQ(actual, expected) \ + do { \ + g_num_tests++; \ + if (actual != expected) { \ + printf("%p != %p\n", actual, expected); \ + FAIL("ASSERT_PTREQ(" #actual ", " #expected ")", __LINE__); \ + } \ + } while (0) + +/* Assert that actual != expected, where both are pointers */ +#define ASSERT_PTRNEQ(actual, expected) \ + do { \ + g_num_tests++; \ + if (actual == expected) { \ + printf("%p == %p\n", actual, expected); \ + FAIL("ASSERT_PTRNEQ(" #actual ", " #expected ")", __LINE__); \ + } \ + } while (0) + +/* Same as STREQ, but only expected is NUL-terminated. */ +#define ASSERT_STREQ_NZ(actual, expected) \ + do { \ + g_num_tests++; \ + if (!_assert_streq_nz(actual, expected)) { \ + FAIL("ASSERT_STREQ_NZ(" #actual ", " #expected ")", __LINE__); \ + } \ + } while (0) + +#define ASSERT_MG_STREQ(actual, expected) \ + do { \ + g_num_tests++; \ + if ((actual).len != strlen(expected) || \ + memcmp((actual).p, expected, (actual).len) != 0) { \ + printf("'%.*s' (%d) != '%s'\n", (int)(actual).len, (actual).p, \ + (int)(actual).len, expected); \ + FAIL("ASSERT_MG_STREQ(" #actual ", " #expected ")", __LINE__); \ + } \ + } while (0) + +#define CHECK_CALL(call) \ + do { \ + const char *r = (call); \ + if (r != NULL) { \ + fprintf(stderr, "... %s:%d in %s\n", __FILE__, __LINE__, __func__); \ + return r; \ + } \ + } while (0) + +#ifndef MG_ENABLE_POLL_UNTIL +#define MG_ENABLE_POLL_UNTIL 0 +#endif + +#if MG_ENABLE_POLL_UNTIL +#include "mongoose.h" +int c_str_ne(void *a, void *b); +int c_int_ne(void *a, void *b); +int c_int_eq(void *a, void *b); +void poll_until(struct mg_mgr *mgr, double timeout, int (*cond)(void *, void *), + void *cond_arg1, void *cond_arg2); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* CS_COMMON_TEST_UTIL_H_ */ diff --git a/src/mongoose-6.11/src/mg_coap.c b/src/mongoose-6.11/src/mg_coap.c new file mode 100644 index 0000000..29667b7 --- /dev/null +++ b/src/mongoose-6.11/src/mg_coap.c @@ -0,0 +1,597 @@ +/* + * Copyright (c) 2015 Cesanta Software Limited + * All rights reserved + * This software is dual-licensed: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. For the terms of this + * license, see . + * + * You are free to use this software under the terms of the GNU General + * Public License, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * Alternatively, you can license this software under a commercial + * license, as set out in . + */ + +#include "mg_internal.h" +#include "mg_coap.h" + +#if MG_ENABLE_COAP + +void mg_coap_free_options(struct mg_coap_message *cm) { + while (cm->options != NULL) { + struct mg_coap_option *next = cm->options->next; + MG_FREE(cm->options); + cm->options = next; + } +} + +struct mg_coap_option *mg_coap_add_option(struct mg_coap_message *cm, + uint32_t number, char *value, + size_t len) { + struct mg_coap_option *new_option = + (struct mg_coap_option *) MG_CALLOC(1, sizeof(*new_option)); + + new_option->number = number; + new_option->value.p = value; + new_option->value.len = len; + + if (cm->options == NULL) { + cm->options = cm->optiomg_tail = new_option; + } else { + /* + * A very simple attention to help clients to compose options: + * CoAP wants to see options ASC ordered. + * Could be change by using sort in coap_compose + */ + if (cm->optiomg_tail->number <= new_option->number) { + /* if option is already ordered just add it */ + cm->optiomg_tail = cm->optiomg_tail->next = new_option; + } else { + /* looking for appropriate position */ + struct mg_coap_option *current_opt = cm->options; + struct mg_coap_option *prev_opt = 0; + + while (current_opt != NULL) { + if (current_opt->number > new_option->number) { + break; + } + prev_opt = current_opt; + current_opt = current_opt->next; + } + + if (prev_opt != NULL) { + prev_opt->next = new_option; + new_option->next = current_opt; + } else { + /* insert new_option to the beginning */ + new_option->next = cm->options; + cm->options = new_option; + } + } + } + + return new_option; +} + +/* + * Fills CoAP header in mg_coap_message. + * + * Helper function. + */ +static char *coap_parse_header(char *ptr, struct mbuf *io, + struct mg_coap_message *cm) { + if (io->len < sizeof(uint32_t)) { + cm->flags |= MG_COAP_NOT_ENOUGH_DATA; + return NULL; + } + + /* + * Version (Ver): 2-bit unsigned integer. Indicates the CoAP version + * number. Implementations of this specification MUST set this field + * to 1 (01 binary). Other values are reserved for future versions. + * Messages with unknown version numbers MUST be silently ignored. + */ + if (((uint8_t) *ptr >> 6) != 1) { + cm->flags |= MG_COAP_IGNORE; + return NULL; + } + + /* + * Type (T): 2-bit unsigned integer. Indicates if this message is of + * type Confirmable (0), Non-confirmable (1), Acknowledgement (2), or + * Reset (3). + */ + cm->msg_type = ((uint8_t) *ptr & 0x30) >> 4; + cm->flags |= MG_COAP_MSG_TYPE_FIELD; + + /* + * Token Length (TKL): 4-bit unsigned integer. Indicates the length of + * the variable-length Token field (0-8 bytes). Lengths 9-15 are + * reserved, MUST NOT be sent, and MUST be processed as a message + * format error. + */ + cm->token.len = *ptr & 0x0F; + if (cm->token.len > 8) { + cm->flags |= MG_COAP_FORMAT_ERROR; + return NULL; + } + + ptr++; + + /* + * Code: 8-bit unsigned integer, split into a 3-bit class (most + * significant bits) and a 5-bit detail (least significant bits) + */ + cm->code_class = (uint8_t) *ptr >> 5; + cm->code_detail = *ptr & 0x1F; + cm->flags |= (MG_COAP_CODE_CLASS_FIELD | MG_COAP_CODE_DETAIL_FIELD); + + ptr++; + + /* Message ID: 16-bit unsigned integer in network byte order. */ + cm->msg_id = (uint8_t) *ptr << 8 | (uint8_t) * (ptr + 1); + cm->flags |= MG_COAP_MSG_ID_FIELD; + + ptr += 2; + + return ptr; +} + +/* + * Fills token information in mg_coap_message. + * + * Helper function. + */ +static char *coap_get_token(char *ptr, struct mbuf *io, + struct mg_coap_message *cm) { + if (cm->token.len != 0) { + if (ptr + cm->token.len > io->buf + io->len) { + cm->flags |= MG_COAP_NOT_ENOUGH_DATA; + return NULL; + } else { + cm->token.p = ptr; + ptr += cm->token.len; + cm->flags |= MG_COAP_TOKEN_FIELD; + } + } + + return ptr; +} + +/* + * Returns Option Delta or Length. + * + * Helper function. + */ +static int coap_get_ext_opt(char *ptr, struct mbuf *io, uint16_t *opt_info) { + int ret = 0; + + if (*opt_info == 13) { + /* + * 13: An 8-bit unsigned integer follows the initial byte and + * indicates the Option Delta/Length minus 13. + */ + if (ptr < io->buf + io->len) { + *opt_info = (uint8_t) *ptr + 13; + ret = sizeof(uint8_t); + } else { + ret = -1; /* LCOV_EXCL_LINE */ + } + } else if (*opt_info == 14) { + /* + * 14: A 16-bit unsigned integer in network byte order follows the + * initial byte and indicates the Option Delta/Length minus 269. + */ + if (ptr + sizeof(uint8_t) < io->buf + io->len) { + *opt_info = ((uint8_t) *ptr << 8 | (uint8_t) * (ptr + 1)) + 269; + ret = sizeof(uint16_t); + } else { + ret = -1; /* LCOV_EXCL_LINE */ + } + } + + return ret; +} + +/* + * Fills options in mg_coap_message. + * + * Helper function. + * + * General options format: + * +---------------+---------------+ + * | Option Delta | Option Length | 1 byte + * +---------------+---------------+ + * \ Option Delta (extended) \ 0-2 bytes + * +-------------------------------+ + * / Option Length (extended) \ 0-2 bytes + * +-------------------------------+ + * \ Option Value \ 0 or more bytes + * +-------------------------------+ + */ +static char *coap_get_options(char *ptr, struct mbuf *io, + struct mg_coap_message *cm) { + uint16_t prev_opt = 0; + + if (ptr == io->buf + io->len) { + /* end of packet, ok */ + return NULL; + } + + /* 0xFF is payload marker */ + while (ptr < io->buf + io->len && (uint8_t) *ptr != 0xFF) { + uint16_t option_delta, option_lenght; + int optinfo_len; + + /* Option Delta: 4-bit unsigned integer */ + option_delta = ((uint8_t) *ptr & 0xF0) >> 4; + /* Option Length: 4-bit unsigned integer */ + option_lenght = *ptr & 0x0F; + + if (option_delta == 15 || option_lenght == 15) { + /* + * 15: Reserved for future use. If the field is set to this value, + * it MUST be processed as a message format error + */ + cm->flags |= MG_COAP_FORMAT_ERROR; + break; + } + + ptr++; + + /* check for extended option delta */ + optinfo_len = coap_get_ext_opt(ptr, io, &option_delta); + if (optinfo_len == -1) { + cm->flags |= MG_COAP_NOT_ENOUGH_DATA; /* LCOV_EXCL_LINE */ + break; /* LCOV_EXCL_LINE */ + } + + ptr += optinfo_len; + + /* check or extended option lenght */ + optinfo_len = coap_get_ext_opt(ptr, io, &option_lenght); + if (optinfo_len == -1) { + cm->flags |= MG_COAP_NOT_ENOUGH_DATA; /* LCOV_EXCL_LINE */ + break; /* LCOV_EXCL_LINE */ + } + + ptr += optinfo_len; + + /* + * Instead of specifying the Option Number directly, the instances MUST + * appear in order of their Option Numbers and a delta encoding is used + * between them. + */ + option_delta += prev_opt; + + mg_coap_add_option(cm, option_delta, ptr, option_lenght); + + prev_opt = option_delta; + + if (ptr + option_lenght > io->buf + io->len) { + cm->flags |= MG_COAP_NOT_ENOUGH_DATA; /* LCOV_EXCL_LINE */ + break; /* LCOV_EXCL_LINE */ + } + + ptr += option_lenght; + } + + if ((cm->flags & MG_COAP_ERROR) != 0) { + mg_coap_free_options(cm); + return NULL; + } + + cm->flags |= MG_COAP_OPTIOMG_FIELD; + + if (ptr == io->buf + io->len) { + /* end of packet, ok */ + return NULL; + } + + ptr++; + + return ptr; +} + +uint32_t mg_coap_parse(struct mbuf *io, struct mg_coap_message *cm) { + char *ptr; + + memset(cm, 0, sizeof(*cm)); + + if ((ptr = coap_parse_header(io->buf, io, cm)) == NULL) { + return cm->flags; + } + + if ((ptr = coap_get_token(ptr, io, cm)) == NULL) { + return cm->flags; + } + + if ((ptr = coap_get_options(ptr, io, cm)) == NULL) { + return cm->flags; + } + + /* the rest is payload */ + cm->payload.len = io->len - (ptr - io->buf); + if (cm->payload.len != 0) { + cm->payload.p = ptr; + cm->flags |= MG_COAP_PAYLOAD_FIELD; + } + + return cm->flags; +} + +/* + * Calculates extended size of given Opt Number/Length in coap message. + * + * Helper function. + */ +static size_t coap_get_ext_opt_size(uint32_t value) { + int ret = 0; + + if (value >= 13 && value <= 0xFF + 13) { + ret = sizeof(uint8_t); + } else if (value > 0xFF + 13 && value <= 0xFFFF + 269) { + ret = sizeof(uint16_t); + } + + return ret; +} + +/* + * Splits given Opt Number/Length into base and ext values. + * + * Helper function. + */ +static int coap_split_opt(uint32_t value, uint8_t *base, uint16_t *ext) { + int ret = 0; + + if (value < 13) { + *base = value; + } else if (value >= 13 && value <= 0xFF + 13) { + *base = 13; + *ext = value - 13; + ret = sizeof(uint8_t); + } else if (value > 0xFF + 13 && value <= 0xFFFF + 269) { + *base = 14; + *ext = value - 269; + ret = sizeof(uint16_t); + } + + return ret; +} + +/* + * Puts uint16_t (in network order) into given char stream. + * + * Helper function. + */ +static char *coap_add_uint16(char *ptr, uint16_t val) { + *ptr = val >> 8; + ptr++; + *ptr = val & 0x00FF; + ptr++; + return ptr; +} + +/* + * Puts extended value of Opt Number/Length into given char stream. + * + * Helper function. + */ +static char *coap_add_opt_info(char *ptr, uint16_t val, size_t len) { + if (len == sizeof(uint8_t)) { + *ptr = (char) val; + ptr++; + } else if (len == sizeof(uint16_t)) { + ptr = coap_add_uint16(ptr, val); + } + + return ptr; +} + +/* + * Verifies given mg_coap_message and calculates message size for it. + * + * Helper function. + */ +static uint32_t coap_calculate_packet_size(struct mg_coap_message *cm, + size_t *len) { + struct mg_coap_option *opt; + uint32_t prev_opt_number; + + *len = 4; /* header */ + if (cm->msg_type > MG_COAP_MSG_MAX) { + return MG_COAP_ERROR | MG_COAP_MSG_TYPE_FIELD; + } + if (cm->token.len > 8) { + return MG_COAP_ERROR | MG_COAP_TOKEN_FIELD; + } + if (cm->code_class > 7) { + return MG_COAP_ERROR | MG_COAP_CODE_CLASS_FIELD; + } + if (cm->code_detail > 31) { + return MG_COAP_ERROR | MG_COAP_CODE_DETAIL_FIELD; + } + + *len += cm->token.len; + if (cm->payload.len != 0) { + *len += cm->payload.len + 1; /* ... + 1; add payload marker */ + } + + opt = cm->options; + prev_opt_number = 0; + while (opt != NULL) { + *len += 1; /* basic delta/length */ + *len += coap_get_ext_opt_size(opt->number - prev_opt_number); + *len += coap_get_ext_opt_size((uint32_t) opt->value.len); + /* + * Current implementation performs check if + * option_number > previous option_number and produces an error + * TODO(alashkin): write design doc with limitations + * May be resorting is more suitable solution. + */ + if ((opt->next != NULL && opt->number > opt->next->number) || + opt->value.len > 0xFFFF + 269 || + opt->number - prev_opt_number > 0xFFFF + 269) { + return MG_COAP_ERROR | MG_COAP_OPTIOMG_FIELD; + } + *len += opt->value.len; + prev_opt_number = opt->number; + opt = opt->next; + } + + return 0; +} + +uint32_t mg_coap_compose(struct mg_coap_message *cm, struct mbuf *io) { + struct mg_coap_option *opt; + uint32_t res, prev_opt_number; + size_t prev_io_len, packet_size; + char *ptr; + + res = coap_calculate_packet_size(cm, &packet_size); + if (res != 0) { + return res; + } + + /* saving previous lenght to handle non-empty mbuf */ + prev_io_len = io->len; + if (mbuf_append(io, NULL, packet_size) == 0) return MG_COAP_ERROR; + ptr = io->buf + prev_io_len; + + /* + * since cm is verified, it is possible to use bits shift operator + * without additional zeroing of unused bits + */ + + /* ver: 2 bits, msg_type: 2 bits, toklen: 4 bits */ + *ptr = (1 << 6) | (cm->msg_type << 4) | (uint8_t)(cm->token.len); + ptr++; + + /* code class: 3 bits, code detail: 5 bits */ + *ptr = (cm->code_class << 5) | (cm->code_detail); + ptr++; + + ptr = coap_add_uint16(ptr, cm->msg_id); + + if (cm->token.len != 0) { + memcpy(ptr, cm->token.p, cm->token.len); + ptr += cm->token.len; + } + + opt = cm->options; + prev_opt_number = 0; + while (opt != NULL) { + uint8_t delta_base = 0, length_base = 0; + uint16_t delta_ext = 0, length_ext = 0; + + size_t opt_delta_len = + coap_split_opt(opt->number - prev_opt_number, &delta_base, &delta_ext); + size_t opt_lenght_len = + coap_split_opt((uint32_t) opt->value.len, &length_base, &length_ext); + + *ptr = (delta_base << 4) | length_base; + ptr++; + + ptr = coap_add_opt_info(ptr, delta_ext, opt_delta_len); + ptr = coap_add_opt_info(ptr, length_ext, opt_lenght_len); + + if (opt->value.len != 0) { + memcpy(ptr, opt->value.p, opt->value.len); + ptr += opt->value.len; + } + + prev_opt_number = opt->number; + opt = opt->next; + } + + if (cm->payload.len != 0) { + *ptr = (char) -1; + ptr++; + memcpy(ptr, cm->payload.p, cm->payload.len); + } + + return 0; +} + +uint32_t mg_coap_send_message(struct mg_connection *nc, + struct mg_coap_message *cm) { + struct mbuf packet_out; + uint32_t compose_res; + + mbuf_init(&packet_out, 0); + compose_res = mg_coap_compose(cm, &packet_out); + if (compose_res != 0) { + return compose_res; /* LCOV_EXCL_LINE */ + } + + mg_send(nc, packet_out.buf, (int) packet_out.len); + mbuf_free(&packet_out); + + return 0; +} + +uint32_t mg_coap_send_ack(struct mg_connection *nc, uint16_t msg_id) { + struct mg_coap_message cm; + memset(&cm, 0, sizeof(cm)); + cm.msg_type = MG_COAP_MSG_ACK; + cm.msg_id = msg_id; + + return mg_coap_send_message(nc, &cm); +} + +static void coap_handler(struct mg_connection *nc, int ev, + void *ev_data MG_UD_ARG(void *user_data)) { + struct mbuf *io = &nc->recv_mbuf; + struct mg_coap_message cm; + uint32_t parse_res; + + memset(&cm, 0, sizeof(cm)); + + nc->handler(nc, ev, ev_data MG_UD_ARG(user_data)); + + switch (ev) { + case MG_EV_RECV: + parse_res = mg_coap_parse(io, &cm); + if ((parse_res & MG_COAP_IGNORE) == 0) { + if ((cm.flags & MG_COAP_NOT_ENOUGH_DATA) != 0) { + /* + * Since we support UDP only + * MG_COAP_NOT_ENOUGH_DATA == MG_COAP_FORMAT_ERROR + */ + cm.flags |= MG_COAP_FORMAT_ERROR; /* LCOV_EXCL_LINE */ + } /* LCOV_EXCL_LINE */ + nc->handler(nc, MG_COAP_EVENT_BASE + cm.msg_type, + &cm MG_UD_ARG(user_data)); + } + + mg_coap_free_options(&cm); + mbuf_remove(io, io->len); + break; + } +} +/* + * Attach built-in CoAP event handler to the given connection. + * + * The user-defined event handler will receive following extra events: + * + * - MG_EV_COAP_CON + * - MG_EV_COAP_NOC + * - MG_EV_COAP_ACK + * - MG_EV_COAP_RST + */ +int mg_set_protocol_coap(struct mg_connection *nc) { + /* supports UDP only */ + if ((nc->flags & MG_F_UDP) == 0) { + return -1; + } + + nc->proto_handler = coap_handler; + + return 0; +} + +#endif /* MG_ENABLE_COAP */ diff --git a/src/mongoose-6.11/src/mg_coap.h b/src/mongoose-6.11/src/mg_coap.h new file mode 100644 index 0000000..296933f --- /dev/null +++ b/src/mongoose-6.11/src/mg_coap.h @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2015 Cesanta Software Limited + * All rights reserved + * This software is dual-licensed: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. For the terms of this + * license, see . + * + * You are free to use this software under the terms of the GNU General + * Public License, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * Alternatively, you can license this software under a commercial + * license, as set out in . + */ + +/* + * === CoAP API reference + * + * CoAP message format: + * + * ``` + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + * |Ver| T | TKL | Code | Message ID | Token (if any, TKL bytes) ... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + * | Options (if any) ... |1 1 1 1 1 1 1 1| Payload (if any) ... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + * ``` + */ + +#ifndef CS_MONGOOSE_SRC_COAP_H_ +#define CS_MONGOOSE_SRC_COAP_H_ + +#if MG_ENABLE_COAP + +#define MG_COAP_MSG_TYPE_FIELD 0x2 +#define MG_COAP_CODE_CLASS_FIELD 0x4 +#define MG_COAP_CODE_DETAIL_FIELD 0x8 +#define MG_COAP_MSG_ID_FIELD 0x10 +#define MG_COAP_TOKEN_FIELD 0x20 +#define MG_COAP_OPTIOMG_FIELD 0x40 +#define MG_COAP_PAYLOAD_FIELD 0x80 + +#define MG_COAP_ERROR 0x10000 +#define MG_COAP_FORMAT_ERROR (MG_COAP_ERROR | 0x20000) +#define MG_COAP_IGNORE (MG_COAP_ERROR | 0x40000) +#define MG_COAP_NOT_ENOUGH_DATA (MG_COAP_ERROR | 0x80000) +#define MG_COAP_NETWORK_ERROR (MG_COAP_ERROR | 0x100000) + +#define MG_COAP_MSG_CON 0 +#define MG_COAP_MSG_NOC 1 +#define MG_COAP_MSG_ACK 2 +#define MG_COAP_MSG_RST 3 +#define MG_COAP_MSG_MAX 3 + +#define MG_COAP_CODECLASS_REQUEST 0 +#define MG_COAP_CODECLASS_RESP_OK 2 +#define MG_COAP_CODECLASS_CLIENT_ERR 4 +#define MG_COAP_CODECLASS_SRV_ERR 5 + +#define MG_COAP_EVENT_BASE 300 +#define MG_EV_COAP_CON (MG_COAP_EVENT_BASE + MG_COAP_MSG_CON) +#define MG_EV_COAP_NOC (MG_COAP_EVENT_BASE + MG_COAP_MSG_NOC) +#define MG_EV_COAP_ACK (MG_COAP_EVENT_BASE + MG_COAP_MSG_ACK) +#define MG_EV_COAP_RST (MG_COAP_EVENT_BASE + MG_COAP_MSG_RST) + +/* + * CoAP options. + * Use mg_coap_add_option and mg_coap_free_options + * for creation and destruction. + */ +struct mg_coap_option { + struct mg_coap_option *next; + uint32_t number; + struct mg_str value; +}; + +/* CoAP message. See RFC 7252 for details. */ +struct mg_coap_message { + uint32_t flags; + uint8_t msg_type; + uint8_t code_class; + uint8_t code_detail; + uint16_t msg_id; + struct mg_str token; + struct mg_coap_option *options; + struct mg_str payload; + struct mg_coap_option *optiomg_tail; +}; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Sets CoAP protocol handler - triggers CoAP specific events. */ +int mg_set_protocol_coap(struct mg_connection *nc); + +/* + * Adds a new option to mg_coap_message structure. + * Returns pointer to the newly created option. + * Note: options must be freed by using mg_coap_free_options + */ +struct mg_coap_option *mg_coap_add_option(struct mg_coap_message *cm, + uint32_t number, char *value, + size_t len); + +/* + * Frees the memory allocated for options. + * If the cm parameter doesn't contain any option it does nothing. + */ +void mg_coap_free_options(struct mg_coap_message *cm); + +/* + * Composes a CoAP message from `mg_coap_message` + * and sends it into `nc` connection. + * Returns 0 on success. On error, it is a bitmask: + * + * - `#define MG_COAP_ERROR 0x10000` + * - `#define MG_COAP_FORMAT_ERROR (MG_COAP_ERROR | 0x20000)` + * - `#define MG_COAP_IGNORE (MG_COAP_ERROR | 0x40000)` + * - `#define MG_COAP_NOT_ENOUGH_DATA (MG_COAP_ERROR | 0x80000)` + * - `#define MG_COAP_NETWORK_ERROR (MG_COAP_ERROR | 0x100000)` + */ +uint32_t mg_coap_send_message(struct mg_connection *nc, + struct mg_coap_message *cm); + +/* + * Composes CoAP acknowledgement from `mg_coap_message` + * and sends it into `nc` connection. + * Return value: see `mg_coap_send_message()` + */ +uint32_t mg_coap_send_ack(struct mg_connection *nc, uint16_t msg_id); + +/* + * Parses CoAP message and fills mg_coap_message and returns cm->flags. + * This is a helper function. + * + * NOTE: usually CoAP works over UDP, so lack of data means format error. + * But, in theory, it is possible to use CoAP over TCP (according to RFC) + * + * The caller has to check results and treat COAP_NOT_ENOUGH_DATA according to + * underlying protocol: + * + * - in case of UDP COAP_NOT_ENOUGH_DATA means COAP_FORMAT_ERROR, + * - in case of TCP client can try to receive more data + * + * Return value: see `mg_coap_send_message()` + */ +uint32_t mg_coap_parse(struct mbuf *io, struct mg_coap_message *cm); + +/* + * Composes CoAP message from mg_coap_message structure. + * This is a helper function. + * Return value: see `mg_coap_send_message()` + */ +uint32_t mg_coap_compose(struct mg_coap_message *cm, struct mbuf *io); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* MG_ENABLE_COAP */ + +#endif /* CS_MONGOOSE_SRC_COAP_H_ */ diff --git a/src/mongoose-6.11/src/mg_common.h b/src/mongoose-6.11/src/mg_common.h new file mode 100644 index 0000000..81ecdf9 --- /dev/null +++ b/src/mongoose-6.11/src/mg_common.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2004-2013 Sergey Lyubka + * Copyright (c) 2013-2015 Cesanta Software Limited + * All rights reserved + * + * This software is dual-licensed: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. For the terms of this + * license, see . + * + * You are free to use this software under the terms of the GNU General + * Public License, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * Alternatively, you can license this software under a commercial + * license, as set out in . + */ + +#ifndef CS_MONGOOSE_SRC_COMMON_H_ +#define CS_MONGOOSE_SRC_COMMON_H_ + +#define MG_VERSION "6.11" + +/* Local tweaks, applied before any of Mongoose's own headers. */ +#ifdef MG_LOCALS +#include +#endif + +#endif /* CS_MONGOOSE_SRC_COMMON_H_ */ diff --git a/src/mongoose-6.11/src/mg_dns.c b/src/mongoose-6.11/src/mg_dns.c new file mode 100644 index 0000000..21bc8c2 --- /dev/null +++ b/src/mongoose-6.11/src/mg_dns.c @@ -0,0 +1,377 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_DNS + +#include "mg_internal.h" +#include "mg_dns.h" + +static int mg_dns_tid = 0xa0; + +struct mg_dns_header { + uint16_t transaction_id; + uint16_t flags; + uint16_t num_questions; + uint16_t num_answers; + uint16_t num_authority_prs; + uint16_t num_other_prs; +}; + +struct mg_dns_resource_record *mg_dns_next_record( + struct mg_dns_message *msg, int query, + struct mg_dns_resource_record *prev) { + struct mg_dns_resource_record *rr; + + for (rr = (prev == NULL ? msg->answers : prev + 1); + rr - msg->answers < msg->num_answers; rr++) { + if (rr->rtype == query) { + return rr; + } + } + return NULL; +} + +int mg_dns_parse_record_data(struct mg_dns_message *msg, + struct mg_dns_resource_record *rr, void *data, + size_t data_len) { + switch (rr->rtype) { + case MG_DNS_A_RECORD: + if (data_len < sizeof(struct in_addr)) { + return -1; + } + if (rr->rdata.p + data_len > msg->pkt.p + msg->pkt.len) { + return -1; + } + memcpy(data, rr->rdata.p, data_len); + return 0; +#if MG_ENABLE_IPV6 + case MG_DNS_AAAA_RECORD: + if (data_len < sizeof(struct in6_addr)) { + return -1; /* LCOV_EXCL_LINE */ + } + memcpy(data, rr->rdata.p, data_len); + return 0; +#endif + case MG_DNS_CNAME_RECORD: + mg_dns_uncompress_name(msg, &rr->rdata, (char *) data, data_len); + return 0; + } + + return -1; +} + +int mg_dns_insert_header(struct mbuf *io, size_t pos, + struct mg_dns_message *msg) { + struct mg_dns_header header; + + memset(&header, 0, sizeof(header)); + header.transaction_id = msg->transaction_id; + header.flags = htons(msg->flags); + header.num_questions = htons(msg->num_questions); + header.num_answers = htons(msg->num_answers); + + return mbuf_insert(io, pos, &header, sizeof(header)); +} + +int mg_dns_copy_questions(struct mbuf *io, struct mg_dns_message *msg) { + unsigned char *begin, *end; + struct mg_dns_resource_record *last_q; + if (msg->num_questions <= 0) return 0; + begin = (unsigned char *) msg->pkt.p + sizeof(struct mg_dns_header); + last_q = &msg->questions[msg->num_questions - 1]; + end = (unsigned char *) last_q->name.p + last_q->name.len + 4; + return mbuf_append(io, begin, end - begin); +} + +int mg_dns_encode_name(struct mbuf *io, const char *name, size_t len) { + const char *s; + unsigned char n; + size_t pos = io->len; + + do { + if ((s = strchr(name, '.')) == NULL) { + s = name + len; + } + + if (s - name > 127) { + return -1; /* TODO(mkm) cover */ + } + n = s - name; /* chunk length */ + mbuf_append(io, &n, 1); /* send length */ + mbuf_append(io, name, n); + + if (*s == '.') { + n++; + } + + name += n; + len -= n; + } while (*s != '\0'); + mbuf_append(io, "\0", 1); /* Mark end of host name */ + + return io->len - pos; +} + +int mg_dns_encode_record(struct mbuf *io, struct mg_dns_resource_record *rr, + const char *name, size_t nlen, const void *rdata, + size_t rlen) { + size_t pos = io->len; + uint16_t u16; + uint32_t u32; + + if (rr->kind == MG_DNS_INVALID_RECORD) { + return -1; /* LCOV_EXCL_LINE */ + } + + if (mg_dns_encode_name(io, name, nlen) == -1) { + return -1; + } + + u16 = htons(rr->rtype); + mbuf_append(io, &u16, 2); + u16 = htons(rr->rclass); + mbuf_append(io, &u16, 2); + + if (rr->kind == MG_DNS_ANSWER) { + u32 = htonl(rr->ttl); + mbuf_append(io, &u32, 4); + + if (rr->rtype == MG_DNS_CNAME_RECORD) { + int clen; + /* fill size after encoding */ + size_t off = io->len; + mbuf_append(io, &u16, 2); + if ((clen = mg_dns_encode_name(io, (const char *) rdata, rlen)) == -1) { + return -1; + } + u16 = clen; + io->buf[off] = u16 >> 8; + io->buf[off + 1] = u16 & 0xff; + } else { + u16 = htons((uint16_t) rlen); + mbuf_append(io, &u16, 2); + mbuf_append(io, rdata, rlen); + } + } + + return io->len - pos; +} + +void mg_send_dns_query(struct mg_connection *nc, const char *name, + int query_type) { + struct mg_dns_message *msg = + (struct mg_dns_message *) MG_CALLOC(1, sizeof(*msg)); + struct mbuf pkt; + struct mg_dns_resource_record *rr = &msg->questions[0]; + + DBG(("%s %d", name, query_type)); + + mbuf_init(&pkt, 64 /* Start small, it'll grow as needed. */); + + msg->transaction_id = ++mg_dns_tid; + msg->flags = 0x100; + msg->num_questions = 1; + + mg_dns_insert_header(&pkt, 0, msg); + + rr->rtype = query_type; + rr->rclass = 1; /* Class: inet */ + rr->kind = MG_DNS_QUESTION; + + if (mg_dns_encode_record(&pkt, rr, name, strlen(name), NULL, 0) == -1) { + /* TODO(mkm): return an error code */ + goto cleanup; /* LCOV_EXCL_LINE */ + } + + /* TCP DNS requires messages to be prefixed with len */ + if (!(nc->flags & MG_F_UDP)) { + uint16_t len = htons((uint16_t) pkt.len); + mbuf_insert(&pkt, 0, &len, 2); + } + + mg_send(nc, pkt.buf, pkt.len); + mbuf_free(&pkt); + +cleanup: + MG_FREE(msg); +} + +static unsigned char *mg_parse_dns_resource_record( + unsigned char *data, unsigned char *end, struct mg_dns_resource_record *rr, + int reply) { + unsigned char *name = data; + int chunk_len, data_len; + + while (data < end && (chunk_len = *data)) { + if (((unsigned char *) data)[0] & 0xc0) { + data += 1; + break; + } + data += chunk_len + 1; + } + + if (data > end - 5) { + return NULL; + } + + rr->name.p = (char *) name; + rr->name.len = data - name + 1; + data++; + + rr->rtype = data[0] << 8 | data[1]; + data += 2; + + rr->rclass = data[0] << 8 | data[1]; + data += 2; + + rr->kind = reply ? MG_DNS_ANSWER : MG_DNS_QUESTION; + if (reply) { + if (data >= end - 6) { + return NULL; + } + + rr->ttl = (uint32_t) data[0] << 24 | (uint32_t) data[1] << 16 | + data[2] << 8 | data[3]; + data += 4; + + data_len = *data << 8 | *(data + 1); + data += 2; + + rr->rdata.p = (char *) data; + rr->rdata.len = data_len; + data += data_len; + } + return data; +} + +int mg_parse_dns(const char *buf, int len, struct mg_dns_message *msg) { + struct mg_dns_header *header = (struct mg_dns_header *) buf; + unsigned char *data = (unsigned char *) buf + sizeof(*header); + unsigned char *end = (unsigned char *) buf + len; + int i; + + memset(msg, 0, sizeof(*msg)); + msg->pkt.p = buf; + msg->pkt.len = len; + + if (len < (int) sizeof(*header)) return -1; + + msg->transaction_id = header->transaction_id; + msg->flags = ntohs(header->flags); + msg->num_questions = ntohs(header->num_questions); + if (msg->num_questions > (int) ARRAY_SIZE(msg->questions)) { + msg->num_questions = (int) ARRAY_SIZE(msg->questions); + } + msg->num_answers = ntohs(header->num_answers); + if (msg->num_answers > (int) ARRAY_SIZE(msg->answers)) { + msg->num_answers = (int) ARRAY_SIZE(msg->answers); + } + + for (i = 0; i < msg->num_questions; i++) { + data = mg_parse_dns_resource_record(data, end, &msg->questions[i], 0); + if (data == NULL) return -1; + } + + for (i = 0; i < msg->num_answers; i++) { + data = mg_parse_dns_resource_record(data, end, &msg->answers[i], 1); + if (data == NULL) return -1; + } + + return 0; +} + +size_t mg_dns_uncompress_name(struct mg_dns_message *msg, struct mg_str *name, + char *dst, int dst_len) { + int chunk_len, num_ptrs = 0; + char *old_dst = dst; + const unsigned char *data = (unsigned char *) name->p; + const unsigned char *end = (unsigned char *) msg->pkt.p + msg->pkt.len; + + if (data >= end) { + return 0; + } + + while ((chunk_len = *data++)) { + int leeway = dst_len - (dst - old_dst); + if (data >= end) { + return 0; + } + + if ((chunk_len & 0xc0) == 0xc0) { + uint16_t off = (data[-1] & (~0xc0)) << 8 | data[0]; + if (off >= msg->pkt.len) { + return 0; + } + /* Basic circular loop avoidance: allow up to 16 pointer hops. */ + if (++num_ptrs > 15) { + return 0; + } + data = (unsigned char *) msg->pkt.p + off; + continue; + } + if (chunk_len > 63) { + return 0; + } + if (chunk_len > leeway) { + chunk_len = leeway; + } + + if (data + chunk_len >= end) { + return 0; + } + + memcpy(dst, data, chunk_len); + data += chunk_len; + dst += chunk_len; + leeway -= chunk_len; + if (leeway == 0) { + return dst - old_dst; + } + *dst++ = '.'; + } + + if (dst != old_dst) { + *--dst = 0; + } + return dst - old_dst; +} + +static void dns_handler(struct mg_connection *nc, int ev, + void *ev_data MG_UD_ARG(void *user_data)) { + struct mbuf *io = &nc->recv_mbuf; + struct mg_dns_message msg; + + /* Pass low-level events to the user handler */ + nc->handler(nc, ev, ev_data MG_UD_ARG(user_data)); + + switch (ev) { + case MG_EV_RECV: + if (!(nc->flags & MG_F_UDP)) { + mbuf_remove(&nc->recv_mbuf, 2); + } + if (mg_parse_dns(nc->recv_mbuf.buf, nc->recv_mbuf.len, &msg) == -1) { + /* reply + recursion allowed + format error */ + memset(&msg, 0, sizeof(msg)); + msg.flags = 0x8081; + mg_dns_insert_header(io, 0, &msg); + if (!(nc->flags & MG_F_UDP)) { + uint16_t len = htons((uint16_t) io->len); + mbuf_insert(io, 0, &len, 2); + } + mg_send(nc, io->buf, io->len); + } else { + /* Call user handler with parsed message */ + nc->handler(nc, MG_DNS_MESSAGE, &msg MG_UD_ARG(user_data)); + } + mbuf_remove(io, io->len); + break; + } +} + +void mg_set_protocol_dns(struct mg_connection *nc) { + nc->proto_handler = dns_handler; +} + +#endif /* MG_ENABLE_DNS */ diff --git a/src/mongoose-6.11/src/mg_dns.h b/src/mongoose-6.11/src/mg_dns.h new file mode 100644 index 0000000..8a76f58 --- /dev/null +++ b/src/mongoose-6.11/src/mg_dns.h @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +/* + * === DNS API reference + */ + +#ifndef CS_MONGOOSE_SRC_DNS_H_ +#define CS_MONGOOSE_SRC_DNS_H_ + +#include "mg_net.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define MG_DNS_A_RECORD 0x01 /* Lookup IP address */ +#define MG_DNS_CNAME_RECORD 0x05 /* Lookup CNAME */ +#define MG_DNS_PTR_RECORD 0x0c /* Lookup PTR */ +#define MG_DNS_TXT_RECORD 0x10 /* Lookup TXT */ +#define MG_DNS_AAAA_RECORD 0x1c /* Lookup IPv6 address */ +#define MG_DNS_SRV_RECORD 0x21 /* Lookup SRV */ +#define MG_DNS_MX_RECORD 0x0f /* Lookup mail server for domain */ +#define MG_DNS_ANY_RECORD 0xff +#define MG_DNS_NSEC_RECORD 0x2f + +#define MG_MAX_DNS_QUESTIONS 32 +#define MG_MAX_DNS_ANSWERS 32 + +#define MG_DNS_MESSAGE 100 /* High-level DNS message event */ + +enum mg_dns_resource_record_kind { + MG_DNS_INVALID_RECORD = 0, + MG_DNS_QUESTION, + MG_DNS_ANSWER +}; + +/* DNS resource record. */ +struct mg_dns_resource_record { + struct mg_str name; /* buffer with compressed name */ + int rtype; + int rclass; + int ttl; + enum mg_dns_resource_record_kind kind; + struct mg_str rdata; /* protocol data (can be a compressed name) */ +}; + +/* DNS message (request and response). */ +struct mg_dns_message { + struct mg_str pkt; /* packet body */ + uint16_t flags; + uint16_t transaction_id; + int num_questions; + int num_answers; + struct mg_dns_resource_record questions[MG_MAX_DNS_QUESTIONS]; + struct mg_dns_resource_record answers[MG_MAX_DNS_ANSWERS]; +}; + +struct mg_dns_resource_record *mg_dns_next_record( + struct mg_dns_message *msg, int query, struct mg_dns_resource_record *prev); + +/* + * Parses the record data from a DNS resource record. + * + * - A: struct in_addr *ina + * - AAAA: struct in6_addr *ina + * - CNAME: char buffer + * + * Returns -1 on error. + * + * TODO(mkm): MX + */ +int mg_dns_parse_record_data(struct mg_dns_message *msg, + struct mg_dns_resource_record *rr, void *data, + size_t data_len); + +/* + * Sends a DNS query to the remote end. + */ +void mg_send_dns_query(struct mg_connection *nc, const char *name, + int query_type); + +/* + * Inserts a DNS header to an IO buffer. + * + * Returns the number of bytes inserted. + */ +int mg_dns_insert_header(struct mbuf *io, size_t pos, + struct mg_dns_message *msg); + +/* + * Appends already encoded questions from an existing message. + * + * This is useful when generating a DNS reply message which includes + * all question records. + * + * Returns the number of appended bytes. + */ +int mg_dns_copy_questions(struct mbuf *io, struct mg_dns_message *msg); + +/* + * Encodes and appends a DNS resource record to an IO buffer. + * + * The record metadata is taken from the `rr` parameter, while the name and data + * are taken from the parameters, encoded in the appropriate format depending on + * record type and stored in the IO buffer. The encoded values might contain + * offsets within the IO buffer. It's thus important that the IO buffer doesn't + * get trimmed while a sequence of records are encoded while preparing a DNS + * reply. + * + * This function doesn't update the `name` and `rdata` pointers in the `rr` + * struct because they might be invalidated as soon as the IO buffer grows + * again. + * + * Returns the number of bytes appended or -1 in case of error. + */ +int mg_dns_encode_record(struct mbuf *io, struct mg_dns_resource_record *rr, + const char *name, size_t nlen, const void *rdata, + size_t rlen); + +/* + * Encodes a DNS name. + */ +int mg_dns_encode_name(struct mbuf *io, const char *name, size_t len); + +/* Low-level: parses a DNS response. */ +int mg_parse_dns(const char *buf, int len, struct mg_dns_message *msg); + +/* + * Uncompresses a DNS compressed name. + * + * The containing DNS message is required because of the compressed encoding + * and reference suffixes present elsewhere in the packet. + * + * If the name is less than `dst_len` characters long, the remainder + * of `dst` is terminated with `\0` characters. Otherwise, `dst` is not + * terminated. + * + * If `dst_len` is 0 `dst` can be NULL. + * Returns the uncompressed name length. + */ +size_t mg_dns_uncompress_name(struct mg_dns_message *msg, struct mg_str *name, + char *dst, int dst_len); + +/* + * Attaches a built-in DNS event handler to the given listening connection. + * + * The DNS event handler parses the incoming UDP packets, treating them as DNS + * requests. If an incoming packet gets successfully parsed by the DNS event + * handler, a user event handler will receive an `MG_DNS_REQUEST` event, with + * `ev_data` pointing to the parsed `struct mg_dns_message`. + * + * See + * [captive_dns_server](https://github.com/cesanta/mongoose/tree/master/examples/captive_dns_server) + * example on how to handle DNS request and send DNS reply. + */ +void mg_set_protocol_dns(struct mg_connection *nc); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* CS_MONGOOSE_SRC_DNS_H_ */ diff --git a/src/mongoose-6.11/src/mg_dns_server.c b/src/mongoose-6.11/src/mg_dns_server.c new file mode 100644 index 0000000..2dafb46 --- /dev/null +++ b/src/mongoose-6.11/src/mg_dns_server.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_DNS_SERVER + +#include "mg_internal.h" +#include "dns-server.h" + +struct mg_dns_reply mg_dns_create_reply(struct mbuf *io, + struct mg_dns_message *msg) { + struct mg_dns_reply rep; + rep.msg = msg; + rep.io = io; + rep.start = io->len; + + /* reply + recursion allowed */ + msg->flags |= 0x8080; + mg_dns_copy_questions(io, msg); + + msg->num_answers = 0; + return rep; +} + +void mg_dns_send_reply(struct mg_connection *nc, struct mg_dns_reply *r) { + size_t sent = r->io->len - r->start; + mg_dns_insert_header(r->io, r->start, r->msg); + if (!(nc->flags & MG_F_UDP)) { + uint16_t len = htons((uint16_t) sent); + mbuf_insert(r->io, r->start, &len, 2); + } + + if (&nc->send_mbuf != r->io) { + mg_send(nc, r->io->buf + r->start, r->io->len - r->start); + r->io->len = r->start; + } +} + +int mg_dns_reply_record(struct mg_dns_reply *reply, + struct mg_dns_resource_record *question, + const char *name, int rtype, int ttl, const void *rdata, + size_t rdata_len) { + struct mg_dns_message *msg = (struct mg_dns_message *) reply->msg; + char rname[512]; + struct mg_dns_resource_record *ans = &msg->answers[msg->num_answers]; + if (msg->num_answers >= MG_MAX_DNS_ANSWERS) { + return -1; /* LCOV_EXCL_LINE */ + } + + if (name == NULL) { + name = rname; + rname[511] = 0; + mg_dns_uncompress_name(msg, &question->name, rname, sizeof(rname) - 1); + } + + *ans = *question; + ans->kind = MG_DNS_ANSWER; + ans->rtype = rtype; + ans->ttl = ttl; + + if (mg_dns_encode_record(reply->io, ans, name, strlen(name), rdata, + rdata_len) == -1) { + return -1; /* LCOV_EXCL_LINE */ + }; + + msg->num_answers++; + return 0; +} + +#endif /* MG_ENABLE_DNS_SERVER */ diff --git a/src/mongoose-6.11/src/mg_dns_server.h b/src/mongoose-6.11/src/mg_dns_server.h new file mode 100644 index 0000000..168a174 --- /dev/null +++ b/src/mongoose-6.11/src/mg_dns_server.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +/* + * === DNS server API reference + * + * Disabled by default; enable with `-DMG_ENABLE_DNS_SERVER`. + */ + +#ifndef CS_MONGOOSE_SRC_DNS_SERVER_H_ +#define CS_MONGOOSE_SRC_DNS_SERVER_H_ + +#if MG_ENABLE_DNS_SERVER + +#include "mg_dns.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define MG_DNS_SERVER_DEFAULT_TTL 3600 + +struct mg_dns_reply { + struct mg_dns_message *msg; + struct mbuf *io; + size_t start; +}; + +/* + * Creates a DNS reply. + * + * The reply will be based on an existing query message `msg`. + * The query body will be appended to the output buffer. + * "reply + recursion allowed" will be added to the message flags and the + * message's num_answers will be set to 0. + * + * Answer records can be appended with `mg_dns_send_reply` or by lower + * level function defined in the DNS API. + * + * In order to send a reply use `mg_dns_send_reply`. + * It's possible to use a connection's send buffer as reply buffer, + * and it will work for both UDP and TCP connections. + * + * Example: + * + * ```c + * reply = mg_dns_create_reply(&nc->send_mbuf, msg); + * for (i = 0; i < msg->num_questions; i++) { + * rr = &msg->questions[i]; + * if (rr->rtype == MG_DNS_A_RECORD) { + * mg_dns_reply_record(&reply, rr, 3600, &dummy_ip_addr, 4); + * } + * } + * mg_dns_send_reply(nc, &reply); + * ``` + */ +struct mg_dns_reply mg_dns_create_reply(struct mbuf *io, + struct mg_dns_message *msg); + +/* + * Appends a DNS reply record to the IO buffer and to the DNS message. + * + * The message's num_answers field will be incremented. It's the caller's duty + * to ensure num_answers is properly initialised. + * + * Returns -1 on error. + */ +int mg_dns_reply_record(struct mg_dns_reply *reply, + struct mg_dns_resource_record *question, + const char *name, int rtype, int ttl, const void *rdata, + size_t rdata_len); + +/* + * Sends a DNS reply through a connection. + * + * The DNS data is stored in an IO buffer pointed by reply structure in `r`. + * This function mutates the content of that buffer in order to ensure that + * the DNS header reflects the size and flags of the message, that might have + * been updated either with `mg_dns_reply_record` or by direct manipulation of + * `r->message`. + * + * Once sent, the IO buffer will be trimmed unless the reply IO buffer + * is the connection's send buffer and the connection is not in UDP mode. + */ +void mg_dns_send_reply(struct mg_connection *nc, struct mg_dns_reply *r); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* MG_ENABLE_DNS_SERVER */ +#endif /* CS_MONGOOSE_SRC_DNS_SERVER_H_ */ diff --git a/src/mongoose-6.11/src/mg_features.h b/src/mongoose-6.11/src/mg_features.h new file mode 100644 index 0000000..515242d --- /dev/null +++ b/src/mongoose-6.11/src/mg_features.h @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_MONGOOSE_SRC_FEATURES_H_ +#define CS_MONGOOSE_SRC_FEATURES_H_ + +#ifndef MG_DISABLE_HTTP_DIGEST_AUTH +#define MG_DISABLE_HTTP_DIGEST_AUTH 0 +#endif + +#ifndef MG_DISABLE_HTTP_KEEP_ALIVE +#define MG_DISABLE_HTTP_KEEP_ALIVE 0 +#endif + +#ifndef MG_DISABLE_PFS +#define MG_DISABLE_PFS 0 +#endif + +#ifndef MG_DISABLE_WS_RANDOM_MASK +#define MG_DISABLE_WS_RANDOM_MASK 0 +#endif + +#ifndef MG_ENABLE_ASYNC_RESOLVER +#define MG_ENABLE_ASYNC_RESOLVER 1 +#endif + +#ifndef MG_ENABLE_BROADCAST +#define MG_ENABLE_BROADCAST 0 +#endif + +#ifndef MG_ENABLE_COAP +#define MG_ENABLE_COAP 0 +#endif + +#ifndef MG_ENABLE_DEBUG +#define MG_ENABLE_DEBUG 0 +#endif + +#ifndef MG_ENABLE_DIRECTORY_LISTING +#define MG_ENABLE_DIRECTORY_LISTING 0 +#endif + +#ifndef MG_ENABLE_DNS +#define MG_ENABLE_DNS 1 +#endif + +#ifndef MG_ENABLE_DNS_SERVER +#define MG_ENABLE_DNS_SERVER 0 +#endif + +#ifndef MG_ENABLE_FAKE_DAVLOCK +#define MG_ENABLE_FAKE_DAVLOCK 0 +#endif + +#ifndef MG_ENABLE_FILESYSTEM +#define MG_ENABLE_FILESYSTEM 0 +#endif + +#ifndef MG_ENABLE_GETADDRINFO +#define MG_ENABLE_GETADDRINFO 0 +#endif + +#ifndef MG_ENABLE_HEXDUMP +#define MG_ENABLE_HEXDUMP CS_ENABLE_STDIO +#endif + +#ifndef MG_ENABLE_HTTP +#define MG_ENABLE_HTTP 1 +#endif + +#ifndef MG_ENABLE_HTTP_CGI +#define MG_ENABLE_HTTP_CGI 0 +#endif + +#ifndef MG_ENABLE_HTTP_SSI +#define MG_ENABLE_HTTP_SSI MG_ENABLE_FILESYSTEM +#endif + +#ifndef MG_ENABLE_HTTP_SSI_EXEC +#define MG_ENABLE_HTTP_SSI_EXEC 0 +#endif + +#ifndef MG_ENABLE_HTTP_STREAMING_MULTIPART +#define MG_ENABLE_HTTP_STREAMING_MULTIPART 0 +#endif + +#ifndef MG_ENABLE_HTTP_WEBDAV +#define MG_ENABLE_HTTP_WEBDAV 0 +#endif + +#ifndef MG_ENABLE_HTTP_WEBSOCKET +#define MG_ENABLE_HTTP_WEBSOCKET MG_ENABLE_HTTP +#endif + +#ifndef MG_ENABLE_IPV6 +#define MG_ENABLE_IPV6 0 +#endif + +#ifndef MG_ENABLE_MQTT +#define MG_ENABLE_MQTT 1 +#endif + +#ifndef MG_ENABLE_SOCKS +#define MG_ENABLE_SOCKS 0 +#endif + +#ifndef MG_ENABLE_MQTT_BROKER +#define MG_ENABLE_MQTT_BROKER 0 +#endif + +#ifndef MG_ENABLE_SSL +#define MG_ENABLE_SSL 0 +#endif + +#ifndef MG_ENABLE_SYNC_RESOLVER +#define MG_ENABLE_SYNC_RESOLVER 0 +#endif + +#ifndef MG_ENABLE_STDIO +#define MG_ENABLE_STDIO CS_ENABLE_STDIO +#endif + +#ifndef MG_NET_IF +#define MG_NET_IF MG_NET_IF_SOCKET +#endif + +#ifndef MG_SSL_IF +#define MG_SSL_IF MG_SSL_IF_OPENSSL +#endif + +#ifndef MG_ENABLE_THREADS /* ifdef-ok */ +#ifdef _WIN32 +#define MG_ENABLE_THREADS 1 +#else +#define MG_ENABLE_THREADS 0 +#endif +#endif + +#if MG_ENABLE_DEBUG && !defined(CS_ENABLE_DEBUG) +#define CS_ENABLE_DEBUG 1 +#endif + +/* MQTT broker requires MQTT */ +#if MG_ENABLE_MQTT_BROKER && !MG_ENABLE_MQTT +#undef MG_ENABLE_MQTT +#define MG_ENABLE_MQTT 1 +#endif + +#ifndef MG_ENABLE_HTTP_URL_REWRITES +#define MG_ENABLE_HTTP_URL_REWRITES \ + (CS_PLATFORM == CS_P_WINDOWS || CS_PLATFORM == CS_P_UNIX) +#endif + +#ifndef MG_ENABLE_SNTP +#define MG_ENABLE_SNTP 0 +#endif + +#ifndef MG_ENABLE_EXTRA_ERRORS_DESC +#define MG_ENABLE_EXTRA_ERRORS_DESC 0 +#endif + +#ifndef MG_ENABLE_CALLBACK_USERDATA +#define MG_ENABLE_CALLBACK_USERDATA 0 +#endif + +#if MG_ENABLE_CALLBACK_USERDATA +#define MG_UD_ARG(ud) , ud +#define MG_CB(cb, ud) cb, ud +#else +#define MG_UD_ARG(ud) +#define MG_CB(cb, ud) cb +#endif + +#endif /* CS_MONGOOSE_SRC_FEATURES_H_ */ diff --git a/src/mongoose-6.11/src/mg_http.c b/src/mongoose-6.11/src/mg_http.c new file mode 100644 index 0000000..068d208 --- /dev/null +++ b/src/mongoose-6.11/src/mg_http.c @@ -0,0 +1,3077 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_HTTP + +#include "common/cs_md5.h" +#include "mg_internal.h" +#include "mg_util.h" + +/* altbuf {{{ */ + +/* + * Alternate buffer: fills the client-provided buffer with data; and if it's + * not large enough, allocates another buffer (via mbuf), similar to asprintf. + */ +struct altbuf { + struct mbuf m; + char *user_buf; + size_t len; + size_t user_buf_size; +}; + +/* + * Initializes altbuf; `buf`, `buf_size` is the client-provided buffer. + */ +MG_INTERNAL void altbuf_init(struct altbuf *ab, char *buf, size_t buf_size) { + mbuf_init(&ab->m, 0); + ab->user_buf = buf; + ab->user_buf_size = buf_size; + ab->len = 0; +} + +/* + * Appends a single char to the altbuf. + */ +MG_INTERNAL void altbuf_append(struct altbuf *ab, char c) { + if (ab->len < ab->user_buf_size) { + /* The data fits into the original buffer */ + ab->user_buf[ab->len++] = c; + } else { + /* The data can't fit into the original buffer, so write it to mbuf. */ + + /* + * First of all, see if that's the first byte which overflows the original + * buffer: if so, copy the existing data from there to a newly allocated + * mbuf. + */ + if (ab->len > 0 && ab->m.len == 0) { + mbuf_append(&ab->m, ab->user_buf, ab->len); + } + + mbuf_append(&ab->m, &c, 1); + ab->len = ab->m.len; + } +} + +/* + * Resets any data previously appended to altbuf. + */ +MG_INTERNAL void altbuf_reset(struct altbuf *ab) { + mbuf_free(&ab->m); + ab->len = 0; +} + +/* + * Returns whether the additional buffer was allocated (and thus the data + * is in the mbuf, not the client-provided buffer) + */ +MG_INTERNAL int altbuf_reallocated(struct altbuf *ab) { + return ab->len > ab->user_buf_size; +} + +/* + * Returns the actual buffer with data, either the client-provided or a newly + * allocated one. If `trim` is non-zero, mbuf-backed buffer is trimmed first. + */ +MG_INTERNAL char *altbuf_get_buf(struct altbuf *ab, int trim) { + if (altbuf_reallocated(ab)) { + if (trim) { + mbuf_trim(&ab->m); + } + return ab->m.buf; + } else { + return ab->user_buf; + } +} + +/* }}} */ + +static const char *mg_version_header = "Mongoose/" MG_VERSION; + +enum mg_http_proto_data_type { DATA_NONE, DATA_FILE, DATA_PUT }; + +struct mg_http_proto_data_file { + FILE *fp; /* Opened file. */ + int64_t cl; /* Content-Length. How many bytes to send. */ + int64_t sent; /* How many bytes have been already sent. */ + int keepalive; /* Keep connection open after sending. */ + enum mg_http_proto_data_type type; +}; + +#if MG_ENABLE_HTTP_CGI +struct mg_http_proto_data_cgi { + struct mg_connection *cgi_nc; +}; +#endif + +struct mg_http_proto_data_chuncked { + int64_t body_len; /* How many bytes of chunked body was reassembled. */ +}; + +struct mg_http_endpoint { + struct mg_http_endpoint *next; + struct mg_str uri_pattern; /* owned */ + char *auth_domain; /* owned */ + char *auth_file; /* owned */ + + mg_event_handler_t handler; +#if MG_ENABLE_CALLBACK_USERDATA + void *user_data; +#endif +}; + +enum mg_http_multipart_stream_state { + MPS_BEGIN, + MPS_WAITING_FOR_BOUNDARY, + MPS_WAITING_FOR_CHUNK, + MPS_GOT_CHUNK, + MPS_GOT_BOUNDARY, + MPS_FINALIZE, + MPS_FINISHED +}; + +struct mg_http_multipart_stream { + const char *boundary; + int boundary_len; + const char *var_name; + const char *file_name; + void *user_data; + int prev_io_len; + enum mg_http_multipart_stream_state state; + int processing_part; +}; + +struct mg_reverse_proxy_data { + struct mg_connection *linked_conn; +}; + +struct mg_ws_proto_data { + /* + * Defragmented size of the frame so far. + * + * First byte of nc->recv_mbuf.buf is an op, the rest of the data is + * defragmented data. + */ + size_t reass_len; +}; + +struct mg_http_proto_data { +#if MG_ENABLE_FILESYSTEM + struct mg_http_proto_data_file file; +#endif +#if MG_ENABLE_HTTP_CGI + struct mg_http_proto_data_cgi cgi; +#endif +#if MG_ENABLE_HTTP_STREAMING_MULTIPART + struct mg_http_multipart_stream mp_stream; +#endif +#if MG_ENABLE_HTTP_WEBSOCKET + struct mg_ws_proto_data ws_data; +#endif + struct mg_http_proto_data_chuncked chunk; + struct mg_http_endpoint *endpoints; + mg_event_handler_t endpoint_handler; + struct mg_reverse_proxy_data reverse_proxy_data; + size_t rcvd; /* How many bytes we have received. */ +}; + +static void mg_http_conn_destructor(void *proto_data); +struct mg_connection *mg_connect_http_base( + struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data), + struct mg_connect_opts opts, const char *scheme1, const char *scheme2, + const char *scheme_ssl1, const char *scheme_ssl2, const char *url, + struct mg_str *path, struct mg_str *user_info, struct mg_str *host); + +static struct mg_http_proto_data *mg_http_get_proto_data( + struct mg_connection *c) { + if (c->proto_data == NULL) { + c->proto_data = MG_CALLOC(1, sizeof(struct mg_http_proto_data)); + c->proto_data_destructor = mg_http_conn_destructor; + } + + return (struct mg_http_proto_data *) c->proto_data; +} + +#if MG_ENABLE_HTTP_STREAMING_MULTIPART +static void mg_http_free_proto_data_mp_stream( + struct mg_http_multipart_stream *mp) { + MG_FREE((void *) mp->boundary); + MG_FREE((void *) mp->var_name); + MG_FREE((void *) mp->file_name); + memset(mp, 0, sizeof(*mp)); +} +#endif + +#if MG_ENABLE_FILESYSTEM +static void mg_http_free_proto_data_file(struct mg_http_proto_data_file *d) { + if (d != NULL) { + if (d->fp != NULL) { + fclose(d->fp); + } + memset(d, 0, sizeof(struct mg_http_proto_data_file)); + } +} +#endif + +static void mg_http_free_proto_data_endpoints(struct mg_http_endpoint **ep) { + struct mg_http_endpoint *current = *ep; + + while (current != NULL) { + struct mg_http_endpoint *tmp = current->next; + MG_FREE((void *) current->uri_pattern.p); + MG_FREE((void *) current->auth_domain); + MG_FREE((void *) current->auth_file); + MG_FREE(current); + current = tmp; + } + + ep = NULL; +} + +static void mg_http_free_reverse_proxy_data(struct mg_reverse_proxy_data *rpd) { + if (rpd->linked_conn != NULL) { + /* + * Connection has linked one, we have to unlink & close it + * since _this_ connection is going to die and + * it doesn't make sense to keep another one + */ + struct mg_http_proto_data *pd = mg_http_get_proto_data(rpd->linked_conn); + if (pd->reverse_proxy_data.linked_conn != NULL) { + pd->reverse_proxy_data.linked_conn->flags |= MG_F_SEND_AND_CLOSE; + pd->reverse_proxy_data.linked_conn = NULL; + } + rpd->linked_conn = NULL; + } +} + +static void mg_http_conn_destructor(void *proto_data) { + struct mg_http_proto_data *pd = (struct mg_http_proto_data *) proto_data; +#if MG_ENABLE_FILESYSTEM + mg_http_free_proto_data_file(&pd->file); +#endif +#if MG_ENABLE_HTTP_CGI + mg_http_free_proto_data_cgi(&pd->cgi); +#endif +#if MG_ENABLE_HTTP_STREAMING_MULTIPART + mg_http_free_proto_data_mp_stream(&pd->mp_stream); +#endif + mg_http_free_proto_data_endpoints(&pd->endpoints); + mg_http_free_reverse_proxy_data(&pd->reverse_proxy_data); + MG_FREE(proto_data); +} + +#if MG_ENABLE_FILESYSTEM + +#define MIME_ENTRY(_ext, _type) \ + { _ext, sizeof(_ext) - 1, _type } +static const struct { + const char *extension; + size_t ext_len; + const char *mime_type; +} mg_static_builtin_mime_types[] = { + MIME_ENTRY("html", "text/html"), + MIME_ENTRY("html", "text/html"), + MIME_ENTRY("htm", "text/html"), + MIME_ENTRY("shtm", "text/html"), + MIME_ENTRY("shtml", "text/html"), + MIME_ENTRY("css", "text/css"), + MIME_ENTRY("js", "application/x-javascript"), + MIME_ENTRY("ico", "image/x-icon"), + MIME_ENTRY("gif", "image/gif"), + MIME_ENTRY("jpg", "image/jpeg"), + MIME_ENTRY("jpeg", "image/jpeg"), + MIME_ENTRY("png", "image/png"), + MIME_ENTRY("svg", "image/svg+xml"), + MIME_ENTRY("txt", "text/plain"), + MIME_ENTRY("torrent", "application/x-bittorrent"), + MIME_ENTRY("wav", "audio/x-wav"), + MIME_ENTRY("mp3", "audio/x-mp3"), + MIME_ENTRY("mid", "audio/mid"), + MIME_ENTRY("m3u", "audio/x-mpegurl"), + MIME_ENTRY("ogg", "application/ogg"), + MIME_ENTRY("ram", "audio/x-pn-realaudio"), + MIME_ENTRY("xml", "text/xml"), + MIME_ENTRY("ttf", "application/x-font-ttf"), + MIME_ENTRY("json", "application/json"), + MIME_ENTRY("xslt", "application/xml"), + MIME_ENTRY("xsl", "application/xml"), + MIME_ENTRY("ra", "audio/x-pn-realaudio"), + MIME_ENTRY("doc", "application/msword"), + MIME_ENTRY("exe", "application/octet-stream"), + MIME_ENTRY("zip", "application/x-zip-compressed"), + MIME_ENTRY("xls", "application/excel"), + MIME_ENTRY("tgz", "application/x-tar-gz"), + MIME_ENTRY("tar", "application/x-tar"), + MIME_ENTRY("gz", "application/x-gunzip"), + MIME_ENTRY("arj", "application/x-arj-compressed"), + MIME_ENTRY("rar", "application/x-rar-compressed"), + MIME_ENTRY("rtf", "application/rtf"), + MIME_ENTRY("pdf", "application/pdf"), + MIME_ENTRY("swf", "application/x-shockwave-flash"), + MIME_ENTRY("mpg", "video/mpeg"), + MIME_ENTRY("webm", "video/webm"), + MIME_ENTRY("mpeg", "video/mpeg"), + MIME_ENTRY("mov", "video/quicktime"), + MIME_ENTRY("mp4", "video/mp4"), + MIME_ENTRY("m4v", "video/x-m4v"), + MIME_ENTRY("asf", "video/x-ms-asf"), + MIME_ENTRY("avi", "video/x-msvideo"), + MIME_ENTRY("bmp", "image/bmp"), + {NULL, 0, NULL}}; + +static struct mg_str mg_get_mime_type(const char *path, const char *dflt, + const struct mg_serve_http_opts *opts) { + const char *ext, *overrides; + size_t i, path_len; + struct mg_str r, k, v; + + path_len = strlen(path); + + overrides = opts->custom_mime_types; + while ((overrides = mg_next_comma_list_entry(overrides, &k, &v)) != NULL) { + ext = path + (path_len - k.len); + if (path_len > k.len && mg_vcasecmp(&k, ext) == 0) { + return v; + } + } + + for (i = 0; mg_static_builtin_mime_types[i].extension != NULL; i++) { + ext = path + (path_len - mg_static_builtin_mime_types[i].ext_len); + if (path_len > mg_static_builtin_mime_types[i].ext_len && ext[-1] == '.' && + mg_casecmp(ext, mg_static_builtin_mime_types[i].extension) == 0) { + r.p = mg_static_builtin_mime_types[i].mime_type; + r.len = strlen(r.p); + return r; + } + } + + r.p = dflt; + r.len = strlen(r.p); + return r; +} +#endif + +/* + * Check whether full request is buffered. Return: + * -1 if request is malformed + * 0 if request is not yet fully buffered + * >0 actual request length, including last \r\n\r\n + */ +static int mg_http_get_request_len(const char *s, int buf_len) { + const unsigned char *buf = (unsigned char *) s; + int i; + + for (i = 0; i < buf_len; i++) { + if (!isprint(buf[i]) && buf[i] != '\r' && buf[i] != '\n' && buf[i] < 128) { + return -1; + } else if (buf[i] == '\n' && i + 1 < buf_len && buf[i + 1] == '\n') { + return i + 2; + } else if (buf[i] == '\n' && i + 2 < buf_len && buf[i + 1] == '\r' && + buf[i + 2] == '\n') { + return i + 3; + } + } + + return 0; +} + +static const char *mg_http_parse_headers(const char *s, const char *end, + int len, struct http_message *req) { + int i = 0; + while (i < (int) ARRAY_SIZE(req->header_names) - 1) { + struct mg_str *k = &req->header_names[i], *v = &req->header_values[i]; + + s = mg_skip(s, end, ": ", k); + s = mg_skip(s, end, "\r\n", v); + + while (v->len > 0 && v->p[v->len - 1] == ' ') { + v->len--; /* Trim trailing spaces in header value */ + } + + /* + * If header value is empty - skip it and go to next (if any). + * NOTE: Do not add it to headers_values because such addition changes API + * behaviour + */ + if (k->len != 0 && v->len == 0) { + continue; + } + + if (k->len == 0 || v->len == 0) { + k->p = v->p = NULL; + k->len = v->len = 0; + break; + } + + if (!mg_ncasecmp(k->p, "Content-Length", 14)) { + req->body.len = (size_t) to64(v->p); + req->message.len = len + req->body.len; + } + + i++; + } + + return s; +} + +int mg_parse_http(const char *s, int n, struct http_message *hm, int is_req) { + const char *end, *qs; + int len = mg_http_get_request_len(s, n); + + if (len <= 0) return len; + + memset(hm, 0, sizeof(*hm)); + hm->message.p = s; + hm->body.p = s + len; + hm->message.len = hm->body.len = (size_t) ~0; + end = s + len; + + /* Request is fully buffered. Skip leading whitespaces. */ + while (s < end && isspace(*(unsigned char *) s)) s++; + + if (is_req) { + /* Parse request line: method, URI, proto */ + s = mg_skip(s, end, " ", &hm->method); + s = mg_skip(s, end, " ", &hm->uri); + s = mg_skip(s, end, "\r\n", &hm->proto); + if (hm->uri.p <= hm->method.p || hm->proto.p <= hm->uri.p) return -1; + + /* If URI contains '?' character, initialize query_string */ + if ((qs = (char *) memchr(hm->uri.p, '?', hm->uri.len)) != NULL) { + hm->query_string.p = qs + 1; + hm->query_string.len = &hm->uri.p[hm->uri.len] - (qs + 1); + hm->uri.len = qs - hm->uri.p; + } + } else { + s = mg_skip(s, end, " ", &hm->proto); + if (end - s < 4 || s[3] != ' ') return -1; + hm->resp_code = atoi(s); + if (hm->resp_code < 100 || hm->resp_code >= 600) return -1; + s += 4; + s = mg_skip(s, end, "\r\n", &hm->resp_status_msg); + } + + s = mg_http_parse_headers(s, end, len, hm); + + /* + * mg_parse_http() is used to parse both HTTP requests and HTTP + * responses. If HTTP response does not have Content-Length set, then + * body is read until socket is closed, i.e. body.len is infinite (~0). + * + * For HTTP requests though, according to + * http://tools.ietf.org/html/rfc7231#section-8.1.3, + * only POST and PUT methods have defined body semantics. + * Therefore, if Content-Length is not specified and methods are + * not one of PUT or POST, set body length to 0. + * + * So, + * if it is HTTP request, and Content-Length is not set, + * and method is not (PUT or POST) then reset body length to zero. + */ + if (hm->body.len == (size_t) ~0 && is_req && + mg_vcasecmp(&hm->method, "PUT") != 0 && + mg_vcasecmp(&hm->method, "POST") != 0) { + hm->body.len = 0; + hm->message.len = len; + } + + return len; +} + +struct mg_str *mg_get_http_header(struct http_message *hm, const char *name) { + size_t i, len = strlen(name); + + for (i = 0; hm->header_names[i].len > 0; i++) { + struct mg_str *h = &hm->header_names[i], *v = &hm->header_values[i]; + if (h->p != NULL && h->len == len && !mg_ncasecmp(h->p, name, len)) + return v; + } + + return NULL; +} + +#if MG_ENABLE_FILESYSTEM +static void mg_http_transfer_file_data(struct mg_connection *nc) { + struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); + char buf[MG_MAX_HTTP_SEND_MBUF]; + size_t n = 0, to_read = 0, left = (size_t)(pd->file.cl - pd->file.sent); + + if (pd->file.type == DATA_FILE) { + struct mbuf *io = &nc->send_mbuf; + if (io->len >= MG_MAX_HTTP_SEND_MBUF) { + to_read = 0; + } else { + to_read = MG_MAX_HTTP_SEND_MBUF - io->len; + } + if (to_read > left) { + to_read = left; + } + if (to_read > 0) { + n = mg_fread(buf, 1, to_read, pd->file.fp); + if (n > 0) { + mg_send(nc, buf, n); + pd->file.sent += n; + DBG(("%p sent %d (total %d)", nc, (int) n, (int) pd->file.sent)); + } + } else { + /* Rate-limited */ + } + if (pd->file.sent >= pd->file.cl) { + LOG(LL_DEBUG, ("%p done, %d bytes", nc, (int) pd->file.sent)); + if (!pd->file.keepalive) nc->flags |= MG_F_SEND_AND_CLOSE; + mg_http_free_proto_data_file(&pd->file); + } + } else if (pd->file.type == DATA_PUT) { + struct mbuf *io = &nc->recv_mbuf; + size_t to_write = left <= 0 ? 0 : left < io->len ? (size_t) left : io->len; + size_t n = mg_fwrite(io->buf, 1, to_write, pd->file.fp); + if (n > 0) { + mbuf_remove(io, n); + pd->file.sent += n; + } + if (n == 0 || pd->file.sent >= pd->file.cl) { + if (!pd->file.keepalive) nc->flags |= MG_F_SEND_AND_CLOSE; + mg_http_free_proto_data_file(&pd->file); + } + } +#if MG_ENABLE_HTTP_CGI + else if (pd->cgi.cgi_nc != NULL) { + /* This is POST data that needs to be forwarded to the CGI process */ + if (pd->cgi.cgi_nc != NULL) { + mg_forward(nc, pd->cgi.cgi_nc); + } else { + nc->flags |= MG_F_SEND_AND_CLOSE; + } + } +#endif +} +#endif /* MG_ENABLE_FILESYSTEM */ + +/* + * Parse chunked-encoded buffer. Return 0 if the buffer is not encoded, or + * if it's incomplete. If the chunk is fully buffered, return total number of + * bytes in a chunk, and store data in `data`, `data_len`. + */ +static size_t mg_http_parse_chunk(char *buf, size_t len, char **chunk_data, + size_t *chunk_len) { + unsigned char *s = (unsigned char *) buf; + size_t n = 0; /* scanned chunk length */ + size_t i = 0; /* index in s */ + + /* Scan chunk length. That should be a hexadecimal number. */ + while (i < len && isxdigit(s[i])) { + n *= 16; + n += (s[i] >= '0' && s[i] <= '9') ? s[i] - '0' : tolower(s[i]) - 'a' + 10; + i++; + } + + /* Skip new line */ + if (i == 0 || i + 2 > len || s[i] != '\r' || s[i + 1] != '\n') { + return 0; + } + i += 2; + + /* Record where the data is */ + *chunk_data = (char *) s + i; + *chunk_len = n; + + /* Skip data */ + i += n; + + /* Skip new line */ + if (i == 0 || i + 2 > len || s[i] != '\r' || s[i + 1] != '\n') { + return 0; + } + return i + 2; +} + +MG_INTERNAL size_t mg_handle_chunked(struct mg_connection *nc, + struct http_message *hm, char *buf, + size_t blen) { + struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); + char *data; + size_t i, n, data_len, body_len, zero_chunk_received = 0; + /* Find out piece of received data that is not yet reassembled */ + body_len = (size_t) pd->chunk.body_len; + assert(blen >= body_len); + + /* Traverse all fully buffered chunks */ + for (i = body_len; + (n = mg_http_parse_chunk(buf + i, blen - i, &data, &data_len)) > 0; + i += n) { + /* Collapse chunk data to the rest of HTTP body */ + memmove(buf + body_len, data, data_len); + body_len += data_len; + hm->body.len = body_len; + + if (data_len == 0) { + zero_chunk_received = 1; + i += n; + break; + } + } + + if (i > body_len) { + /* Shift unparsed content to the parsed body */ + assert(i <= blen); + memmove(buf + body_len, buf + i, blen - i); + memset(buf + body_len + blen - i, 0, i - body_len); + nc->recv_mbuf.len -= i - body_len; + pd->chunk.body_len = body_len; + + /* Send MG_EV_HTTP_CHUNK event */ + nc->flags &= ~MG_F_DELETE_CHUNK; + mg_call(nc, nc->handler, nc->user_data, MG_EV_HTTP_CHUNK, hm); + + /* Delete processed data if user set MG_F_DELETE_CHUNK flag */ + if (nc->flags & MG_F_DELETE_CHUNK) { + memset(buf, 0, body_len); + memmove(buf, buf + body_len, blen - i); + nc->recv_mbuf.len -= body_len; + hm->body.len = 0; + pd->chunk.body_len = 0; + } + + if (zero_chunk_received) { + /* Total message size is len(body) + len(headers) */ + hm->message.len = + (size_t) pd->chunk.body_len + blen - i + (hm->body.p - hm->message.p); + } + } + + return body_len; +} + +struct mg_http_endpoint *mg_http_get_endpoint_handler(struct mg_connection *nc, + struct mg_str *uri_path) { + struct mg_http_proto_data *pd; + struct mg_http_endpoint *ret = NULL; + int matched, matched_max = 0; + struct mg_http_endpoint *ep; + + if (nc == NULL) { + return NULL; + } + + pd = mg_http_get_proto_data(nc); + + ep = pd->endpoints; + while (ep != NULL) { + if ((matched = mg_match_prefix_n(ep->uri_pattern, *uri_path)) > 0) { + if (matched > matched_max) { + /* Looking for the longest suitable handler */ + ret = ep; + matched_max = matched; + } + } + + ep = ep->next; + } + + return ret; +} + +#if MG_ENABLE_HTTP_STREAMING_MULTIPART +static void mg_http_multipart_continue(struct mg_connection *nc); + +static void mg_http_multipart_begin(struct mg_connection *nc, + struct http_message *hm, int req_len); + +#endif + +static void mg_http_call_endpoint_handler(struct mg_connection *nc, int ev, + struct http_message *hm); + +static void deliver_chunk(struct mg_connection *c, struct http_message *hm, + int req_len) { + /* Incomplete message received. Send MG_EV_HTTP_CHUNK event */ + hm->body.len = c->recv_mbuf.len - req_len; + c->flags &= ~MG_F_DELETE_CHUNK; + mg_call(c, c->handler, c->user_data, MG_EV_HTTP_CHUNK, hm); + /* Delete processed data if user set MG_F_DELETE_CHUNK flag */ + if (c->flags & MG_F_DELETE_CHUNK) c->recv_mbuf.len = req_len; +} + +/* + * lx106 compiler has a bug (TODO(mkm) report and insert tracking bug here) + * If a big structure is declared in a big function, lx106 gcc will make it + * even bigger (round up to 4k, from 700 bytes of actual size). + */ +#ifdef __xtensa__ +static void mg_http_handler2(struct mg_connection *nc, int ev, + void *ev_data MG_UD_ARG(void *user_data), + struct http_message *hm) __attribute__((noinline)); + +void mg_http_handler(struct mg_connection *nc, int ev, + void *ev_data MG_UD_ARG(void *user_data)) { + struct http_message hm; + mg_http_handler2(nc, ev, ev_data MG_UD_ARG(user_data), &hm); +} + +static void mg_http_handler2(struct mg_connection *nc, int ev, + void *ev_data MG_UD_ARG(void *user_data), + struct http_message *hm) { +#else /* !__XTENSA__ */ +void mg_http_handler(struct mg_connection *nc, int ev, + void *ev_data MG_UD_ARG(void *user_data)) { + struct http_message shm, *hm = &shm; +#endif /* __XTENSA__ */ + struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); + struct mbuf *io = &nc->recv_mbuf; + int req_len; + const int is_req = (nc->listener != NULL); +#if MG_ENABLE_HTTP_WEBSOCKET + struct mg_str *vec; +#endif + if (ev == MG_EV_CLOSE) { +#if MG_ENABLE_HTTP_CGI + /* Close associated CGI forwarder connection */ + if (pd->cgi.cgi_nc != NULL) { + pd->cgi.cgi_nc->user_data = NULL; + pd->cgi.cgi_nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } +#endif +#if MG_ENABLE_HTTP_STREAMING_MULTIPART + if (pd->mp_stream.boundary != NULL) { + /* + * Multipart message is in progress, but connection is closed. + * Finish part and request with an error flag. + */ + struct mg_http_multipart_part mp; + memset(&mp, 0, sizeof(mp)); + mp.status = -1; + mp.var_name = pd->mp_stream.var_name; + mp.file_name = pd->mp_stream.file_name; + mg_call(nc, (pd->endpoint_handler ? pd->endpoint_handler : nc->handler), + nc->user_data, MG_EV_HTTP_PART_END, &mp); + mp.var_name = NULL; + mp.file_name = NULL; + mg_call(nc, (pd->endpoint_handler ? pd->endpoint_handler : nc->handler), + nc->user_data, MG_EV_HTTP_MULTIPART_REQUEST_END, &mp); + } else +#endif + if (io->len > 0 && + (req_len = mg_parse_http(io->buf, io->len, hm, is_req)) > 0) { + /* + * For HTTP messages without Content-Length, always send HTTP message + * before MG_EV_CLOSE message. + */ + int ev2 = is_req ? MG_EV_HTTP_REQUEST : MG_EV_HTTP_REPLY; + hm->message.len = io->len; + hm->body.len = io->buf + io->len - hm->body.p; + deliver_chunk(nc, hm, req_len); + mg_http_call_endpoint_handler(nc, ev2, hm); + } + pd->rcvd = 0; + } + +#if MG_ENABLE_FILESYSTEM + if (pd->file.fp != NULL) { + mg_http_transfer_file_data(nc); + } +#endif + + mg_call(nc, nc->handler, nc->user_data, ev, ev_data); + + if (ev == MG_EV_RECV) { + struct mg_str *s; + pd->rcvd += *(int *) ev_data; + +#if MG_ENABLE_HTTP_STREAMING_MULTIPART + if (pd->mp_stream.boundary != NULL) { + mg_http_multipart_continue(nc); + return; + } +#endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */ + + req_len = mg_parse_http(io->buf, io->len, hm, is_req); + + if (req_len > 0 && + (s = mg_get_http_header(hm, "Transfer-Encoding")) != NULL && + mg_vcasecmp(s, "chunked") == 0) { + mg_handle_chunked(nc, hm, io->buf + req_len, io->len - req_len); + } + +#if MG_ENABLE_HTTP_STREAMING_MULTIPART + if (req_len > 0 && (s = mg_get_http_header(hm, "Content-Type")) != NULL && + s->len >= 9 && strncmp(s->p, "multipart", 9) == 0) { + mg_http_multipart_begin(nc, hm, req_len); + mg_http_multipart_continue(nc); + return; + } +#endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */ + + /* TODO(alashkin): refactor this ifelseifelseifelseifelse */ + if ((req_len < 0 || + (req_len == 0 && io->len >= MG_MAX_HTTP_REQUEST_SIZE))) { + DBG(("invalid request")); + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } else if (req_len == 0) { + /* Do nothing, request is not yet fully buffered */ + } +#if MG_ENABLE_HTTP_WEBSOCKET + else if (nc->listener == NULL && + mg_get_http_header(hm, "Sec-WebSocket-Accept")) { + /* We're websocket client, got handshake response from server. */ + /* TODO(lsm): check the validity of accept Sec-WebSocket-Accept */ + mbuf_remove(io, req_len); + nc->proto_handler = mg_ws_handler; + nc->flags |= MG_F_IS_WEBSOCKET; + mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_HANDSHAKE_DONE, + NULL); + mg_ws_handler(nc, MG_EV_RECV, ev_data MG_UD_ARG(user_data)); + } else if (nc->listener != NULL && + (vec = mg_get_http_header(hm, "Sec-WebSocket-Key")) != NULL) { + struct mg_http_endpoint *ep; + + /* This is a websocket request. Switch protocol handlers. */ + mbuf_remove(io, req_len); + nc->proto_handler = mg_ws_handler; + nc->flags |= MG_F_IS_WEBSOCKET; + + /* + * If we have a handler set up with mg_register_http_endpoint(), + * deliver subsequent websocket events to this handler after the + * protocol switch. + */ + ep = mg_http_get_endpoint_handler(nc->listener, &hm->uri); + if (ep != NULL) { + nc->handler = ep->handler; +#if MG_ENABLE_CALLBACK_USERDATA + nc->user_data = ep->user_data; +#endif + } + + /* Send handshake */ + mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_HANDSHAKE_REQUEST, + hm); + if (!(nc->flags & (MG_F_CLOSE_IMMEDIATELY | MG_F_SEND_AND_CLOSE))) { + if (nc->send_mbuf.len == 0) { + mg_ws_handshake(nc, vec, hm); + } + mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_HANDSHAKE_DONE, + NULL); + mg_ws_handler(nc, MG_EV_RECV, ev_data MG_UD_ARG(user_data)); + } + } +#endif /* MG_ENABLE_HTTP_WEBSOCKET */ + else if (hm->message.len > pd->rcvd) { + /* Not yet received all HTTP body, deliver MG_EV_HTTP_CHUNK */ + deliver_chunk(nc, hm, req_len); + if (nc->recv_mbuf_limit > 0 && nc->recv_mbuf.len >= nc->recv_mbuf_limit) { + LOG(LL_ERROR, ("%p recv buffer (%lu bytes) exceeds the limit " + "%lu bytes, and not drained, closing", + nc, (unsigned long) nc->recv_mbuf.len, + (unsigned long) nc->recv_mbuf_limit)); + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } + } else { + /* We did receive all HTTP body. */ + int trigger_ev = nc->listener ? MG_EV_HTTP_REQUEST : MG_EV_HTTP_REPLY; + char addr[32]; + mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr), + MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT); + DBG(("%p %s %.*s %.*s", nc, addr, (int) hm->method.len, hm->method.p, + (int) hm->uri.len, hm->uri.p)); + deliver_chunk(nc, hm, req_len); + /* Whole HTTP message is fully buffered, call event handler */ + mg_http_call_endpoint_handler(nc, trigger_ev, hm); + mbuf_remove(io, hm->message.len); + pd->rcvd = 0; + } + } +} + +static size_t mg_get_line_len(const char *buf, size_t buf_len) { + size_t len = 0; + while (len < buf_len && buf[len] != '\n') len++; + return len == buf_len ? 0 : len + 1; +} + +#if MG_ENABLE_HTTP_STREAMING_MULTIPART +static void mg_http_multipart_begin(struct mg_connection *nc, + struct http_message *hm, int req_len) { + struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); + struct mg_str *ct; + struct mbuf *io = &nc->recv_mbuf; + + char boundary_buf[100]; + char *boundary = boundary_buf; + int boundary_len; + + ct = mg_get_http_header(hm, "Content-Type"); + if (ct == NULL) { + /* We need more data - or it isn't multipart mesage */ + goto exit_mp; + } + + /* Content-type should start with "multipart" */ + if (ct->len < 9 || strncmp(ct->p, "multipart", 9) != 0) { + goto exit_mp; + } + + boundary_len = + mg_http_parse_header2(ct, "boundary", &boundary, sizeof(boundary_buf)); + if (boundary_len == 0) { + /* + * Content type is multipart, but there is no boundary, + * probably malformed request + */ + nc->flags = MG_F_CLOSE_IMMEDIATELY; + DBG(("invalid request")); + goto exit_mp; + } + + /* If we reach this place - that is multipart request */ + + if (pd->mp_stream.boundary != NULL) { + /* + * Another streaming request was in progress, + * looks like protocol error + */ + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } else { + struct mg_http_endpoint *ep = NULL; + pd->mp_stream.state = MPS_BEGIN; + pd->mp_stream.boundary = strdup(boundary); + pd->mp_stream.boundary_len = strlen(boundary); + pd->mp_stream.var_name = pd->mp_stream.file_name = NULL; + pd->endpoint_handler = nc->handler; + + ep = mg_http_get_endpoint_handler(nc->listener, &hm->uri); + if (ep != NULL) { + pd->endpoint_handler = ep->handler; + } + + mg_http_call_endpoint_handler(nc, MG_EV_HTTP_MULTIPART_REQUEST, hm); + + mbuf_remove(io, req_len); + } +exit_mp: + if (boundary != boundary_buf) MG_FREE(boundary); +} + +#define CONTENT_DISPOSITION "Content-Disposition: " + +static void mg_http_multipart_call_handler(struct mg_connection *c, int ev, + const char *data, size_t data_len) { + struct mg_http_multipart_part mp; + struct mg_http_proto_data *pd = mg_http_get_proto_data(c); + memset(&mp, 0, sizeof(mp)); + + mp.var_name = pd->mp_stream.var_name; + mp.file_name = pd->mp_stream.file_name; + mp.user_data = pd->mp_stream.user_data; + mp.data.p = data; + mp.data.len = data_len; + mg_call(c, pd->endpoint_handler, c->user_data, ev, &mp); + pd->mp_stream.user_data = mp.user_data; +} + +static int mg_http_multipart_got_chunk(struct mg_connection *c) { + struct mg_http_proto_data *pd = mg_http_get_proto_data(c); + struct mbuf *io = &c->recv_mbuf; + + mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_DATA, io->buf, + pd->mp_stream.prev_io_len); + mbuf_remove(io, pd->mp_stream.prev_io_len); + pd->mp_stream.prev_io_len = 0; + pd->mp_stream.state = MPS_WAITING_FOR_CHUNK; + + return 0; +} + +static int mg_http_multipart_finalize(struct mg_connection *c) { + struct mg_http_proto_data *pd = mg_http_get_proto_data(c); + + mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_END, NULL, 0); + MG_FREE((void *) pd->mp_stream.file_name); + pd->mp_stream.file_name = NULL; + MG_FREE((void *) pd->mp_stream.var_name); + pd->mp_stream.var_name = NULL; + mg_http_multipart_call_handler(c, MG_EV_HTTP_MULTIPART_REQUEST_END, NULL, 0); + mg_http_free_proto_data_mp_stream(&pd->mp_stream); + pd->mp_stream.state = MPS_FINISHED; + + return 1; +} + +static int mg_http_multipart_wait_for_boundary(struct mg_connection *c) { + const char *boundary; + struct mbuf *io = &c->recv_mbuf; + struct mg_http_proto_data *pd = mg_http_get_proto_data(c); + + if (pd->mp_stream.boundary == NULL) { + pd->mp_stream.state = MPS_FINALIZE; + DBG(("Invalid request: boundary not initialized")); + return 0; + } + + if ((int) io->len < pd->mp_stream.boundary_len + 2) { + return 0; + } + + boundary = c_strnstr(io->buf, pd->mp_stream.boundary, io->len); + if (boundary != NULL) { + const char *boundary_end = (boundary + pd->mp_stream.boundary_len); + if (io->len - (boundary_end - io->buf) < 4) { + return 0; + } + if (strncmp(boundary_end, "--\r\n", 4) == 0) { + pd->mp_stream.state = MPS_FINALIZE; + mbuf_remove(io, (boundary_end - io->buf) + 4); + } else { + pd->mp_stream.state = MPS_GOT_BOUNDARY; + } + } else { + return 0; + } + + return 1; +} + +static void mg_http_parse_header_internal(struct mg_str *hdr, + const char *var_name, + struct altbuf *ab); + +static int mg_http_multipart_process_boundary(struct mg_connection *c) { + int data_size; + const char *boundary, *block_begin; + struct mbuf *io = &c->recv_mbuf; + struct mg_http_proto_data *pd = mg_http_get_proto_data(c); + struct altbuf ab_file_name, ab_var_name; + int line_len; + boundary = c_strnstr(io->buf, pd->mp_stream.boundary, io->len); + block_begin = boundary + pd->mp_stream.boundary_len + 2; + data_size = io->len - (block_begin - io->buf); + + altbuf_init(&ab_file_name, NULL, 0); + altbuf_init(&ab_var_name, NULL, 0); + + while (data_size > 0 && + (line_len = mg_get_line_len(block_begin, data_size)) != 0) { + if (line_len > (int) sizeof(CONTENT_DISPOSITION) && + mg_ncasecmp(block_begin, CONTENT_DISPOSITION, + sizeof(CONTENT_DISPOSITION) - 1) == 0) { + struct mg_str header; + + header.p = block_begin + sizeof(CONTENT_DISPOSITION) - 1; + header.len = line_len - sizeof(CONTENT_DISPOSITION) - 1; + + altbuf_reset(&ab_var_name); + mg_http_parse_header_internal(&header, "name", &ab_var_name); + + altbuf_reset(&ab_file_name); + mg_http_parse_header_internal(&header, "filename", &ab_file_name); + + block_begin += line_len; + data_size -= line_len; + + continue; + } + + if (line_len == 2 && mg_ncasecmp(block_begin, "\r\n", 2) == 0) { + mbuf_remove(io, block_begin - io->buf + 2); + + if (pd->mp_stream.processing_part != 0) { + mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_END, NULL, 0); + } + + /* Reserve 2 bytes for "\r\n" in file_name and var_name */ + altbuf_append(&ab_file_name, '\0'); + altbuf_append(&ab_file_name, '\0'); + altbuf_append(&ab_var_name, '\0'); + altbuf_append(&ab_var_name, '\0'); + + MG_FREE((void *) pd->mp_stream.file_name); + pd->mp_stream.file_name = altbuf_get_buf(&ab_file_name, 1 /* trim */); + MG_FREE((void *) pd->mp_stream.var_name); + pd->mp_stream.var_name = altbuf_get_buf(&ab_var_name, 1 /* trim */); + + mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_BEGIN, NULL, 0); + pd->mp_stream.state = MPS_WAITING_FOR_CHUNK; + pd->mp_stream.processing_part++; + return 1; + } + + block_begin += line_len; + } + + pd->mp_stream.state = MPS_WAITING_FOR_BOUNDARY; + + altbuf_reset(&ab_var_name); + altbuf_reset(&ab_file_name); + + return 0; +} + +static int mg_http_multipart_continue_wait_for_chunk(struct mg_connection *c) { + struct mg_http_proto_data *pd = mg_http_get_proto_data(c); + struct mbuf *io = &c->recv_mbuf; + + const char *boundary; + if ((int) io->len < pd->mp_stream.boundary_len + 6 /* \r\n, --, -- */) { + return 0; + } + + boundary = c_strnstr(io->buf, pd->mp_stream.boundary, io->len); + if (boundary == NULL && pd->mp_stream.prev_io_len == 0) { + pd->mp_stream.prev_io_len = io->len; + return 0; + } else if (boundary == NULL && + (int) io->len > + pd->mp_stream.prev_io_len + pd->mp_stream.boundary_len + 4) { + pd->mp_stream.state = MPS_GOT_CHUNK; + return 1; + } else if (boundary != NULL) { + int data_size = (boundary - io->buf - 4); + mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_DATA, io->buf, data_size); + mbuf_remove(io, (boundary - io->buf)); + pd->mp_stream.prev_io_len = 0; + pd->mp_stream.state = MPS_WAITING_FOR_BOUNDARY; + return 1; + } else { + return 0; + } +} + +static void mg_http_multipart_continue(struct mg_connection *c) { + struct mg_http_proto_data *pd = mg_http_get_proto_data(c); + while (1) { + switch (pd->mp_stream.state) { + case MPS_BEGIN: { + pd->mp_stream.state = MPS_WAITING_FOR_BOUNDARY; + break; + } + case MPS_WAITING_FOR_BOUNDARY: { + if (mg_http_multipart_wait_for_boundary(c) == 0) { + return; + } + break; + } + case MPS_GOT_BOUNDARY: { + if (mg_http_multipart_process_boundary(c) == 0) { + return; + } + break; + } + case MPS_WAITING_FOR_CHUNK: { + if (mg_http_multipart_continue_wait_for_chunk(c) == 0) { + return; + } + break; + } + case MPS_GOT_CHUNK: { + if (mg_http_multipart_got_chunk(c) == 0) { + return; + } + break; + } + case MPS_FINALIZE: { + if (mg_http_multipart_finalize(c) == 0) { + return; + } + break; + } + case MPS_FINISHED: { + mbuf_remove(&c->recv_mbuf, c->recv_mbuf.len); + return; + } + } + } +} + +struct file_upload_state { + char *lfn; + size_t num_recd; + FILE *fp; +}; + +#endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */ + +void mg_set_protocol_http_websocket(struct mg_connection *nc) { + nc->proto_handler = mg_http_handler; +} + +const char *mg_status_message(int status_code) { + switch (status_code) { + case 206: + return "Partial Content"; + case 301: + return "Moved"; + case 302: + return "Found"; + case 400: + return "Bad Request"; + case 401: + return "Unauthorized"; + case 403: + return "Forbidden"; + case 404: + return "Not Found"; + case 416: + return "Requested Range Not Satisfiable"; + case 418: + return "I'm a teapot"; + case 500: + return "Internal Server Error"; + case 502: + return "Bad Gateway"; + case 503: + return "Service Unavailable"; + +#if MG_ENABLE_EXTRA_ERRORS_DESC + case 100: + return "Continue"; + case 101: + return "Switching Protocols"; + case 102: + return "Processing"; + case 200: + return "OK"; + case 201: + return "Created"; + case 202: + return "Accepted"; + case 203: + return "Non-Authoritative Information"; + case 204: + return "No Content"; + case 205: + return "Reset Content"; + case 207: + return "Multi-Status"; + case 208: + return "Already Reported"; + case 226: + return "IM Used"; + case 300: + return "Multiple Choices"; + case 303: + return "See Other"; + case 304: + return "Not Modified"; + case 305: + return "Use Proxy"; + case 306: + return "Switch Proxy"; + case 307: + return "Temporary Redirect"; + case 308: + return "Permanent Redirect"; + case 402: + return "Payment Required"; + case 405: + return "Method Not Allowed"; + case 406: + return "Not Acceptable"; + case 407: + return "Proxy Authentication Required"; + case 408: + return "Request Timeout"; + case 409: + return "Conflict"; + case 410: + return "Gone"; + case 411: + return "Length Required"; + case 412: + return "Precondition Failed"; + case 413: + return "Payload Too Large"; + case 414: + return "URI Too Long"; + case 415: + return "Unsupported Media Type"; + case 417: + return "Expectation Failed"; + case 422: + return "Unprocessable Entity"; + case 423: + return "Locked"; + case 424: + return "Failed Dependency"; + case 426: + return "Upgrade Required"; + case 428: + return "Precondition Required"; + case 429: + return "Too Many Requests"; + case 431: + return "Request Header Fields Too Large"; + case 451: + return "Unavailable For Legal Reasons"; + case 501: + return "Not Implemented"; + case 504: + return "Gateway Timeout"; + case 505: + return "HTTP Version Not Supported"; + case 506: + return "Variant Also Negotiates"; + case 507: + return "Insufficient Storage"; + case 508: + return "Loop Detected"; + case 510: + return "Not Extended"; + case 511: + return "Network Authentication Required"; +#endif /* MG_ENABLE_EXTRA_ERRORS_DESC */ + + default: + return "OK"; + } +} + +void mg_send_response_line_s(struct mg_connection *nc, int status_code, + const struct mg_str extra_headers) { + mg_printf(nc, "HTTP/1.1 %d %s\r\nServer: %s\r\n", status_code, + mg_status_message(status_code), mg_version_header); + if (extra_headers.len > 0) { + mg_printf(nc, "%.*s\r\n", (int) extra_headers.len, extra_headers.p); + } +} + +void mg_send_response_line(struct mg_connection *nc, int status_code, + const char *extra_headers) { + mg_send_response_line_s(nc, status_code, mg_mk_str(extra_headers)); +} + +void mg_http_send_redirect(struct mg_connection *nc, int status_code, + const struct mg_str location, + const struct mg_str extra_headers) { + char bbody[100], *pbody = bbody; + int bl = mg_asprintf(&pbody, sizeof(bbody), + "

Moved here.\r\n", + (int) location.len, location.p); + char bhead[150], *phead = bhead; + mg_asprintf(&phead, sizeof(bhead), + "Location: %.*s\r\n" + "Content-Type: text/html\r\n" + "Content-Length: %d\r\n" + "Cache-Control: no-cache\r\n" + "%.*s%s", + (int) location.len, location.p, bl, (int) extra_headers.len, + extra_headers.p, (extra_headers.len > 0 ? "\r\n" : "")); + mg_send_response_line(nc, status_code, phead); + if (phead != bhead) MG_FREE(phead); + mg_send(nc, pbody, bl); + if (pbody != bbody) MG_FREE(pbody); +} + +void mg_send_head(struct mg_connection *c, int status_code, + int64_t content_length, const char *extra_headers) { + mg_send_response_line(c, status_code, extra_headers); + if (content_length < 0) { + mg_printf(c, "%s", "Transfer-Encoding: chunked\r\n"); + } else { + mg_printf(c, "Content-Length: %" INT64_FMT "\r\n", content_length); + } + mg_send(c, "\r\n", 2); +} + +void mg_http_send_error(struct mg_connection *nc, int code, + const char *reason) { + if (!reason) reason = mg_status_message(code); + LOG(LL_DEBUG, ("%p %d %s", nc, code, reason)); + mg_send_head(nc, code, strlen(reason), + "Content-Type: text/plain\r\nConnection: close"); + mg_send(nc, reason, strlen(reason)); + nc->flags |= MG_F_SEND_AND_CLOSE; +} + +#if MG_ENABLE_FILESYSTEM +static void mg_http_construct_etag(char *buf, size_t buf_len, + const cs_stat_t *st) { + snprintf(buf, buf_len, "\"%lx.%" INT64_FMT "\"", (unsigned long) st->st_mtime, + (int64_t) st->st_size); +} + +#ifndef WINCE +static void mg_gmt_time_string(char *buf, size_t buf_len, time_t *t) { + strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", gmtime(t)); +} +#else +/* Look wince_lib.c for WindowsCE implementation */ +static void mg_gmt_time_string(char *buf, size_t buf_len, time_t *t); +#endif + +static int mg_http_parse_range_header(const struct mg_str *header, int64_t *a, + int64_t *b) { + /* + * There is no snscanf. Headers are not guaranteed to be NUL-terminated, + * so we have this. Ugh. + */ + int result; + char *p = (char *) MG_MALLOC(header->len + 1); + if (p == NULL) return 0; + memcpy(p, header->p, header->len); + p[header->len] = '\0'; + result = sscanf(p, "bytes=%" INT64_FMT "-%" INT64_FMT, a, b); + MG_FREE(p); + return result; +} + +void mg_http_serve_file(struct mg_connection *nc, struct http_message *hm, + const char *path, const struct mg_str mime_type, + const struct mg_str extra_headers) { + struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); + cs_stat_t st; + LOG(LL_DEBUG, ("%p [%s] %.*s", nc, path, (int) mime_type.len, mime_type.p)); + if (mg_stat(path, &st) != 0 || (pd->file.fp = mg_fopen(path, "rb")) == NULL) { + int code, err = mg_get_errno(); + switch (err) { + case EACCES: + code = 403; + break; + case ENOENT: + code = 404; + break; + default: + code = 500; + }; + mg_http_send_error(nc, code, "Open failed"); + } else { + char etag[50], current_time[50], last_modified[50], range[70]; + time_t t = (time_t) mg_time(); + int64_t r1 = 0, r2 = 0, cl = st.st_size; + struct mg_str *range_hdr = mg_get_http_header(hm, "Range"); + int n, status_code = 200; + + /* Handle Range header */ + range[0] = '\0'; + if (range_hdr != NULL && + (n = mg_http_parse_range_header(range_hdr, &r1, &r2)) > 0 && r1 >= 0 && + r2 >= 0) { + /* If range is specified like "400-", set second limit to content len */ + if (n == 1) { + r2 = cl - 1; + } + if (r1 > r2 || r2 >= cl) { + status_code = 416; + cl = 0; + snprintf(range, sizeof(range), + "Content-Range: bytes */%" INT64_FMT "\r\n", + (int64_t) st.st_size); + } else { + status_code = 206; + cl = r2 - r1 + 1; + snprintf(range, sizeof(range), "Content-Range: bytes %" INT64_FMT + "-%" INT64_FMT "/%" INT64_FMT "\r\n", + r1, r1 + cl - 1, (int64_t) st.st_size); +#if _FILE_OFFSET_BITS == 64 || _POSIX_C_SOURCE >= 200112L || \ + _XOPEN_SOURCE >= 600 + fseeko(pd->file.fp, r1, SEEK_SET); +#else + fseek(pd->file.fp, (long) r1, SEEK_SET); +#endif + } + } + +#if !MG_DISABLE_HTTP_KEEP_ALIVE + { + struct mg_str *conn_hdr = mg_get_http_header(hm, "Connection"); + if (conn_hdr != NULL) { + pd->file.keepalive = (mg_vcasecmp(conn_hdr, "keep-alive") == 0); + } else { + pd->file.keepalive = (mg_vcmp(&hm->proto, "HTTP/1.1") == 0); + } + } +#endif + + mg_http_construct_etag(etag, sizeof(etag), &st); + mg_gmt_time_string(current_time, sizeof(current_time), &t); + mg_gmt_time_string(last_modified, sizeof(last_modified), &st.st_mtime); + /* + * Content length casted to size_t because: + * 1) that's the maximum buffer size anyway + * 2) ESP8266 RTOS SDK newlib vprintf cannot contain a 64bit arg at non-last + * position + * TODO(mkm): fix ESP8266 RTOS SDK + */ + mg_send_response_line_s(nc, status_code, extra_headers); + mg_printf(nc, + "Date: %s\r\n" + "Last-Modified: %s\r\n" + "Accept-Ranges: bytes\r\n" + "Content-Type: %.*s\r\n" + "Connection: %s\r\n" + "Content-Length: %" SIZE_T_FMT + "\r\n" + "%sEtag: %s\r\n\r\n", + current_time, last_modified, (int) mime_type.len, mime_type.p, + (pd->file.keepalive ? "keep-alive" : "close"), (size_t) cl, range, + etag); + + pd->file.cl = cl; + pd->file.type = DATA_FILE; + mg_http_transfer_file_data(nc); + } +} + +static void mg_http_serve_file2(struct mg_connection *nc, const char *path, + struct http_message *hm, + struct mg_serve_http_opts *opts) { +#if MG_ENABLE_HTTP_SSI + if (mg_match_prefix(opts->ssi_pattern, strlen(opts->ssi_pattern), path) > 0) { + mg_handle_ssi_request(nc, hm, path, opts); + return; + } +#endif + mg_http_serve_file(nc, hm, path, mg_get_mime_type(path, "text/plain", opts), + mg_mk_str(opts->extra_headers)); +} + +#endif + +int mg_url_decode(const char *src, int src_len, char *dst, int dst_len, + int is_form_url_encoded) { + int i, j, a, b; +#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W') + + for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) { + if (src[i] == '%') { + if (i < src_len - 2 && isxdigit(*(const unsigned char *) (src + i + 1)) && + isxdigit(*(const unsigned char *) (src + i + 2))) { + a = tolower(*(const unsigned char *) (src + i + 1)); + b = tolower(*(const unsigned char *) (src + i + 2)); + dst[j] = (char) ((HEXTOI(a) << 4) | HEXTOI(b)); + i += 2; + } else { + return -1; + } + } else if (is_form_url_encoded && src[i] == '+') { + dst[j] = ' '; + } else { + dst[j] = src[i]; + } + } + + dst[j] = '\0'; /* Null-terminate the destination */ + + return i >= src_len ? j : -1; +} + +int mg_get_http_var(const struct mg_str *buf, const char *name, char *dst, + size_t dst_len) { + const char *p, *e, *s; + size_t name_len; + int len; + + /* + * According to the documentation function returns negative + * value in case of error. For debug purposes it returns: + * -1 - src is wrong (NUUL) + * -2 - dst is wrong (NULL) + * -3 - failed to decode url or dst is to small + * -4 - name does not exist + */ + if (dst == NULL || dst_len == 0) { + len = -2; + } else if (buf->p == NULL || name == NULL || buf->len == 0) { + len = -1; + dst[0] = '\0'; + } else { + name_len = strlen(name); + e = buf->p + buf->len; + len = -4; + dst[0] = '\0'; + + for (p = buf->p; p + name_len < e; p++) { + if ((p == buf->p || p[-1] == '&') && p[name_len] == '=' && + !mg_ncasecmp(name, p, name_len)) { + p += name_len + 1; + s = (const char *) memchr(p, '&', (size_t)(e - p)); + if (s == NULL) { + s = e; + } + len = mg_url_decode(p, (size_t)(s - p), dst, dst_len, 1); + /* -1 means: failed to decode or dst is too small */ + if (len == -1) { + len = -3; + } + break; + } + } + } + + return len; +} + +void mg_send_http_chunk(struct mg_connection *nc, const char *buf, size_t len) { + char chunk_size[50]; + int n; + + n = snprintf(chunk_size, sizeof(chunk_size), "%lX\r\n", (unsigned long) len); + mg_send(nc, chunk_size, n); + mg_send(nc, buf, len); + mg_send(nc, "\r\n", 2); +} + +void mg_printf_http_chunk(struct mg_connection *nc, const char *fmt, ...) { + char mem[MG_VPRINTF_BUFFER_SIZE], *buf = mem; + int len; + va_list ap; + + va_start(ap, fmt); + len = mg_avprintf(&buf, sizeof(mem), fmt, ap); + va_end(ap); + + if (len >= 0) { + mg_send_http_chunk(nc, buf, len); + } + + /* LCOV_EXCL_START */ + if (buf != mem && buf != NULL) { + MG_FREE(buf); + } + /* LCOV_EXCL_STOP */ +} + +void mg_printf_html_escape(struct mg_connection *nc, const char *fmt, ...) { + char mem[MG_VPRINTF_BUFFER_SIZE], *buf = mem; + int i, j, len; + va_list ap; + + va_start(ap, fmt); + len = mg_avprintf(&buf, sizeof(mem), fmt, ap); + va_end(ap); + + if (len >= 0) { + for (i = j = 0; i < len; i++) { + if (buf[i] == '<' || buf[i] == '>') { + mg_send(nc, buf + j, i - j); + mg_send(nc, buf[i] == '<' ? "<" : ">", 4); + j = i + 1; + } + } + mg_send(nc, buf + j, i - j); + } + + /* LCOV_EXCL_START */ + if (buf != mem && buf != NULL) { + MG_FREE(buf); + } + /* LCOV_EXCL_STOP */ +} + +static void mg_http_parse_header_internal(struct mg_str *hdr, + const char *var_name, + struct altbuf *ab) { + int ch = ' ', ch1 = ',', n = strlen(var_name); + const char *p, *end = hdr ? hdr->p + hdr->len : NULL, *s = NULL; + + /* Find where variable starts */ + for (s = hdr->p; s != NULL && s + n < end; s++) { + if ((s == hdr->p || s[-1] == ch || s[-1] == ch1 || s[-1] == ';') && + s[n] == '=' && !strncmp(s, var_name, n)) + break; + } + + if (s != NULL && &s[n + 1] < end) { + s += n + 1; + if (*s == '"' || *s == '\'') { + ch = ch1 = *s++; + } + p = s; + while (p < end && p[0] != ch && p[0] != ch1) { + if (ch != ' ' && p[0] == '\\' && p[1] == ch) p++; + altbuf_append(ab, *p++); + } + + if (ch != ' ' && *p != ch) { + altbuf_reset(ab); + } + } + + /* If there is some data, append a NUL. */ + if (ab->len > 0) { + altbuf_append(ab, '\0'); + } +} + +int mg_http_parse_header2(struct mg_str *hdr, const char *var_name, char **buf, + size_t buf_size) { + struct altbuf ab; + altbuf_init(&ab, *buf, buf_size); + if (hdr == NULL) return 0; + if (*buf != NULL && buf_size > 0) *buf[0] = '\0'; + + mg_http_parse_header_internal(hdr, var_name, &ab); + + /* + * Get a (trimmed) buffer, and return a len without a NUL byte which might + * have been added. + */ + *buf = altbuf_get_buf(&ab, 1 /* trim */); + return ab.len > 0 ? ab.len - 1 : 0; +} + +int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf, + size_t buf_size) { + char *buf2 = buf; + + int len = mg_http_parse_header2(hdr, var_name, &buf2, buf_size); + + if (buf2 != buf) { + /* Buffer was not enough and was reallocated: free it and just return 0 */ + MG_FREE(buf2); + return 0; + } + + return len; +} + +int mg_get_http_basic_auth(struct http_message *hm, char *user, size_t user_len, + char *pass, size_t pass_len) { + struct mg_str *hdr = mg_get_http_header(hm, "Authorization"); + if (hdr == NULL) return -1; + return mg_parse_http_basic_auth(hdr, user, user_len, pass, pass_len); +} + +int mg_parse_http_basic_auth(struct mg_str *hdr, char *user, size_t user_len, + char *pass, size_t pass_len) { + char *buf = NULL; + char fmt[64]; + int res = 0; + + if (mg_strncmp(*hdr, mg_mk_str("Basic "), 6) != 0) return -1; + + buf = (char *) MG_MALLOC(hdr->len); + cs_base64_decode((unsigned char *) hdr->p + 6, hdr->len, buf, NULL); + + /* e.g. "%123[^:]:%321[^\n]" */ + snprintf(fmt, sizeof(fmt), "%%%" SIZE_T_FMT "[^:]:%%%" SIZE_T_FMT "[^\n]", + user_len - 1, pass_len - 1); + if (sscanf(buf, fmt, user, pass) == 0) { + res = -1; + } + + MG_FREE(buf); + return res; +} + +#if MG_ENABLE_FILESYSTEM +static int mg_is_file_hidden(const char *path, + const struct mg_serve_http_opts *opts, + int exclude_specials) { + const char *p1 = opts->per_directory_auth_file; + const char *p2 = opts->hidden_file_pattern; + + /* Strip directory path from the file name */ + const char *pdir = strrchr(path, DIRSEP); + if (pdir != NULL) { + path = pdir + 1; + } + + return (exclude_specials && (!strcmp(path, ".") || !strcmp(path, ".."))) || + (p1 != NULL && mg_match_prefix(p1, strlen(p1), path) == strlen(p1)) || + (p2 != NULL && mg_match_prefix(p2, strlen(p2), path) > 0); +} + +#if !MG_DISABLE_HTTP_DIGEST_AUTH + +#ifndef MG_EXT_MD5 +void mg_hash_md5_v(size_t num_msgs, const uint8_t *msgs[], + const size_t *msg_lens, uint8_t *digest) { + size_t i; + cs_md5_ctx md5_ctx; + cs_md5_init(&md5_ctx); + for (i = 0; i < num_msgs; i++) { + cs_md5_update(&md5_ctx, msgs[i], msg_lens[i]); + } + cs_md5_final(digest, &md5_ctx); +} +#else +extern void mg_hash_md5_v(size_t num_msgs, const uint8_t *msgs[], + const size_t *msg_lens, uint8_t *digest); +#endif + +void cs_md5(char buf[33], ...) { + unsigned char hash[16]; + const uint8_t *msgs[20], *p; + size_t msg_lens[20]; + size_t num_msgs = 0; + va_list ap; + + va_start(ap, buf); + while ((p = va_arg(ap, const unsigned char *) ) != NULL) { + msgs[num_msgs] = p; + msg_lens[num_msgs] = va_arg(ap, size_t); + num_msgs++; + } + va_end(ap); + + mg_hash_md5_v(num_msgs, msgs, msg_lens, hash); + cs_to_hex(buf, hash, sizeof(hash)); +} + +static void mg_mkmd5resp(const char *method, size_t method_len, const char *uri, + size_t uri_len, const char *ha1, size_t ha1_len, + const char *nonce, size_t nonce_len, const char *nc, + size_t nc_len, const char *cnonce, size_t cnonce_len, + const char *qop, size_t qop_len, char *resp) { + static const char colon[] = ":"; + static const size_t one = 1; + char ha2[33]; + cs_md5(ha2, method, method_len, colon, one, uri, uri_len, NULL); + cs_md5(resp, ha1, ha1_len, colon, one, nonce, nonce_len, colon, one, nc, + nc_len, colon, one, cnonce, cnonce_len, colon, one, qop, qop_len, + colon, one, ha2, sizeof(ha2) - 1, NULL); +} + +int mg_http_create_digest_auth_header(char *buf, size_t buf_len, + const char *method, const char *uri, + const char *auth_domain, const char *user, + const char *passwd, const char *nonce) { + static const char colon[] = ":", qop[] = "auth"; + static const size_t one = 1; + char ha1[33], resp[33], cnonce[40]; + + snprintf(cnonce, sizeof(cnonce), "%lx", (unsigned long) mg_time()); + cs_md5(ha1, user, (size_t) strlen(user), colon, one, auth_domain, + (size_t) strlen(auth_domain), colon, one, passwd, + (size_t) strlen(passwd), NULL); + mg_mkmd5resp(method, strlen(method), uri, strlen(uri), ha1, sizeof(ha1) - 1, + nonce, strlen(nonce), "1", one, cnonce, strlen(cnonce), qop, + sizeof(qop) - 1, resp); + return snprintf(buf, buf_len, + "Authorization: Digest username=\"%s\"," + "realm=\"%s\",uri=\"%s\",qop=%s,nc=1,cnonce=%s," + "nonce=%s,response=%s\r\n", + user, auth_domain, uri, qop, cnonce, nonce, resp); +} + +/* + * Check for authentication timeout. + * Clients send time stamp encoded in nonce. Make sure it is not too old, + * to prevent replay attacks. + * Assumption: nonce is a hexadecimal number of seconds since 1970. + */ +static int mg_check_nonce(const char *nonce) { + unsigned long now = (unsigned long) mg_time(); + unsigned long val = (unsigned long) strtoul(nonce, NULL, 16); + return (now >= val) && (now - val < 60 * 60); +} + +int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain, + FILE *fp) { + int ret = 0; + struct mg_str *hdr; + char username_buf[50], cnonce_buf[64], response_buf[40], uri_buf[200], + qop_buf[20], nc_buf[20], nonce_buf[16]; + + char *username = username_buf, *cnonce = cnonce_buf, *response = response_buf, + *uri = uri_buf, *qop = qop_buf, *nc = nc_buf, *nonce = nonce_buf; + + /* Parse "Authorization:" header, fail fast on parse error */ + if (hm == NULL || fp == NULL || + (hdr = mg_get_http_header(hm, "Authorization")) == NULL || + mg_http_parse_header2(hdr, "username", &username, sizeof(username_buf)) == + 0 || + mg_http_parse_header2(hdr, "cnonce", &cnonce, sizeof(cnonce_buf)) == 0 || + mg_http_parse_header2(hdr, "response", &response, sizeof(response_buf)) == + 0 || + mg_http_parse_header2(hdr, "uri", &uri, sizeof(uri_buf)) == 0 || + mg_http_parse_header2(hdr, "qop", &qop, sizeof(qop_buf)) == 0 || + mg_http_parse_header2(hdr, "nc", &nc, sizeof(nc_buf)) == 0 || + mg_http_parse_header2(hdr, "nonce", &nonce, sizeof(nonce_buf)) == 0 || + mg_check_nonce(nonce) == 0) { + ret = 0; + goto clean; + } + + /* NOTE(lsm): due to a bug in MSIE, we do not compare URIs */ + + ret = mg_check_digest_auth( + hm->method, + mg_mk_str_n( + hm->uri.p, + hm->uri.len + (hm->query_string.len ? hm->query_string.len + 1 : 0)), + mg_mk_str(username), mg_mk_str(cnonce), mg_mk_str(response), + mg_mk_str(qop), mg_mk_str(nc), mg_mk_str(nonce), mg_mk_str(auth_domain), + fp); + +clean: + if (username != username_buf) MG_FREE(username); + if (cnonce != cnonce_buf) MG_FREE(cnonce); + if (response != response_buf) MG_FREE(response); + if (uri != uri_buf) MG_FREE(uri); + if (qop != qop_buf) MG_FREE(qop); + if (nc != nc_buf) MG_FREE(nc); + if (nonce != nonce_buf) MG_FREE(nonce); + + return ret; +} + +int mg_check_digest_auth(struct mg_str method, struct mg_str uri, + struct mg_str username, struct mg_str cnonce, + struct mg_str response, struct mg_str qop, + struct mg_str nc, struct mg_str nonce, + struct mg_str auth_domain, FILE *fp) { + char buf[128], f_user[sizeof(buf)], f_ha1[sizeof(buf)], f_domain[sizeof(buf)]; + char expected_response[33]; + + /* + * Read passwords file line by line. If should have htdigest format, + * i.e. each line should be a colon-separated sequence: + * USER_NAME:DOMAIN_NAME:HA1_HASH_OF_USER_DOMAIN_AND_PASSWORD + */ + while (fgets(buf, sizeof(buf), fp) != NULL) { + if (sscanf(buf, "%[^:]:%[^:]:%s", f_user, f_domain, f_ha1) == 3 && + mg_vcmp(&username, f_user) == 0 && + mg_vcmp(&auth_domain, f_domain) == 0) { + /* Username and domain matched, check the password */ + mg_mkmd5resp(method.p, method.len, uri.p, uri.len, f_ha1, strlen(f_ha1), + nonce.p, nonce.len, nc.p, nc.len, cnonce.p, cnonce.len, + qop.p, qop.len, expected_response); + LOG(LL_DEBUG, + ("%.*s %s %.*s %s", (int) username.len, username.p, f_domain, + (int) response.len, response.p, expected_response)); + return mg_ncasecmp(response.p, expected_response, response.len) == 0; + } + } + + /* None of the entries in the passwords file matched - return failure */ + return 0; +} + +int mg_http_is_authorized(struct http_message *hm, struct mg_str path, + const char *domain, const char *passwords_file, + int flags) { + char buf[MG_MAX_PATH]; + const char *p; + FILE *fp; + int authorized = 1; + + if (domain != NULL && passwords_file != NULL) { + if (flags & MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE) { + fp = mg_fopen(passwords_file, "r"); + } else if (flags & MG_AUTH_FLAG_IS_DIRECTORY) { + snprintf(buf, sizeof(buf), "%.*s%c%s", (int) path.len, path.p, DIRSEP, + passwords_file); + fp = mg_fopen(buf, "r"); + } else { + p = strrchr(path.p, DIRSEP); + if (p == NULL) p = path.p; + snprintf(buf, sizeof(buf), "%.*s%c%s", (int) (p - path.p), path.p, DIRSEP, + passwords_file); + fp = mg_fopen(buf, "r"); + } + + if (fp != NULL) { + authorized = mg_http_check_digest_auth(hm, domain, fp); + fclose(fp); + } else if (!(flags & MG_AUTH_FLAG_ALLOW_MISSING_FILE)) { + authorized = 0; + } + } + + LOG(LL_DEBUG, ("%.*s %s %x %d", (int) path.len, path.p, + passwords_file ? passwords_file : "", flags, authorized)); + return authorized; +} +#else +int mg_http_is_authorized(struct http_message *hm, const struct mg_str path, + const char *domain, const char *passwords_file, + int flags) { + (void) hm; + (void) path; + (void) domain; + (void) passwords_file; + (void) flags; + return 1; +} +#endif + +#if MG_ENABLE_DIRECTORY_LISTING +static void mg_escape(const char *src, char *dst, size_t dst_len) { + size_t n = 0; + while (*src != '\0' && n + 5 < dst_len) { + unsigned char ch = *(unsigned char *) src++; + if (ch == '<') { + n += snprintf(dst + n, dst_len - n, "%s", "<"); + } else { + dst[n++] = ch; + } + } + dst[n] = '\0'; +} + +static void mg_print_dir_entry(struct mg_connection *nc, const char *file_name, + cs_stat_t *stp) { + char size[64], mod[64], path[MG_MAX_PATH]; + int64_t fsize = stp->st_size; + int is_dir = S_ISDIR(stp->st_mode); + const char *slash = is_dir ? "/" : ""; + struct mg_str href; + + if (is_dir) { + snprintf(size, sizeof(size), "%s", "[DIRECTORY]"); + } else { + /* + * We use (double) cast below because MSVC 6 compiler cannot + * convert unsigned __int64 to double. + */ + if (fsize < 1024) { + snprintf(size, sizeof(size), "%d", (int) fsize); + } else if (fsize < 0x100000) { + snprintf(size, sizeof(size), "%.1fk", (double) fsize / 1024.0); + } else if (fsize < 0x40000000) { + snprintf(size, sizeof(size), "%.1fM", (double) fsize / 1048576); + } else { + snprintf(size, sizeof(size), "%.1fG", (double) fsize / 1073741824); + } + } + strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", localtime(&stp->st_mtime)); + mg_escape(file_name, path, sizeof(path)); + href = mg_url_encode(mg_mk_str(file_name)); + mg_printf_http_chunk(nc, + "%s%s" + "%s%s\n", + href.p, slash, path, slash, mod, is_dir ? -1 : fsize, + size); + free((void *) href.p); +} + +static void mg_scan_directory(struct mg_connection *nc, const char *dir, + const struct mg_serve_http_opts *opts, + void (*func)(struct mg_connection *, const char *, + cs_stat_t *)) { + char path[MG_MAX_PATH]; + cs_stat_t st; + struct dirent *dp; + DIR *dirp; + + LOG(LL_DEBUG, ("%p [%s]", nc, dir)); + if ((dirp = (opendir(dir))) != NULL) { + while ((dp = readdir(dirp)) != NULL) { + /* Do not show current dir and hidden files */ + if (mg_is_file_hidden((const char *) dp->d_name, opts, 1)) { + continue; + } + snprintf(path, sizeof(path), "%s/%s", dir, dp->d_name); + if (mg_stat(path, &st) == 0) { + func(nc, (const char *) dp->d_name, &st); + } + } + closedir(dirp); + } else { + LOG(LL_DEBUG, ("%p opendir(%s) -> %d", nc, dir, mg_get_errno())); + } +} + +static void mg_send_directory_listing(struct mg_connection *nc, const char *dir, + struct http_message *hm, + struct mg_serve_http_opts *opts) { + static const char *sort_js_code = + ""; + + mg_send_response_line(nc, 200, opts->extra_headers); + mg_printf(nc, "%s: %s\r\n%s: %s\r\n\r\n", "Transfer-Encoding", "chunked", + "Content-Type", "text/html; charset=utf-8"); + + mg_printf_http_chunk( + nc, + "Index of %.*s%s%s" + "\n" + "

Index of %.*s

\n" + "" + "\n" + "\n" + "", + (int) hm->uri.len, hm->uri.p, sort_js_code, sort_js_code2, + (int) hm->uri.len, hm->uri.p); + mg_scan_directory(nc, dir, opts, mg_print_dir_entry); + mg_printf_http_chunk(nc, + "\n" + "
Name" + "Modified" + "Size


\n" + "
%s
\n" + "", + mg_version_header); + mg_send_http_chunk(nc, "", 0); + /* TODO(rojer): Remove when cesanta/dev/issues/197 is fixed. */ + nc->flags |= MG_F_SEND_AND_CLOSE; +} +#endif /* MG_ENABLE_DIRECTORY_LISTING */ + +/* + * Given a directory path, find one of the files specified in the + * comma-separated list of index files `list`. + * First found index file wins. If an index file is found, then gets + * appended to the `path`, stat-ed, and result of `stat()` passed to `stp`. + * If index file is not found, then `path` and `stp` remain unchanged. + */ +MG_INTERNAL void mg_find_index_file(const char *path, const char *list, + char **index_file, cs_stat_t *stp) { + struct mg_str vec; + size_t path_len = strlen(path); + int found = 0; + *index_file = NULL; + + /* Traverse index files list. For each entry, append it to the given */ + /* path and see if the file exists. If it exists, break the loop */ + while ((list = mg_next_comma_list_entry(list, &vec, NULL)) != NULL) { + cs_stat_t st; + size_t len = path_len + 1 + vec.len + 1; + *index_file = (char *) MG_REALLOC(*index_file, len); + if (*index_file == NULL) break; + snprintf(*index_file, len, "%s%c%.*s", path, DIRSEP, (int) vec.len, vec.p); + + /* Does it exist? Is it a file? */ + if (mg_stat(*index_file, &st) == 0 && S_ISREG(st.st_mode)) { + /* Yes it does, break the loop */ + *stp = st; + found = 1; + break; + } + } + if (!found) { + MG_FREE(*index_file); + *index_file = NULL; + } + LOG(LL_DEBUG, ("[%s] [%s]", path, (*index_file ? *index_file : ""))); +} + +#if MG_ENABLE_HTTP_URL_REWRITES +static int mg_http_send_port_based_redirect( + struct mg_connection *c, struct http_message *hm, + const struct mg_serve_http_opts *opts) { + const char *rewrites = opts->url_rewrites; + struct mg_str a, b; + char local_port[20] = {'%'}; + + mg_conn_addr_to_str(c, local_port + 1, sizeof(local_port) - 1, + MG_SOCK_STRINGIFY_PORT); + + while ((rewrites = mg_next_comma_list_entry(rewrites, &a, &b)) != NULL) { + if (mg_vcmp(&a, local_port) == 0) { + mg_send_response_line(c, 301, NULL); + mg_printf(c, "Content-Length: 0\r\nLocation: %.*s%.*s\r\n\r\n", + (int) b.len, b.p, (int) (hm->proto.p - hm->uri.p - 1), + hm->uri.p); + return 1; + } + } + + return 0; +} + +static void mg_reverse_proxy_handler(struct mg_connection *nc, int ev, + void *ev_data MG_UD_ARG(void *user_data)) { + struct http_message *hm = (struct http_message *) ev_data; + struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); + + if (pd == NULL || pd->reverse_proxy_data.linked_conn == NULL) { + DBG(("%p: upstream closed", nc)); + return; + } + + switch (ev) { + case MG_EV_CONNECT: + if (*(int *) ev_data != 0) { + mg_http_send_error(pd->reverse_proxy_data.linked_conn, 502, NULL); + } + break; + /* TODO(mkm): handle streaming */ + case MG_EV_HTTP_REPLY: + mg_send(pd->reverse_proxy_data.linked_conn, hm->message.p, + hm->message.len); + pd->reverse_proxy_data.linked_conn->flags |= MG_F_SEND_AND_CLOSE; + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + break; + case MG_EV_CLOSE: + pd->reverse_proxy_data.linked_conn->flags |= MG_F_SEND_AND_CLOSE; + break; + } + +#if MG_ENABLE_CALLBACK_USERDATA + (void) user_data; +#endif +} + +void mg_http_reverse_proxy(struct mg_connection *nc, + const struct http_message *hm, struct mg_str mount, + struct mg_str upstream) { + struct mg_connection *be; + char burl[256], *purl = burl; + int i; + const char *error; + struct mg_connect_opts opts; + struct mg_str path = MG_NULL_STR, user_info = MG_NULL_STR, host = MG_NULL_STR; + memset(&opts, 0, sizeof(opts)); + opts.error_string = &error; + + mg_asprintf(&purl, sizeof(burl), "%.*s%.*s", (int) upstream.len, upstream.p, + (int) (hm->uri.len - mount.len), hm->uri.p + mount.len); + + be = mg_connect_http_base(nc->mgr, MG_CB(mg_reverse_proxy_handler, NULL), + opts, "http", NULL, "https", NULL, purl, &path, + &user_info, &host); + LOG(LL_DEBUG, ("Proxying %.*s to %s (rule: %.*s)", (int) hm->uri.len, + hm->uri.p, purl, (int) mount.len, mount.p)); + + if (be == NULL) { + LOG(LL_ERROR, ("Error connecting to %s: %s", purl, error)); + mg_http_send_error(nc, 502, NULL); + goto cleanup; + } + + /* link connections to each other, they must live and die together */ + mg_http_get_proto_data(be)->reverse_proxy_data.linked_conn = nc; + mg_http_get_proto_data(nc)->reverse_proxy_data.linked_conn = be; + + /* send request upstream */ + mg_printf(be, "%.*s %.*s HTTP/1.1\r\n", (int) hm->method.len, hm->method.p, + (int) path.len, path.p); + + mg_printf(be, "Host: %.*s\r\n", (int) host.len, host.p); + for (i = 0; i < MG_MAX_HTTP_HEADERS && hm->header_names[i].len > 0; i++) { + struct mg_str hn = hm->header_names[i]; + struct mg_str hv = hm->header_values[i]; + + /* we rewrite the host header */ + if (mg_vcasecmp(&hn, "Host") == 0) continue; + /* + * Don't pass chunked transfer encoding to the client because hm->body is + * already dechunked when we arrive here. + */ + if (mg_vcasecmp(&hn, "Transfer-encoding") == 0 && + mg_vcasecmp(&hv, "chunked") == 0) { + mg_printf(be, "Content-Length: %" SIZE_T_FMT "\r\n", hm->body.len); + continue; + } + /* We don't support proxying Expect: 100-continue. */ + if (mg_vcasecmp(&hn, "Expect") == 0 && + mg_vcasecmp(&hv, "100-continue") == 0) { + continue; + } + + mg_printf(be, "%.*s: %.*s\r\n", (int) hn.len, hn.p, (int) hv.len, hv.p); + } + + mg_send(be, "\r\n", 2); + mg_send(be, hm->body.p, hm->body.len); + +cleanup: + if (purl != burl) MG_FREE(purl); +} + +static int mg_http_handle_forwarding(struct mg_connection *nc, + struct http_message *hm, + const struct mg_serve_http_opts *opts) { + const char *rewrites = opts->url_rewrites; + struct mg_str a, b; + struct mg_str p1 = MG_MK_STR("http://"), p2 = MG_MK_STR("https://"); + + while ((rewrites = mg_next_comma_list_entry(rewrites, &a, &b)) != NULL) { + if (mg_strncmp(a, hm->uri, a.len) == 0) { + if (mg_strncmp(b, p1, p1.len) == 0 || mg_strncmp(b, p2, p2.len) == 0) { + mg_http_reverse_proxy(nc, hm, a, b); + return 1; + } + } + } + + return 0; +} +#endif /* MG_ENABLE_FILESYSTEM */ + +MG_INTERNAL int mg_uri_to_local_path(struct http_message *hm, + const struct mg_serve_http_opts *opts, + char **local_path, + struct mg_str *remainder) { + int ok = 1; + const char *cp = hm->uri.p, *cp_end = hm->uri.p + hm->uri.len; + struct mg_str root = {NULL, 0}; + const char *file_uri_start = cp; + *local_path = NULL; + remainder->p = NULL; + remainder->len = 0; + + { /* 1. Determine which root to use. */ + +#if MG_ENABLE_HTTP_URL_REWRITES + const char *rewrites = opts->url_rewrites; +#else + const char *rewrites = ""; +#endif + struct mg_str *hh = mg_get_http_header(hm, "Host"); + struct mg_str a, b; + /* Check rewrites first. */ + while ((rewrites = mg_next_comma_list_entry(rewrites, &a, &b)) != NULL) { + if (a.len > 1 && a.p[0] == '@') { + /* Host rewrite. */ + if (hh != NULL && hh->len == a.len - 1 && + mg_ncasecmp(a.p + 1, hh->p, a.len - 1) == 0) { + root = b; + break; + } + } else { + /* Regular rewrite, URI=directory */ + size_t match_len = mg_match_prefix_n(a, hm->uri); + if (match_len > 0) { + file_uri_start = hm->uri.p + match_len; + if (*file_uri_start == '/' || file_uri_start == cp_end) { + /* Match ended at component boundary, ok. */ + } else if (*(file_uri_start - 1) == '/') { + /* Pattern ends with '/', backtrack. */ + file_uri_start--; + } else { + /* No match: must fall on the component boundary. */ + continue; + } + root = b; + break; + } + } + } + /* If no rewrite rules matched, use DAV or regular document root. */ + if (root.p == NULL) { +#if MG_ENABLE_HTTP_WEBDAV + if (opts->dav_document_root != NULL && mg_is_dav_request(&hm->method)) { + root.p = opts->dav_document_root; + root.len = strlen(opts->dav_document_root); + } else +#endif + { + root.p = opts->document_root; + root.len = strlen(opts->document_root); + } + } + assert(root.p != NULL && root.len > 0); + } + + { /* 2. Find where in the canonical URI path the local path ends. */ + const char *u = file_uri_start + 1; + char *lp = (char *) MG_MALLOC(root.len + hm->uri.len + 1); + char *lp_end = lp + root.len + hm->uri.len + 1; + char *p = lp, *ps; + int exists = 1; + if (lp == NULL) { + ok = 0; + goto out; + } + memcpy(p, root.p, root.len); + p += root.len; + if (*(p - 1) == DIRSEP) p--; + *p = '\0'; + ps = p; + + /* Chop off URI path components one by one and build local path. */ + while (u <= cp_end) { + const char *next = u; + struct mg_str component; + if (exists) { + cs_stat_t st; + exists = (mg_stat(lp, &st) == 0); + if (exists && S_ISREG(st.st_mode)) { + /* We found the terminal, the rest of the URI (if any) is path_info. + */ + if (*(u - 1) == '/') u--; + break; + } + } + if (u >= cp_end) break; + parse_uri_component((const char **) &next, cp_end, "/", &component); + if (component.len > 0) { + int len; + memmove(p + 1, component.p, component.len); + len = mg_url_decode(p + 1, component.len, p + 1, lp_end - p - 1, 0); + if (len <= 0) { + ok = 0; + break; + } + component.p = p + 1; + component.len = len; + if (mg_vcmp(&component, ".") == 0) { + /* Yum. */ + } else if (mg_vcmp(&component, "..") == 0) { + while (p > ps && *p != DIRSEP) p--; + *p = '\0'; + } else { + size_t i; +#ifdef _WIN32 + /* On Windows, make sure it's valid Unicode (no funny stuff). */ + wchar_t buf[MG_MAX_PATH * 2]; + if (to_wchar(component.p, buf, MG_MAX_PATH) == 0) { + DBG(("[%.*s] smells funny", (int) component.len, component.p)); + ok = 0; + break; + } +#endif + *p++ = DIRSEP; + /* No NULs and DIRSEPs in the component (percent-encoded). */ + for (i = 0; i < component.len; i++, p++) { + if (*p == '\0' || *p == DIRSEP +#ifdef _WIN32 + /* On Windows, "/" is also accepted, so check for that too. */ + || + *p == '/' +#endif + ) { + ok = 0; + break; + } + } + } + } + u = next; + } + if (ok) { + *local_path = lp; + if (u > cp_end) u = cp_end; + remainder->p = u; + remainder->len = cp_end - u; + } else { + MG_FREE(lp); + } + } + +out: + LOG(LL_DEBUG, + ("'%.*s' -> '%s' + '%.*s'", (int) hm->uri.len, hm->uri.p, + *local_path ? *local_path : "", (int) remainder->len, remainder->p)); + return ok; +} + +static int mg_get_month_index(const char *s) { + static const char *month_names[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + size_t i; + + for (i = 0; i < ARRAY_SIZE(month_names); i++) + if (!strcmp(s, month_names[i])) return (int) i; + + return -1; +} + +static int mg_num_leap_years(int year) { + return year / 4 - year / 100 + year / 400; +} + +/* Parse UTC date-time string, and return the corresponding time_t value. */ +MG_INTERNAL time_t mg_parse_date_string(const char *datetime) { + static const unsigned short days_before_month[] = { + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; + char month_str[32]; + int second, minute, hour, day, month, year, leap_days, days; + time_t result = (time_t) 0; + + if (((sscanf(datetime, "%d/%3s/%d %d:%d:%d", &day, month_str, &year, &hour, + &minute, &second) == 6) || + (sscanf(datetime, "%d %3s %d %d:%d:%d", &day, month_str, &year, &hour, + &minute, &second) == 6) || + (sscanf(datetime, "%*3s, %d %3s %d %d:%d:%d", &day, month_str, &year, + &hour, &minute, &second) == 6) || + (sscanf(datetime, "%d-%3s-%d %d:%d:%d", &day, month_str, &year, &hour, + &minute, &second) == 6)) && + year > 1970 && (month = mg_get_month_index(month_str)) != -1) { + leap_days = mg_num_leap_years(year) - mg_num_leap_years(1970); + year -= 1970; + days = year * 365 + days_before_month[month] + (day - 1) + leap_days; + result = days * 24 * 3600 + hour * 3600 + minute * 60 + second; + } + + return result; +} + +MG_INTERNAL int mg_is_not_modified(struct http_message *hm, cs_stat_t *st) { + struct mg_str *hdr; + if ((hdr = mg_get_http_header(hm, "If-None-Match")) != NULL) { + char etag[64]; + mg_http_construct_etag(etag, sizeof(etag), st); + return mg_vcasecmp(hdr, etag) == 0; + } else if ((hdr = mg_get_http_header(hm, "If-Modified-Since")) != NULL) { + return st->st_mtime <= mg_parse_date_string(hdr->p); + } else { + return 0; + } +} + +void mg_http_send_digest_auth_request(struct mg_connection *c, + const char *domain) { + mg_printf(c, + "HTTP/1.1 401 Unauthorized\r\n" + "WWW-Authenticate: Digest qop=\"auth\", " + "realm=\"%s\", nonce=\"%lx\"\r\n" + "Content-Length: 0\r\n\r\n", + domain, (unsigned long) mg_time()); +} + +static void mg_http_send_options(struct mg_connection *nc) { + mg_printf(nc, "%s", + "HTTP/1.1 200 OK\r\nAllow: GET, POST, HEAD, CONNECT, OPTIONS" +#if MG_ENABLE_HTTP_WEBDAV + ", MKCOL, PUT, DELETE, PROPFIND, MOVE\r\nDAV: 1,2" +#endif + "\r\n\r\n"); + nc->flags |= MG_F_SEND_AND_CLOSE; +} + +static int mg_is_creation_request(const struct http_message *hm) { + return mg_vcmp(&hm->method, "MKCOL") == 0 || mg_vcmp(&hm->method, "PUT") == 0; +} + +MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path, + const struct mg_str *path_info, + struct http_message *hm, + struct mg_serve_http_opts *opts) { + int exists, is_directory, is_cgi; +#if MG_ENABLE_HTTP_WEBDAV + int is_dav = mg_is_dav_request(&hm->method); +#else + int is_dav = 0; +#endif + char *index_file = NULL; + cs_stat_t st; + + exists = (mg_stat(path, &st) == 0); + is_directory = exists && S_ISDIR(st.st_mode); + + if (is_directory) + mg_find_index_file(path, opts->index_files, &index_file, &st); + + is_cgi = + (mg_match_prefix(opts->cgi_file_pattern, strlen(opts->cgi_file_pattern), + index_file ? index_file : path) > 0); + + LOG(LL_DEBUG, + ("%p %.*s [%s] exists=%d is_dir=%d is_dav=%d is_cgi=%d index=%s", nc, + (int) hm->method.len, hm->method.p, path, exists, is_directory, is_dav, + is_cgi, index_file ? index_file : "")); + + if (is_directory && hm->uri.p[hm->uri.len - 1] != '/' && !is_dav) { + mg_printf(nc, + "HTTP/1.1 301 Moved\r\nLocation: %.*s/\r\n" + "Content-Length: 0\r\n\r\n", + (int) hm->uri.len, hm->uri.p); + MG_FREE(index_file); + return; + } + + /* If we have path_info, the only way to handle it is CGI. */ + if (path_info->len > 0 && !is_cgi) { + mg_http_send_error(nc, 501, NULL); + MG_FREE(index_file); + return; + } + + if (is_dav && opts->dav_document_root == NULL) { + mg_http_send_error(nc, 501, NULL); + } else if (!mg_http_is_authorized( + hm, mg_mk_str(path), opts->auth_domain, opts->global_auth_file, + ((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) | + MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE | + MG_AUTH_FLAG_ALLOW_MISSING_FILE)) || + !mg_http_is_authorized( + hm, mg_mk_str(path), opts->auth_domain, + opts->per_directory_auth_file, + ((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) | + MG_AUTH_FLAG_ALLOW_MISSING_FILE))) { + mg_http_send_digest_auth_request(nc, opts->auth_domain); + } else if (is_cgi) { +#if MG_ENABLE_HTTP_CGI + mg_handle_cgi(nc, index_file ? index_file : path, path_info, hm, opts); +#else + mg_http_send_error(nc, 501, NULL); +#endif /* MG_ENABLE_HTTP_CGI */ + } else if ((!exists || + mg_is_file_hidden(path, opts, 0 /* specials are ok */)) && + !mg_is_creation_request(hm)) { + mg_http_send_error(nc, 404, NULL); +#if MG_ENABLE_HTTP_WEBDAV + } else if (!mg_vcmp(&hm->method, "PROPFIND")) { + mg_handle_propfind(nc, path, &st, hm, opts); +#if !MG_DISABLE_DAV_AUTH + } else if (is_dav && + (opts->dav_auth_file == NULL || + (strcmp(opts->dav_auth_file, "-") != 0 && + !mg_http_is_authorized( + hm, mg_mk_str(path), opts->auth_domain, opts->dav_auth_file, + ((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) | + MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE | + MG_AUTH_FLAG_ALLOW_MISSING_FILE))))) { + mg_http_send_digest_auth_request(nc, opts->auth_domain); +#endif + } else if (!mg_vcmp(&hm->method, "MKCOL")) { + mg_handle_mkcol(nc, path, hm); + } else if (!mg_vcmp(&hm->method, "DELETE")) { + mg_handle_delete(nc, opts, path); + } else if (!mg_vcmp(&hm->method, "PUT")) { + mg_handle_put(nc, path, hm); + } else if (!mg_vcmp(&hm->method, "MOVE")) { + mg_handle_move(nc, opts, path, hm); +#if MG_ENABLE_FAKE_DAVLOCK + } else if (!mg_vcmp(&hm->method, "LOCK")) { + mg_handle_lock(nc, path); +#endif +#endif /* MG_ENABLE_HTTP_WEBDAV */ + } else if (!mg_vcmp(&hm->method, "OPTIONS")) { + mg_http_send_options(nc); + } else if (is_directory && index_file == NULL) { +#if MG_ENABLE_DIRECTORY_LISTING + if (strcmp(opts->enable_directory_listing, "yes") == 0) { + mg_send_directory_listing(nc, path, hm, opts); + } else { + mg_http_send_error(nc, 403, NULL); + } +#else + mg_http_send_error(nc, 501, NULL); +#endif + } else if (mg_is_not_modified(hm, &st)) { + mg_http_send_error(nc, 304, "Not Modified"); + } else { + mg_http_serve_file2(nc, index_file ? index_file : path, hm, opts); + } + MG_FREE(index_file); +} + +void mg_serve_http(struct mg_connection *nc, struct http_message *hm, + struct mg_serve_http_opts opts) { + char *path = NULL; + struct mg_str *hdr, path_info; + uint32_t remote_ip = ntohl(*(uint32_t *) &nc->sa.sin.sin_addr); + + if (mg_check_ip_acl(opts.ip_acl, remote_ip) != 1) { + /* Not allowed to connect */ + mg_http_send_error(nc, 403, NULL); + nc->flags |= MG_F_SEND_AND_CLOSE; + return; + } + +#if MG_ENABLE_HTTP_URL_REWRITES + if (mg_http_handle_forwarding(nc, hm, &opts)) { + return; + } + + if (mg_http_send_port_based_redirect(nc, hm, &opts)) { + return; + } +#endif + + if (opts.document_root == NULL) { + opts.document_root = "."; + } + if (opts.per_directory_auth_file == NULL) { + opts.per_directory_auth_file = ".htpasswd"; + } + if (opts.enable_directory_listing == NULL) { + opts.enable_directory_listing = "yes"; + } + if (opts.cgi_file_pattern == NULL) { + opts.cgi_file_pattern = "**.cgi$|**.php$"; + } + if (opts.ssi_pattern == NULL) { + opts.ssi_pattern = "**.shtml$|**.shtm$"; + } + if (opts.index_files == NULL) { + opts.index_files = "index.html,index.htm,index.shtml,index.cgi,index.php"; + } + /* Normalize path - resolve "." and ".." (in-place). */ + if (!mg_normalize_uri_path(&hm->uri, &hm->uri)) { + mg_http_send_error(nc, 400, NULL); + return; + } + if (mg_uri_to_local_path(hm, &opts, &path, &path_info) == 0) { + mg_http_send_error(nc, 404, NULL); + return; + } + mg_send_http_file(nc, path, &path_info, hm, &opts); + + MG_FREE(path); + path = NULL; + + /* Close connection for non-keep-alive requests */ + if (mg_vcmp(&hm->proto, "HTTP/1.1") != 0 || + ((hdr = mg_get_http_header(hm, "Connection")) != NULL && + mg_vcmp(hdr, "keep-alive") != 0)) { +#if 0 + nc->flags |= MG_F_SEND_AND_CLOSE; +#endif + } +} + +#if MG_ENABLE_HTTP_STREAMING_MULTIPART +void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data, + mg_fu_fname_fn local_name_fn + MG_UD_ARG(void *user_data)) { + switch (ev) { + case MG_EV_HTTP_PART_BEGIN: { + struct mg_http_multipart_part *mp = + (struct mg_http_multipart_part *) ev_data; + struct file_upload_state *fus = + (struct file_upload_state *) MG_CALLOC(1, sizeof(*fus)); + struct mg_str lfn = local_name_fn(nc, mg_mk_str(mp->file_name)); + mp->user_data = NULL; + if (lfn.p == NULL || lfn.len == 0) { + LOG(LL_ERROR, ("%p Not allowed to upload %s", nc, mp->file_name)); + mg_printf(nc, + "HTTP/1.1 403 Not Allowed\r\n" + "Content-Type: text/plain\r\n" + "Connection: close\r\n\r\n" + "Not allowed to upload %s\r\n", + mp->file_name); + nc->flags |= MG_F_SEND_AND_CLOSE; + return; + } + fus->lfn = (char *) MG_MALLOC(lfn.len + 1); + memcpy(fus->lfn, lfn.p, lfn.len); + fus->lfn[lfn.len] = '\0'; + if (lfn.p != mp->file_name) MG_FREE((char *) lfn.p); + LOG(LL_DEBUG, + ("%p Receiving file %s -> %s", nc, mp->file_name, fus->lfn)); + fus->fp = mg_fopen(fus->lfn, "w"); + if (fus->fp == NULL) { + mg_printf(nc, + "HTTP/1.1 500 Internal Server Error\r\n" + "Content-Type: text/plain\r\n" + "Connection: close\r\n\r\n"); + LOG(LL_ERROR, ("Failed to open %s: %d\n", fus->lfn, mg_get_errno())); + mg_printf(nc, "Failed to open %s: %d\n", fus->lfn, mg_get_errno()); + /* Do not close the connection just yet, discard remainder of the data. + * This is because at the time of writing some browsers (Chrome) fail to + * render response before all the data is sent. */ + } + mp->user_data = (void *) fus; + break; + } + case MG_EV_HTTP_PART_DATA: { + struct mg_http_multipart_part *mp = + (struct mg_http_multipart_part *) ev_data; + struct file_upload_state *fus = + (struct file_upload_state *) mp->user_data; + if (fus == NULL || fus->fp == NULL) break; + if (mg_fwrite(mp->data.p, 1, mp->data.len, fus->fp) != mp->data.len) { + LOG(LL_ERROR, ("Failed to write to %s: %d, wrote %d", fus->lfn, + mg_get_errno(), (int) fus->num_recd)); + if (mg_get_errno() == ENOSPC +#ifdef SPIFFS_ERR_FULL + || mg_get_errno() == SPIFFS_ERR_FULL +#endif + ) { + mg_printf(nc, + "HTTP/1.1 413 Payload Too Large\r\n" + "Content-Type: text/plain\r\n" + "Connection: close\r\n\r\n"); + mg_printf(nc, "Failed to write to %s: no space left; wrote %d\r\n", + fus->lfn, (int) fus->num_recd); + } else { + mg_printf(nc, + "HTTP/1.1 500 Internal Server Error\r\n" + "Content-Type: text/plain\r\n" + "Connection: close\r\n\r\n"); + mg_printf(nc, "Failed to write to %s: %d, wrote %d", mp->file_name, + mg_get_errno(), (int) fus->num_recd); + } + fclose(fus->fp); + remove(fus->lfn); + fus->fp = NULL; + /* Do not close the connection just yet, discard remainder of the data. + * This is because at the time of writing some browsers (Chrome) fail to + * render response before all the data is sent. */ + return; + } + fus->num_recd += mp->data.len; + LOG(LL_DEBUG, ("%p rec'd %d bytes, %d total", nc, (int) mp->data.len, + (int) fus->num_recd)); + break; + } + case MG_EV_HTTP_PART_END: { + struct mg_http_multipart_part *mp = + (struct mg_http_multipart_part *) ev_data; + struct file_upload_state *fus = + (struct file_upload_state *) mp->user_data; + if (fus == NULL) break; + if (mp->status >= 0 && fus->fp != NULL) { + LOG(LL_DEBUG, ("%p Uploaded %s (%s), %d bytes", nc, mp->file_name, + fus->lfn, (int) fus->num_recd)); + mg_printf(nc, + "HTTP/1.1 200 OK\r\n" + "Content-Type: text/plain\r\n" + "Connection: close\r\n\r\n" + "Ok, %s - %d bytes.\r\n", + mp->file_name, (int) fus->num_recd); + } else { + LOG(LL_ERROR, ("Failed to store %s (%s)", mp->file_name, fus->lfn)); + /* + * mp->status < 0 means connection was terminated, so no reason to send + * HTTP reply + */ + } + if (fus->fp != NULL) fclose(fus->fp); + MG_FREE(fus->lfn); + MG_FREE(fus); + mp->user_data = NULL; + nc->flags |= MG_F_SEND_AND_CLOSE; + break; + } + } + +#if MG_ENABLE_CALLBACK_USERDATA + (void) user_data; +#endif +} + +#endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */ +#endif /* MG_ENABLE_FILESYSTEM */ + +struct mg_connection *mg_connect_http_base( + struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data), + struct mg_connect_opts opts, const char *scheme1, const char *scheme2, + const char *scheme_ssl1, const char *scheme_ssl2, const char *url, + struct mg_str *path, struct mg_str *user_info, struct mg_str *host) { + struct mg_connection *nc = NULL; + unsigned int port_i = 0; + int use_ssl = 0; + struct mg_str scheme, query, fragment; + char conn_addr_buf[2]; + char *conn_addr = conn_addr_buf; + + if (mg_parse_uri(mg_mk_str(url), &scheme, user_info, host, &port_i, path, + &query, &fragment) != 0) { + MG_SET_PTRPTR(opts.error_string, "cannot parse url"); + goto out; + } + + /* If query is present, do not strip it. Pass to the caller. */ + if (query.len > 0) path->len += query.len + 1; + + if (scheme.len == 0 || mg_vcmp(&scheme, scheme1) == 0 || + (scheme2 != NULL && mg_vcmp(&scheme, scheme2) == 0)) { + use_ssl = 0; + if (port_i == 0) port_i = 80; + } else if (mg_vcmp(&scheme, scheme_ssl1) == 0 || + (scheme2 != NULL && mg_vcmp(&scheme, scheme_ssl2) == 0)) { + use_ssl = 1; + if (port_i == 0) port_i = 443; + } else { + goto out; + } + + mg_asprintf(&conn_addr, sizeof(conn_addr_buf), "tcp://%.*s:%u", + (int) host->len, host->p, port_i); + if (conn_addr == NULL) goto out; + + LOG(LL_DEBUG, ("%s use_ssl? %d %s", url, use_ssl, conn_addr)); + if (use_ssl) { +#if MG_ENABLE_SSL + /* + * Schema requires SSL, but no SSL parameters were provided in opts. + * In order to maintain backward compatibility, use a faux-SSL with no + * verification. + */ + if (opts.ssl_ca_cert == NULL) { + opts.ssl_ca_cert = "*"; + } +#else + MG_SET_PTRPTR(opts.error_string, "ssl is disabled"); + goto out; +#endif + } + + if ((nc = mg_connect_opt(mgr, conn_addr, MG_CB(ev_handler, user_data), + opts)) != NULL) { + mg_set_protocol_http_websocket(nc); + } + +out: + if (conn_addr != NULL && conn_addr != conn_addr_buf) MG_FREE(conn_addr); + return nc; +} + +struct mg_connection *mg_connect_http_opt( + struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data), + struct mg_connect_opts opts, const char *url, const char *extra_headers, + const char *post_data) { + struct mg_str user = MG_NULL_STR, null_str = MG_NULL_STR; + struct mg_str host = MG_NULL_STR, path = MG_NULL_STR; + struct mbuf auth; + struct mg_connection *nc = + mg_connect_http_base(mgr, MG_CB(ev_handler, user_data), opts, "http", + NULL, "https", NULL, url, &path, &user, &host); + + if (nc == NULL) { + return NULL; + } + + mbuf_init(&auth, 0); + if (user.len > 0) { + mg_basic_auth_header(user, null_str, &auth); + } + + if (post_data == NULL) post_data = ""; + if (extra_headers == NULL) extra_headers = ""; + if (path.len == 0) path = mg_mk_str("/"); + if (host.len == 0) host = mg_mk_str(""); + + mg_printf(nc, "%s %.*s HTTP/1.1\r\nHost: %.*s\r\nContent-Length: %" SIZE_T_FMT + "\r\n%.*s%s\r\n%s", + (post_data[0] == '\0' ? "GET" : "POST"), (int) path.len, path.p, + (int) (path.p - host.p), host.p, strlen(post_data), (int) auth.len, + (auth.buf == NULL ? "" : auth.buf), extra_headers, post_data); + + mbuf_free(&auth); + return nc; +} + +struct mg_connection *mg_connect_http( + struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data), + const char *url, const char *extra_headers, const char *post_data) { + struct mg_connect_opts opts; + memset(&opts, 0, sizeof(opts)); + return mg_connect_http_opt(mgr, MG_CB(ev_handler, user_data), opts, url, + extra_headers, post_data); +} + +size_t mg_parse_multipart(const char *buf, size_t buf_len, char *var_name, + size_t var_name_len, char *file_name, + size_t file_name_len, const char **data, + size_t *data_len) { + static const char cd[] = "Content-Disposition: "; + size_t hl, bl, n, ll, pos, cdl = sizeof(cd) - 1; + int shl; + + if (buf == NULL || buf_len <= 0) return 0; + if ((shl = mg_http_get_request_len(buf, buf_len)) <= 0) return 0; + hl = shl; + if (buf[0] != '-' || buf[1] != '-' || buf[2] == '\n') return 0; + + /* Get boundary length */ + bl = mg_get_line_len(buf, buf_len); + + /* Loop through headers, fetch variable name and file name */ + var_name[0] = file_name[0] = '\0'; + for (n = bl; (ll = mg_get_line_len(buf + n, hl - n)) > 0; n += ll) { + if (mg_ncasecmp(cd, buf + n, cdl) == 0) { + struct mg_str header; + header.p = buf + n + cdl; + header.len = ll - (cdl + 2); + { + char *var_name2 = var_name; + mg_http_parse_header2(&header, "name", &var_name2, var_name_len); + /* TODO: handle reallocated buffer correctly */ + if (var_name2 != var_name) { + MG_FREE(var_name2); + var_name[0] = '\0'; + } + } + { + char *file_name2 = file_name; + mg_http_parse_header2(&header, "filename", &file_name2, file_name_len); + /* TODO: handle reallocated buffer correctly */ + if (file_name2 != file_name) { + MG_FREE(file_name2); + file_name[0] = '\0'; + } + } + } + } + + /* Scan through the body, search for terminating boundary */ + for (pos = hl; pos + (bl - 2) < buf_len; pos++) { + if (buf[pos] == '-' && !strncmp(buf, &buf[pos], bl - 2)) { + if (data_len != NULL) *data_len = (pos - 2) - hl; + if (data != NULL) *data = buf + hl; + return pos; + } + } + + return 0; +} + +void mg_register_http_endpoint_opt(struct mg_connection *nc, + const char *uri_path, + mg_event_handler_t handler, + struct mg_http_endpoint_opts opts) { + struct mg_http_proto_data *pd = NULL; + struct mg_http_endpoint *new_ep = NULL; + + if (nc == NULL) return; + new_ep = (struct mg_http_endpoint *) MG_CALLOC(1, sizeof(*new_ep)); + if (new_ep == NULL) return; + + pd = mg_http_get_proto_data(nc); + new_ep->uri_pattern = mg_strdup(mg_mk_str(uri_path)); + if (opts.auth_domain != NULL && opts.auth_file != NULL) { + new_ep->auth_domain = strdup(opts.auth_domain); + new_ep->auth_file = strdup(opts.auth_file); + } + new_ep->handler = handler; +#if MG_ENABLE_CALLBACK_USERDATA + new_ep->user_data = opts.user_data; +#endif + new_ep->next = pd->endpoints; + pd->endpoints = new_ep; +} + +static void mg_http_call_endpoint_handler(struct mg_connection *nc, int ev, + struct http_message *hm) { + struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); + void *user_data = nc->user_data; + + if (ev == MG_EV_HTTP_REQUEST +#if MG_ENABLE_HTTP_STREAMING_MULTIPART + || ev == MG_EV_HTTP_MULTIPART_REQUEST +#endif + ) { + struct mg_http_endpoint *ep = + mg_http_get_endpoint_handler(nc->listener, &hm->uri); + if (ep != NULL) { +#if MG_ENABLE_FILESYSTEM && !MG_DISABLE_HTTP_DIGEST_AUTH + if (!mg_http_is_authorized(hm, hm->uri, ep->auth_domain, ep->auth_file, + MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE)) { + mg_http_send_digest_auth_request(nc, ep->auth_domain); + return; + } +#endif + pd->endpoint_handler = ep->handler; +#if MG_ENABLE_CALLBACK_USERDATA + user_data = ep->user_data; +#endif + } + } + mg_call(nc, pd->endpoint_handler ? pd->endpoint_handler : nc->handler, + user_data, ev, hm); +} + +void mg_register_http_endpoint(struct mg_connection *nc, const char *uri_path, + MG_CB(mg_event_handler_t handler, + void *user_data)) { + struct mg_http_endpoint_opts opts; + memset(&opts, 0, sizeof(opts)); +#if MG_ENABLE_CALLBACK_USERDATA + opts.user_data = user_data; +#endif + mg_register_http_endpoint_opt(nc, uri_path, handler, opts); +} + +#endif /* MG_ENABLE_HTTP */ diff --git a/src/mongoose-6.11/src/mg_http.h b/src/mongoose-6.11/src/mg_http.h new file mode 100644 index 0000000..c89d38f --- /dev/null +++ b/src/mongoose-6.11/src/mg_http.h @@ -0,0 +1,364 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +/* + * === Common API reference + */ + +#ifndef CS_MONGOOSE_SRC_HTTP_H_ +#define CS_MONGOOSE_SRC_HTTP_H_ + +#if MG_ENABLE_HTTP + +#include "mg_net.h" +#include "common/mg_str.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef MG_MAX_HTTP_HEADERS +#define MG_MAX_HTTP_HEADERS 20 +#endif + +#ifndef MG_MAX_HTTP_REQUEST_SIZE +#define MG_MAX_HTTP_REQUEST_SIZE 1024 +#endif + +#ifndef MG_MAX_HTTP_SEND_MBUF +#define MG_MAX_HTTP_SEND_MBUF 1024 +#endif + +#ifndef MG_CGI_ENVIRONMENT_SIZE +#define MG_CGI_ENVIRONMENT_SIZE 8192 +#endif + +/* HTTP message */ +struct http_message { + struct mg_str message; /* Whole message: request line + headers + body */ + struct mg_str body; /* Message body. 0-length for requests with no body */ + + /* HTTP Request line (or HTTP response line) */ + struct mg_str method; /* "GET" */ + struct mg_str uri; /* "/my_file.html" */ + struct mg_str proto; /* "HTTP/1.1" -- for both request and response */ + + /* For responses, code and response status message are set */ + int resp_code; + struct mg_str resp_status_msg; + + /* + * Query-string part of the URI. For example, for HTTP request + * GET /foo/bar?param1=val1¶m2=val2 + * | uri | query_string | + * + * Note that question mark character doesn't belong neither to the uri, + * nor to the query_string + */ + struct mg_str query_string; + + /* Headers */ + struct mg_str header_names[MG_MAX_HTTP_HEADERS]; + struct mg_str header_values[MG_MAX_HTTP_HEADERS]; +}; + +#if MG_ENABLE_HTTP_WEBSOCKET +/* WebSocket message */ +struct websocket_message { + unsigned char *data; + size_t size; + unsigned char flags; +}; +#endif + +/* HTTP multipart part */ +struct mg_http_multipart_part { + const char *file_name; + const char *var_name; + struct mg_str data; + int status; /* <0 on error */ + void *user_data; +}; + +/* SSI call context */ +struct mg_ssi_call_ctx { + struct http_message *req; /* The request being processed. */ + struct mg_str file; /* Filesystem path of the file being processed. */ + struct mg_str arg; /* The argument passed to the tag: . */ +}; + +/* HTTP and websocket events. void *ev_data is described in a comment. */ +#define MG_EV_HTTP_REQUEST 100 /* struct http_message * */ +#define MG_EV_HTTP_REPLY 101 /* struct http_message * */ +#define MG_EV_HTTP_CHUNK 102 /* struct http_message * */ +#define MG_EV_SSI_CALL 105 /* char * */ +#define MG_EV_SSI_CALL_CTX 106 /* struct mg_ssi_call_ctx * */ + +#if MG_ENABLE_HTTP_WEBSOCKET +#define MG_EV_WEBSOCKET_HANDSHAKE_REQUEST 111 /* struct http_message * */ +#define MG_EV_WEBSOCKET_HANDSHAKE_DONE 112 /* NULL */ +#define MG_EV_WEBSOCKET_FRAME 113 /* struct websocket_message * */ +#define MG_EV_WEBSOCKET_CONTROL_FRAME 114 /* struct websocket_message * */ +#endif + +#if MG_ENABLE_HTTP_STREAMING_MULTIPART +#define MG_EV_HTTP_MULTIPART_REQUEST 121 /* struct http_message */ +#define MG_EV_HTTP_PART_BEGIN 122 /* struct mg_http_multipart_part */ +#define MG_EV_HTTP_PART_DATA 123 /* struct mg_http_multipart_part */ +#define MG_EV_HTTP_PART_END 124 /* struct mg_http_multipart_part */ +/* struct mg_http_multipart_part */ +#define MG_EV_HTTP_MULTIPART_REQUEST_END 125 +#endif + +/* + * Attaches a built-in HTTP event handler to the given connection. + * The user-defined event handler will receive following extra events: + * + * - MG_EV_HTTP_REQUEST: HTTP request has arrived. Parsed HTTP request + * is passed as + * `struct http_message` through the handler's `void *ev_data` pointer. + * - MG_EV_HTTP_REPLY: The HTTP reply has arrived. The parsed HTTP reply is + * passed as `struct http_message` through the handler's `void *ev_data` + * pointer. + * - MG_EV_HTTP_CHUNK: The HTTP chunked-encoding chunk has arrived. + * The parsed HTTP reply is passed as `struct http_message` through the + * handler's `void *ev_data` pointer. `http_message::body` would contain + * incomplete, reassembled HTTP body. + * It will grow with every new chunk that arrives, and it can + * potentially consume a lot of memory. An event handler may process + * the body as chunks are coming, and signal Mongoose to delete processed + * body by setting `MG_F_DELETE_CHUNK` in `mg_connection::flags`. When + * the last zero chunk is received, + * Mongoose sends `MG_EV_HTTP_REPLY` event with + * full reassembled body (if handler did not signal to delete chunks) or + * with empty body (if handler did signal to delete chunks). + * - MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: server has received the WebSocket + * handshake request. `ev_data` contains parsed HTTP request. + * - MG_EV_WEBSOCKET_HANDSHAKE_DONE: server has completed the WebSocket + * handshake. `ev_data` is `NULL`. + * - MG_EV_WEBSOCKET_FRAME: new WebSocket frame has arrived. `ev_data` is + * `struct websocket_message *` + * + * When compiled with MG_ENABLE_HTTP_STREAMING_MULTIPART, Mongoose parses + * multipart requests and splits them into separate events: + * - MG_EV_HTTP_MULTIPART_REQUEST: Start of the request. + * This event is sent before body is parsed. After this, the user + * should expect a sequence of PART_BEGIN/DATA/END requests. + * This is also the last time when headers and other request fields are + * accessible. + * - MG_EV_HTTP_PART_BEGIN: Start of a part of a multipart message. + * Argument: mg_http_multipart_part with var_name and file_name set + * (if present). No data is passed in this message. + * - MG_EV_HTTP_PART_DATA: new portion of data from the multipart message. + * Argument: mg_http_multipart_part. var_name and file_name are preserved, + * data is available in mg_http_multipart_part.data. + * - MG_EV_HTTP_PART_END: End of the current part. var_name, file_name are + * the same, no data in the message. If status is 0, then the part is + * properly terminated with a boundary, status < 0 means that connection + * was terminated. + * - MG_EV_HTTP_MULTIPART_REQUEST_END: End of the multipart request. + * Argument: mg_http_multipart_part, var_name and file_name are NULL, + * status = 0 means request was properly closed, < 0 means connection + * was terminated (note: in this case both PART_END and REQUEST_END are + * delivered). + */ +void mg_set_protocol_http_websocket(struct mg_connection *nc); + +#if MG_ENABLE_HTTP_WEBSOCKET +/* + * Send websocket handshake to the server. + * + * `nc` must be a valid connection, connected to a server. `uri` is an URI + * to fetch, extra_headers` is extra HTTP headers to send or `NULL`. + * + * This function is intended to be used by websocket client. + * + * Note that the Host header is mandatory in HTTP/1.1 and must be + * included in `extra_headers`. `mg_send_websocket_handshake2` offers + * a better API for that. + * + * Deprecated in favour of `mg_send_websocket_handshake2` + */ +void mg_send_websocket_handshake(struct mg_connection *nc, const char *uri, + const char *extra_headers); + +/* + * Send websocket handshake to the server. + * + * `nc` must be a valid connection, connected to a server. `uri` is an URI + * to fetch, `host` goes into the `Host` header, `protocol` goes into the + * `Sec-WebSocket-Proto` header (NULL to omit), extra_headers` is extra HTTP + * headers to send or `NULL`. + * + * This function is intended to be used by websocket client. + */ +void mg_send_websocket_handshake2(struct mg_connection *nc, const char *path, + const char *host, const char *protocol, + const char *extra_headers); + +/* Like mg_send_websocket_handshake2 but also passes basic auth header */ +void mg_send_websocket_handshake3(struct mg_connection *nc, const char *path, + const char *host, const char *protocol, + const char *extra_headers, const char *user, + const char *pass); + +/* Same as mg_send_websocket_handshake3 but with strings not necessarily + * NUL-temrinated */ +void mg_send_websocket_handshake3v(struct mg_connection *nc, + const struct mg_str path, + const struct mg_str host, + const struct mg_str protocol, + const struct mg_str extra_headers, + const struct mg_str user, + const struct mg_str pass); + +/* + * Helper function that creates an outbound WebSocket connection. + * + * `url` is a URL to connect to. It must be properly URL-encoded, e.g. have + * no spaces, etc. By default, `mg_connect_ws()` sends Connection and + * Host headers. `extra_headers` is an extra HTTP header to send, e.g. + * `"User-Agent: my-app\r\n"`. + * If `protocol` is not NULL, then a `Sec-WebSocket-Protocol` header is sent. + * + * Examples: + * + * ```c + * nc1 = mg_connect_ws(mgr, ev_handler_1, "ws://echo.websocket.org", NULL, + * NULL); + * nc2 = mg_connect_ws(mgr, ev_handler_1, "wss://echo.websocket.org", NULL, + * NULL); + * nc3 = mg_connect_ws(mgr, ev_handler_1, "ws://api.cesanta.com", + * "clubby.cesanta.com", NULL); + * ``` + */ +struct mg_connection *mg_connect_ws(struct mg_mgr *mgr, + MG_CB(mg_event_handler_t event_handler, + void *user_data), + const char *url, const char *protocol, + const char *extra_headers); + +/* + * Helper function that creates an outbound WebSocket connection + * + * Mostly identical to `mg_connect_ws`, but allows to provide extra parameters + * (for example, SSL parameters) + */ +struct mg_connection *mg_connect_ws_opt( + struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data), + struct mg_connect_opts opts, const char *url, const char *protocol, + const char *extra_headers); + +/* + * Send WebSocket frame to the remote end. + * + * `op_and_flags` specifies the frame's type. It's one of: + * + * - WEBSOCKET_OP_CONTINUE + * - WEBSOCKET_OP_TEXT + * - WEBSOCKET_OP_BINARY + * - WEBSOCKET_OP_CLOSE + * - WEBSOCKET_OP_PING + * - WEBSOCKET_OP_PONG + * + * Orred with one of the flags: + * + * - WEBSOCKET_DONT_FIN: Don't set the FIN flag on the frame to be sent. + * + * `data` and `data_len` contain frame data. + */ +void mg_send_websocket_frame(struct mg_connection *nc, int op_and_flags, + const void *data, size_t data_len); + +/* + * Like `mg_send_websocket_frame()`, but composes a single frame from multiple + * buffers. + */ +void mg_send_websocket_framev(struct mg_connection *nc, int op_and_flags, + const struct mg_str *strings, int num_strings); + +/* + * Sends WebSocket frame to the remote end. + * + * Like `mg_send_websocket_frame()`, but allows to create formatted messages + * with `printf()`-like semantics. + */ +void mg_printf_websocket_frame(struct mg_connection *nc, int op_and_flags, + const char *fmt, ...); + +/* Websocket opcodes, from http://tools.ietf.org/html/rfc6455 */ +#define WEBSOCKET_OP_CONTINUE 0 +#define WEBSOCKET_OP_TEXT 1 +#define WEBSOCKET_OP_BINARY 2 +#define WEBSOCKET_OP_CLOSE 8 +#define WEBSOCKET_OP_PING 9 +#define WEBSOCKET_OP_PONG 10 + +/* + * If set causes the FIN flag to not be set on outbound + * frames. This enables sending multiple fragments of a single + * logical message. + * + * The WebSocket protocol mandates that if the FIN flag of a data + * frame is not set, the next frame must be a WEBSOCKET_OP_CONTINUE. + * The last frame must have the FIN bit set. + * + * Note that mongoose will automatically defragment incoming messages, + * so this flag is used only on outbound messages. + */ +#define WEBSOCKET_DONT_FIN 0x100 + +#endif /* MG_ENABLE_HTTP_WEBSOCKET */ + +/* + * Decodes a URL-encoded string. + * + * Source string is specified by (`src`, `src_len`), and destination is + * (`dst`, `dst_len`). If `is_form_url_encoded` is non-zero, then + * `+` character is decoded as a blank space character. This function + * guarantees to NUL-terminate the destination. If destination is too small, + * then the source string is partially decoded and `-1` is returned. + *Otherwise, + * a length of the decoded string is returned, not counting final NUL. + */ +int mg_url_decode(const char *src, int src_len, char *dst, int dst_len, + int is_form_url_encoded); + +extern void mg_hash_md5_v(size_t num_msgs, const uint8_t *msgs[], + const size_t *msg_lens, uint8_t *digest); +extern void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[], + const size_t *msg_lens, uint8_t *digest); + +/* + * Flags for `mg_http_is_authorized()`. + */ +#define MG_AUTH_FLAG_IS_DIRECTORY (1 << 0) +#define MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE (1 << 1) +#define MG_AUTH_FLAG_ALLOW_MISSING_FILE (1 << 2) + +/* + * Checks whether an http request is authorized. `domain` is the authentication + * realm, `passwords_file` is a htdigest file (can be created e.g. with + * `htdigest` utility). If either `domain` or `passwords_file` is NULL, this + * function always returns 1; otherwise checks the authentication in the + * http request and returns 1 only if there is a match; 0 otherwise. + */ +int mg_http_is_authorized(struct http_message *hm, struct mg_str path, + const char *domain, const char *passwords_file, + int flags); + +/* + * Sends 401 Unauthorized response. + */ +void mg_http_send_digest_auth_request(struct mg_connection *c, + const char *domain); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* MG_ENABLE_HTTP */ + +#endif /* CS_MONGOOSE_SRC_HTTP_H_ */ diff --git a/src/mongoose-6.11/src/mg_http_cgi.c b/src/mongoose-6.11/src/mg_http_cgi.c new file mode 100644 index 0000000..437c93c --- /dev/null +++ b/src/mongoose-6.11/src/mg_http_cgi.c @@ -0,0 +1,514 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef _WIN32 +#include +#endif + +#if MG_ENABLE_HTTP && MG_ENABLE_HTTP_CGI + +#ifndef MG_MAX_CGI_ENVIR_VARS +#define MG_MAX_CGI_ENVIR_VARS 64 +#endif + +#ifndef MG_ENV_EXPORT_TO_CGI +#define MG_ENV_EXPORT_TO_CGI "MONGOOSE_CGI" +#endif + +#define MG_F_HTTP_CGI_PARSE_HEADERS MG_F_USER_1 + +/* + * This structure helps to create an environment for the spawned CGI program. + * Environment is an array of "VARIABLE=VALUE\0" ASCIIZ strings, + * last element must be NULL. + * However, on Windows there is a requirement that all these VARIABLE=VALUE\0 + * strings must reside in a contiguous buffer. The end of the buffer is + * marked by two '\0' characters. + * We satisfy both worlds: we create an envp array (which is vars), all + * entries are actually pointers inside buf. + */ +struct mg_cgi_env_block { + struct mg_connection *nc; + char buf[MG_CGI_ENVIRONMENT_SIZE]; /* Environment buffer */ + const char *vars[MG_MAX_CGI_ENVIR_VARS]; /* char *envp[] */ + int len; /* Space taken */ + int nvars; /* Number of variables in envp[] */ +}; + +#ifdef _WIN32 +struct mg_threadparam { + sock_t s; + HANDLE hPipe; +}; + +static int mg_wait_until_ready(sock_t sock, int for_read) { + fd_set set; + FD_ZERO(&set); + FD_SET(sock, &set); + return select(sock + 1, for_read ? &set : 0, for_read ? 0 : &set, 0, 0) == 1; +} + +static void *mg_push_to_stdin(void *arg) { + struct mg_threadparam *tp = (struct mg_threadparam *) arg; + int n, sent, stop = 0; + DWORD k; + char buf[BUFSIZ]; + + while (!stop && mg_wait_until_ready(tp->s, 1) && + (n = recv(tp->s, buf, sizeof(buf), 0)) > 0) { + if (n == -1 && GetLastError() == WSAEWOULDBLOCK) continue; + for (sent = 0; !stop && sent < n; sent += k) { + if (!WriteFile(tp->hPipe, buf + sent, n - sent, &k, 0)) stop = 1; + } + } + DBG(("%s", "FORWARED EVERYTHING TO CGI")); + CloseHandle(tp->hPipe); + MG_FREE(tp); + return NULL; +} + +static void *mg_pull_from_stdout(void *arg) { + struct mg_threadparam *tp = (struct mg_threadparam *) arg; + int k = 0, stop = 0; + DWORD n, sent; + char buf[BUFSIZ]; + + while (!stop && ReadFile(tp->hPipe, buf, sizeof(buf), &n, NULL)) { + for (sent = 0; !stop && sent < n; sent += k) { + if (mg_wait_until_ready(tp->s, 0) && + (k = send(tp->s, buf + sent, n - sent, 0)) <= 0) + stop = 1; + } + } + DBG(("%s", "EOF FROM CGI")); + CloseHandle(tp->hPipe); + shutdown(tp->s, 2); // Without this, IO thread may get truncated data + closesocket(tp->s); + MG_FREE(tp); + return NULL; +} + +static void mg_spawn_stdio_thread(sock_t sock, HANDLE hPipe, + void *(*func)(void *)) { + struct mg_threadparam *tp = (struct mg_threadparam *) MG_MALLOC(sizeof(*tp)); + if (tp != NULL) { + tp->s = sock; + tp->hPipe = hPipe; + mg_start_thread(func, tp); + } +} + +static void mg_abs_path(const char *utf8_path, char *abs_path, size_t len) { + wchar_t buf[MG_MAX_PATH], buf2[MG_MAX_PATH]; + to_wchar(utf8_path, buf, ARRAY_SIZE(buf)); + GetFullPathNameW(buf, ARRAY_SIZE(buf2), buf2, NULL); + WideCharToMultiByte(CP_UTF8, 0, buf2, wcslen(buf2) + 1, abs_path, len, 0, 0); +} + +static int mg_start_process(const char *interp, const char *cmd, + const char *env, const char *envp[], + const char *dir, sock_t sock) { + STARTUPINFOW si; + PROCESS_INFORMATION pi; + HANDLE a[2], b[2], me = GetCurrentProcess(); + wchar_t wcmd[MG_MAX_PATH], full_dir[MG_MAX_PATH]; + char buf[MG_MAX_PATH], buf2[MG_MAX_PATH], buf5[MG_MAX_PATH], + buf4[MG_MAX_PATH], cmdline[MG_MAX_PATH]; + DWORD flags = DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS; + FILE *fp; + + memset(&si, 0, sizeof(si)); + memset(&pi, 0, sizeof(pi)); + + si.cb = sizeof(si); + si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + si.hStdError = GetStdHandle(STD_ERROR_HANDLE); + + CreatePipe(&a[0], &a[1], NULL, 0); + CreatePipe(&b[0], &b[1], NULL, 0); + DuplicateHandle(me, a[0], me, &si.hStdInput, 0, TRUE, flags); + DuplicateHandle(me, b[1], me, &si.hStdOutput, 0, TRUE, flags); + + if (interp == NULL && (fp = mg_fopen(cmd, "r")) != NULL) { + buf[0] = buf[1] = '\0'; + fgets(buf, sizeof(buf), fp); + buf[sizeof(buf) - 1] = '\0'; + if (buf[0] == '#' && buf[1] == '!') { + interp = buf + 2; + /* Trim leading spaces: https://github.com/cesanta/mongoose/issues/489 */ + while (*interp != '\0' && isspace(*(unsigned char *) interp)) { + interp++; + } + } + fclose(fp); + } + + snprintf(buf, sizeof(buf), "%s/%s", dir, cmd); + mg_abs_path(buf, buf2, ARRAY_SIZE(buf2)); + + mg_abs_path(dir, buf5, ARRAY_SIZE(buf5)); + to_wchar(dir, full_dir, ARRAY_SIZE(full_dir)); + + if (interp != NULL) { + mg_abs_path(interp, buf4, ARRAY_SIZE(buf4)); + snprintf(cmdline, sizeof(cmdline), "%s \"%s\"", buf4, buf2); + } else { + snprintf(cmdline, sizeof(cmdline), "\"%s\"", buf2); + } + to_wchar(cmdline, wcmd, ARRAY_SIZE(wcmd)); + + if (CreateProcessW(NULL, wcmd, NULL, NULL, TRUE, CREATE_NEW_PROCESS_GROUP, + (void *) env, full_dir, &si, &pi) != 0) { + mg_spawn_stdio_thread(sock, a[1], mg_push_to_stdin); + mg_spawn_stdio_thread(sock, b[0], mg_pull_from_stdout); + + CloseHandle(si.hStdOutput); + CloseHandle(si.hStdInput); + + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + } else { + CloseHandle(a[1]); + CloseHandle(b[0]); + closesocket(sock); + } + DBG(("CGI command: [%ls] -> %p", wcmd, pi.hProcess)); + + /* Not closing a[0] and b[1] because we've used DUPLICATE_CLOSE_SOURCE */ + (void) envp; + return (pi.hProcess != NULL); +} +#else +static int mg_start_process(const char *interp, const char *cmd, + const char *env, const char *envp[], + const char *dir, sock_t sock) { + char buf[500]; + pid_t pid = fork(); + (void) env; + + if (pid == 0) { + /* + * In Linux `chdir` declared with `warn_unused_result` attribute + * To shutup compiler we have yo use result in some way + */ + int tmp = chdir(dir); + (void) tmp; + (void) dup2(sock, 0); + (void) dup2(sock, 1); + closesocket(sock); + + /* + * After exec, all signal handlers are restored to their default values, + * with one exception of SIGCHLD. According to POSIX.1-2001 and Linux's + * implementation, SIGCHLD's handler will leave unchanged after exec + * if it was set to be ignored. Restore it to default action. + */ + signal(SIGCHLD, SIG_DFL); + + if (interp == NULL) { + execle(cmd, cmd, (char *) 0, envp); /* (char *) 0 to squash warning */ + } else { + execle(interp, interp, cmd, (char *) 0, envp); + } + snprintf(buf, sizeof(buf), + "Status: 500\r\n\r\n" + "500 Server Error: %s%s%s: %s", + interp == NULL ? "" : interp, interp == NULL ? "" : " ", cmd, + strerror(errno)); + send(1, buf, strlen(buf), 0); + _exit(EXIT_FAILURE); /* exec call failed */ + } + + return (pid != 0); +} +#endif /* _WIN32 */ + +/* + * Append VARIABLE=VALUE\0 string to the buffer, and add a respective + * pointer into the vars array. + */ +static char *mg_addenv(struct mg_cgi_env_block *block, const char *fmt, ...) { + int n, space; + char *added = block->buf + block->len; + va_list ap; + + /* Calculate how much space is left in the buffer */ + space = sizeof(block->buf) - (block->len + 2); + if (space > 0) { + /* Copy VARIABLE=VALUE\0 string into the free space */ + va_start(ap, fmt); + n = vsnprintf(added, (size_t) space, fmt, ap); + va_end(ap); + + /* Make sure we do not overflow buffer and the envp array */ + if (n > 0 && n + 1 < space && + block->nvars < (int) ARRAY_SIZE(block->vars) - 2) { + /* Append a pointer to the added string into the envp array */ + block->vars[block->nvars++] = added; + /* Bump up used length counter. Include \0 terminator */ + block->len += n + 1; + } + } + + return added; +} + +static void mg_addenv2(struct mg_cgi_env_block *blk, const char *name) { + const char *s; + if ((s = getenv(name)) != NULL) mg_addenv(blk, "%s=%s", name, s); +} + +static void mg_prepare_cgi_environment(struct mg_connection *nc, + const char *prog, + const struct mg_str *path_info, + const struct http_message *hm, + const struct mg_serve_http_opts *opts, + struct mg_cgi_env_block *blk) { + const char *s; + struct mg_str *h; + char *p; + size_t i; + char buf[100]; + size_t path_info_len = path_info != NULL ? path_info->len : 0; + + blk->len = blk->nvars = 0; + blk->nc = nc; + + if ((s = getenv("SERVER_NAME")) != NULL) { + mg_addenv(blk, "SERVER_NAME=%s", s); + } else { + mg_sock_to_str(nc->sock, buf, sizeof(buf), 3); + mg_addenv(blk, "SERVER_NAME=%s", buf); + } + mg_addenv(blk, "SERVER_ROOT=%s", opts->document_root); + mg_addenv(blk, "DOCUMENT_ROOT=%s", opts->document_root); + mg_addenv(blk, "SERVER_SOFTWARE=%s/%s", "Mongoose", MG_VERSION); + + /* Prepare the environment block */ + mg_addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1"); + mg_addenv(blk, "%s", "SERVER_PROTOCOL=HTTP/1.1"); + mg_addenv(blk, "%s", "REDIRECT_STATUS=200"); /* For PHP */ + + mg_addenv(blk, "REQUEST_METHOD=%.*s", (int) hm->method.len, hm->method.p); + + mg_addenv(blk, "REQUEST_URI=%.*s%s%.*s", (int) hm->uri.len, hm->uri.p, + hm->query_string.len == 0 ? "" : "?", (int) hm->query_string.len, + hm->query_string.p); + + mg_conn_addr_to_str(nc, buf, sizeof(buf), + MG_SOCK_STRINGIFY_REMOTE | MG_SOCK_STRINGIFY_IP); + mg_addenv(blk, "REMOTE_ADDR=%s", buf); + mg_conn_addr_to_str(nc, buf, sizeof(buf), MG_SOCK_STRINGIFY_PORT); + mg_addenv(blk, "SERVER_PORT=%s", buf); + + s = hm->uri.p + hm->uri.len - path_info_len - 1; + if (*s == '/') { + const char *base_name = strrchr(prog, DIRSEP); + mg_addenv(blk, "SCRIPT_NAME=%.*s/%s", (int) (s - hm->uri.p), hm->uri.p, + (base_name != NULL ? base_name + 1 : prog)); + } else { + mg_addenv(blk, "SCRIPT_NAME=%.*s", (int) (s - hm->uri.p + 1), hm->uri.p); + } + mg_addenv(blk, "SCRIPT_FILENAME=%s", prog); + + if (path_info != NULL && path_info->len > 0) { + mg_addenv(blk, "PATH_INFO=%.*s", (int) path_info->len, path_info->p); + /* Not really translated... */ + mg_addenv(blk, "PATH_TRANSLATED=%.*s", (int) path_info->len, path_info->p); + } + +#if MG_ENABLE_SSL + mg_addenv(blk, "HTTPS=%s", (nc->flags & MG_F_SSL ? "on" : "off")); +#else + mg_addenv(blk, "HTTPS=off"); +#endif + + if ((h = mg_get_http_header((struct http_message *) hm, "Content-Type")) != + NULL) { + mg_addenv(blk, "CONTENT_TYPE=%.*s", (int) h->len, h->p); + } + + if (hm->query_string.len > 0) { + mg_addenv(blk, "QUERY_STRING=%.*s", (int) hm->query_string.len, + hm->query_string.p); + } + + if ((h = mg_get_http_header((struct http_message *) hm, "Content-Length")) != + NULL) { + mg_addenv(blk, "CONTENT_LENGTH=%.*s", (int) h->len, h->p); + } + + mg_addenv2(blk, "PATH"); + mg_addenv2(blk, "TMP"); + mg_addenv2(blk, "TEMP"); + mg_addenv2(blk, "TMPDIR"); + mg_addenv2(blk, "PERLLIB"); + mg_addenv2(blk, MG_ENV_EXPORT_TO_CGI); + +#ifdef _WIN32 + mg_addenv2(blk, "COMSPEC"); + mg_addenv2(blk, "SYSTEMROOT"); + mg_addenv2(blk, "SystemDrive"); + mg_addenv2(blk, "ProgramFiles"); + mg_addenv2(blk, "ProgramFiles(x86)"); + mg_addenv2(blk, "CommonProgramFiles(x86)"); +#else + mg_addenv2(blk, "LD_LIBRARY_PATH"); +#endif /* _WIN32 */ + + /* Add all headers as HTTP_* variables */ + for (i = 0; hm->header_names[i].len > 0; i++) { + p = mg_addenv(blk, "HTTP_%.*s=%.*s", (int) hm->header_names[i].len, + hm->header_names[i].p, (int) hm->header_values[i].len, + hm->header_values[i].p); + + /* Convert variable name into uppercase, and change - to _ */ + for (; *p != '=' && *p != '\0'; p++) { + if (*p == '-') *p = '_'; + *p = (char) toupper(*(unsigned char *) p); + } + } + + blk->vars[blk->nvars++] = NULL; + blk->buf[blk->len++] = '\0'; +} + +static void mg_cgi_ev_handler(struct mg_connection *cgi_nc, int ev, + void *ev_data MG_UD_ARG(void *user_data)) { +#if !MG_ENABLE_CALLBACK_USERDATA + void *user_data = cgi_nc->user_data; +#endif + struct mg_connection *nc = (struct mg_connection *) user_data; + (void) ev_data; + + if (nc == NULL) { + /* The corresponding network connection was closed. */ + cgi_nc->flags |= MG_F_CLOSE_IMMEDIATELY; + return; + } + + switch (ev) { + case MG_EV_RECV: + /* + * CGI script does not output reply line, like "HTTP/1.1 CODE XXXXX\n" + * It outputs headers, then body. Headers might include "Status" + * header, which changes CODE, and it might include "Location" header + * which changes CODE to 302. + * + * Therefore we do not send the output from the CGI script to the user + * until all CGI headers are received. + * + * Here we parse the output from the CGI script, and if all headers has + * been received, send appropriate reply line, and forward all + * received headers to the client. + */ + if (nc->flags & MG_F_HTTP_CGI_PARSE_HEADERS) { + struct mbuf *io = &cgi_nc->recv_mbuf; + int len = mg_http_get_request_len(io->buf, io->len); + + if (len == 0) break; + if (len < 0 || io->len > MG_MAX_HTTP_REQUEST_SIZE) { + cgi_nc->flags |= MG_F_CLOSE_IMMEDIATELY; + mg_http_send_error(nc, 500, "Bad headers"); + } else { + struct http_message hm; + struct mg_str *h; + mg_http_parse_headers(io->buf, io->buf + io->len, io->len, &hm); + if (mg_get_http_header(&hm, "Location") != NULL) { + mg_printf(nc, "%s", "HTTP/1.1 302 Moved\r\n"); + } else if ((h = mg_get_http_header(&hm, "Status")) != NULL) { + mg_printf(nc, "HTTP/1.1 %.*s\r\n", (int) h->len, h->p); + } else { + mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\n"); + } + } + nc->flags &= ~MG_F_HTTP_CGI_PARSE_HEADERS; + } + if (!(nc->flags & MG_F_HTTP_CGI_PARSE_HEADERS)) { + mg_forward(cgi_nc, nc); + } + break; + case MG_EV_CLOSE: + DBG(("%p CLOSE", cgi_nc)); + mg_http_free_proto_data_cgi(&mg_http_get_proto_data(nc)->cgi); + nc->flags |= MG_F_SEND_AND_CLOSE; + break; + } +} + +MG_INTERNAL void mg_handle_cgi(struct mg_connection *nc, const char *prog, + const struct mg_str *path_info, + const struct http_message *hm, + const struct mg_serve_http_opts *opts) { + struct mg_cgi_env_block blk; + char dir[MG_MAX_PATH]; + const char *p; + sock_t fds[2]; + + DBG(("%p [%s]", nc, prog)); + mg_prepare_cgi_environment(nc, prog, path_info, hm, opts, &blk); + /* + * CGI must be executed in its own directory. 'dir' must point to the + * directory containing executable program, 'p' must point to the + * executable program name relative to 'dir'. + */ + if ((p = strrchr(prog, DIRSEP)) == NULL) { + snprintf(dir, sizeof(dir), "%s", "."); + } else { + snprintf(dir, sizeof(dir), "%.*s", (int) (p - prog), prog); + prog = p + 1; + } + + if (!mg_socketpair(fds, SOCK_STREAM)) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + return; + } + +#ifndef _WIN32 + struct sigaction sa; + + sigemptyset(&sa.sa_mask); + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + sigaction(SIGCHLD, &sa, NULL); +#endif + + if (mg_start_process(opts->cgi_interpreter, prog, blk.buf, blk.vars, dir, + fds[1]) != 0) { + size_t n = nc->recv_mbuf.len - (hm->message.len - hm->body.len); + struct mg_connection *cgi_nc = + mg_add_sock(nc->mgr, fds[0], mg_cgi_ev_handler MG_UD_ARG(nc)); + struct mg_http_proto_data *cgi_pd = mg_http_get_proto_data(nc); + cgi_pd->cgi.cgi_nc = cgi_nc; +#if !MG_ENABLE_CALLBACK_USERDATA + cgi_pd->cgi.cgi_nc->user_data = nc; +#endif + nc->flags |= MG_F_HTTP_CGI_PARSE_HEADERS; + /* Push POST data to the CGI */ + if (n > 0 && n < nc->recv_mbuf.len) { + mg_send(cgi_pd->cgi.cgi_nc, hm->body.p, n); + } + mbuf_remove(&nc->recv_mbuf, nc->recv_mbuf.len); + } else { + closesocket(fds[0]); + mg_http_send_error(nc, 500, "CGI failure"); + } + +#ifndef _WIN32 + closesocket(fds[1]); /* On Windows, CGI stdio thread closes that socket */ +#endif +} + +MG_INTERNAL void mg_http_free_proto_data_cgi(struct mg_http_proto_data_cgi *d) { + if (d == NULL) return; + if (d->cgi_nc != NULL) { + d->cgi_nc->flags |= MG_F_CLOSE_IMMEDIATELY; + d->cgi_nc->user_data = NULL; + } + memset(d, 0, sizeof(*d)); +} + +#endif /* MG_ENABLE_HTTP && MG_ENABLE_HTTP_CGI */ diff --git a/src/mongoose-6.11/src/mg_http_client.h b/src/mongoose-6.11/src/mg_http_client.h new file mode 100644 index 0000000..9597ec5 --- /dev/null +++ b/src/mongoose-6.11/src/mg_http_client.h @@ -0,0 +1,62 @@ +/* + * === Client API reference + */ + +#ifndef CS_MONGOOSE_SRC_HTTP_CLIENT_H_ +#define CS_MONGOOSE_SRC_HTTP_CLIENT_H_ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * Helper function that creates an outbound HTTP connection. + * + * `url` is the URL to fetch. It must be properly URL-encoded, e.g. have + * no spaces, etc. By default, `mg_connect_http()` sends the Connection and + * Host headers. `extra_headers` is an extra HTTP header to send, e.g. + * `"User-Agent: my-app\r\n"`. + * If `post_data` is NULL, then a GET request is created. Otherwise, a POST + * request is created with the specified POST data. Note that if the data being + * posted is a form submission, the `Content-Type` header should be set + * accordingly (see example below). + * + * Examples: + * + * ```c + * nc1 = mg_connect_http(mgr, ev_handler_1, "http://www.google.com", NULL, + * NULL); + * nc2 = mg_connect_http(mgr, ev_handler_1, "https://github.com", NULL, NULL); + * nc3 = mg_connect_http( + * mgr, ev_handler_1, "my_server:8000/form_submit/", + * "Content-Type: application/x-www-form-urlencoded\r\n", + * "var_1=value_1&var_2=value_2"); + * ``` + */ +struct mg_connection *mg_connect_http( + struct mg_mgr *mgr, + MG_CB(mg_event_handler_t event_handler, void *user_data), const char *url, + const char *extra_headers, const char *post_data); + +/* + * Helper function that creates an outbound HTTP connection. + * + * Mostly identical to mg_connect_http, but allows you to provide extra + *parameters + * (for example, SSL parameters) + */ +struct mg_connection *mg_connect_http_opt( + struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data), + struct mg_connect_opts opts, const char *url, const char *extra_headers, + const char *post_data); + +/* Creates digest authentication header for a client request. */ +int mg_http_create_digest_auth_header(char *buf, size_t buf_len, + const char *method, const char *uri, + const char *auth_domain, const char *user, + const char *passwd, const char *nonce); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* CS_MONGOOSE_SRC_HTTP_CLIENT_H_ */ diff --git a/src/mongoose-6.11/src/mg_http_server.h b/src/mongoose-6.11/src/mg_http_server.h new file mode 100644 index 0000000..c4dfee4 --- /dev/null +++ b/src/mongoose-6.11/src/mg_http_server.h @@ -0,0 +1,561 @@ +/* + * === Server API reference + */ + +#ifndef CS_MONGOOSE_SRC_HTTP_SERVER_H_ +#define CS_MONGOOSE_SRC_HTTP_SERVER_H_ + +#if MG_ENABLE_HTTP + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * Parses a HTTP message. + * + * `is_req` should be set to 1 if parsing a request, 0 if reply. + * + * Returns the number of bytes parsed. If HTTP message is + * incomplete `0` is returned. On parse error, a negative number is returned. + */ +int mg_parse_http(const char *s, int n, struct http_message *hm, int is_req); + +/* + * Searches and returns the header `name` in parsed HTTP message `hm`. + * If header is not found, NULL is returned. Example: + * + * struct mg_str *host_hdr = mg_get_http_header(hm, "Host"); + */ +struct mg_str *mg_get_http_header(struct http_message *hm, const char *name); + +/* + * Parses the HTTP header `hdr`. Finds variable `var_name` and stores its value + * in the buffer `*buf`, `buf_size`. If the buffer size is not enough, + * allocates a buffer of required size and writes it to `*buf`, similar to + * asprintf(). The caller should always check whether the buffer was updated, + * and free it if so. + * + * This function is supposed to parse cookies, authentication headers, etc. + * Example (error handling omitted): + * + * char user_buf[20]; + * char *user = user_buf; + * struct mg_str *hdr = mg_get_http_header(hm, "Authorization"); + * mg_http_parse_header2(hdr, "username", &user, sizeof(user_buf)); + * // ... do something useful with user + * if (user != user_buf) { + * free(user); + * } + * + * Returns the length of the variable's value. If variable is not found, 0 is + * returned. + */ +int mg_http_parse_header2(struct mg_str *hdr, const char *var_name, char **buf, + size_t buf_size); + +/* + * DEPRECATED: use mg_http_parse_header2() instead. + * + * Same as mg_http_parse_header2(), but takes buffer as a `char *` (instead of + * `char **`), and thus it cannot allocate a new buffer if the provided one + * is not enough, and just returns 0 in that case. + */ +int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf, + size_t buf_size) +#ifdef __GNUC__ + __attribute__((deprecated)); +#endif +; + +/* + * Gets and parses the Authorization: Basic header + * Returns -1 if no Authorization header is found, or if + * mg_parse_http_basic_auth + * fails parsing the resulting header. + */ +int mg_get_http_basic_auth(struct http_message *hm, char *user, size_t user_len, + char *pass, size_t pass_len); + +/* + * Parses the Authorization: Basic header + * Returns -1 iif the authorization type is not "Basic" or any other error such + * as incorrectly encoded base64 user password pair. + */ +int mg_parse_http_basic_auth(struct mg_str *hdr, char *user, size_t user_len, + char *pass, size_t pass_len); + +/* + * Parses the buffer `buf`, `buf_len` that contains multipart form data chunks. + * Stores the chunk name in a `var_name`, `var_name_len` buffer. + * If a chunk is an uploaded file, then `file_name`, `file_name_len` is + * filled with an uploaded file name. `chunk`, `chunk_len` + * points to the chunk data. + * + * Return: number of bytes to skip to the next chunk or 0 if there are + * no more chunks. + * + * Usage example: + * + * ```c + * static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { + * switch(ev) { + * case MG_EV_HTTP_REQUEST: { + * struct http_message *hm = (struct http_message *) ev_data; + * char var_name[100], file_name[100]; + * const char *chunk; + * size_t chunk_len, n1, n2; + * + * n1 = n2 = 0; + * while ((n2 = mg_parse_multipart(hm->body.p + n1, + * hm->body.len - n1, + * var_name, sizeof(var_name), + * file_name, sizeof(file_name), + * &chunk, &chunk_len)) > 0) { + * printf("var: %s, file_name: %s, size: %d, chunk: [%.*s]\n", + * var_name, file_name, (int) chunk_len, + * (int) chunk_len, chunk); + * n1 += n2; + * } + * } + * break; + * ``` + */ +size_t mg_parse_multipart(const char *buf, size_t buf_len, char *var_name, + size_t var_name_len, char *file_name, + size_t file_name_len, const char **chunk, + size_t *chunk_len); + +/* + * Fetches a HTTP form variable. + * + * Fetches a variable `name` from a `buf` into a buffer specified by `dst`, + * `dst_len`. The destination is always zero-terminated. Returns the length of + * a fetched variable. If not found, 0 is returned. `buf` must be valid + * url-encoded buffer. If destination is too small or an error occured, + * negative number is returned. + */ +int mg_get_http_var(const struct mg_str *buf, const char *name, char *dst, + size_t dst_len); + +#if MG_ENABLE_FILESYSTEM +/* + * This structure defines how `mg_serve_http()` works. + * Best practice is to set only required settings, and leave the rest as NULL. + */ +struct mg_serve_http_opts { + /* Path to web root directory */ + const char *document_root; + + /* List of index files. Default is "" */ + const char *index_files; + + /* + * Leave as NULL to disable authentication. + * To enable directory protection with authentication, set this to ".htpasswd" + * Then, creating ".htpasswd" file in any directory automatically protects + * it with digest authentication. + * Use `mongoose` web server binary, or `htdigest` Apache utility to + * create/manipulate passwords file. + * Make sure `auth_domain` is set to a valid domain name. + */ + const char *per_directory_auth_file; + + /* Authorization domain (domain name of this web server) */ + const char *auth_domain; + + /* + * Leave as NULL to disable authentication. + * Normally, only selected directories in the document root are protected. + * If absolutely every access to the web server needs to be authenticated, + * regardless of the URI, set this option to the path to the passwords file. + * Format of that file is the same as ".htpasswd" file. Make sure that file + * is located outside document root to prevent people fetching it. + */ + const char *global_auth_file; + + /* Set to "no" to disable directory listing. Enabled by default. */ + const char *enable_directory_listing; + + /* + * SSI files pattern. If not set, "**.shtml$|**.shtm$" is used. + * + * All files that match ssi_pattern are treated as SSI. + * + * Server Side Includes (SSI) is a simple interpreted server-side scripting + * language which is most commonly used to include the contents of a file + * into a web page. It can be useful when it is desirable to include a common + * piece of code throughout a website, for example, headers and footers. + * + * In order for a webpage to recognize an SSI-enabled HTML file, the + * filename should end with a special extension, by default the extension + * should be either .shtml or .shtm + * + * Unknown SSI directives are silently ignored by Mongoose. Currently, + * the following SSI directives are supported: + * <!--#include FILE_TO_INCLUDE --> + * <!--#exec "COMMAND_TO_EXECUTE" --> + * <!--#call COMMAND --> + * + * Note that <!--#include ...> directive supports three path + *specifications: + * + * <!--#include virtual="path" --> Path is relative to web server root + * <!--#include abspath="path" --> Path is absolute or relative to the + * web server working dir + * <!--#include file="path" -->, Path is relative to current document + * <!--#include "path" --> + * + * The include directive may be used to include the contents of a file or + * the result of running a CGI script. + * + * The exec directive is used to execute + * a command on a server, and show command's output. Example: + * + * <!--#exec "ls -l" --> + * + * The call directive is a way to invoke a C handler from the HTML page. + * On each occurence of <!--#call COMMAND OPTIONAL_PARAMS> directive, + * Mongoose calls a registered event handler with MG_EV_SSI_CALL event, + * and event parameter will point to the COMMAND OPTIONAL_PARAMS string. + * An event handler can output any text, for example by calling + * `mg_printf()`. This is a flexible way of generating a web page on + * server side by calling a C event handler. Example: + * + * <!--#call foo --> ... <!--#call bar --> + * + * In the event handler: + * case MG_EV_SSI_CALL: { + * const char *param = (const char *) ev_data; + * if (strcmp(param, "foo") == 0) { + * mg_printf(c, "hello from foo"); + * } else if (strcmp(param, "bar") == 0) { + * mg_printf(c, "hello from bar"); + * } + * break; + * } + */ + const char *ssi_pattern; + + /* IP ACL. By default, NULL, meaning all IPs are allowed to connect */ + const char *ip_acl; + +#if MG_ENABLE_HTTP_URL_REWRITES + /* URL rewrites. + * + * Comma-separated list of `uri_pattern=url_file_or_directory_path` rewrites. + * When HTTP request is received, Mongoose constructs a file name from the + * requested URI by combining `document_root` and the URI. However, if the + * rewrite option is used and `uri_pattern` matches requested URI, then + * `document_root` is ignored. Instead, `url_file_or_directory_path` is used, + * which should be a full path name or a path relative to the web server's + * current working directory. It can also be an URI (http:// or https://) + * in which case mongoose will behave as a reverse proxy for that destination. + * + * Note that `uri_pattern`, as all Mongoose patterns, is a prefix pattern. + * + * If uri_pattern starts with `@` symbol, then Mongoose compares it with the + * HOST header of the request. If they are equal, Mongoose sets document root + * to `file_or_directory_path`, implementing virtual hosts support. + * Example: `@foo.com=/document/root/for/foo.com` + * + * If `uri_pattern` starts with `%` symbol, then Mongoose compares it with + * the listening port. If they match, then Mongoose issues a 301 redirect. + * For example, to redirect all HTTP requests to the + * HTTPS port, do `%80=https://my.site.com`. Note that the request URI is + * automatically appended to the redirect location. + */ + const char *url_rewrites; +#endif + + /* DAV document root. If NULL, DAV requests are going to fail. */ + const char *dav_document_root; + + /* + * DAV passwords file. If NULL, DAV requests are going to fail. + * If passwords file is set to "-", then DAV auth is disabled. + */ + const char *dav_auth_file; + + /* Glob pattern for the files to hide. */ + const char *hidden_file_pattern; + + /* Set to non-NULL to enable CGI, e.g. **.cgi$|**.php$" */ + const char *cgi_file_pattern; + + /* If not NULL, ignore CGI script hashbang and use this interpreter */ + const char *cgi_interpreter; + + /* + * Comma-separated list of Content-Type overrides for path suffixes, e.g. + * ".txt=text/plain; charset=utf-8,.c=text/plain" + */ + const char *custom_mime_types; + + /* + * Extra HTTP headers to add to each server response. + * Example: to enable CORS, set this to "Access-Control-Allow-Origin: *". + */ + const char *extra_headers; +}; + +/* + * Serves given HTTP request according to the `options`. + * + * Example code snippet: + * + * ```c + * static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { + * struct http_message *hm = (struct http_message *) ev_data; + * struct mg_serve_http_opts opts = { .document_root = "/var/www" }; // C99 + * + * switch (ev) { + * case MG_EV_HTTP_REQUEST: + * mg_serve_http(nc, hm, opts); + * break; + * default: + * break; + * } + * } + * ``` + */ +void mg_serve_http(struct mg_connection *nc, struct http_message *hm, + struct mg_serve_http_opts opts); + +/* + * Serves a specific file with a given MIME type and optional extra headers. + * + * Example code snippet: + * + * ```c + * static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { + * switch (ev) { + * case MG_EV_HTTP_REQUEST: { + * struct http_message *hm = (struct http_message *) ev_data; + * mg_http_serve_file(nc, hm, "file.txt", + * mg_mk_str("text/plain"), mg_mk_str("")); + * break; + * } + * ... + * } + * } + * ``` + */ +void mg_http_serve_file(struct mg_connection *nc, struct http_message *hm, + const char *path, const struct mg_str mime_type, + const struct mg_str extra_headers); + +#if MG_ENABLE_HTTP_STREAMING_MULTIPART + +/* Callback prototype for `mg_file_upload_handler()`. */ +typedef struct mg_str (*mg_fu_fname_fn)(struct mg_connection *nc, + struct mg_str fname); + +/* + * File upload handler. + * This handler can be used to implement file uploads with minimum code. + * This handler will process MG_EV_HTTP_PART_* events and store file data into + * a local file. + * `local_name_fn` will be invoked with whatever name was provided by the client + * and will expect the name of the local file to open. A return value of NULL + * will abort file upload (client will get a "403 Forbidden" response). If + * non-null, the returned string must be heap-allocated and will be freed by + * the caller. + * Exception: it is ok to return the same string verbatim. + * + * Example: + * + * ```c + * struct mg_str upload_fname(struct mg_connection *nc, struct mg_str fname) { + * // Just return the same filename. Do not actually do this except in test! + * // fname is user-controlled and needs to be sanitized. + * return fname; + * } + * void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { + * switch (ev) { + * ... + * case MG_EV_HTTP_PART_BEGIN: + * case MG_EV_HTTP_PART_DATA: + * case MG_EV_HTTP_PART_END: + * mg_file_upload_handler(nc, ev, ev_data, upload_fname); + * break; + * } + * } + * ``` + */ +void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data, + mg_fu_fname_fn local_name_fn + MG_UD_ARG(void *user_data)); +#endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */ +#endif /* MG_ENABLE_FILESYSTEM */ + +/* + * Registers a callback for a specified http endpoint + * Note: if callback is registered it is called instead of the + * callback provided in mg_bind + * + * Example code snippet: + * + * ```c + * static void handle_hello1(struct mg_connection *nc, int ev, void *ev_data) { + * (void) ev; (void) ev_data; + * mg_printf(nc, "HTTP/1.0 200 OK\r\n\r\n[I am Hello1]"); + * nc->flags |= MG_F_SEND_AND_CLOSE; + * } + * + * static void handle_hello2(struct mg_connection *nc, int ev, void *ev_data) { + * (void) ev; (void) ev_data; + * mg_printf(nc, "HTTP/1.0 200 OK\r\n\r\n[I am Hello2]"); + * nc->flags |= MG_F_SEND_AND_CLOSE; + * } + * + * void init() { + * nc = mg_bind(&mgr, local_addr, cb1); + * mg_register_http_endpoint(nc, "/hello1", handle_hello1); + * mg_register_http_endpoint(nc, "/hello1/hello2", handle_hello2); + * } + * ``` + */ +void mg_register_http_endpoint(struct mg_connection *nc, const char *uri_path, + MG_CB(mg_event_handler_t handler, + void *user_data)); + +struct mg_http_endpoint_opts { + void *user_data; + /* Authorization domain (realm) */ + const char *auth_domain; + const char *auth_file; +}; + +void mg_register_http_endpoint_opt(struct mg_connection *nc, + const char *uri_path, + mg_event_handler_t handler, + struct mg_http_endpoint_opts opts); + +/* + * Authenticates a HTTP request against an opened password file. + * Returns 1 if authenticated, 0 otherwise. + */ +int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain, + FILE *fp); + +/* + * Authenticates given response params against an opened password file. + * Returns 1 if authenticated, 0 otherwise. + * + * It's used by mg_http_check_digest_auth(). + */ +int mg_check_digest_auth(struct mg_str method, struct mg_str uri, + struct mg_str username, struct mg_str cnonce, + struct mg_str response, struct mg_str qop, + struct mg_str nc, struct mg_str nonce, + struct mg_str auth_domain, FILE *fp); + +/* + * Sends buffer `buf` of size `len` to the client using chunked HTTP encoding. + * This function sends the buffer size as hex number + newline first, then + * the buffer itself, then the newline. For example, + * `mg_send_http_chunk(nc, "foo", 3)` will append the `3\r\nfoo\r\n` string + * to the `nc->send_mbuf` output IO buffer. + * + * NOTE: The HTTP header "Transfer-Encoding: chunked" should be sent prior to + * using this function. + * + * NOTE: do not forget to send an empty chunk at the end of the response, + * to tell the client that everything was sent. Example: + * + * ``` + * mg_printf_http_chunk(nc, "%s", "my response!"); + * mg_send_http_chunk(nc, "", 0); // Tell the client we're finished + * ``` + */ +void mg_send_http_chunk(struct mg_connection *nc, const char *buf, size_t len); + +/* + * Sends a printf-formatted HTTP chunk. + * Functionality is similar to `mg_send_http_chunk()`. + */ +void mg_printf_http_chunk(struct mg_connection *nc, const char *fmt, ...); + +/* + * Sends the response status line. + * If `extra_headers` is not NULL, then `extra_headers` are also sent + * after the response line. `extra_headers` must NOT end end with new line. + * Example: + * + * mg_send_response_line(nc, 200, "Access-Control-Allow-Origin: *"); + * + * Will result in: + * + * HTTP/1.1 200 OK\r\n + * Access-Control-Allow-Origin: *\r\n + */ +void mg_send_response_line(struct mg_connection *nc, int status_code, + const char *extra_headers); + +/* + * Sends an error response. If reason is NULL, the message will be inferred + * from the error code (if supported). + */ +void mg_http_send_error(struct mg_connection *nc, int code, const char *reason); + +/* + * Sends a redirect response. + * `status_code` should be either 301 or 302 and `location` point to the + * new location. + * If `extra_headers` is not empty, then `extra_headers` are also sent + * after the response line. `extra_headers` must NOT end end with new line. + * + * Example: + * + * mg_http_send_redirect(nc, 302, mg_mk_str("/login"), mg_mk_str(NULL)); + */ +void mg_http_send_redirect(struct mg_connection *nc, int status_code, + const struct mg_str location, + const struct mg_str extra_headers); + +/* + * Sends the response line and headers. + * This function sends the response line with the `status_code`, and + * automatically + * sends one header: either "Content-Length" or "Transfer-Encoding". + * If `content_length` is negative, then "Transfer-Encoding: chunked" header + * is sent, otherwise, "Content-Length" header is sent. + * + * NOTE: If `Transfer-Encoding` is `chunked`, then message body must be sent + * using `mg_send_http_chunk()` or `mg_printf_http_chunk()` functions. + * Otherwise, `mg_send()` or `mg_printf()` must be used. + * Extra headers could be set through `extra_headers`. Note `extra_headers` + * must NOT be terminated by a new line. + */ +void mg_send_head(struct mg_connection *n, int status_code, + int64_t content_length, const char *extra_headers); + +/* + * Sends a printf-formatted HTTP chunk, escaping HTML tags. + */ +void mg_printf_html_escape(struct mg_connection *nc, const char *fmt, ...); + +#if MG_ENABLE_HTTP_URL_REWRITES +/* + * Proxies a given request to a given upstream http server. The path prefix + * in `mount` will be stripped of the path requested to the upstream server, + * e.g. if mount is /api and upstream is http://localhost:8001/foo + * then an incoming request to /api/bar will cause a request to + * http://localhost:8001/foo/bar + * + * EXPERIMENTAL API. Please use http_serve_http + url_rewrites if a static + * mapping is good enough. + */ +void mg_http_reverse_proxy(struct mg_connection *nc, + const struct http_message *hm, struct mg_str mount, + struct mg_str upstream); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* MG_ENABLE_HTTP */ + +#endif /* CS_MONGOOSE_SRC_HTTP_SERVER_H_ */ diff --git a/src/mongoose-6.11/src/mg_http_ssi.c b/src/mongoose-6.11/src/mg_http_ssi.c new file mode 100644 index 0000000..6d384db --- /dev/null +++ b/src/mongoose-6.11/src/mg_http_ssi.c @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_HTTP && MG_ENABLE_HTTP_SSI && MG_ENABLE_FILESYSTEM + +static void mg_send_ssi_file(struct mg_connection *nc, struct http_message *hm, + const char *path, FILE *fp, int include_level, + const struct mg_serve_http_opts *opts); + +static void mg_send_file_data(struct mg_connection *nc, FILE *fp) { + char buf[BUFSIZ]; + size_t n; + while ((n = mg_fread(buf, 1, sizeof(buf), fp)) > 0) { + mg_send(nc, buf, n); + } +} + +static void mg_do_ssi_include(struct mg_connection *nc, struct http_message *hm, + const char *ssi, char *tag, int include_level, + const struct mg_serve_http_opts *opts) { + char file_name[MG_MAX_PATH], path[MG_MAX_PATH], *p; + FILE *fp; + + /* + * sscanf() is safe here, since send_ssi_file() also uses buffer + * of size MG_BUF_LEN to get the tag. So strlen(tag) is always < MG_BUF_LEN. + */ + if (sscanf(tag, " virtual=\"%[^\"]\"", file_name) == 1) { + /* File name is relative to the webserver root */ + snprintf(path, sizeof(path), "%s/%s", opts->document_root, file_name); + } else if (sscanf(tag, " abspath=\"%[^\"]\"", file_name) == 1) { + /* + * File name is relative to the webserver working directory + * or it is absolute system path + */ + snprintf(path, sizeof(path), "%s", file_name); + } else if (sscanf(tag, " file=\"%[^\"]\"", file_name) == 1 || + sscanf(tag, " \"%[^\"]\"", file_name) == 1) { + /* File name is relative to the currect document */ + snprintf(path, sizeof(path), "%s", ssi); + if ((p = strrchr(path, DIRSEP)) != NULL) { + p[1] = '\0'; + } + snprintf(path + strlen(path), sizeof(path) - strlen(path), "%s", file_name); + } else { + mg_printf(nc, "Bad SSI #include: [%s]", tag); + return; + } + + if ((fp = mg_fopen(path, "rb")) == NULL) { + mg_printf(nc, "SSI include error: mg_fopen(%s): %s", path, + strerror(mg_get_errno())); + } else { + mg_set_close_on_exec((sock_t) fileno(fp)); + if (mg_match_prefix(opts->ssi_pattern, strlen(opts->ssi_pattern), path) > + 0) { + mg_send_ssi_file(nc, hm, path, fp, include_level + 1, opts); + } else { + mg_send_file_data(nc, fp); + } + fclose(fp); + } +} + +#if MG_ENABLE_HTTP_SSI_EXEC +static void do_ssi_exec(struct mg_connection *nc, char *tag) { + char cmd[BUFSIZ]; + FILE *fp; + + if (sscanf(tag, " \"%[^\"]\"", cmd) != 1) { + mg_printf(nc, "Bad SSI #exec: [%s]", tag); + } else if ((fp = popen(cmd, "r")) == NULL) { + mg_printf(nc, "Cannot SSI #exec: [%s]: %s", cmd, strerror(mg_get_errno())); + } else { + mg_send_file_data(nc, fp); + pclose(fp); + } +} +#endif /* MG_ENABLE_HTTP_SSI_EXEC */ + +/* + * SSI directive has the following format: + * + */ +static void mg_send_ssi_file(struct mg_connection *nc, struct http_message *hm, + const char *path, FILE *fp, int include_level, + const struct mg_serve_http_opts *opts) { + static const struct mg_str btag = MG_MK_STR(" */ + buf[i--] = '\0'; + while (i > 0 && buf[i] == ' ') { + buf[i--] = '\0'; + } + + /* Handle known SSI directives */ + if (strncmp(p, d_include.p, d_include.len) == 0) { + mg_do_ssi_include(nc, hm, path, p + d_include.len + 1, include_level, + opts); + } else if (strncmp(p, d_call.p, d_call.len) == 0) { + struct mg_ssi_call_ctx cctx; + memset(&cctx, 0, sizeof(cctx)); + cctx.req = hm; + cctx.file = mg_mk_str(path); + cctx.arg = mg_mk_str(p + d_call.len + 1); + mg_call(nc, NULL, nc->user_data, MG_EV_SSI_CALL, + (void *) cctx.arg.p); /* NUL added above */ + mg_call(nc, NULL, nc->user_data, MG_EV_SSI_CALL_CTX, &cctx); +#if MG_ENABLE_HTTP_SSI_EXEC + } else if (strncmp(p, d_exec.p, d_exec.len) == 0) { + do_ssi_exec(nc, p + d_exec.len + 1); +#endif + } else { + /* Silently ignore unknown SSI directive. */ + } + len = 0; + } else if (ch == '<') { + in_ssi_tag = 1; + if (len > 0) { + mg_send(nc, buf, (size_t) len); + } + len = 0; + buf[len++] = ch & 0xff; + } else if (in_ssi_tag) { + if (len == (int) btag.len && strncmp(buf, btag.p, btag.len) != 0) { + /* Not an SSI tag */ + in_ssi_tag = 0; + } else if (len == (int) sizeof(buf) - 2) { + mg_printf(nc, "%s: SSI tag is too large", path); + len = 0; + } + buf[len++] = ch & 0xff; + } else { + buf[len++] = ch & 0xff; + if (len == (int) sizeof(buf)) { + mg_send(nc, buf, (size_t) len); + len = 0; + } + } + } + + /* Send the rest of buffered data */ + if (len > 0) { + mg_send(nc, buf, (size_t) len); + } +} + +MG_INTERNAL void mg_handle_ssi_request(struct mg_connection *nc, + struct http_message *hm, + const char *path, + const struct mg_serve_http_opts *opts) { + FILE *fp; + struct mg_str mime_type; + DBG(("%p %s", nc, path)); + + if ((fp = mg_fopen(path, "rb")) == NULL) { + mg_http_send_error(nc, 404, NULL); + } else { + mg_set_close_on_exec((sock_t) fileno(fp)); + + mime_type = mg_get_mime_type(path, "text/plain", opts); + mg_send_response_line(nc, 200, opts->extra_headers); + mg_printf(nc, + "Content-Type: %.*s\r\n" + "Connection: close\r\n\r\n", + (int) mime_type.len, mime_type.p); + mg_send_ssi_file(nc, hm, path, fp, 0, opts); + fclose(fp); + nc->flags |= MG_F_SEND_AND_CLOSE; + } +} + +#endif /* MG_ENABLE_HTTP_SSI && MG_ENABLE_HTTP && MG_ENABLE_FILESYSTEM */ diff --git a/src/mongoose-6.11/src/mg_http_webdav.c b/src/mongoose-6.11/src/mg_http_webdav.c new file mode 100644 index 0000000..6efb928 --- /dev/null +++ b/src/mongoose-6.11/src/mg_http_webdav.c @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_HTTP && MG_ENABLE_HTTP_WEBDAV + +MG_INTERNAL int mg_is_dav_request(const struct mg_str *s) { + static const char *methods[] = { + "PUT", + "DELETE", + "MKCOL", + "PROPFIND", + "MOVE" +#if MG_ENABLE_FAKE_DAVLOCK + , + "LOCK", + "UNLOCK" +#endif + }; + size_t i; + + for (i = 0; i < ARRAY_SIZE(methods); i++) { + if (mg_vcmp(s, methods[i]) == 0) { + return 1; + } + } + + return 0; +} + +static int mg_mkdir(const char *path, uint32_t mode) { +#ifndef _WIN32 + return mkdir(path, mode); +#else + (void) mode; + return _mkdir(path); +#endif +} + +static void mg_print_props(struct mg_connection *nc, const char *name, + cs_stat_t *stp) { + char mtime[64]; + time_t t = stp->st_mtime; /* store in local variable for NDK compile */ + struct mg_str name_esc = mg_url_encode(mg_mk_str(name)); + mg_gmt_time_string(mtime, sizeof(mtime), &t); + mg_printf(nc, + "" + "%s" + "" + "" + "%s" + "%" INT64_FMT + "" + "%s" + "" + "HTTP/1.1 200 OK" + "" + "\n", + name_esc.p, S_ISDIR(stp->st_mode) ? "" : "", + (int64_t) stp->st_size, mtime); + free((void *) name_esc.p); +} + +MG_INTERNAL void mg_handle_propfind(struct mg_connection *nc, const char *path, + cs_stat_t *stp, struct http_message *hm, + struct mg_serve_http_opts *opts) { + static const char header[] = + "HTTP/1.1 207 Multi-Status\r\n" + "Connection: close\r\n" + "Content-Type: text/xml; charset=utf-8\r\n\r\n" + "" + "\n"; + static const char footer[] = "\n"; + const struct mg_str *depth = mg_get_http_header(hm, "Depth"); + + /* Print properties for the requested resource itself */ + if (S_ISDIR(stp->st_mode) && + strcmp(opts->enable_directory_listing, "yes") != 0) { + mg_printf(nc, "%s", "HTTP/1.1 403 Directory Listing Denied\r\n\r\n"); + } else { + char uri[MG_MAX_PATH]; + mg_send(nc, header, sizeof(header) - 1); + snprintf(uri, sizeof(uri), "%.*s", (int) hm->uri.len, hm->uri.p); + mg_print_props(nc, uri, stp); + if (S_ISDIR(stp->st_mode) && (depth == NULL || mg_vcmp(depth, "0") != 0)) { + mg_scan_directory(nc, path, opts, mg_print_props); + } + mg_send(nc, footer, sizeof(footer) - 1); + nc->flags |= MG_F_SEND_AND_CLOSE; + } +} + +#if MG_ENABLE_FAKE_DAVLOCK +/* + * Windows explorer (probably there are another WebDav clients like it) + * requires LOCK support in webdav. W/out this, it still works, but fails + * to save file: shows error message and offers "Save As". + * "Save as" works, but this message is very annoying. + * This is fake lock, which doesn't lock something, just returns LOCK token, + * UNLOCK always answers "OK". + * With this fake LOCK Windows Explorer looks happy and saves file. + * NOTE: that is not DAV LOCK imlementation, it is just a way to shut up + * Windows native DAV client. This is why FAKE LOCK is not enabed by default + */ +MG_INTERNAL void mg_handle_lock(struct mg_connection *nc, const char *path) { + static const char *reply = + "HTTP/1.1 207 Multi-Status\r\n" + "Connection: close\r\n" + "Content-Type: text/xml; charset=utf-8\r\n\r\n" + "" + "\n" + "\n" + "\n" + "\n" + "\n" + "opaquelocktoken:%s%u" + "" + "" + "\n" + "" + "\n"; + mg_printf(nc, reply, path, (unsigned int) mg_time()); + nc->flags |= MG_F_SEND_AND_CLOSE; +} +#endif + +MG_INTERNAL void mg_handle_mkcol(struct mg_connection *nc, const char *path, + struct http_message *hm) { + int status_code = 500; + if (hm->body.len != (size_t) ~0 && hm->body.len > 0) { + status_code = 415; + } else if (!mg_mkdir(path, 0755)) { + status_code = 201; + } else if (errno == EEXIST) { + status_code = 405; + } else if (errno == EACCES) { + status_code = 403; + } else if (errno == ENOENT) { + status_code = 409; + } else { + status_code = 500; + } + mg_http_send_error(nc, status_code, NULL); +} + +static int mg_remove_directory(const struct mg_serve_http_opts *opts, + const char *dir) { + char path[MG_MAX_PATH]; + struct dirent *dp; + cs_stat_t st; + DIR *dirp; + + if ((dirp = opendir(dir)) == NULL) return 0; + + while ((dp = readdir(dirp)) != NULL) { + if (mg_is_file_hidden((const char *) dp->d_name, opts, 1)) { + continue; + } + snprintf(path, sizeof(path), "%s%c%s", dir, '/', dp->d_name); + mg_stat(path, &st); + if (S_ISDIR(st.st_mode)) { + mg_remove_directory(opts, path); + } else { + remove(path); + } + } + closedir(dirp); + rmdir(dir); + + return 1; +} + +MG_INTERNAL void mg_handle_move(struct mg_connection *c, + const struct mg_serve_http_opts *opts, + const char *path, struct http_message *hm) { + const struct mg_str *dest = mg_get_http_header(hm, "Destination"); + if (dest == NULL) { + mg_http_send_error(c, 411, NULL); + } else { + const char *p = (char *) memchr(dest->p, '/', dest->len); + if (p != NULL && p[1] == '/' && + (p = (char *) memchr(p + 2, '/', dest->p + dest->len - p)) != NULL) { + char buf[MG_MAX_PATH]; + snprintf(buf, sizeof(buf), "%s%.*s", opts->dav_document_root, + (int) (dest->p + dest->len - p), p); + if (rename(path, buf) == 0) { + mg_http_send_error(c, 200, NULL); + } else { + mg_http_send_error(c, 418, NULL); + } + } else { + mg_http_send_error(c, 500, NULL); + } + } +} + +MG_INTERNAL void mg_handle_delete(struct mg_connection *nc, + const struct mg_serve_http_opts *opts, + const char *path) { + cs_stat_t st; + if (mg_stat(path, &st) != 0) { + mg_http_send_error(nc, 404, NULL); + } else if (S_ISDIR(st.st_mode)) { + mg_remove_directory(opts, path); + mg_http_send_error(nc, 204, NULL); + } else if (remove(path) == 0) { + mg_http_send_error(nc, 204, NULL); + } else { + mg_http_send_error(nc, 423, NULL); + } +} + +/* Return -1 on error, 1 on success. */ +static int mg_create_itermediate_directories(const char *path) { + const char *s; + + /* Create intermediate directories if they do not exist */ + for (s = path + 1; *s != '\0'; s++) { + if (*s == '/') { + char buf[MG_MAX_PATH]; + cs_stat_t st; + snprintf(buf, sizeof(buf), "%.*s", (int) (s - path), path); + buf[sizeof(buf) - 1] = '\0'; + if (mg_stat(buf, &st) != 0 && mg_mkdir(buf, 0755) != 0) { + return -1; + } + } + } + + return 1; +} + +MG_INTERNAL void mg_handle_put(struct mg_connection *nc, const char *path, + struct http_message *hm) { + struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); + cs_stat_t st; + const struct mg_str *cl_hdr = mg_get_http_header(hm, "Content-Length"); + int rc, status_code = mg_stat(path, &st) == 0 ? 200 : 201; + + mg_http_free_proto_data_file(&pd->file); + if ((rc = mg_create_itermediate_directories(path)) == 0) { + mg_printf(nc, "HTTP/1.1 %d OK\r\nContent-Length: 0\r\n\r\n", status_code); + } else if (rc == -1) { + mg_http_send_error(nc, 500, NULL); + } else if (cl_hdr == NULL) { + mg_http_send_error(nc, 411, NULL); + } else if ((pd->file.fp = mg_fopen(path, "w+b")) == NULL) { + mg_http_send_error(nc, 500, NULL); + } else { + const struct mg_str *range_hdr = mg_get_http_header(hm, "Content-Range"); + int64_t r1 = 0, r2 = 0; + pd->file.type = DATA_PUT; + mg_set_close_on_exec((sock_t) fileno(pd->file.fp)); + pd->file.cl = to64(cl_hdr->p); + if (range_hdr != NULL && + mg_http_parse_range_header(range_hdr, &r1, &r2) > 0) { + status_code = 206; + fseeko(pd->file.fp, r1, SEEK_SET); + pd->file.cl = r2 > r1 ? r2 - r1 + 1 : pd->file.cl - r1; + } + mg_printf(nc, "HTTP/1.1 %d OK\r\nContent-Length: 0\r\n\r\n", status_code); + /* Remove HTTP request from the mbuf, leave only payload */ + mbuf_remove(&nc->recv_mbuf, hm->message.len - hm->body.len); + mg_http_transfer_file_data(nc); + } +} + +#endif /* MG_ENABLE_HTTP && MG_ENABLE_HTTP_WEBDAV */ diff --git a/src/mongoose-6.11/src/mg_http_websocket.c b/src/mongoose-6.11/src/mg_http_websocket.c new file mode 100644 index 0000000..e69e4b7 --- /dev/null +++ b/src/mongoose-6.11/src/mg_http_websocket.c @@ -0,0 +1,517 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_HTTP && MG_ENABLE_HTTP_WEBSOCKET + +#include "common/cs_sha1.h" + +#ifndef MG_WEBSOCKET_PING_INTERVAL_SECONDS +#define MG_WEBSOCKET_PING_INTERVAL_SECONDS 5 +#endif + +#define FLAGS_MASK_FIN (1 << 7) +#define FLAGS_MASK_OP 0x0f + +static int mg_is_ws_fragment(unsigned char flags) { + return (flags & FLAGS_MASK_FIN) == 0 || + (flags & FLAGS_MASK_OP) == WEBSOCKET_OP_CONTINUE; +} + +static int mg_is_ws_first_fragment(unsigned char flags) { + return (flags & FLAGS_MASK_FIN) == 0 && + (flags & FLAGS_MASK_OP) != WEBSOCKET_OP_CONTINUE; +} + +static int mg_is_ws_control_frame(unsigned char flags) { + unsigned char op = (flags & FLAGS_MASK_OP); + return op == WEBSOCKET_OP_CLOSE || op == WEBSOCKET_OP_PING || + op == WEBSOCKET_OP_PONG; +} + +static void mg_handle_incoming_websocket_frame(struct mg_connection *nc, + struct websocket_message *wsm) { + if (wsm->flags & 0x8) { + mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_CONTROL_FRAME, wsm); + } else { + mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_FRAME, wsm); + } +} + +static struct mg_ws_proto_data *mg_ws_get_proto_data(struct mg_connection *nc) { + struct mg_http_proto_data *htd = mg_http_get_proto_data(nc); + return (htd != NULL ? &htd->ws_data : NULL); +} + +/* + * Sends a Close websocket frame with the given data, and closes the underlying + * connection. If `len` is ~0, strlen(data) is used. + */ +static void mg_ws_close(struct mg_connection *nc, const void *data, + size_t len) { + if ((int) len == ~0) { + len = strlen((const char *) data); + } + mg_send_websocket_frame(nc, WEBSOCKET_OP_CLOSE, data, len); + nc->flags |= MG_F_SEND_AND_CLOSE; +} + +static int mg_deliver_websocket_data(struct mg_connection *nc) { + /* Using unsigned char *, cause of integer arithmetic below */ + uint64_t i, data_len = 0, frame_len = 0, new_data_len = nc->recv_mbuf.len, + len, mask_len = 0, header_len = 0; + struct mg_ws_proto_data *wsd = mg_ws_get_proto_data(nc); + unsigned char *new_data = (unsigned char *) nc->recv_mbuf.buf, + *e = (unsigned char *) nc->recv_mbuf.buf + nc->recv_mbuf.len; + uint8_t flags; + int ok, reass; + + if (wsd->reass_len > 0) { + /* + * We already have some previously received data which we need to + * reassemble and deliver to the client code when we get the final + * fragment. + * + * NOTE: it doesn't mean that the current message must be a continuation: + * it might be a control frame (Close, Ping or Pong), which should be + * handled without breaking the fragmented message. + */ + + size_t existing_len = wsd->reass_len; + assert(new_data_len >= existing_len); + + new_data += existing_len; + new_data_len -= existing_len; + } + + flags = new_data[0]; + + reass = new_data_len > 0 && mg_is_ws_fragment(flags) && + !(nc->flags & MG_F_WEBSOCKET_NO_DEFRAG); + + if (reass && mg_is_ws_control_frame(flags)) { + /* + * Control frames can't be fragmented, so if we encounter fragmented + * control frame, close connection immediately. + */ + mg_ws_close(nc, "fragmented control frames are illegal", ~0); + return 0; + } else if (new_data_len > 0 && !reass && !mg_is_ws_control_frame(flags) && + wsd->reass_len > 0) { + /* + * When in the middle of a fragmented message, only the continuations + * and control frames are allowed. + */ + mg_ws_close(nc, "non-continuation in the middle of a fragmented message", + ~0); + return 0; + } + + if (new_data_len >= 2) { + len = new_data[1] & 0x7f; + mask_len = new_data[1] & FLAGS_MASK_FIN ? 4 : 0; + if (len < 126 && new_data_len >= mask_len) { + data_len = len; + header_len = 2 + mask_len; + } else if (len == 126 && new_data_len >= 4 + mask_len) { + header_len = 4 + mask_len; + data_len = ntohs(*(uint16_t *) &new_data[2]); + } else if (new_data_len >= 10 + mask_len) { + header_len = 10 + mask_len; + data_len = (((uint64_t) ntohl(*(uint32_t *) &new_data[2])) << 32) + + ntohl(*(uint32_t *) &new_data[6]); + } + } + + frame_len = header_len + data_len; + ok = (frame_len > 0 && frame_len <= new_data_len); + + /* Check for overflow */ + if (frame_len < header_len || frame_len < data_len) { + ok = 0; + mg_ws_close(nc, "overflowed message", ~0); + } + + if (ok) { + size_t cleanup_len = 0; + struct websocket_message wsm; + + wsm.size = (size_t) data_len; + wsm.data = new_data + header_len; + wsm.flags = flags; + + /* Apply mask if necessary */ + if (mask_len > 0) { + for (i = 0; i < data_len; i++) { + new_data[i + header_len] ^= (new_data + header_len - mask_len)[i % 4]; + } + } + + if (reass) { + /* This is a message fragment */ + + if (mg_is_ws_first_fragment(flags)) { + /* + * On the first fragmented frame, skip the first byte (op) and also + * reset size to 1 (op), it'll be incremented with the data len below. + */ + new_data += 1; + wsd->reass_len = 1 /* op */; + } + + /* Append this frame to the reassembled buffer */ + memmove(new_data, wsm.data, e - wsm.data); + wsd->reass_len += wsm.size; + nc->recv_mbuf.len -= wsm.data - new_data; + + if (flags & FLAGS_MASK_FIN) { + /* On last fragmented frame - call user handler and remove data */ + wsm.flags = FLAGS_MASK_FIN | nc->recv_mbuf.buf[0]; + wsm.data = (unsigned char *) nc->recv_mbuf.buf + 1 /* op */; + wsm.size = wsd->reass_len - 1 /* op */; + cleanup_len = wsd->reass_len; + wsd->reass_len = 0; + + /* Pass reassembled message to the client code. */ + mg_handle_incoming_websocket_frame(nc, &wsm); + mbuf_remove(&nc->recv_mbuf, cleanup_len); /* Cleanup frame */ + } + } else { + /* + * This is a complete message, not a fragment. It might happen in between + * of a fragmented message (in this case, WebSocket protocol requires + * current message to be a control frame). + */ + cleanup_len = (size_t) frame_len; + + /* First of all, check if we need to react on a control frame. */ + switch (flags & FLAGS_MASK_OP) { + case WEBSOCKET_OP_PING: + mg_send_websocket_frame(nc, WEBSOCKET_OP_PONG, wsm.data, wsm.size); + break; + + case WEBSOCKET_OP_CLOSE: + mg_ws_close(nc, wsm.data, wsm.size); + break; + } + + /* Pass received message to the client code. */ + mg_handle_incoming_websocket_frame(nc, &wsm); + + /* Cleanup frame */ + memmove(nc->recv_mbuf.buf + wsd->reass_len, + nc->recv_mbuf.buf + wsd->reass_len + cleanup_len, + nc->recv_mbuf.len - wsd->reass_len - cleanup_len); + nc->recv_mbuf.len -= cleanup_len; + } + } + + return ok; +} + +struct ws_mask_ctx { + size_t pos; /* zero means unmasked */ + uint32_t mask; +}; + +static uint32_t mg_ws_random_mask(void) { + uint32_t mask; +/* + * The spec requires WS client to generate hard to + * guess mask keys. From RFC6455, Section 5.3: + * + * The unpredictability of the masking key is essential to prevent + * authors of malicious applications from selecting the bytes that appear on + * the wire. + * + * Hence this feature is essential when the actual end user of this API + * is untrusted code that wouldn't have access to a lower level net API + * anyway (e.g. web browsers). Hence this feature is low prio for most + * mongoose use cases and thus can be disabled, e.g. when porting to a platform + * that lacks rand(). + */ +#if MG_DISABLE_WS_RANDOM_MASK + mask = 0xefbeadde; /* generated with a random number generator, I swear */ +#else + if (sizeof(long) >= 4) { + mask = (uint32_t) rand(); + } else if (sizeof(long) == 2) { + mask = (uint32_t) rand() << 16 | (uint32_t) rand(); + } +#endif + return mask; +} + +static void mg_send_ws_header(struct mg_connection *nc, int op, size_t len, + struct ws_mask_ctx *ctx) { + int header_len; + unsigned char header[10]; + + header[0] = + (op & WEBSOCKET_DONT_FIN ? 0x0 : FLAGS_MASK_FIN) | (op & FLAGS_MASK_OP); + if (len < 126) { + header[1] = (unsigned char) len; + header_len = 2; + } else if (len < 65535) { + uint16_t tmp = htons((uint16_t) len); + header[1] = 126; + memcpy(&header[2], &tmp, sizeof(tmp)); + header_len = 4; + } else { + uint32_t tmp; + header[1] = 127; + tmp = htonl((uint32_t)((uint64_t) len >> 32)); + memcpy(&header[2], &tmp, sizeof(tmp)); + tmp = htonl((uint32_t)(len & 0xffffffff)); + memcpy(&header[6], &tmp, sizeof(tmp)); + header_len = 10; + } + + /* client connections enable masking */ + if (nc->listener == NULL) { + header[1] |= 1 << 7; /* set masking flag */ + mg_send(nc, header, header_len); + ctx->mask = mg_ws_random_mask(); + mg_send(nc, &ctx->mask, sizeof(ctx->mask)); + ctx->pos = nc->send_mbuf.len; + } else { + mg_send(nc, header, header_len); + ctx->pos = 0; + } +} + +static void mg_ws_mask_frame(struct mbuf *mbuf, struct ws_mask_ctx *ctx) { + size_t i; + if (ctx->pos == 0) return; + for (i = 0; i < (mbuf->len - ctx->pos); i++) { + mbuf->buf[ctx->pos + i] ^= ((char *) &ctx->mask)[i % 4]; + } +} + +void mg_send_websocket_frame(struct mg_connection *nc, int op, const void *data, + size_t len) { + struct ws_mask_ctx ctx; + DBG(("%p %d %d", nc, op, (int) len)); + mg_send_ws_header(nc, op, len, &ctx); + mg_send(nc, data, len); + + mg_ws_mask_frame(&nc->send_mbuf, &ctx); + + if (op == WEBSOCKET_OP_CLOSE) { + nc->flags |= MG_F_SEND_AND_CLOSE; + } +} + +void mg_send_websocket_framev(struct mg_connection *nc, int op, + const struct mg_str *strv, int strvcnt) { + struct ws_mask_ctx ctx; + int i; + int len = 0; + for (i = 0; i < strvcnt; i++) { + len += strv[i].len; + } + + mg_send_ws_header(nc, op, len, &ctx); + + for (i = 0; i < strvcnt; i++) { + mg_send(nc, strv[i].p, strv[i].len); + } + + mg_ws_mask_frame(&nc->send_mbuf, &ctx); + + if (op == WEBSOCKET_OP_CLOSE) { + nc->flags |= MG_F_SEND_AND_CLOSE; + } +} + +void mg_printf_websocket_frame(struct mg_connection *nc, int op, + const char *fmt, ...) { + char mem[MG_VPRINTF_BUFFER_SIZE], *buf = mem; + va_list ap; + int len; + + va_start(ap, fmt); + if ((len = mg_avprintf(&buf, sizeof(mem), fmt, ap)) > 0) { + mg_send_websocket_frame(nc, op, buf, len); + } + va_end(ap); + + if (buf != mem && buf != NULL) { + MG_FREE(buf); + } +} + +MG_INTERNAL void mg_ws_handler(struct mg_connection *nc, int ev, + void *ev_data MG_UD_ARG(void *user_data)) { + mg_call(nc, nc->handler, nc->user_data, ev, ev_data); + + switch (ev) { + case MG_EV_RECV: + do { + } while (mg_deliver_websocket_data(nc)); + break; + case MG_EV_POLL: + /* Ping idle websocket connections */ + { + time_t now = *(time_t *) ev_data; + if (nc->flags & MG_F_IS_WEBSOCKET && + now > nc->last_io_time + MG_WEBSOCKET_PING_INTERVAL_SECONDS) { + mg_send_websocket_frame(nc, WEBSOCKET_OP_PING, "", 0); + } + } + break; + default: + break; + } +#if MG_ENABLE_CALLBACK_USERDATA + (void) user_data; +#endif +} + +#ifndef MG_EXT_SHA1 +void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[], + const size_t *msg_lens, uint8_t *digest) { + size_t i; + cs_sha1_ctx sha_ctx; + cs_sha1_init(&sha_ctx); + for (i = 0; i < num_msgs; i++) { + cs_sha1_update(&sha_ctx, msgs[i], msg_lens[i]); + } + cs_sha1_final(digest, &sha_ctx); +} +#else +extern void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[], + const size_t *msg_lens, uint8_t *digest); +#endif + +MG_INTERNAL void mg_ws_handshake(struct mg_connection *nc, + const struct mg_str *key, + struct http_message *hm) { + static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + const uint8_t *msgs[2] = {(const uint8_t *) key->p, (const uint8_t *) magic}; + const size_t msg_lens[2] = {key->len, 36}; + unsigned char sha[20]; + char b64_sha[30]; + struct mg_str *s; + + mg_hash_sha1_v(2, msgs, msg_lens, sha); + mg_base64_encode(sha, sizeof(sha), b64_sha); + mg_printf(nc, "%s", + "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n"); + + s = mg_get_http_header(hm, "Sec-WebSocket-Protocol"); + if (s != NULL) { + mg_printf(nc, "Sec-WebSocket-Protocol: %.*s\r\n", (int) s->len, s->p); + } + mg_printf(nc, "Sec-WebSocket-Accept: %s%s", b64_sha, "\r\n\r\n"); + + DBG(("%p %.*s %s", nc, (int) key->len, key->p, b64_sha)); +} + +void mg_send_websocket_handshake2(struct mg_connection *nc, const char *path, + const char *host, const char *protocol, + const char *extra_headers) { + mg_send_websocket_handshake3(nc, path, host, protocol, extra_headers, NULL, + NULL); +} + +void mg_send_websocket_handshake3(struct mg_connection *nc, const char *path, + const char *host, const char *protocol, + const char *extra_headers, const char *user, + const char *pass) { + mg_send_websocket_handshake3v(nc, mg_mk_str(path), mg_mk_str(host), + mg_mk_str(protocol), mg_mk_str(extra_headers), + mg_mk_str(user), mg_mk_str(pass)); +} + +void mg_send_websocket_handshake3v(struct mg_connection *nc, + const struct mg_str path, + const struct mg_str host, + const struct mg_str protocol, + const struct mg_str extra_headers, + const struct mg_str user, + const struct mg_str pass) { + struct mbuf auth; + char key[25]; + uint32_t nonce[4]; + nonce[0] = mg_ws_random_mask(); + nonce[1] = mg_ws_random_mask(); + nonce[2] = mg_ws_random_mask(); + nonce[3] = mg_ws_random_mask(); + mg_base64_encode((unsigned char *) &nonce, sizeof(nonce), key); + + mbuf_init(&auth, 0); + if (user.len > 0) { + mg_basic_auth_header(user, pass, &auth); + } + + /* + * NOTE: the (auth.buf == NULL ? "" : auth.buf) is because cc3200 libc is + * broken: it doesn't like zero length to be passed to %.*s + * i.e. sprintf("f%.*so", (int)0, NULL), yields `f\0o`. + * because it handles NULL specially (and incorrectly). + */ + mg_printf(nc, + "GET %.*s HTTP/1.1\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "%.*s" + "Sec-WebSocket-Version: 13\r\n" + "Sec-WebSocket-Key: %s\r\n", + (int) path.len, path.p, (int) auth.len, + (auth.buf == NULL ? "" : auth.buf), key); + + /* TODO(mkm): take default hostname from http proto data if host == NULL */ + if (host.len > 0) { + int host_len = (int) (path.p - host.p); /* Account for possible :PORT */ + mg_printf(nc, "Host: %.*s\r\n", host_len, host.p); + } + if (protocol.len > 0) { + mg_printf(nc, "Sec-WebSocket-Protocol: %.*s\r\n", (int) protocol.len, + protocol.p); + } + if (extra_headers.len > 0) { + mg_printf(nc, "%.*s", (int) extra_headers.len, extra_headers.p); + } + mg_printf(nc, "\r\n"); + + mbuf_free(&auth); +} + +void mg_send_websocket_handshake(struct mg_connection *nc, const char *path, + const char *extra_headers) { + struct mg_str null_str = MG_NULL_STR; + mg_send_websocket_handshake3v( + nc, mg_mk_str(path), null_str /* host */, null_str /* protocol */, + mg_mk_str(extra_headers), null_str /* user */, null_str /* pass */); +} + +struct mg_connection *mg_connect_ws_opt( + struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data), + struct mg_connect_opts opts, const char *url, const char *protocol, + const char *extra_headers) { + struct mg_str null_str = MG_NULL_STR; + struct mg_str host = MG_NULL_STR, path = MG_NULL_STR, user_info = MG_NULL_STR; + struct mg_connection *nc = + mg_connect_http_base(mgr, MG_CB(ev_handler, user_data), opts, "http", + "ws", "https", "wss", url, &path, &user_info, &host); + if (nc != NULL) { + mg_send_websocket_handshake3v(nc, path, host, mg_mk_str(protocol), + mg_mk_str(extra_headers), user_info, + null_str); + } + return nc; +} + +struct mg_connection *mg_connect_ws( + struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data), + const char *url, const char *protocol, const char *extra_headers) { + struct mg_connect_opts opts; + memset(&opts, 0, sizeof(opts)); + return mg_connect_ws_opt(mgr, MG_CB(ev_handler, user_data), opts, url, + protocol, extra_headers); +} +#endif /* MG_ENABLE_HTTP && MG_ENABLE_HTTP_WEBSOCKET */ diff --git a/src/mongoose-6.11/src/mg_internal.h b/src/mongoose-6.11/src/mg_internal.h new file mode 100644 index 0000000..2061423 --- /dev/null +++ b/src/mongoose-6.11/src/mg_internal.h @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_MONGOOSE_SRC_INTERNAL_H_ +#define CS_MONGOOSE_SRC_INTERNAL_H_ + +#include "common/mg_mem.h" + +#ifndef MBUF_REALLOC +#define MBUF_REALLOC MG_REALLOC +#endif + +#ifndef MBUF_FREE +#define MBUF_FREE MG_FREE +#endif + +#define MG_SET_PTRPTR(_ptr, _v) \ + do { \ + if (_ptr) *(_ptr) = _v; \ + } while (0) + +#ifndef MG_INTERNAL +#define MG_INTERNAL static +#endif + +#ifdef PICOTCP +#define NO_LIBC +#define MG_DISABLE_PFS +#endif + +#include "common/cs_dbg.h" +#include "mg_http.h" +#include "mg_net.h" + +#define MG_CTL_MSG_MESSAGE_SIZE 8192 + +/* internals that need to be accessible in unit tests */ +MG_INTERNAL struct mg_connection *mg_do_connect(struct mg_connection *nc, + int proto, + union socket_address *sa); + +MG_INTERNAL int mg_parse_address(const char *str, union socket_address *sa, + int *proto, char *host, size_t host_len); +MG_INTERNAL void mg_call(struct mg_connection *nc, + mg_event_handler_t ev_handler, void *user_data, int ev, + void *ev_data); +void mg_forward(struct mg_connection *from, struct mg_connection *to); +MG_INTERNAL void mg_add_conn(struct mg_mgr *mgr, struct mg_connection *c); +MG_INTERNAL void mg_remove_conn(struct mg_connection *c); +MG_INTERNAL struct mg_connection *mg_create_connection( + struct mg_mgr *mgr, mg_event_handler_t callback, + struct mg_add_sock_opts opts); +#ifdef _WIN32 +/* Retur value is the same as for MultiByteToWideChar. */ +int to_wchar(const char *path, wchar_t *wbuf, size_t wbuf_len); +#endif + +struct ctl_msg { + mg_event_handler_t callback; + char message[MG_CTL_MSG_MESSAGE_SIZE]; +}; + +#if MG_ENABLE_MQTT +struct mg_mqtt_message; + +#define MG_MQTT_ERROR_INCOMPLETE_MSG -1 +#define MG_MQTT_ERROR_MALFORMED_MSG -2 + +MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm); +#endif + +/* Forward declarations for testing. */ +extern void *(*test_malloc)(size_t size); +extern void *(*test_calloc)(size_t count, size_t size); + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +#if MG_ENABLE_HTTP +struct mg_serve_http_opts; + +/* + * Reassemble the content of the buffer (buf, blen) which should be + * in the HTTP chunked encoding, by collapsing data chunks to the + * beginning of the buffer. + * + * If chunks get reassembled, modify hm->body to point to the reassembled + * body and fire MG_EV_HTTP_CHUNK event. If handler sets MG_F_DELETE_CHUNK + * in nc->flags, delete reassembled body from the mbuf. + * + * Return reassembled body size. + */ +MG_INTERNAL size_t mg_handle_chunked(struct mg_connection *nc, + struct http_message *hm, char *buf, + size_t blen); + +#if MG_ENABLE_FILESYSTEM +MG_INTERNAL int mg_uri_to_local_path(struct http_message *hm, + const struct mg_serve_http_opts *opts, + char **local_path, + struct mg_str *remainder); +MG_INTERNAL time_t mg_parse_date_string(const char *datetime); +MG_INTERNAL int mg_is_not_modified(struct http_message *hm, cs_stat_t *st); +#endif +#if MG_ENABLE_HTTP_CGI +MG_INTERNAL void mg_handle_cgi(struct mg_connection *nc, const char *prog, + const struct mg_str *path_info, + const struct http_message *hm, + const struct mg_serve_http_opts *opts); +struct mg_http_proto_data_cgi; +MG_INTERNAL void mg_http_free_proto_data_cgi(struct mg_http_proto_data_cgi *d); +#endif +#if MG_ENABLE_HTTP_SSI +MG_INTERNAL void mg_handle_ssi_request(struct mg_connection *nc, + struct http_message *hm, + const char *path, + const struct mg_serve_http_opts *opts); +#endif +#if MG_ENABLE_HTTP_WEBDAV +MG_INTERNAL int mg_is_dav_request(const struct mg_str *s); +MG_INTERNAL void mg_handle_propfind(struct mg_connection *nc, const char *path, + cs_stat_t *stp, struct http_message *hm, + struct mg_serve_http_opts *opts); +MG_INTERNAL void mg_handle_lock(struct mg_connection *nc, const char *path); +MG_INTERNAL void mg_handle_mkcol(struct mg_connection *nc, const char *path, + struct http_message *hm); +MG_INTERNAL void mg_handle_move(struct mg_connection *c, + const struct mg_serve_http_opts *opts, + const char *path, struct http_message *hm); +MG_INTERNAL void mg_handle_delete(struct mg_connection *nc, + const struct mg_serve_http_opts *opts, + const char *path); +MG_INTERNAL void mg_handle_put(struct mg_connection *nc, const char *path, + struct http_message *hm); +#endif +#if MG_ENABLE_HTTP_WEBSOCKET +MG_INTERNAL void mg_ws_handler(struct mg_connection *nc, int ev, + void *ev_data MG_UD_ARG(void *user_data)); +MG_INTERNAL void mg_ws_handshake(struct mg_connection *nc, + const struct mg_str *key, + struct http_message *); +#endif +#endif /* MG_ENABLE_HTTP */ + +MG_INTERNAL int mg_get_errno(void); + +MG_INTERNAL void mg_close_conn(struct mg_connection *conn); + +#if MG_ENABLE_SNTP +MG_INTERNAL int mg_sntp_parse_reply(const char *buf, int len, + struct mg_sntp_message *msg); +#endif + +#endif /* CS_MONGOOSE_SRC_INTERNAL_H_ */ diff --git a/src/mongoose-6.11/src/mg_modules.mk b/src/mongoose-6.11/src/mg_modules.mk new file mode 100644 index 0000000..235e0d5 --- /dev/null +++ b/src/mongoose-6.11/src/mg_modules.mk @@ -0,0 +1,103 @@ +COMMON = ../../common + +HEADERS = mg_common.h \ + $(COMMON)/platform.h \ + $(COMMON)/platforms/platform_windows.h \ + $(COMMON)/platforms/platform_unix.h \ + $(COMMON)/platforms/platform_esp32.h \ + $(COMMON)/platforms/platform_esp8266.h \ + $(COMMON)/platforms/platform_cc3100.h \ + $(COMMON)/platforms/platform_cc3200.h \ + $(COMMON)/platforms/platform_msp432.h \ + $(COMMON)/platforms/platform_tm4c129.h \ + $(COMMON)/platforms/platform_mbed.h \ + $(COMMON)/platforms/platform_nrf51.h \ + $(COMMON)/platforms/platform_nrf52.h \ + $(COMMON)/platforms/simplelink/cs_simplelink.h \ + $(COMMON)/platforms/platform_wince.h \ + $(COMMON)/platforms/platform_nxp_lpc.h \ + $(COMMON)/platforms/platform_nxp_kinetis.h \ + $(COMMON)/platforms/platform_pic32.h \ + $(COMMON)/platforms/platform_stm32.h \ + $(COMMON)/platforms/lwip/mg_lwip.h \ + $(COMMON)/cs_md5.h \ + $(COMMON)/cs_sha1.h \ + $(COMMON)/cs_time.h \ + $(COMMON)/mg_str.h \ + $(COMMON)/mbuf.h \ + $(COMMON)/cs_base64.h \ + $(COMMON)/str_util.h \ + $(COMMON)/queue.h \ + mg_features.h \ + mg_net_if.h \ + mg_ssl_if.h \ + mg_net.h \ + mg_uri.h \ + mg_util.h \ + mg_http.h \ + mg_http_server.h \ + mg_http_client.h \ + mg_mqtt.h \ + mg_mqtt_server.h \ + mg_dns.h \ + mg_dns_server.h \ + mg_resolv.h \ + mg_coap.h \ + mg_sntp.h \ + mg_socks.h + +SOURCES = $(COMMON)/mg_mem.h \ + $(COMMON)/cs_base64.c \ + $(COMMON)/cs_dbg.h \ + $(COMMON)/cs_dbg.c \ + $(COMMON)/cs_dirent.h \ + $(COMMON)/cs_dirent.c \ + $(COMMON)/cs_time.c \ + $(COMMON)/cs_endian.h \ + $(COMMON)/cs_md5.c \ + $(COMMON)/cs_sha1.c \ + $(COMMON)/mbuf.c \ + $(COMMON)/mg_str.c \ + $(COMMON)/str_util.c \ + mg_net.c \ + mg_net_if_socket.h \ + mg_net_if_socks.h \ + mg_net_if.c \ + mg_net_if_socket.c \ + mg_net_if_socks.c \ + mg_ssl_if_openssl.c \ + mg_ssl_if_mbedtls.c \ + mg_uri.c \ + mg_http.c \ + mg_http_cgi.c \ + mg_http_ssi.c \ + mg_http_webdav.c \ + mg_http_websocket.c \ + mg_util.c \ + mg_mqtt.c \ + mg_mqtt_server.c \ + mg_dns.c \ + mg_dns_server.c \ + mg_resolv.c \ + mg_coap.c \ + mg_sntp.c \ + mg_socks.c \ + $(COMMON)/platforms/cc3200/cc3200_libc.c \ + $(COMMON)/platforms/msp432/msp432_libc.c \ + $(COMMON)/platforms/nrf5/nrf5_libc.c \ + $(COMMON)/platforms/simplelink/sl_fs_slfs.h \ + $(COMMON)/platforms/simplelink/sl_fs_slfs.c \ + $(COMMON)/platforms/simplelink/sl_fs.c \ + $(COMMON)/platforms/simplelink/sl_socket.c \ + $(COMMON)/platforms/simplelink/sl_mg_task.c \ + $(COMMON)/platforms/simplelink/sl_net_if.h \ + $(COMMON)/platforms/simplelink/sl_net_if.c \ + $(COMMON)/platforms/simplelink/sl_ssl_if.c \ + $(COMMON)/platforms/lwip/mg_lwip_net_if.h \ + $(COMMON)/platforms/lwip/mg_lwip_net_if.c \ + $(COMMON)/platforms/lwip/mg_lwip_ev_mgr.c \ + $(COMMON)/platforms/lwip/mg_lwip_ssl_if.c \ + $(COMMON)/platforms/wince/wince_libc.c \ + $(COMMON)/platforms/pic32/pic32_net_if.h \ + $(COMMON)/platforms/pic32/pic32_net_if.c \ + $(COMMON)/platforms/windows/windows_direct.c diff --git a/src/mongoose-6.11/src/mg_mqtt.c b/src/mongoose-6.11/src/mg_mqtt.c new file mode 100644 index 0000000..369b456 --- /dev/null +++ b/src/mongoose-6.11/src/mg_mqtt.c @@ -0,0 +1,466 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_MQTT + +#include + +#include "mg_internal.h" +#include "mg_mqtt.h" + +static uint16_t getu16(const char *p) { + const uint8_t *up = (const uint8_t *) p; + return (up[0] << 8) + up[1]; +} + +static const char *scanto(const char *p, struct mg_str *s) { + s->len = getu16(p); + s->p = p + 2; + return s->p + s->len; +} + +MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm) { + uint8_t header; + size_t len = 0, len_len = 0; + const char *p, *end; + unsigned char lc = 0; + int cmd; + + if (io->len < 2) return MG_MQTT_ERROR_INCOMPLETE_MSG; + header = io->buf[0]; + cmd = header >> 4; + + /* decode mqtt variable length */ + len = len_len = 0; + p = io->buf + 1; + while ((size_t)(p - io->buf) < io->len) { + lc = *((const unsigned char *) p++); + len += (lc & 0x7f) << 7 * len_len; + len_len++; + if (!(lc & 0x80)) break; + if (len_len > 4) return MG_MQTT_ERROR_MALFORMED_MSG; + } + + end = p + len; + if (lc & 0x80 || len > (io->len - (p - io->buf))) { + return MG_MQTT_ERROR_INCOMPLETE_MSG; + } + + mm->cmd = cmd; + mm->qos = MG_MQTT_GET_QOS(header); + + switch (cmd) { + case MG_MQTT_CMD_CONNECT: { + p = scanto(p, &mm->protocol_name); + if (p > end - 4) return MG_MQTT_ERROR_MALFORMED_MSG; + mm->protocol_version = *(uint8_t *) p++; + mm->connect_flags = *(uint8_t *) p++; + mm->keep_alive_timer = getu16(p); + p += 2; + if (p >= end) return MG_MQTT_ERROR_MALFORMED_MSG; + p = scanto(p, &mm->client_id); + if (p > end) return MG_MQTT_ERROR_MALFORMED_MSG; + if (mm->connect_flags & MG_MQTT_HAS_WILL) { + if (p >= end) return MG_MQTT_ERROR_MALFORMED_MSG; + p = scanto(p, &mm->will_topic); + } + if (mm->connect_flags & MG_MQTT_HAS_WILL) { + if (p >= end) return MG_MQTT_ERROR_MALFORMED_MSG; + p = scanto(p, &mm->will_message); + } + if (mm->connect_flags & MG_MQTT_HAS_USER_NAME) { + if (p >= end) return MG_MQTT_ERROR_MALFORMED_MSG; + p = scanto(p, &mm->user_name); + } + if (mm->connect_flags & MG_MQTT_HAS_PASSWORD) { + if (p >= end) return MG_MQTT_ERROR_MALFORMED_MSG; + p = scanto(p, &mm->password); + } + if (p != end) return MG_MQTT_ERROR_MALFORMED_MSG; + + LOG(LL_DEBUG, + ("%d %2x %d proto [%.*s] client_id [%.*s] will_topic [%.*s] " + "will_msg [%.*s] user_name [%.*s] password [%.*s]", + (int) len, (int) mm->connect_flags, (int) mm->keep_alive_timer, + (int) mm->protocol_name.len, mm->protocol_name.p, + (int) mm->client_id.len, mm->client_id.p, (int) mm->will_topic.len, + mm->will_topic.p, (int) mm->will_message.len, mm->will_message.p, + (int) mm->user_name.len, mm->user_name.p, (int) mm->password.len, + mm->password.p)); + break; + } + case MG_MQTT_CMD_CONNACK: + if (end - p < 2) return MG_MQTT_ERROR_MALFORMED_MSG; + mm->connack_ret_code = p[1]; + break; + case MG_MQTT_CMD_PUBACK: + case MG_MQTT_CMD_PUBREC: + case MG_MQTT_CMD_PUBREL: + case MG_MQTT_CMD_PUBCOMP: + case MG_MQTT_CMD_SUBACK: + mm->message_id = getu16(p); + break; + case MG_MQTT_CMD_PUBLISH: { + p = scanto(p, &mm->topic); + if (p > end) return MG_MQTT_ERROR_MALFORMED_MSG; + if (mm->qos > 0) { + if (end - p < 2) return MG_MQTT_ERROR_MALFORMED_MSG; + mm->message_id = getu16(p); + p += 2; + } + mm->payload.p = p; + mm->payload.len = end - p; + break; + } + case MG_MQTT_CMD_SUBSCRIBE: + if (end - p < 2) return MG_MQTT_ERROR_MALFORMED_MSG; + mm->message_id = getu16(p); + p += 2; + /* + * topic expressions are left in the payload and can be parsed with + * `mg_mqtt_next_subscribe_topic` + */ + mm->payload.p = p; + mm->payload.len = end - p; + break; + default: + /* Unhandled command */ + break; + } + + mm->len = end - io->buf; + return mm->len; +} + +static void mqtt_handler(struct mg_connection *nc, int ev, + void *ev_data MG_UD_ARG(void *user_data)) { + struct mbuf *io = &nc->recv_mbuf; + struct mg_mqtt_message mm; + memset(&mm, 0, sizeof(mm)); + + nc->handler(nc, ev, ev_data MG_UD_ARG(user_data)); + + switch (ev) { + case MG_EV_ACCEPT: + if (nc->proto_data == NULL) mg_set_protocol_mqtt(nc); + break; + case MG_EV_RECV: { + /* There can be multiple messages in the buffer, process them all. */ + while (1) { + int len = parse_mqtt(io, &mm); + if (len < 0) { + if (len == MG_MQTT_ERROR_MALFORMED_MSG) { + /* Protocol error. */ + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } else if (len == MG_MQTT_ERROR_INCOMPLETE_MSG) { + /* Not fully buffered, let's check if we have a chance to get more + * data later */ + if (nc->recv_mbuf_limit > 0 && + nc->recv_mbuf.len >= nc->recv_mbuf_limit) { + LOG(LL_ERROR, ("%p recv buffer (%lu bytes) exceeds the limit " + "%lu bytes, and not drained, closing", + nc, (unsigned long) nc->recv_mbuf.len, + (unsigned long) nc->recv_mbuf_limit)); + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } + } else { + /* Should never be here */ + LOG(LL_ERROR, ("%p invalid len: %d, closing", nc, len)); + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } + break; + } + + nc->handler(nc, MG_MQTT_EVENT_BASE + mm.cmd, &mm MG_UD_ARG(user_data)); + mbuf_remove(io, len); + } + break; + } + case MG_EV_POLL: { + struct mg_mqtt_proto_data *pd = + (struct mg_mqtt_proto_data *) nc->proto_data; + double now = mg_time(); + if (pd->keep_alive > 0 && pd->last_control_time > 0 && + (now - pd->last_control_time) > pd->keep_alive) { + LOG(LL_DEBUG, ("Send PINGREQ")); + mg_mqtt_ping(nc); + } + break; + } + } +} + +static void mg_mqtt_proto_data_destructor(void *proto_data) { + MG_FREE(proto_data); +} + +int mg_mqtt_match_topic_expression(struct mg_str exp, struct mg_str topic) { + /* TODO(mkm): implement real matching */ + if (memchr(exp.p, '#', exp.len)) { + /* exp `foo/#` will become `foo/` */ + exp.len -= 1; + /* + * topic should be longer than the expression: e.g. topic `foo/bar` does + * match `foo/#`, but neither `foo` nor `foo/` do. + */ + if (topic.len <= exp.len) { + return 0; + } + + /* Truncate topic so that it'll pass the next length check */ + topic.len = exp.len; + } + if (topic.len != exp.len) { + return 0; + } + return strncmp(topic.p, exp.p, exp.len) == 0; +} + +int mg_mqtt_vmatch_topic_expression(const char *exp, struct mg_str topic) { + return mg_mqtt_match_topic_expression(mg_mk_str(exp), topic); +} + +void mg_set_protocol_mqtt(struct mg_connection *nc) { + nc->proto_handler = mqtt_handler; + nc->proto_data = MG_CALLOC(1, sizeof(struct mg_mqtt_proto_data)); + nc->proto_data_destructor = mg_mqtt_proto_data_destructor; +} + +static void mg_mqtt_prepend_header(struct mg_connection *nc, uint8_t cmd, + uint8_t flags, size_t len) { + struct mg_mqtt_proto_data *pd = (struct mg_mqtt_proto_data *) nc->proto_data; + size_t off = nc->send_mbuf.len - len; + uint8_t header = cmd << 4 | (uint8_t) flags; + + uint8_t buf[1 + sizeof(size_t)]; + uint8_t *vlen = &buf[1]; + + assert(nc->send_mbuf.len >= len); + + buf[0] = header; + + /* mqtt variable length encoding */ + do { + *vlen = len % 0x80; + len /= 0x80; + if (len > 0) *vlen |= 0x80; + vlen++; + } while (len > 0); + + mbuf_insert(&nc->send_mbuf, off, buf, vlen - buf); + pd->last_control_time = mg_time(); +} + +void mg_send_mqtt_handshake(struct mg_connection *nc, const char *client_id) { + static struct mg_send_mqtt_handshake_opts opts; + mg_send_mqtt_handshake_opt(nc, client_id, opts); +} + +void mg_send_mqtt_handshake_opt(struct mg_connection *nc, const char *client_id, + struct mg_send_mqtt_handshake_opts opts) { + uint16_t hlen, nlen, rem_len = 0; + struct mg_mqtt_proto_data *pd = (struct mg_mqtt_proto_data *) nc->proto_data; + + mg_send(nc, "\00\04MQTT\04", 7); + rem_len += 7; + + if (opts.user_name != NULL) { + opts.flags |= MG_MQTT_HAS_USER_NAME; + } + if (opts.password != NULL) { + opts.flags |= MG_MQTT_HAS_PASSWORD; + } + if (opts.will_topic != NULL && opts.will_message != NULL) { + opts.flags |= MG_MQTT_HAS_WILL; + } + if (opts.keep_alive == 0) { + opts.keep_alive = 60; + } + + mg_send(nc, &opts.flags, 1); + rem_len += 1; + + nlen = htons(opts.keep_alive); + mg_send(nc, &nlen, 2); + rem_len += 2; + + hlen = strlen(client_id); + nlen = htons((uint16_t) hlen); + mg_send(nc, &nlen, 2); + mg_send(nc, client_id, hlen); + rem_len += 2 + hlen; + + if (opts.flags & MG_MQTT_HAS_WILL) { + hlen = strlen(opts.will_topic); + nlen = htons((uint16_t) hlen); + mg_send(nc, &nlen, 2); + mg_send(nc, opts.will_topic, hlen); + rem_len += 2 + hlen; + + hlen = strlen(opts.will_message); + nlen = htons((uint16_t) hlen); + mg_send(nc, &nlen, 2); + mg_send(nc, opts.will_message, hlen); + rem_len += 2 + hlen; + } + + if (opts.flags & MG_MQTT_HAS_USER_NAME) { + hlen = strlen(opts.user_name); + nlen = htons((uint16_t) hlen); + mg_send(nc, &nlen, 2); + mg_send(nc, opts.user_name, hlen); + rem_len += 2 + hlen; + } + if (opts.flags & MG_MQTT_HAS_PASSWORD) { + hlen = strlen(opts.password); + nlen = htons((uint16_t) hlen); + mg_send(nc, &nlen, 2); + mg_send(nc, opts.password, hlen); + rem_len += 2 + hlen; + } + + mg_mqtt_prepend_header(nc, MG_MQTT_CMD_CONNECT, 0, rem_len); + + if (pd != NULL) { + pd->keep_alive = opts.keep_alive; + } +} + +void mg_mqtt_publish(struct mg_connection *nc, const char *topic, + uint16_t message_id, int flags, const void *data, + size_t len) { + size_t old_len = nc->send_mbuf.len; + + uint16_t topic_len = htons((uint16_t) strlen(topic)); + uint16_t message_id_net = htons(message_id); + + mg_send(nc, &topic_len, 2); + mg_send(nc, topic, strlen(topic)); + if (MG_MQTT_GET_QOS(flags) > 0) { + mg_send(nc, &message_id_net, 2); + } + mg_send(nc, data, len); + + mg_mqtt_prepend_header(nc, MG_MQTT_CMD_PUBLISH, flags, + nc->send_mbuf.len - old_len); +} + +void mg_mqtt_subscribe(struct mg_connection *nc, + const struct mg_mqtt_topic_expression *topics, + size_t topics_len, uint16_t message_id) { + size_t old_len = nc->send_mbuf.len; + + uint16_t message_id_n = htons(message_id); + size_t i; + + mg_send(nc, (char *) &message_id_n, 2); + for (i = 0; i < topics_len; i++) { + uint16_t topic_len_n = htons((uint16_t) strlen(topics[i].topic)); + mg_send(nc, &topic_len_n, 2); + mg_send(nc, topics[i].topic, strlen(topics[i].topic)); + mg_send(nc, &topics[i].qos, 1); + } + + mg_mqtt_prepend_header(nc, MG_MQTT_CMD_SUBSCRIBE, MG_MQTT_QOS(1), + nc->send_mbuf.len - old_len); +} + +int mg_mqtt_next_subscribe_topic(struct mg_mqtt_message *msg, + struct mg_str *topic, uint8_t *qos, int pos) { + unsigned char *buf = (unsigned char *) msg->payload.p + pos; + int new_pos; + + if ((size_t) pos >= msg->payload.len) return -1; + + topic->len = buf[0] << 8 | buf[1]; + topic->p = (char *) buf + 2; + new_pos = pos + 2 + topic->len + 1; + if ((size_t) new_pos > msg->payload.len) return -1; + *qos = buf[2 + topic->len]; + return new_pos; +} + +void mg_mqtt_unsubscribe(struct mg_connection *nc, char **topics, + size_t topics_len, uint16_t message_id) { + size_t old_len = nc->send_mbuf.len; + + uint16_t message_id_n = htons(message_id); + size_t i; + + mg_send(nc, (char *) &message_id_n, 2); + for (i = 0; i < topics_len; i++) { + uint16_t topic_len_n = htons((uint16_t) strlen(topics[i])); + mg_send(nc, &topic_len_n, 2); + mg_send(nc, topics[i], strlen(topics[i])); + } + + mg_mqtt_prepend_header(nc, MG_MQTT_CMD_UNSUBSCRIBE, MG_MQTT_QOS(1), + nc->send_mbuf.len - old_len); +} + +void mg_mqtt_connack(struct mg_connection *nc, uint8_t return_code) { + uint8_t unused = 0; + mg_send(nc, &unused, 1); + mg_send(nc, &return_code, 1); + mg_mqtt_prepend_header(nc, MG_MQTT_CMD_CONNACK, 0, 2); +} + +/* + * Sends a command which contains only a `message_id` and a QoS level of 1. + * + * Helper function. + */ +static void mg_send_mqtt_short_command(struct mg_connection *nc, uint8_t cmd, + uint16_t message_id) { + uint16_t message_id_net = htons(message_id); + uint8_t flags = (cmd == MG_MQTT_CMD_PUBREL ? 2 : 0); + mg_send(nc, &message_id_net, 2); + mg_mqtt_prepend_header(nc, cmd, flags, 2 /* len */); +} + +void mg_mqtt_puback(struct mg_connection *nc, uint16_t message_id) { + mg_send_mqtt_short_command(nc, MG_MQTT_CMD_PUBACK, message_id); +} + +void mg_mqtt_pubrec(struct mg_connection *nc, uint16_t message_id) { + mg_send_mqtt_short_command(nc, MG_MQTT_CMD_PUBREC, message_id); +} + +void mg_mqtt_pubrel(struct mg_connection *nc, uint16_t message_id) { + mg_send_mqtt_short_command(nc, MG_MQTT_CMD_PUBREL, message_id); +} + +void mg_mqtt_pubcomp(struct mg_connection *nc, uint16_t message_id) { + mg_send_mqtt_short_command(nc, MG_MQTT_CMD_PUBCOMP, message_id); +} + +void mg_mqtt_suback(struct mg_connection *nc, uint8_t *qoss, size_t qoss_len, + uint16_t message_id) { + size_t i; + uint16_t message_id_net = htons(message_id); + mg_send(nc, &message_id_net, 2); + for (i = 0; i < qoss_len; i++) { + mg_send(nc, &qoss[i], 1); + } + mg_mqtt_prepend_header(nc, MG_MQTT_CMD_SUBACK, MG_MQTT_QOS(1), 2 + qoss_len); +} + +void mg_mqtt_unsuback(struct mg_connection *nc, uint16_t message_id) { + mg_send_mqtt_short_command(nc, MG_MQTT_CMD_UNSUBACK, message_id); +} + +void mg_mqtt_ping(struct mg_connection *nc) { + mg_mqtt_prepend_header(nc, MG_MQTT_CMD_PINGREQ, 0, 0); +} + +void mg_mqtt_pong(struct mg_connection *nc) { + mg_mqtt_prepend_header(nc, MG_MQTT_CMD_PINGRESP, 0, 0); +} + +void mg_mqtt_disconnect(struct mg_connection *nc) { + mg_mqtt_prepend_header(nc, MG_MQTT_CMD_DISCONNECT, 0, 0); +} + +#endif /* MG_ENABLE_MQTT */ diff --git a/src/mongoose-6.11/src/mg_mqtt.h b/src/mongoose-6.11/src/mg_mqtt.h new file mode 100644 index 0000000..6067ccc --- /dev/null +++ b/src/mongoose-6.11/src/mg_mqtt.h @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + * This software is dual-licensed: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. For the terms of this + * license, see . + * + * You are free to use this software under the terms of the GNU General + * Public License, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * Alternatively, you can license this software under a commercial + * license, as set out in . + */ + +/* + * === MQTT API reference + */ + +#ifndef CS_MONGOOSE_SRC_MQTT_H_ +#define CS_MONGOOSE_SRC_MQTT_H_ + +#include "mg_net.h" + +struct mg_mqtt_message { + int cmd; + int qos; + int len; /* message length in the IO buffer */ + struct mg_str topic; + struct mg_str payload; + + uint8_t connack_ret_code; /* connack */ + uint16_t message_id; /* puback */ + + /* connect */ + uint8_t protocol_version; + uint8_t connect_flags; + uint16_t keep_alive_timer; + struct mg_str protocol_name; + struct mg_str client_id; + struct mg_str will_topic; + struct mg_str will_message; + struct mg_str user_name; + struct mg_str password; +}; + +struct mg_mqtt_topic_expression { + const char *topic; + uint8_t qos; +}; + +struct mg_send_mqtt_handshake_opts { + unsigned char flags; /* connection flags */ + uint16_t keep_alive; + const char *will_topic; + const char *will_message; + const char *user_name; + const char *password; +}; + +/* mg_mqtt_proto_data should be in header to allow external access to it */ +struct mg_mqtt_proto_data { + uint16_t keep_alive; + double last_control_time; +}; + +/* Message types */ +#define MG_MQTT_CMD_CONNECT 1 +#define MG_MQTT_CMD_CONNACK 2 +#define MG_MQTT_CMD_PUBLISH 3 +#define MG_MQTT_CMD_PUBACK 4 +#define MG_MQTT_CMD_PUBREC 5 +#define MG_MQTT_CMD_PUBREL 6 +#define MG_MQTT_CMD_PUBCOMP 7 +#define MG_MQTT_CMD_SUBSCRIBE 8 +#define MG_MQTT_CMD_SUBACK 9 +#define MG_MQTT_CMD_UNSUBSCRIBE 10 +#define MG_MQTT_CMD_UNSUBACK 11 +#define MG_MQTT_CMD_PINGREQ 12 +#define MG_MQTT_CMD_PINGRESP 13 +#define MG_MQTT_CMD_DISCONNECT 14 + +/* MQTT event types */ +#define MG_MQTT_EVENT_BASE 200 +#define MG_EV_MQTT_CONNECT (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_CONNECT) +#define MG_EV_MQTT_CONNACK (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_CONNACK) +#define MG_EV_MQTT_PUBLISH (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_PUBLISH) +#define MG_EV_MQTT_PUBACK (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_PUBACK) +#define MG_EV_MQTT_PUBREC (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_PUBREC) +#define MG_EV_MQTT_PUBREL (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_PUBREL) +#define MG_EV_MQTT_PUBCOMP (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_PUBCOMP) +#define MG_EV_MQTT_SUBSCRIBE (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_SUBSCRIBE) +#define MG_EV_MQTT_SUBACK (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_SUBACK) +#define MG_EV_MQTT_UNSUBSCRIBE (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_UNSUBSCRIBE) +#define MG_EV_MQTT_UNSUBACK (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_UNSUBACK) +#define MG_EV_MQTT_PINGREQ (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_PINGREQ) +#define MG_EV_MQTT_PINGRESP (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_PINGRESP) +#define MG_EV_MQTT_DISCONNECT (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_DISCONNECT) + +/* Message flags */ +#define MG_MQTT_RETAIN 0x1 +#define MG_MQTT_DUP 0x4 +#define MG_MQTT_QOS(qos) ((qos) << 1) +#define MG_MQTT_GET_QOS(flags) (((flags) &0x6) >> 1) +#define MG_MQTT_SET_QOS(flags, qos) (flags) = ((flags) & ~0x6) | ((qos) << 1) + +/* Connection flags */ +#define MG_MQTT_CLEAN_SESSION 0x02 +#define MG_MQTT_HAS_WILL 0x04 +#define MG_MQTT_WILL_RETAIN 0x20 +#define MG_MQTT_HAS_PASSWORD 0x40 +#define MG_MQTT_HAS_USER_NAME 0x80 +#define MG_MQTT_GET_WILL_QOS(flags) (((flags) &0x18) >> 3) +#define MG_MQTT_SET_WILL_QOS(flags, qos) \ + (flags) = ((flags) & ~0x18) | ((qos) << 3) + +/* CONNACK return codes */ +#define MG_EV_MQTT_CONNACK_ACCEPTED 0 +#define MG_EV_MQTT_CONNACK_UNACCEPTABLE_VERSION 1 +#define MG_EV_MQTT_CONNACK_IDENTIFIER_REJECTED 2 +#define MG_EV_MQTT_CONNACK_SERVER_UNAVAILABLE 3 +#define MG_EV_MQTT_CONNACK_BAD_AUTH 4 +#define MG_EV_MQTT_CONNACK_NOT_AUTHORIZED 5 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * Attaches a built-in MQTT event handler to the given connection. + * + * The user-defined event handler will receive following extra events: + * + * - MG_EV_MQTT_CONNACK + * - MG_EV_MQTT_PUBLISH + * - MG_EV_MQTT_PUBACK + * - MG_EV_MQTT_PUBREC + * - MG_EV_MQTT_PUBREL + * - MG_EV_MQTT_PUBCOMP + * - MG_EV_MQTT_SUBACK + */ +void mg_set_protocol_mqtt(struct mg_connection *nc); + +/* Sends an MQTT handshake. */ +void mg_send_mqtt_handshake(struct mg_connection *nc, const char *client_id); + +/* Sends an MQTT handshake with optional parameters. */ +void mg_send_mqtt_handshake_opt(struct mg_connection *nc, const char *client_id, + struct mg_send_mqtt_handshake_opts); + +/* Publishes a message to a given topic. */ +void mg_mqtt_publish(struct mg_connection *nc, const char *topic, + uint16_t message_id, int flags, const void *data, + size_t len); + +/* Subscribes to a bunch of topics. */ +void mg_mqtt_subscribe(struct mg_connection *nc, + const struct mg_mqtt_topic_expression *topics, + size_t topics_len, uint16_t message_id); + +/* Unsubscribes from a bunch of topics. */ +void mg_mqtt_unsubscribe(struct mg_connection *nc, char **topics, + size_t topics_len, uint16_t message_id); + +/* Sends a DISCONNECT command. */ +void mg_mqtt_disconnect(struct mg_connection *nc); + +/* Sends a CONNACK command with a given `return_code`. */ +void mg_mqtt_connack(struct mg_connection *nc, uint8_t return_code); + +/* Sends a PUBACK command with a given `message_id`. */ +void mg_mqtt_puback(struct mg_connection *nc, uint16_t message_id); + +/* Sends a PUBREC command with a given `message_id`. */ +void mg_mqtt_pubrec(struct mg_connection *nc, uint16_t message_id); + +/* Sends a PUBREL command with a given `message_id`. */ +void mg_mqtt_pubrel(struct mg_connection *nc, uint16_t message_id); + +/* Sends a PUBCOMP command with a given `message_id`. */ +void mg_mqtt_pubcomp(struct mg_connection *nc, uint16_t message_id); + +/* + * Sends a SUBACK command with a given `message_id` + * and a sequence of granted QoSs. + */ +void mg_mqtt_suback(struct mg_connection *nc, uint8_t *qoss, size_t qoss_len, + uint16_t message_id); + +/* Sends a UNSUBACK command with a given `message_id`. */ +void mg_mqtt_unsuback(struct mg_connection *nc, uint16_t message_id); + +/* Sends a PINGREQ command. */ +void mg_mqtt_ping(struct mg_connection *nc); + +/* Sends a PINGRESP command. */ +void mg_mqtt_pong(struct mg_connection *nc); + +/* + * Extracts the next topic expression from a SUBSCRIBE command payload. + * + * The topic expression name will point to a string in the payload buffer. + * Returns the pos of the next topic expression or -1 when the list + * of topics is exhausted. + */ +int mg_mqtt_next_subscribe_topic(struct mg_mqtt_message *msg, + struct mg_str *topic, uint8_t *qos, int pos); + +/* + * Matches a topic against a topic expression + * + * Returns 1 if it matches; 0 otherwise. + */ +int mg_mqtt_match_topic_expression(struct mg_str exp, struct mg_str topic); + +/* + * Same as `mg_mqtt_match_topic_expression()`, but takes `exp` as a + * NULL-terminated string. + */ +int mg_mqtt_vmatch_topic_expression(const char *exp, struct mg_str topic); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CS_MONGOOSE_SRC_MQTT_H_ */ diff --git a/src/mongoose-6.11/src/mg_mqtt_client.h b/src/mongoose-6.11/src/mg_mqtt_client.h new file mode 100644 index 0000000..998414d --- /dev/null +++ b/src/mongoose-6.11/src/mg_mqtt_client.h @@ -0,0 +1,8 @@ +/* + * === API reference + */ + +#ifndef CS_MONGOOSE_SRC_MQTT_CLIENT_H_ +#define CS_MONGOOSE_SRC_MQTT_CLIENT_H_ + +#endif /* CS_MONGOOSE_SRC_MQTT_CLIENT_H_ */ diff --git a/src/mongoose-6.11/src/mg_mqtt_server.c b/src/mongoose-6.11/src/mg_mqtt_server.c new file mode 100644 index 0000000..cb56648 --- /dev/null +++ b/src/mongoose-6.11/src/mg_mqtt_server.c @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#include "mg_internal.h" +#include "mg_mqtt_server.h" + +#if MG_ENABLE_MQTT_BROKER + +static void mg_mqtt_session_init(struct mg_mqtt_broker *brk, + struct mg_mqtt_session *s, + struct mg_connection *nc) { + s->brk = brk; + s->subscriptions = NULL; + s->num_subscriptions = 0; + s->nc = nc; +} + +static void mg_mqtt_add_session(struct mg_mqtt_session *s) { + LIST_INSERT_HEAD(&s->brk->sessions, s, link); +} + +static void mg_mqtt_remove_session(struct mg_mqtt_session *s) { + LIST_REMOVE(s, link); +} + +static void mg_mqtt_destroy_session(struct mg_mqtt_session *s) { + size_t i; + for (i = 0; i < s->num_subscriptions; i++) { + MG_FREE((void *) s->subscriptions[i].topic); + } + MG_FREE(s->subscriptions); + MG_FREE(s); +} + +static void mg_mqtt_close_session(struct mg_mqtt_session *s) { + mg_mqtt_remove_session(s); + mg_mqtt_destroy_session(s); +} + +void mg_mqtt_broker_init(struct mg_mqtt_broker *brk, void *user_data) { + LIST_INIT(&brk->sessions); + brk->user_data = user_data; +} + +static void mg_mqtt_broker_handle_connect(struct mg_mqtt_broker *brk, + struct mg_connection *nc) { + struct mg_mqtt_session *s = + (struct mg_mqtt_session *) MG_CALLOC(1, sizeof *s); + if (s == NULL) { + /* LCOV_EXCL_START */ + mg_mqtt_connack(nc, MG_EV_MQTT_CONNACK_SERVER_UNAVAILABLE); + return; + /* LCOV_EXCL_STOP */ + } + + /* TODO(mkm): check header (magic and version) */ + + mg_mqtt_session_init(brk, s, nc); + nc->priv_2 = s; + mg_mqtt_add_session(s); + + mg_mqtt_connack(nc, MG_EV_MQTT_CONNACK_ACCEPTED); +} + +static void mg_mqtt_broker_handle_subscribe(struct mg_connection *nc, + struct mg_mqtt_message *msg) { + struct mg_mqtt_session *ss = (struct mg_mqtt_session *) nc->priv_2; + uint8_t qoss[MG_MQTT_MAX_SESSION_SUBSCRIPTIONS]; + size_t num_subs = 0; + struct mg_str topic; + uint8_t qos; + int pos; + struct mg_mqtt_topic_expression *te; + + for (pos = 0; + (pos = mg_mqtt_next_subscribe_topic(msg, &topic, &qos, pos)) != -1;) { + if (num_subs >= sizeof(MG_MQTT_MAX_SESSION_SUBSCRIPTIONS) || + (ss->num_subscriptions + num_subs >= + MG_MQTT_MAX_SESSION_SUBSCRIPTIONS)) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + return; + } + qoss[num_subs++] = qos; + } + + if (num_subs > 0) { + te = (struct mg_mqtt_topic_expression *) MG_REALLOC( + ss->subscriptions, + sizeof(*ss->subscriptions) * (ss->num_subscriptions + num_subs)); + if (te == NULL) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + return; + } + ss->subscriptions = te; + for (pos = 0; + pos < (int) msg->payload.len && + (pos = mg_mqtt_next_subscribe_topic(msg, &topic, &qos, pos)) != -1; + ss->num_subscriptions++) { + te = &ss->subscriptions[ss->num_subscriptions]; + te->topic = (char *) MG_MALLOC(topic.len + 1); + te->qos = qos; + memcpy((char *) te->topic, topic.p, topic.len); + ((char *) te->topic)[topic.len] = '\0'; + } + } + + if (pos == (int) msg->payload.len) { + mg_mqtt_suback(nc, qoss, num_subs, msg->message_id); + } else { + /* We did not fully parse the payload, something must be wrong. */ + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } +} + +static void mg_mqtt_broker_handle_publish(struct mg_mqtt_broker *brk, + struct mg_mqtt_message *msg) { + struct mg_mqtt_session *s; + size_t i; + + for (s = mg_mqtt_next(brk, NULL); s != NULL; s = mg_mqtt_next(brk, s)) { + for (i = 0; i < s->num_subscriptions; i++) { + if (mg_mqtt_vmatch_topic_expression(s->subscriptions[i].topic, + msg->topic)) { + char buf[100], *p = buf; + mg_asprintf(&p, sizeof(buf), "%.*s", (int) msg->topic.len, + msg->topic.p); + if (p == NULL) { + return; + } + mg_mqtt_publish(s->nc, p, 0, 0, msg->payload.p, msg->payload.len); + if (p != buf) { + MG_FREE(p); + } + break; + } + } + } +} + +void mg_mqtt_broker(struct mg_connection *nc, int ev, void *data) { + struct mg_mqtt_message *msg = (struct mg_mqtt_message *) data; + struct mg_mqtt_broker *brk; + + if (nc->listener) { + brk = (struct mg_mqtt_broker *) nc->listener->priv_2; + } else { + brk = (struct mg_mqtt_broker *) nc->priv_2; + } + + switch (ev) { + case MG_EV_ACCEPT: + if (nc->proto_data == NULL) mg_set_protocol_mqtt(nc); + nc->priv_2 = NULL; /* Clear up the inherited pointer to broker */ + break; + case MG_EV_MQTT_CONNECT: + if (nc->priv_2 == NULL) { + mg_mqtt_broker_handle_connect(brk, nc); + } else { + /* Repeated CONNECT */ + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } + break; + case MG_EV_MQTT_SUBSCRIBE: + if (nc->priv_2 != NULL) { + mg_mqtt_broker_handle_subscribe(nc, msg); + } else { + /* Subscribe before CONNECT */ + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } + break; + case MG_EV_MQTT_PUBLISH: + if (nc->priv_2 != NULL) { + mg_mqtt_broker_handle_publish(brk, msg); + } else { + /* Publish before CONNECT */ + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } + break; + case MG_EV_CLOSE: + if (nc->listener && nc->priv_2 != NULL) { + mg_mqtt_close_session((struct mg_mqtt_session *) nc->priv_2); + } + break; + } +} + +struct mg_mqtt_session *mg_mqtt_next(struct mg_mqtt_broker *brk, + struct mg_mqtt_session *s) { + return s == NULL ? LIST_FIRST(&brk->sessions) : LIST_NEXT(s, link); +} + +#endif /* MG_ENABLE_MQTT_BROKER */ diff --git a/src/mongoose-6.11/src/mg_mqtt_server.h b/src/mongoose-6.11/src/mg_mqtt_server.h new file mode 100644 index 0000000..8c34b5e --- /dev/null +++ b/src/mongoose-6.11/src/mg_mqtt_server.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + * This software is dual-licensed: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. For the terms of this + * license, see . + * + * You are free to use this software under the terms of the GNU General + * Public License, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * Alternatively, you can license this software under a commercial + * license, as set out in . + */ + +/* + * === MQTT Server API reference + */ + +#ifndef CS_MONGOOSE_SRC_MQTT_BROKER_H_ +#define CS_MONGOOSE_SRC_MQTT_BROKER_H_ + +#if MG_ENABLE_MQTT_BROKER + +#include "common/queue.h" +#include "mg_mqtt.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef MG_MQTT_MAX_SESSION_SUBSCRIPTIONS +#define MG_MQTT_MAX_SESSION_SUBSCRIPTIONS 512 +#endif + +struct mg_mqtt_broker; + +/* MQTT session (Broker side). */ +struct mg_mqtt_session { + struct mg_mqtt_broker *brk; /* Broker */ + LIST_ENTRY(mg_mqtt_session) link; /* mg_mqtt_broker::sessions linkage */ + struct mg_connection *nc; /* Connection with the client */ + size_t num_subscriptions; /* Size of `subscriptions` array */ + void *user_data; /* User data */ + struct mg_mqtt_topic_expression *subscriptions; +}; + +/* MQTT broker. */ +struct mg_mqtt_broker { + LIST_HEAD(_mg_sesshead, mg_mqtt_session) sessions; /* Session list */ + void *user_data; /* User data */ +}; + +/* Initialises a MQTT broker. */ +void mg_mqtt_broker_init(struct mg_mqtt_broker *brk, void *user_data); + +/* + * Processes a MQTT broker message. + * + * The listening connection expects a pointer to an initialised + * `mg_mqtt_broker` structure in the `user_data` field. + * + * Basic usage: + * + * ```c + * mg_mqtt_broker_init(&brk, NULL); + * + * if ((nc = mg_bind(&mgr, address, mg_mqtt_broker)) == NULL) { + * // fail; + * } + * nc->user_data = &brk; + * ``` + * + * New incoming connections will receive a `mg_mqtt_session` structure + * in the connection `user_data`. The original `user_data` will be stored + * in the `user_data` field of the session structure. This allows the user + * handler to store user data before `mg_mqtt_broker` creates the session. + * + * Since only the MG_EV_ACCEPT message is processed by the listening socket, + * for most events the `user_data` will thus point to a `mg_mqtt_session`. + */ +void mg_mqtt_broker(struct mg_connection *brk, int ev, void *data); + +/* + * Iterates over all MQTT session connections. Example: + * + * ```c + * struct mg_mqtt_session *s; + * for (s = mg_mqtt_next(brk, NULL); s != NULL; s = mg_mqtt_next(brk, s)) { + * // Do something + * } + * ``` + */ +struct mg_mqtt_session *mg_mqtt_next(struct mg_mqtt_broker *brk, + struct mg_mqtt_session *s); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* MG_ENABLE_MQTT_BROKER */ +#endif /* CS_MONGOOSE_SRC_MQTT_BROKER_H_ */ diff --git a/src/mongoose-6.11/src/mg_net.c b/src/mongoose-6.11/src/mg_net.c new file mode 100644 index 0000000..70e2cc8 --- /dev/null +++ b/src/mongoose-6.11/src/mg_net.c @@ -0,0 +1,1008 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + * + * This software is dual-licensed: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. For the terms of this + * license, see . + * + * You are free to use this software under the terms of the GNU General + * Public License, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * Alternatively, you can license this software under a commercial + * license, as set out in . + */ + +#include "common/cs_time.h" +#include "mg_dns.h" +#include "mg_internal.h" +#include "mg_resolv.h" +#include "mg_util.h" + +#define MG_MAX_HOST_LEN 200 + +#define MG_COPY_COMMON_CONNECTION_OPTIONS(dst, src) \ + memcpy(dst, src, sizeof(*dst)); + +/* Which flags can be pre-set by the user at connection creation time. */ +#define _MG_ALLOWED_CONNECT_FLAGS_MASK \ + (MG_F_USER_1 | MG_F_USER_2 | MG_F_USER_3 | MG_F_USER_4 | MG_F_USER_5 | \ + MG_F_USER_6 | MG_F_WEBSOCKET_NO_DEFRAG | MG_F_ENABLE_BROADCAST) +/* Which flags should be modifiable by user's callbacks. */ +#define _MG_CALLBACK_MODIFIABLE_FLAGS_MASK \ + (MG_F_USER_1 | MG_F_USER_2 | MG_F_USER_3 | MG_F_USER_4 | MG_F_USER_5 | \ + MG_F_USER_6 | MG_F_WEBSOCKET_NO_DEFRAG | MG_F_SEND_AND_CLOSE | \ + MG_F_CLOSE_IMMEDIATELY | MG_F_IS_WEBSOCKET | MG_F_DELETE_CHUNK) + +#ifndef intptr_t +#define intptr_t long +#endif + +MG_INTERNAL void mg_add_conn(struct mg_mgr *mgr, struct mg_connection *c) { + DBG(("%p %p", mgr, c)); + c->mgr = mgr; + c->next = mgr->active_connections; + mgr->active_connections = c; + c->prev = NULL; + if (c->next != NULL) c->next->prev = c; + if (c->sock != INVALID_SOCKET) { + c->iface->vtable->add_conn(c); + } +} + +MG_INTERNAL void mg_remove_conn(struct mg_connection *conn) { + if (conn->prev == NULL) conn->mgr->active_connections = conn->next; + if (conn->prev) conn->prev->next = conn->next; + if (conn->next) conn->next->prev = conn->prev; + conn->prev = conn->next = NULL; + conn->iface->vtable->remove_conn(conn); +} + +MG_INTERNAL void mg_call(struct mg_connection *nc, + mg_event_handler_t ev_handler, void *user_data, int ev, + void *ev_data) { + static int nesting_level = 0; + nesting_level++; + if (ev_handler == NULL) { + /* + * If protocol handler is specified, call it. Otherwise, call user-specified + * event handler. + */ + ev_handler = nc->proto_handler ? nc->proto_handler : nc->handler; + } + if (ev != MG_EV_POLL) { + DBG(("%p %s ev=%d ev_data=%p flags=%lu rmbl=%d smbl=%d", nc, + ev_handler == nc->handler ? "user" : "proto", ev, ev_data, nc->flags, + (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); + } + +#if !defined(NO_LIBC) && MG_ENABLE_HEXDUMP + if (nc->mgr->hexdump_file != NULL && ev != MG_EV_POLL && ev != MG_EV_RECV && + ev != MG_EV_SEND /* handled separately */) { + mg_hexdump_connection(nc, nc->mgr->hexdump_file, NULL, 0, ev); + } +#endif + if (ev_handler != NULL) { + unsigned long flags_before = nc->flags; + size_t recv_mbuf_before = nc->recv_mbuf.len, recved; + ev_handler(nc, ev, ev_data MG_UD_ARG(user_data)); + recved = (recv_mbuf_before - nc->recv_mbuf.len); + /* Prevent user handler from fiddling with system flags. */ + if (ev_handler == nc->handler && nc->flags != flags_before) { + nc->flags = (flags_before & ~_MG_CALLBACK_MODIFIABLE_FLAGS_MASK) | + (nc->flags & _MG_CALLBACK_MODIFIABLE_FLAGS_MASK); + } + /* It's important to not double-count recved bytes, and since mg_call can be + * called recursively (e.g. proto_handler invokes user handler), we keep + * track of recursion and only report received bytes at the top level. */ + if (nesting_level == 1 && recved > 0 && !(nc->flags & MG_F_UDP)) { + nc->iface->vtable->recved(nc, recved); + } + } + if (ev != MG_EV_POLL) { + DBG(("%p after %s flags=%lu rmbl=%d smbl=%d", nc, + ev_handler == nc->handler ? "user" : "proto", nc->flags, + (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); + } + nesting_level--; +#if !MG_ENABLE_CALLBACK_USERDATA + (void) user_data; +#endif +} + +void mg_if_timer(struct mg_connection *c, double now) { + if (c->ev_timer_time > 0 && now >= c->ev_timer_time) { + double old_value = c->ev_timer_time; + c->ev_timer_time = 0; + mg_call(c, NULL, c->user_data, MG_EV_TIMER, &old_value); + } +} + +void mg_if_poll(struct mg_connection *nc, time_t now) { + if (!(nc->flags & MG_F_SSL) || (nc->flags & MG_F_SSL_HANDSHAKE_DONE)) { + mg_call(nc, NULL, nc->user_data, MG_EV_POLL, &now); + } +} + +void mg_destroy_conn(struct mg_connection *conn, int destroy_if) { + if (destroy_if) conn->iface->vtable->destroy_conn(conn); + if (conn->proto_data != NULL && conn->proto_data_destructor != NULL) { + conn->proto_data_destructor(conn->proto_data); + } +#if MG_ENABLE_SSL + mg_ssl_if_conn_free(conn); +#endif + mbuf_free(&conn->recv_mbuf); + mbuf_free(&conn->send_mbuf); + + memset(conn, 0, sizeof(*conn)); + MG_FREE(conn); +} + +void mg_close_conn(struct mg_connection *conn) { + DBG(("%p %lu %d", conn, conn->flags, conn->sock)); +#if MG_ENABLE_SSL + if (conn->flags & MG_F_SSL_HANDSHAKE_DONE) { + mg_ssl_if_conn_close_notify(conn); + } +#endif + mg_remove_conn(conn); + conn->iface->vtable->destroy_conn(conn); + mg_call(conn, NULL, conn->user_data, MG_EV_CLOSE, NULL); + mg_destroy_conn(conn, 0 /* destroy_if */); +} + +void mg_mgr_init(struct mg_mgr *m, void *user_data) { + struct mg_mgr_init_opts opts; + memset(&opts, 0, sizeof(opts)); + mg_mgr_init_opt(m, user_data, opts); +} + +void mg_mgr_init_opt(struct mg_mgr *m, void *user_data, + struct mg_mgr_init_opts opts) { + memset(m, 0, sizeof(*m)); +#if MG_ENABLE_BROADCAST + m->ctl[0] = m->ctl[1] = INVALID_SOCKET; +#endif + m->user_data = user_data; + +#ifdef _WIN32 + { + WSADATA data; + WSAStartup(MAKEWORD(2, 2), &data); + } +#elif defined(__unix__) + /* Ignore SIGPIPE signal, so if client cancels the request, it + * won't kill the whole process. */ + signal(SIGPIPE, SIG_IGN); +#endif + +#if MG_ENABLE_SSL + { + static int init_done; + if (!init_done) { + mg_ssl_if_init(); + init_done++; + } + } +#endif + { + int i; + if (opts.num_ifaces == 0) { + opts.num_ifaces = mg_num_ifaces; + opts.ifaces = mg_ifaces; + } + if (opts.main_iface != NULL) { + opts.ifaces[MG_MAIN_IFACE] = opts.main_iface; + } + m->num_ifaces = opts.num_ifaces; + m->ifaces = + (struct mg_iface **) MG_MALLOC(sizeof(*m->ifaces) * opts.num_ifaces); + for (i = 0; i < mg_num_ifaces; i++) { + m->ifaces[i] = mg_if_create_iface(opts.ifaces[i], m); + m->ifaces[i]->vtable->init(m->ifaces[i]); + } + } + if (opts.nameserver != NULL) { + m->nameserver = strdup(opts.nameserver); + } + DBG(("==================================")); + DBG(("init mgr=%p", m)); +} + +void mg_mgr_free(struct mg_mgr *m) { + struct mg_connection *conn, *tmp_conn; + + DBG(("%p", m)); + if (m == NULL) return; + /* Do one last poll, see https://github.com/cesanta/mongoose/issues/286 */ + mg_mgr_poll(m, 0); + +#if MG_ENABLE_BROADCAST + if (m->ctl[0] != INVALID_SOCKET) closesocket(m->ctl[0]); + if (m->ctl[1] != INVALID_SOCKET) closesocket(m->ctl[1]); + m->ctl[0] = m->ctl[1] = INVALID_SOCKET; +#endif + + for (conn = m->active_connections; conn != NULL; conn = tmp_conn) { + tmp_conn = conn->next; + mg_close_conn(conn); + } + + { + int i; + for (i = 0; i < m->num_ifaces; i++) { + m->ifaces[i]->vtable->free(m->ifaces[i]); + MG_FREE(m->ifaces[i]); + } + MG_FREE(m->ifaces); + } + + MG_FREE((char *) m->nameserver); +} + +time_t mg_mgr_poll(struct mg_mgr *m, int timeout_ms) { + int i; + time_t now = 0; /* oh GCC, seriously ? */ + + if (m->num_ifaces == 0) { + LOG(LL_ERROR, ("cannot poll: no interfaces")); + return 0; + } + + for (i = 0; i < m->num_ifaces; i++) { + now = m->ifaces[i]->vtable->poll(m->ifaces[i], timeout_ms); + } + return now; +} + +int mg_vprintf(struct mg_connection *nc, const char *fmt, va_list ap) { + char mem[MG_VPRINTF_BUFFER_SIZE], *buf = mem; + int len; + + if ((len = mg_avprintf(&buf, sizeof(mem), fmt, ap)) > 0) { + mg_send(nc, buf, len); + } + if (buf != mem && buf != NULL) { + MG_FREE(buf); /* LCOV_EXCL_LINE */ + } /* LCOV_EXCL_LINE */ + + return len; +} + +int mg_printf(struct mg_connection *conn, const char *fmt, ...) { + int len; + va_list ap; + va_start(ap, fmt); + len = mg_vprintf(conn, fmt, ap); + va_end(ap); + return len; +} + +#if MG_ENABLE_SYNC_RESOLVER +/* TODO(lsm): use non-blocking resolver */ +static int mg_resolve2(const char *host, struct in_addr *ina) { +#if MG_ENABLE_GETADDRINFO + int rv = 0; + struct addrinfo hints, *servinfo, *p; + struct sockaddr_in *h = NULL; + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + if ((rv = getaddrinfo(host, NULL, NULL, &servinfo)) != 0) { + DBG(("getaddrinfo(%s) failed: %s", host, strerror(mg_get_errno()))); + return 0; + } + for (p = servinfo; p != NULL; p = p->ai_next) { + memcpy(&h, &p->ai_addr, sizeof(struct sockaddr_in *)); + memcpy(ina, &h->sin_addr, sizeof(ina)); + } + freeaddrinfo(servinfo); + return 1; +#else + struct hostent *he; + if ((he = gethostbyname(host)) == NULL) { + DBG(("gethostbyname(%s) failed: %s", host, strerror(mg_get_errno()))); + } else { + memcpy(ina, he->h_addr_list[0], sizeof(*ina)); + return 1; + } + return 0; +#endif /* MG_ENABLE_GETADDRINFO */ +} + +int mg_resolve(const char *host, char *buf, size_t n) { + struct in_addr ad; + return mg_resolve2(host, &ad) ? snprintf(buf, n, "%s", inet_ntoa(ad)) : 0; +} +#endif /* MG_ENABLE_SYNC_RESOLVER */ + +MG_INTERNAL struct mg_connection *mg_create_connection_base( + struct mg_mgr *mgr, mg_event_handler_t callback, + struct mg_add_sock_opts opts) { + struct mg_connection *conn; + + if ((conn = (struct mg_connection *) MG_CALLOC(1, sizeof(*conn))) != NULL) { + conn->sock = INVALID_SOCKET; + conn->handler = callback; + conn->mgr = mgr; + conn->last_io_time = (time_t) mg_time(); + conn->iface = + (opts.iface != NULL ? opts.iface : mgr->ifaces[MG_MAIN_IFACE]); + conn->flags = opts.flags & _MG_ALLOWED_CONNECT_FLAGS_MASK; + conn->user_data = opts.user_data; + /* + * SIZE_MAX is defined as a long long constant in + * system headers on some platforms and so it + * doesn't compile with pedantic ansi flags. + */ + conn->recv_mbuf_limit = ~0; + } else { + MG_SET_PTRPTR(opts.error_string, "failed to create connection"); + } + + return conn; +} + +MG_INTERNAL struct mg_connection *mg_create_connection( + struct mg_mgr *mgr, mg_event_handler_t callback, + struct mg_add_sock_opts opts) { + struct mg_connection *conn = mg_create_connection_base(mgr, callback, opts); + + if (conn != NULL && !conn->iface->vtable->create_conn(conn)) { + MG_FREE(conn); + conn = NULL; + } + if (conn == NULL) { + MG_SET_PTRPTR(opts.error_string, "failed to init connection"); + } + + return conn; +} + +/* + * Address format: [PROTO://][HOST]:PORT + * + * HOST could be IPv4/IPv6 address or a host name. + * `host` is a destination buffer to hold parsed HOST part. Should be at least + * MG_MAX_HOST_LEN bytes long. + * `proto` is a returned socket type, either SOCK_STREAM or SOCK_DGRAM + * + * Return: + * -1 on parse error + * 0 if HOST needs DNS lookup + * >0 length of the address string + */ +MG_INTERNAL int mg_parse_address(const char *str, union socket_address *sa, + int *proto, char *host, size_t host_len) { + unsigned int a, b, c, d, port = 0; + int ch, len = 0; +#if MG_ENABLE_IPV6 + char buf[100]; +#endif + + /* + * MacOS needs that. If we do not zero it, subsequent bind() will fail. + * Also, all-zeroes in the socket address means binding to all addresses + * for both IPv4 and IPv6 (INADDR_ANY and IN6ADDR_ANY_INIT). + */ + memset(sa, 0, sizeof(*sa)); + sa->sin.sin_family = AF_INET; + + *proto = SOCK_STREAM; + + if (strncmp(str, "udp://", 6) == 0) { + str += 6; + *proto = SOCK_DGRAM; + } else if (strncmp(str, "tcp://", 6) == 0) { + str += 6; + } + + if (sscanf(str, "%u.%u.%u.%u:%u%n", &a, &b, &c, &d, &port, &len) == 5) { + /* Bind to a specific IPv4 address, e.g. 192.168.1.5:8080 */ + sa->sin.sin_addr.s_addr = + htonl(((uint32_t) a << 24) | ((uint32_t) b << 16) | c << 8 | d); + sa->sin.sin_port = htons((uint16_t) port); +#if MG_ENABLE_IPV6 + } else if (sscanf(str, "[%99[^]]]:%u%n", buf, &port, &len) == 2 && + inet_pton(AF_INET6, buf, &sa->sin6.sin6_addr)) { + /* IPv6 address, e.g. [3ffe:2a00:100:7031::1]:8080 */ + sa->sin6.sin6_family = AF_INET6; + sa->sin.sin_port = htons((uint16_t) port); +#endif +#if MG_ENABLE_ASYNC_RESOLVER + } else if (strlen(str) < host_len && + sscanf(str, "%[^ :]:%u%n", host, &port, &len) == 2) { + sa->sin.sin_port = htons((uint16_t) port); + if (mg_resolve_from_hosts_file(host, sa) != 0) { + /* + * if resolving from hosts file failed and the host + * we are trying to resolve is `localhost` - we should + * try to resolve it using `gethostbyname` and do not try + * to resolve it via DNS server if gethostbyname has failed too + */ + if (mg_ncasecmp(host, "localhost", 9) != 0) { + return 0; + } + +#if MG_ENABLE_SYNC_RESOLVER + if (!mg_resolve2(host, &sa->sin.sin_addr)) { + return -1; + } +#else + return -1; +#endif + } +#endif + } else if (sscanf(str, ":%u%n", &port, &len) == 1 || + sscanf(str, "%u%n", &port, &len) == 1) { + /* If only port is specified, bind to IPv4, INADDR_ANY */ + sa->sin.sin_port = htons((uint16_t) port); + } else { + return -1; + } + + /* Required for MG_ENABLE_ASYNC_RESOLVER=0 */ + (void) host; + (void) host_len; + + ch = str[len]; /* Character that follows the address */ + return port < 0xffffUL && (ch == '\0' || ch == ',' || isspace(ch)) ? len : -1; +} + +struct mg_connection *mg_if_accept_new_conn(struct mg_connection *lc) { + struct mg_add_sock_opts opts; + struct mg_connection *nc; + memset(&opts, 0, sizeof(opts)); + nc = mg_create_connection(lc->mgr, lc->handler, opts); + if (nc == NULL) return NULL; + nc->listener = lc; + nc->proto_handler = lc->proto_handler; + nc->user_data = lc->user_data; + nc->recv_mbuf_limit = lc->recv_mbuf_limit; + nc->iface = lc->iface; + if (lc->flags & MG_F_SSL) nc->flags |= MG_F_SSL; + mg_add_conn(nc->mgr, nc); + DBG(("%p %p %d %d", lc, nc, nc->sock, (int) nc->flags)); + return nc; +} + +void mg_if_accept_tcp_cb(struct mg_connection *nc, union socket_address *sa, + size_t sa_len) { + (void) sa_len; + nc->sa = *sa; + mg_call(nc, NULL, nc->user_data, MG_EV_ACCEPT, &nc->sa); +} + +void mg_send(struct mg_connection *nc, const void *buf, int len) { + nc->last_io_time = (time_t) mg_time(); + if (nc->flags & MG_F_UDP) { + nc->iface->vtable->udp_send(nc, buf, len); + } else { + nc->iface->vtable->tcp_send(nc, buf, len); + } +} + +void mg_if_sent_cb(struct mg_connection *nc, int num_sent) { + DBG(("%p %d", nc, num_sent)); +#if !defined(NO_LIBC) && MG_ENABLE_HEXDUMP + if (nc->mgr && nc->mgr->hexdump_file != NULL) { + char *buf = nc->send_mbuf.buf; + mg_hexdump_connection(nc, nc->mgr->hexdump_file, buf, num_sent, MG_EV_SEND); + } +#endif + if (num_sent < 0) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } else { + mbuf_remove(&nc->send_mbuf, num_sent); + mbuf_trim(&nc->send_mbuf); + } + mg_call(nc, NULL, nc->user_data, MG_EV_SEND, &num_sent); +} + +MG_INTERNAL void mg_recv_common(struct mg_connection *nc, void *buf, int len, + int own) { + DBG(("%p %d %u", nc, len, (unsigned int) nc->recv_mbuf.len)); + +#if !defined(NO_LIBC) && MG_ENABLE_HEXDUMP + if (nc->mgr && nc->mgr->hexdump_file != NULL) { + mg_hexdump_connection(nc, nc->mgr->hexdump_file, buf, len, MG_EV_RECV); + } +#endif + + if (nc->flags & MG_F_CLOSE_IMMEDIATELY) { + DBG(("%p discarded %d bytes", nc, len)); + /* + * This connection will not survive next poll. Do not deliver events, + * send data to /dev/null without acking. + */ + if (own) { + MG_FREE(buf); + } + return; + } + nc->last_io_time = (time_t) mg_time(); + if (!own) { + mbuf_append(&nc->recv_mbuf, buf, len); + } else if (nc->recv_mbuf.len == 0) { + /* Adopt buf as recv_mbuf's backing store. */ + mbuf_free(&nc->recv_mbuf); + nc->recv_mbuf.buf = (char *) buf; + nc->recv_mbuf.size = nc->recv_mbuf.len = len; + } else { + mbuf_append(&nc->recv_mbuf, buf, len); + MG_FREE(buf); + } + mg_call(nc, NULL, nc->user_data, MG_EV_RECV, &len); +} + +void mg_if_recv_tcp_cb(struct mg_connection *nc, void *buf, int len, int own) { + mg_recv_common(nc, buf, len, own); +} + +void mg_if_recv_udp_cb(struct mg_connection *nc, void *buf, int len, + union socket_address *sa, size_t sa_len) { + assert(nc->flags & MG_F_UDP); + DBG(("%p %u", nc, (unsigned int) len)); + if (nc->flags & MG_F_LISTENING) { + struct mg_connection *lc = nc; + /* + * Do we have an existing connection for this source? + * This is very inefficient for long connection lists. + */ + for (nc = mg_next(lc->mgr, NULL); nc != NULL; nc = mg_next(lc->mgr, nc)) { + if (memcmp(&nc->sa.sa, &sa->sa, sa_len) == 0 && nc->listener == lc) { + break; + } + } + if (nc == NULL) { + struct mg_add_sock_opts opts; + memset(&opts, 0, sizeof(opts)); + /* Create fake connection w/out sock initialization */ + nc = mg_create_connection_base(lc->mgr, lc->handler, opts); + if (nc != NULL) { + nc->sock = lc->sock; + nc->listener = lc; + nc->sa = *sa; + nc->proto_handler = lc->proto_handler; + nc->user_data = lc->user_data; + nc->recv_mbuf_limit = lc->recv_mbuf_limit; + nc->flags = MG_F_UDP; + /* + * Long-lived UDP "connections" i.e. interactions that involve more + * than one request and response are rare, most are transactional: + * response is sent and the "connection" is closed. Or - should be. + * But users (including ourselves) tend to forget about that part, + * because UDP is connectionless and one does not think about + * processing a UDP request as handling a connection that needs to be + * closed. Thus, we begin with SEND_AND_CLOSE flag set, which should + * be a reasonable default for most use cases, but it is possible to + * turn it off the connection should be kept alive after processing. + */ + nc->flags |= MG_F_SEND_AND_CLOSE; + mg_add_conn(lc->mgr, nc); + mg_call(nc, NULL, nc->user_data, MG_EV_ACCEPT, &nc->sa); + } else { + DBG(("OOM")); + /* No return here, we still need to drop on the floor */ + } + } + } + if (nc != NULL) { + mg_recv_common(nc, buf, len, 1); + } else { + /* Drop on the floor. */ + MG_FREE(buf); + } +} + +/* + * Schedules an async connect for a resolved address and proto. + * Called from two places: `mg_connect_opt()` and from async resolver. + * When called from the async resolver, it must trigger `MG_EV_CONNECT` event + * with a failure flag to indicate connection failure. + */ +MG_INTERNAL struct mg_connection *mg_do_connect(struct mg_connection *nc, + int proto, + union socket_address *sa) { + DBG(("%p %s://%s:%hu", nc, proto == SOCK_DGRAM ? "udp" : "tcp", + inet_ntoa(sa->sin.sin_addr), ntohs(sa->sin.sin_port))); + + nc->flags |= MG_F_CONNECTING; + if (proto == SOCK_DGRAM) { + nc->iface->vtable->connect_udp(nc); + } else { + nc->iface->vtable->connect_tcp(nc, sa); + } + mg_add_conn(nc->mgr, nc); + return nc; +} + +void mg_if_connect_cb(struct mg_connection *nc, int err) { + DBG(("%p connect, err=%d", nc, err)); + nc->flags &= ~MG_F_CONNECTING; + if (err != 0) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } + mg_call(nc, NULL, nc->user_data, MG_EV_CONNECT, &err); +} + +#if MG_ENABLE_ASYNC_RESOLVER +/* + * Callback for the async resolver on mg_connect_opt() call. + * Main task of this function is to trigger MG_EV_CONNECT event with + * either failure (and dealloc the connection) + * or success (and proceed with connect() + */ +static void resolve_cb(struct mg_dns_message *msg, void *data, + enum mg_resolve_err e) { + struct mg_connection *nc = (struct mg_connection *) data; + int i; + int failure = -1; + + nc->flags &= ~MG_F_RESOLVING; + if (msg != NULL) { + /* + * Take the first DNS A answer and run... + */ + for (i = 0; i < msg->num_answers; i++) { + if (msg->answers[i].rtype == MG_DNS_A_RECORD) { + /* + * Async resolver guarantees that there is at least one answer. + * TODO(lsm): handle IPv6 answers too + */ + mg_dns_parse_record_data(msg, &msg->answers[i], &nc->sa.sin.sin_addr, + 4); + mg_do_connect(nc, nc->flags & MG_F_UDP ? SOCK_DGRAM : SOCK_STREAM, + &nc->sa); + return; + } + } + } + + if (e == MG_RESOLVE_TIMEOUT) { + double now = mg_time(); + mg_call(nc, NULL, nc->user_data, MG_EV_TIMER, &now); + } + + /* + * If we get there was no MG_DNS_A_RECORD in the answer + */ + mg_call(nc, NULL, nc->user_data, MG_EV_CONNECT, &failure); + mg_call(nc, NULL, nc->user_data, MG_EV_CLOSE, NULL); + mg_destroy_conn(nc, 1 /* destroy_if */); +} +#endif + +struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *address, + MG_CB(mg_event_handler_t callback, + void *user_data)) { + struct mg_connect_opts opts; + memset(&opts, 0, sizeof(opts)); + return mg_connect_opt(mgr, address, MG_CB(callback, user_data), opts); +} + +struct mg_connection *mg_connect_opt(struct mg_mgr *mgr, const char *address, + MG_CB(mg_event_handler_t callback, + void *user_data), + struct mg_connect_opts opts) { + struct mg_connection *nc = NULL; + int proto, rc; + struct mg_add_sock_opts add_sock_opts; + char host[MG_MAX_HOST_LEN]; + + MG_COPY_COMMON_CONNECTION_OPTIONS(&add_sock_opts, &opts); + + if ((nc = mg_create_connection(mgr, callback, add_sock_opts)) == NULL) { + return NULL; + } + + if ((rc = mg_parse_address(address, &nc->sa, &proto, host, sizeof(host))) < + 0) { + /* Address is malformed */ + MG_SET_PTRPTR(opts.error_string, "cannot parse address"); + mg_destroy_conn(nc, 1 /* destroy_if */); + return NULL; + } + + nc->flags |= opts.flags & _MG_ALLOWED_CONNECT_FLAGS_MASK; + nc->flags |= (proto == SOCK_DGRAM) ? MG_F_UDP : 0; +#if MG_ENABLE_CALLBACK_USERDATA + nc->user_data = user_data; +#else + nc->user_data = opts.user_data; +#endif + +#if MG_ENABLE_SSL + DBG(("%p %s %s,%s,%s", nc, address, (opts.ssl_cert ? opts.ssl_cert : "-"), + (opts.ssl_key ? opts.ssl_key : "-"), + (opts.ssl_ca_cert ? opts.ssl_ca_cert : "-"))); + + if (opts.ssl_cert != NULL || opts.ssl_ca_cert != NULL || + opts.ssl_psk_identity != NULL) { + const char *err_msg = NULL; + struct mg_ssl_if_conn_params params; + if (nc->flags & MG_F_UDP) { + MG_SET_PTRPTR(opts.error_string, "SSL for UDP is not supported"); + mg_destroy_conn(nc, 1 /* destroy_if */); + return NULL; + } + memset(¶ms, 0, sizeof(params)); + params.cert = opts.ssl_cert; + params.key = opts.ssl_key; + params.ca_cert = opts.ssl_ca_cert; + params.cipher_suites = opts.ssl_cipher_suites; + params.psk_identity = opts.ssl_psk_identity; + params.psk_key = opts.ssl_psk_key; + if (opts.ssl_ca_cert != NULL) { + if (opts.ssl_server_name != NULL) { + if (strcmp(opts.ssl_server_name, "*") != 0) { + params.server_name = opts.ssl_server_name; + } + } else if (rc == 0) { /* If it's a DNS name, use host. */ + params.server_name = host; + } + } + if (mg_ssl_if_conn_init(nc, ¶ms, &err_msg) != MG_SSL_OK) { + MG_SET_PTRPTR(opts.error_string, err_msg); + mg_destroy_conn(nc, 1 /* destroy_if */); + return NULL; + } + nc->flags |= MG_F_SSL; + } +#endif /* MG_ENABLE_SSL */ + + if (rc == 0) { +#if MG_ENABLE_ASYNC_RESOLVER + /* + * DNS resolution is required for host. + * mg_parse_address() fills port in nc->sa, which we pass to resolve_cb() + */ + struct mg_connection *dns_conn = NULL; + struct mg_resolve_async_opts o; + memset(&o, 0, sizeof(o)); + o.dns_conn = &dns_conn; + o.nameserver = opts.nameserver; + if (mg_resolve_async_opt(nc->mgr, host, MG_DNS_A_RECORD, resolve_cb, nc, + o) != 0) { + MG_SET_PTRPTR(opts.error_string, "cannot schedule DNS lookup"); + mg_destroy_conn(nc, 1 /* destroy_if */); + return NULL; + } + nc->priv_2 = dns_conn; + nc->flags |= MG_F_RESOLVING; + return nc; +#else + MG_SET_PTRPTR(opts.error_string, "Resolver is disabled"); + mg_destroy_conn(nc, 1 /* destroy_if */); + return NULL; +#endif + } else { + /* Address is parsed and resolved to IP. proceed with connect() */ + return mg_do_connect(nc, proto, &nc->sa); + } +} + +struct mg_connection *mg_bind(struct mg_mgr *srv, const char *address, + MG_CB(mg_event_handler_t event_handler, + void *user_data)) { + struct mg_bind_opts opts; + memset(&opts, 0, sizeof(opts)); + return mg_bind_opt(srv, address, MG_CB(event_handler, user_data), opts); +} + +struct mg_connection *mg_bind_opt(struct mg_mgr *mgr, const char *address, + MG_CB(mg_event_handler_t callback, + void *user_data), + struct mg_bind_opts opts) { + union socket_address sa; + struct mg_connection *nc = NULL; + int proto, rc; + struct mg_add_sock_opts add_sock_opts; + char host[MG_MAX_HOST_LEN]; + +#if MG_ENABLE_CALLBACK_USERDATA + opts.user_data = user_data; +#endif + + if (callback == NULL) { + MG_SET_PTRPTR(opts.error_string, "handler is required"); + return NULL; + } + + MG_COPY_COMMON_CONNECTION_OPTIONS(&add_sock_opts, &opts); + + if (mg_parse_address(address, &sa, &proto, host, sizeof(host)) <= 0) { + MG_SET_PTRPTR(opts.error_string, "cannot parse address"); + return NULL; + } + + nc = mg_create_connection(mgr, callback, add_sock_opts); + if (nc == NULL) { + return NULL; + } + + nc->sa = sa; + nc->flags |= MG_F_LISTENING; + if (proto == SOCK_DGRAM) nc->flags |= MG_F_UDP; + +#if MG_ENABLE_SSL + DBG(("%p %s %s,%s,%s", nc, address, (opts.ssl_cert ? opts.ssl_cert : "-"), + (opts.ssl_key ? opts.ssl_key : "-"), + (opts.ssl_ca_cert ? opts.ssl_ca_cert : "-"))); + + if (opts.ssl_cert != NULL || opts.ssl_ca_cert != NULL) { + const char *err_msg = NULL; + struct mg_ssl_if_conn_params params; + if (nc->flags & MG_F_UDP) { + MG_SET_PTRPTR(opts.error_string, "SSL for UDP is not supported"); + mg_destroy_conn(nc, 1 /* destroy_if */); + return NULL; + } + memset(¶ms, 0, sizeof(params)); + params.cert = opts.ssl_cert; + params.key = opts.ssl_key; + params.ca_cert = opts.ssl_ca_cert; + params.cipher_suites = opts.ssl_cipher_suites; + if (mg_ssl_if_conn_init(nc, ¶ms, &err_msg) != MG_SSL_OK) { + MG_SET_PTRPTR(opts.error_string, err_msg); + mg_destroy_conn(nc, 1 /* destroy_if */); + return NULL; + } + nc->flags |= MG_F_SSL; + } +#endif /* MG_ENABLE_SSL */ + + if (nc->flags & MG_F_UDP) { + rc = nc->iface->vtable->listen_udp(nc, &nc->sa); + } else { + rc = nc->iface->vtable->listen_tcp(nc, &nc->sa); + } + if (rc != 0) { + DBG(("Failed to open listener: %d", rc)); + MG_SET_PTRPTR(opts.error_string, "failed to open listener"); + mg_destroy_conn(nc, 1 /* destroy_if */); + return NULL; + } + mg_add_conn(nc->mgr, nc); + + return nc; +} + +struct mg_connection *mg_next(struct mg_mgr *s, struct mg_connection *conn) { + return conn == NULL ? s->active_connections : conn->next; +} + +#if MG_ENABLE_BROADCAST +void mg_broadcast(struct mg_mgr *mgr, mg_event_handler_t cb, void *data, + size_t len) { + struct ctl_msg ctl_msg; + + /* + * Mongoose manager has a socketpair, `struct mg_mgr::ctl`, + * where `mg_broadcast()` pushes the message. + * `mg_mgr_poll()` wakes up, reads a message from the socket pair, and calls + * specified callback for each connection. Thus the callback function executes + * in event manager thread. + */ + if (mgr->ctl[0] != INVALID_SOCKET && data != NULL && + len < sizeof(ctl_msg.message)) { + size_t dummy; + + ctl_msg.callback = cb; + memcpy(ctl_msg.message, data, len); + dummy = MG_SEND_FUNC(mgr->ctl[0], (char *) &ctl_msg, + offsetof(struct ctl_msg, message) + len, 0); + dummy = MG_RECV_FUNC(mgr->ctl[0], (char *) &len, 1, 0); + (void) dummy; /* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25509 */ + } +} +#endif /* MG_ENABLE_BROADCAST */ + +static int isbyte(int n) { + return n >= 0 && n <= 255; +} + +static int parse_net(const char *spec, uint32_t *net, uint32_t *mask) { + int n, a, b, c, d, slash = 32, len = 0; + + if ((sscanf(spec, "%d.%d.%d.%d/%d%n", &a, &b, &c, &d, &slash, &n) == 5 || + sscanf(spec, "%d.%d.%d.%d%n", &a, &b, &c, &d, &n) == 4) && + isbyte(a) && isbyte(b) && isbyte(c) && isbyte(d) && slash >= 0 && + slash < 33) { + len = n; + *net = + ((uint32_t) a << 24) | ((uint32_t) b << 16) | ((uint32_t) c << 8) | d; + *mask = slash ? 0xffffffffU << (32 - slash) : 0; + } + + return len; +} + +int mg_check_ip_acl(const char *acl, uint32_t remote_ip) { + int allowed, flag; + uint32_t net, mask; + struct mg_str vec; + + /* If any ACL is set, deny by default */ + allowed = (acl == NULL || *acl == '\0') ? '+' : '-'; + + while ((acl = mg_next_comma_list_entry(acl, &vec, NULL)) != NULL) { + flag = vec.p[0]; + if ((flag != '+' && flag != '-') || + parse_net(&vec.p[1], &net, &mask) == 0) { + return -1; + } + + if (net == (remote_ip & mask)) { + allowed = flag; + } + } + + DBG(("%08x %c", (unsigned int) remote_ip, allowed)); + return allowed == '+'; +} + +/* Move data from one connection to another */ +void mg_forward(struct mg_connection *from, struct mg_connection *to) { + mg_send(to, from->recv_mbuf.buf, from->recv_mbuf.len); + mbuf_remove(&from->recv_mbuf, from->recv_mbuf.len); +} + +double mg_set_timer(struct mg_connection *c, double timestamp) { + double result = c->ev_timer_time; + c->ev_timer_time = timestamp; + /* + * If this connection is resolving, it's not in the list of active + * connections, so not processed yet. It has a DNS resolver connection + * linked to it. Set up a timer for the DNS connection. + */ + DBG(("%p %p %d -> %lu", c, c->priv_2, (c->flags & MG_F_RESOLVING ? 1 : 0), + (unsigned long) timestamp)); + if ((c->flags & MG_F_RESOLVING) && c->priv_2 != NULL) { + ((struct mg_connection *) c->priv_2)->ev_timer_time = timestamp; + } + return result; +} + +void mg_sock_set(struct mg_connection *nc, sock_t sock) { + if (sock != INVALID_SOCKET) { + nc->iface->vtable->sock_set(nc, sock); + } +} + +void mg_if_get_conn_addr(struct mg_connection *nc, int remote, + union socket_address *sa) { + nc->iface->vtable->get_conn_addr(nc, remote, sa); +} + +struct mg_connection *mg_add_sock_opt(struct mg_mgr *s, sock_t sock, + MG_CB(mg_event_handler_t callback, + void *user_data), + struct mg_add_sock_opts opts) { +#if MG_ENABLE_CALLBACK_USERDATA + opts.user_data = user_data; +#endif + + struct mg_connection *nc = mg_create_connection_base(s, callback, opts); + if (nc != NULL) { + mg_sock_set(nc, sock); + mg_add_conn(nc->mgr, nc); + } + return nc; +} + +struct mg_connection *mg_add_sock(struct mg_mgr *s, sock_t sock, + MG_CB(mg_event_handler_t callback, + void *user_data)) { + struct mg_add_sock_opts opts; + memset(&opts, 0, sizeof(opts)); + return mg_add_sock_opt(s, sock, MG_CB(callback, user_data), opts); +} + +double mg_time(void) { + return cs_time(); +} diff --git a/src/mongoose-6.11/src/mg_net.h b/src/mongoose-6.11/src/mg_net.h new file mode 100644 index 0000000..90b42b4 --- /dev/null +++ b/src/mongoose-6.11/src/mg_net.h @@ -0,0 +1,591 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + * This software is dual-licensed: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. For the terms of this + * license, see . + * + * You are free to use this software under the terms of the GNU General + * Public License, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * Alternatively, you can license this software under a commercial + * license, as set out in . + */ + +/* + * === Core API: TCP/UDP/SSL + * + * NOTE: Mongoose manager is single threaded. It does not protect + * its data structures by mutexes, therefore all functions that are dealing + * with a particular event manager should be called from the same thread, + * with exception of the `mg_broadcast()` function. It is fine to have different + * event managers handled by different threads. + */ + +#ifndef CS_MONGOOSE_SRC_NET_H_ +#define CS_MONGOOSE_SRC_NET_H_ + +#include "mg_common.h" +#include "mg_net_if.h" +#include "common/mbuf.h" + +#ifndef MG_VPRINTF_BUFFER_SIZE +#define MG_VPRINTF_BUFFER_SIZE 100 +#endif + +#ifdef MG_USE_READ_WRITE +#define MG_RECV_FUNC(s, b, l, f) read(s, b, l) +#define MG_SEND_FUNC(s, b, l, f) write(s, b, l) +#else +#define MG_RECV_FUNC(s, b, l, f) recv(s, b, l, f) +#define MG_SEND_FUNC(s, b, l, f) send(s, b, l, f) +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +union socket_address { + struct sockaddr sa; + struct sockaddr_in sin; +#if MG_ENABLE_IPV6 + struct sockaddr_in6 sin6; +#else + struct sockaddr sin6; +#endif +}; + +struct mg_connection; + +/* + * Callback function (event handler) prototype. Must be defined by the user. + * Mongoose calls the event handler, passing the events defined below. + */ +typedef void (*mg_event_handler_t)(struct mg_connection *nc, int ev, + void *ev_data MG_UD_ARG(void *user_data)); + +/* Events. Meaning of event parameter (evp) is given in the comment. */ +#define MG_EV_POLL 0 /* Sent to each connection on each mg_mgr_poll() call */ +#define MG_EV_ACCEPT 1 /* New connection accepted. union socket_address * */ +#define MG_EV_CONNECT 2 /* connect() succeeded or failed. int * */ +#define MG_EV_RECV 3 /* Data has been received. int *num_bytes */ +#define MG_EV_SEND 4 /* Data has been written to a socket. int *num_bytes */ +#define MG_EV_CLOSE 5 /* Connection is closed. NULL */ +#define MG_EV_TIMER 6 /* now >= conn->ev_timer_time. double * */ + +/* + * Mongoose event manager. + */ +struct mg_mgr { + struct mg_connection *active_connections; +#if MG_ENABLE_HEXDUMP + const char *hexdump_file; /* Debug hexdump file path */ +#endif +#if MG_ENABLE_BROADCAST + sock_t ctl[2]; /* Socketpair for mg_broadcast() */ +#endif + void *user_data; /* User data */ + int num_ifaces; + struct mg_iface **ifaces; /* network interfaces */ + const char *nameserver; /* DNS server to use */ +}; + +/* + * Mongoose connection. + */ +struct mg_connection { + struct mg_connection *next, *prev; /* mg_mgr::active_connections linkage */ + struct mg_connection *listener; /* Set only for accept()-ed connections */ + struct mg_mgr *mgr; /* Pointer to containing manager */ + + sock_t sock; /* Socket to the remote peer */ + int err; + union socket_address sa; /* Remote peer address */ + size_t recv_mbuf_limit; /* Max size of recv buffer */ + struct mbuf recv_mbuf; /* Received data */ + struct mbuf send_mbuf; /* Data scheduled for sending */ + time_t last_io_time; /* Timestamp of the last socket IO */ + double ev_timer_time; /* Timestamp of the future MG_EV_TIMER */ +#if MG_ENABLE_SSL + void *ssl_if_data; /* SSL library data. */ +#endif + mg_event_handler_t proto_handler; /* Protocol-specific event handler */ + void *proto_data; /* Protocol-specific data */ + void (*proto_data_destructor)(void *proto_data); + mg_event_handler_t handler; /* Event handler function */ + void *user_data; /* User-specific data */ + union { + void *v; + /* + * the C standard is fussy about fitting function pointers into + * void pointers, since some archs might have fat pointers for functions. + */ + mg_event_handler_t f; + } priv_1; + void *priv_2; + void *mgr_data; /* Implementation-specific event manager's data. */ + struct mg_iface *iface; + unsigned long flags; +/* Flags set by Mongoose */ +#define MG_F_LISTENING (1 << 0) /* This connection is listening */ +#define MG_F_UDP (1 << 1) /* This connection is UDP */ +#define MG_F_RESOLVING (1 << 2) /* Waiting for async resolver */ +#define MG_F_CONNECTING (1 << 3) /* connect() call in progress */ +#define MG_F_SSL (1 << 4) /* SSL is enabled on the connection */ +#define MG_F_SSL_HANDSHAKE_DONE (1 << 5) /* SSL hanshake has completed */ +#define MG_F_WANT_READ (1 << 6) /* SSL specific */ +#define MG_F_WANT_WRITE (1 << 7) /* SSL specific */ +#define MG_F_IS_WEBSOCKET (1 << 8) /* Websocket specific */ + +/* Flags that are settable by user */ +#define MG_F_SEND_AND_CLOSE (1 << 10) /* Push remaining data and close */ +#define MG_F_CLOSE_IMMEDIATELY (1 << 11) /* Disconnect */ +#define MG_F_WEBSOCKET_NO_DEFRAG (1 << 12) /* Websocket specific */ +#define MG_F_DELETE_CHUNK (1 << 13) /* HTTP specific */ +#define MG_F_ENABLE_BROADCAST (1 << 14) /* Allow broadcast address usage */ + +#define MG_F_USER_1 (1 << 20) /* Flags left for application */ +#define MG_F_USER_2 (1 << 21) +#define MG_F_USER_3 (1 << 22) +#define MG_F_USER_4 (1 << 23) +#define MG_F_USER_5 (1 << 24) +#define MG_F_USER_6 (1 << 25) +}; + +/* + * Initialise Mongoose manager. Side effect: ignores SIGPIPE signal. + * `mgr->user_data` field will be initialised with a `user_data` parameter. + * That is an arbitrary pointer, where the user code can associate some data + * with the particular Mongoose manager. For example, a C++ wrapper class + * could be written in which case `user_data` can hold a pointer to the + * class instance. + */ +void mg_mgr_init(struct mg_mgr *mgr, void *user_data); + +/* + * Optional parameters to `mg_mgr_init_opt()`. + * + * If `main_iface` is not NULL, it will be used as the main interface in the + * default interface set. The pointer will be free'd by `mg_mgr_free`. + * Otherwise, the main interface will be autodetected based on the current + * platform. + * + * If `num_ifaces` is 0 and `ifaces` is NULL, the default interface set will be + * used. + * This is an advanced option, as it requires you to construct a full interface + * set, including special networking interfaces required by some optional + * features such as TCP tunneling. Memory backing `ifaces` and each of the + * `num_ifaces` pointers it contains will be reclaimed by `mg_mgr_free`. + */ +struct mg_mgr_init_opts { + const struct mg_iface_vtable *main_iface; + int num_ifaces; + const struct mg_iface_vtable **ifaces; + const char *nameserver; +}; + +/* + * Like `mg_mgr_init` but with more options. + * + * Notably, this allows you to create a manger and choose + * dynamically which networking interface implementation to use. + */ +void mg_mgr_init_opt(struct mg_mgr *mgr, void *user_data, + struct mg_mgr_init_opts opts); + +/* + * De-initialises Mongoose manager. + * + * Closes and deallocates all active connections. + */ +void mg_mgr_free(struct mg_mgr *); + +/* + * This function performs the actual IO and must be called in a loop + * (an event loop). It returns the current timestamp. + * `milli` is the maximum number of milliseconds to sleep. + * `mg_mgr_poll()` checks all connections for IO readiness. If at least one + * of the connections is IO-ready, `mg_mgr_poll()` triggers the respective + * event handlers and returns. + */ +time_t mg_mgr_poll(struct mg_mgr *, int milli); + +#if MG_ENABLE_BROADCAST +/* + * Passes a message of a given length to all connections. + * + * Must be called from a thread that does NOT call `mg_mgr_poll()`. + * Note that `mg_broadcast()` is the only function + * that can be, and must be, called from a different (non-IO) thread. + * + * `func` callback function will be called by the IO thread for each + * connection. When called, the event will be `MG_EV_POLL`, and a message will + * be passed as the `ev_data` pointer. Maximum message size is capped + * by `MG_CTL_MSG_MESSAGE_SIZE` which is set to 8192 bytes. + */ +void mg_broadcast(struct mg_mgr *mgr, mg_event_handler_t cb, void *data, + size_t len); +#endif + +/* + * Iterates over all active connections. + * + * Returns the next connection from the list + * of active connections or `NULL` if there are no more connections. Below + * is the iteration idiom: + * + * ```c + * for (c = mg_next(srv, NULL); c != NULL; c = mg_next(srv, c)) { + * // Do something with connection `c` + * } + * ``` + */ +struct mg_connection *mg_next(struct mg_mgr *mgr, struct mg_connection *c); + +/* + * Optional parameters to `mg_add_sock_opt()`. + * + * `flags` is an initial `struct mg_connection::flags` bitmask to set, + * see `MG_F_*` flags definitions. + */ +struct mg_add_sock_opts { + void *user_data; /* Initial value for connection's user_data */ + unsigned int flags; /* Initial connection flags */ + const char **error_string; /* Placeholder for the error string */ + struct mg_iface *iface; /* Interface instance */ +}; + +/* + * Creates a connection, associates it with the given socket and event handler + * and adds it to the manager. + * + * For more options see the `mg_add_sock_opt` variant. + */ +struct mg_connection *mg_add_sock(struct mg_mgr *mgr, sock_t sock, + MG_CB(mg_event_handler_t handler, + void *user_data)); + +/* + * Creates a connection, associates it with the given socket and event handler + * and adds to the manager. + * + * See the `mg_add_sock_opts` structure for a description of the options. + */ +struct mg_connection *mg_add_sock_opt(struct mg_mgr *mgr, sock_t sock, + MG_CB(mg_event_handler_t handler, + void *user_data), + struct mg_add_sock_opts opts); + +/* + * Optional parameters to `mg_bind_opt()`. + * + * `flags` is an initial `struct mg_connection::flags` bitmask to set, + * see `MG_F_*` flags definitions. + */ +struct mg_bind_opts { + void *user_data; /* Initial value for connection's user_data */ + unsigned int flags; /* Extra connection flags */ + const char **error_string; /* Placeholder for the error string */ + struct mg_iface *iface; /* Interface instance */ +#if MG_ENABLE_SSL + /* + * SSL settings. + * + * Server certificate to present to clients or client certificate to + * present to tunnel dispatcher (for tunneled connections). + */ + const char *ssl_cert; + /* Private key corresponding to the certificate. If ssl_cert is set but + * ssl_key is not, ssl_cert is used. */ + const char *ssl_key; + /* CA bundle used to verify client certificates or tunnel dispatchers. */ + const char *ssl_ca_cert; + /* Colon-delimited list of acceptable cipher suites. + * Names depend on the library used, for example: + * + * ECDH-ECDSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256 (OpenSSL) + * TLS-ECDH-ECDSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256 + * (mbedTLS) + * + * For OpenSSL the list can be obtained by running "openssl ciphers". + * For mbedTLS, names can be found in library/ssl_ciphersuites.c + * If NULL, a reasonable default is used. + */ + const char *ssl_cipher_suites; +#endif +}; + +/* + * Creates a listening connection. + * + * See `mg_bind_opt` for full documentation. + */ +struct mg_connection *mg_bind(struct mg_mgr *mgr, const char *address, + MG_CB(mg_event_handler_t handler, + void *user_data)); +/* + * Creates a listening connection. + * + * The `address` parameter specifies which address to bind to. It's format is + * the same as for the `mg_connect()` call, where `HOST` part is optional. + * `address` can be just a port number, e.g. `:8000`. To bind to a specific + * interface, an IP address can be specified, e.g. `1.2.3.4:8000`. By default, + * a TCP connection is created. To create UDP connection, prepend `udp://` + * prefix, e.g. `udp://:8000`. To summarize, `address` parameter has following + * format: `[PROTO://][IP_ADDRESS]:PORT`, where `PROTO` could be `tcp` or + * `udp`. + * + * See the `mg_bind_opts` structure for a description of the optional + * parameters. + * + * Returns a new listening connection or `NULL` on error. + * NOTE: The connection remains owned by the manager, do not free(). + */ +struct mg_connection *mg_bind_opt(struct mg_mgr *mgr, const char *address, + MG_CB(mg_event_handler_t handler, + void *user_data), + struct mg_bind_opts opts); + +/* Optional parameters to `mg_connect_opt()` */ +struct mg_connect_opts { + void *user_data; /* Initial value for connection's user_data */ + unsigned int flags; /* Extra connection flags */ + const char **error_string; /* Placeholder for the error string */ + struct mg_iface *iface; /* Interface instance */ + const char *nameserver; /* DNS server to use, NULL for default */ +#if MG_ENABLE_SSL + /* + * SSL settings. + * Client certificate to present to the server. + */ + const char *ssl_cert; + /* + * Private key corresponding to the certificate. + * If ssl_cert is set but ssl_key is not, ssl_cert is used. + */ + const char *ssl_key; + /* + * Verify server certificate using this CA bundle. If set to "*", then SSL + * is enabled but no cert verification is performed. + */ + const char *ssl_ca_cert; + /* Colon-delimited list of acceptable cipher suites. + * Names depend on the library used, for example: + * + * ECDH-ECDSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256 (OpenSSL) + * TLS-ECDH-ECDSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256 + * (mbedTLS) + * + * For OpenSSL the list can be obtained by running "openssl ciphers". + * For mbedTLS, names can be found in library/ssl_ciphersuites.c + * If NULL, a reasonable default is used. + */ + const char *ssl_cipher_suites; + /* + * Server name verification. If ssl_ca_cert is set and the certificate has + * passed verification, its subject will be verified against this string. + * By default (if ssl_server_name is NULL) hostname part of the address will + * be used. Wildcard matching is supported. A special value of "*" disables + * name verification. + */ + const char *ssl_server_name; + /* + * PSK identity and key. Identity is a NUL-terminated string and key is a hex + * string. Key must be either 16 or 32 bytes (32 or 64 hex digits) for AES-128 + * or AES-256 respectively. + * Note: Default list of cipher suites does not include PSK suites, if you + * want to use PSK you will need to set ssl_cipher_suites as well. + */ + const char *ssl_psk_identity; + const char *ssl_psk_key; +#endif +}; + +/* + * Connects to a remote host. + * + * See `mg_connect_opt()` for full documentation. + */ +struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *address, + MG_CB(mg_event_handler_t handler, + void *user_data)); + +/* + * Connects to a remote host. + * + * The `address` format is `[PROTO://]HOST:PORT`. `PROTO` could be `tcp` or + * `udp`. `HOST` could be an IP address, + * IPv6 address (if Mongoose is compiled with `-DMG_ENABLE_IPV6`) or a host + * name. If `HOST` is a name, Mongoose will resolve it asynchronously. Examples + * of valid addresses: `google.com:80`, `udp://1.2.3.4:53`, `10.0.0.1:443`, + * `[::1]:80` + * + * See the `mg_connect_opts` structure for a description of the optional + * parameters. + * + * Returns a new outbound connection or `NULL` on error. + * + * NOTE: The connection remains owned by the manager, do not free(). + * + * NOTE: To enable IPv6 addresses `-DMG_ENABLE_IPV6` should be specified + * in the compilation flags. + * + * NOTE: The new connection will receive `MG_EV_CONNECT` as its first event + * which will report the connect success status. + * If the asynchronous resolution fails or the `connect()` syscall fails for + * whatever reason (e.g. with `ECONNREFUSED` or `ENETUNREACH`), then + * `MG_EV_CONNECT` event will report failure. Code example below: + * + * ```c + * static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { + * int connect_status; + * + * switch (ev) { + * case MG_EV_CONNECT: + * connect_status = * (int *) ev_data; + * if (connect_status == 0) { + * // Success + * } else { + * // Error + * printf("connect() error: %s\n", strerror(connect_status)); + * } + * break; + * ... + * } + * } + * + * ... + * mg_connect(mgr, "my_site.com:80", ev_handler); + * ``` + */ +struct mg_connection *mg_connect_opt(struct mg_mgr *mgr, const char *address, + MG_CB(mg_event_handler_t handler, + void *user_data), + struct mg_connect_opts opts); + +#if MG_ENABLE_SSL && MG_NET_IF != MG_NET_IF_SIMPLELINK +/* + * Note: This function is deprecated. Please, use SSL options in + * mg_connect_opt. + * + * Enables SSL for a given connection. + * `cert` is a server certificate file name for a listening connection + * or a client certificate file name for an outgoing connection. + * The certificate files must be in PEM format. The server certificate file + * must contain a certificate, concatenated with a private key, optionally + * concatenated with DH parameters. + * `ca_cert` is a CA certificate or NULL if peer verification is not + * required. + * Return: NULL on success or error message on error. + */ +const char *mg_set_ssl(struct mg_connection *nc, const char *cert, + const char *ca_cert); +#endif + +/* + * Sends data to the connection. + * + * Note that sending functions do not actually push data to the socket. + * They just append data to the output buffer. MG_EV_SEND will be delivered when + * the data has actually been pushed out. + */ +void mg_send(struct mg_connection *, const void *buf, int len); + +/* Enables format string warnings for mg_printf */ +#if defined(__GNUC__) +__attribute__((format(printf, 2, 3))) +#endif +/* don't separate from mg_printf declaration */ + +/* + * Sends `printf`-style formatted data to the connection. + * + * See `mg_send` for more details on send semantics. + */ +int mg_printf(struct mg_connection *, const char *fmt, ...); + +/* Same as `mg_printf()`, but takes `va_list ap` as an argument. */ +int mg_vprintf(struct mg_connection *, const char *fmt, va_list ap); + +/* + * Creates a socket pair. + * `sock_type` can be either `SOCK_STREAM` or `SOCK_DGRAM`. + * Returns 0 on failure and 1 on success. + */ +int mg_socketpair(sock_t[2], int sock_type); + +#if MG_ENABLE_SYNC_RESOLVER +/* + * Convert domain name into IP address. + * + * This is a utility function. If compilation flags have + * `-DMG_ENABLE_GETADDRINFO`, then `getaddrinfo()` call is used for name + * resolution. Otherwise, `gethostbyname()` is used. + * + * CAUTION: this function can block. + * Return 1 on success, 0 on failure. + */ +int mg_resolve(const char *domain_name, char *ip_addr_buf, size_t buf_len); +#endif + +/* + * Verify given IP address against the ACL. + * + * `remote_ip` - an IPv4 address to check, in host byte order + * `acl` - a comma separated list of IP subnets: `x.x.x.x/x` or `x.x.x.x`. + * Each subnet is + * prepended by either a - or a + sign. A plus sign means allow, where a + * minus sign means deny. If a subnet mask is omitted, such as `-1.2.3.4`, + * it means that only that single IP address is denied. + * Subnet masks may vary from 0 to 32, inclusive. The default setting + * is to allow all access. On each request the full list is traversed, + * and the last match wins. Example: + * + * `-0.0.0.0/0,+192.168/16` - deny all accesses, only allow 192.168/16 subnet + * + * To learn more about subnet masks, see this + * link:https://en.wikipedia.org/wiki/Subnetwork[Wikipedia page on Subnetwork]. + * + * Returns -1 if ACL is malformed, 0 if address is disallowed, 1 if allowed. + */ +int mg_check_ip_acl(const char *acl, uint32_t remote_ip); + +/* + * Schedules an MG_EV_TIMER event to be delivered at `timestamp` time. + * `timestamp` is UNIX time (the number of seconds since Epoch). It is + * `double` instead of `time_t` to allow for sub-second precision. + * Returns the old timer value. + * + * Example: set the connect timeout to 1.5 seconds: + * + * ``` + * c = mg_connect(&mgr, "cesanta.com", ev_handler); + * mg_set_timer(c, mg_time() + 1.5); + * ... + * + * void ev_handler(struct mg_connection *c, int ev, void *ev_data) { + * switch (ev) { + * case MG_EV_CONNECT: + * mg_set_timer(c, 0); // Clear connect timer + * break; + * case MG_EV_TIMER: + * log("Connect timeout"); + * c->flags |= MG_F_CLOSE_IMMEDIATELY; + * break; + * ``` + */ +double mg_set_timer(struct mg_connection *c, double timestamp); + +/* + * A sub-second precision version of time(). + */ +double mg_time(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CS_MONGOOSE_SRC_NET_H_ */ diff --git a/src/mongoose-6.11/src/mg_net_if.c b/src/mongoose-6.11/src/mg_net_if.c new file mode 100644 index 0000000..2a93dcf --- /dev/null +++ b/src/mongoose-6.11/src/mg_net_if.c @@ -0,0 +1,41 @@ +#include "mg_net_if.h" +#include "mg_internal.h" +#include "mg_net_if_socket.h" + +extern const struct mg_iface_vtable mg_default_iface_vtable; + +const struct mg_iface_vtable *mg_ifaces[] = { + &mg_default_iface_vtable, +}; + +int mg_num_ifaces = (int) (sizeof(mg_ifaces) / sizeof(mg_ifaces[0])); + +struct mg_iface *mg_if_create_iface(const struct mg_iface_vtable *vtable, + struct mg_mgr *mgr) { + struct mg_iface *iface = (struct mg_iface *) MG_CALLOC(1, sizeof(*iface)); + iface->mgr = mgr; + iface->data = NULL; + iface->vtable = vtable; + return iface; +} + +struct mg_iface *mg_find_iface(struct mg_mgr *mgr, + const struct mg_iface_vtable *vtable, + struct mg_iface *from) { + int i = 0; + if (from != NULL) { + for (i = 0; i < mgr->num_ifaces; i++) { + if (mgr->ifaces[i] == from) { + i++; + break; + } + } + } + + for (; i < mgr->num_ifaces; i++) { + if (mgr->ifaces[i]->vtable == vtable) { + return mgr->ifaces[i]; + } + } + return NULL; +} diff --git a/src/mongoose-6.11/src/mg_net_if.h b/src/mongoose-6.11/src/mg_net_if.h new file mode 100644 index 0000000..43eb2d9 --- /dev/null +++ b/src/mongoose-6.11/src/mg_net_if.h @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_MONGOOSE_SRC_NET_IF_H_ +#define CS_MONGOOSE_SRC_NET_IF_H_ + +#include "common/platform.h" + +/* + * Internal async networking core interface. + * Consists of calls made by the core, which should not block, + * and callbacks back into the core ("..._cb"). + * Callbacks may (will) cause methods to be invoked from within, + * but methods are not allowed to invoke callbacks inline. + * + * Implementation must ensure that only one callback is invoked at any time. + */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define MG_MAIN_IFACE 0 + +struct mg_mgr; +struct mg_connection; +union socket_address; + +struct mg_iface_vtable; + +struct mg_iface { + struct mg_mgr *mgr; + void *data; /* Implementation-specific data */ + const struct mg_iface_vtable *vtable; +}; + +struct mg_iface_vtable { + void (*init)(struct mg_iface *iface); + void (*free)(struct mg_iface *iface); + void (*add_conn)(struct mg_connection *nc); + void (*remove_conn)(struct mg_connection *nc); + time_t (*poll)(struct mg_iface *iface, int timeout_ms); + + /* Set up a listening TCP socket on a given address. rv = 0 -> ok. */ + int (*listen_tcp)(struct mg_connection *nc, union socket_address *sa); + /* Request that a "listening" UDP socket be created. */ + int (*listen_udp)(struct mg_connection *nc, union socket_address *sa); + + /* Request that a TCP connection is made to the specified address. */ + void (*connect_tcp)(struct mg_connection *nc, const union socket_address *sa); + /* Open a UDP socket. Doesn't actually connect anything. */ + void (*connect_udp)(struct mg_connection *nc); + + /* Send functions for TCP and UDP. Sent data is copied before return. */ + void (*tcp_send)(struct mg_connection *nc, const void *buf, size_t len); + void (*udp_send)(struct mg_connection *nc, const void *buf, size_t len); + + void (*recved)(struct mg_connection *nc, size_t len); + + /* Perform interface-related connection initialization. Return 1 on ok. */ + int (*create_conn)(struct mg_connection *nc); + /* Perform interface-related cleanup on connection before destruction. */ + void (*destroy_conn)(struct mg_connection *nc); + + /* Associate a socket to a connection. */ + void (*sock_set)(struct mg_connection *nc, sock_t sock); + + /* Put connection's address into *sa, local (remote = 0) or remote. */ + void (*get_conn_addr)(struct mg_connection *nc, int remote, + union socket_address *sa); +}; + +extern const struct mg_iface_vtable *mg_ifaces[]; +extern int mg_num_ifaces; + +/* Creates a new interface instance. */ +struct mg_iface *mg_if_create_iface(const struct mg_iface_vtable *vtable, + struct mg_mgr *mgr); + +/* + * Find an interface with a given implementation. The search is started from + * interface `from`, exclusive. Returns NULL if none is found. + */ +struct mg_iface *mg_find_iface(struct mg_mgr *mgr, + const struct mg_iface_vtable *vtable, + struct mg_iface *from); +/* + * Deliver a new TCP connection. Returns NULL in case on error (unable to + * create connection, in which case interface state should be discarded. + * This is phase 1 of the two-phase process - MG_EV_ACCEPT will be delivered + * when mg_if_accept_tcp_cb is invoked. + */ +struct mg_connection *mg_if_accept_new_conn(struct mg_connection *lc); +void mg_if_accept_tcp_cb(struct mg_connection *nc, union socket_address *sa, + size_t sa_len); + +/* Callback invoked by connect methods. err = 0 -> ok, != 0 -> error. */ +void mg_if_connect_cb(struct mg_connection *nc, int err); +/* Callback that reports that data has been put on the wire. */ +void mg_if_sent_cb(struct mg_connection *nc, int num_sent); +/* + * Receive callback. + * if `own` is true, buf must be heap-allocated and ownership is transferred + * to the core. + * Core will acknowledge consumption by calling iface::recved. + */ +void mg_if_recv_tcp_cb(struct mg_connection *nc, void *buf, int len, int own); +/* + * Receive callback. + * buf must be heap-allocated and ownership is transferred to the core. + * Core will acknowledge consumption by calling iface::recved. + */ +void mg_if_recv_udp_cb(struct mg_connection *nc, void *buf, int len, + union socket_address *sa, size_t sa_len); + +/* void mg_if_close_conn(struct mg_connection *nc); */ + +/* Deliver a POLL event to the connection. */ +void mg_if_poll(struct mg_connection *nc, time_t now); + +/* Deliver a TIMER event to the connection. */ +void mg_if_timer(struct mg_connection *c, double now); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CS_MONGOOSE_SRC_NET_IF_H_ */ diff --git a/src/mongoose-6.11/src/mg_net_if_socket.c b/src/mongoose-6.11/src/mg_net_if_socket.c new file mode 100644 index 0000000..0d15764 --- /dev/null +++ b/src/mongoose-6.11/src/mg_net_if_socket.c @@ -0,0 +1,749 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_NET_IF_SOCKET + +#include "mg_net_if_socket.h" +#include "mg_internal.h" +#include "mg_util.h" + +#define MG_TCP_RECV_BUFFER_SIZE 1024 +#define MG_UDP_RECV_BUFFER_SIZE 1500 + +static sock_t mg_open_listening_socket(union socket_address *sa, int type, + int proto); +#if MG_ENABLE_SSL +static void mg_ssl_begin(struct mg_connection *nc); +#endif + +void mg_set_non_blocking_mode(sock_t sock) { +#ifdef _WIN32 + unsigned long on = 1; + ioctlsocket(sock, FIONBIO, &on); +#else + int flags = fcntl(sock, F_GETFL, 0); + fcntl(sock, F_SETFL, flags | O_NONBLOCK); +#endif +} + +static int mg_is_error(void) { + int err = mg_get_errno(); + return err != EINPROGRESS && err != EWOULDBLOCK +#ifndef WINCE + && err != EAGAIN && err != EINTR +#endif +#ifdef _WIN32 + && WSAGetLastError() != WSAEINTR && WSAGetLastError() != WSAEWOULDBLOCK +#endif + ; +} + +void mg_socket_if_connect_tcp(struct mg_connection *nc, + const union socket_address *sa) { + int rc, proto = 0; + nc->sock = socket(AF_INET, SOCK_STREAM, proto); + if (nc->sock == INVALID_SOCKET) { + nc->err = mg_get_errno() ? mg_get_errno() : 1; + return; + } +#if !defined(MG_ESP8266) + mg_set_non_blocking_mode(nc->sock); +#endif + rc = connect(nc->sock, &sa->sa, sizeof(sa->sin)); + nc->err = rc < 0 && mg_is_error() ? mg_get_errno() : 0; + DBG(("%p sock %d rc %d errno %d err %d", nc, nc->sock, rc, mg_get_errno(), + nc->err)); +} + +void mg_socket_if_connect_udp(struct mg_connection *nc) { + nc->sock = socket(AF_INET, SOCK_DGRAM, 0); + if (nc->sock == INVALID_SOCKET) { + nc->err = mg_get_errno() ? mg_get_errno() : 1; + return; + } + if (nc->flags & MG_F_ENABLE_BROADCAST) { + int optval = 1; + if (setsockopt(nc->sock, SOL_SOCKET, SO_BROADCAST, (const char *) &optval, + sizeof(optval)) < 0) { + nc->err = mg_get_errno() ? mg_get_errno() : 1; + return; + } + } + nc->err = 0; +} + +int mg_socket_if_listen_tcp(struct mg_connection *nc, + union socket_address *sa) { + int proto = 0; + sock_t sock = mg_open_listening_socket(sa, SOCK_STREAM, proto); + if (sock == INVALID_SOCKET) { + return (mg_get_errno() ? mg_get_errno() : 1); + } + mg_sock_set(nc, sock); + return 0; +} + +int mg_socket_if_listen_udp(struct mg_connection *nc, + union socket_address *sa) { + sock_t sock = mg_open_listening_socket(sa, SOCK_DGRAM, 0); + if (sock == INVALID_SOCKET) return (mg_get_errno() ? mg_get_errno() : 1); + mg_sock_set(nc, sock); + return 0; +} + +void mg_socket_if_tcp_send(struct mg_connection *nc, const void *buf, + size_t len) { + mbuf_append(&nc->send_mbuf, buf, len); +} + +void mg_socket_if_udp_send(struct mg_connection *nc, const void *buf, + size_t len) { + mbuf_append(&nc->send_mbuf, buf, len); +} + +void mg_socket_if_recved(struct mg_connection *nc, size_t len) { + (void) nc; + (void) len; +} + +int mg_socket_if_create_conn(struct mg_connection *nc) { + (void) nc; + return 1; +} + +void mg_socket_if_destroy_conn(struct mg_connection *nc) { + if (nc->sock == INVALID_SOCKET) return; + if (!(nc->flags & MG_F_UDP)) { + closesocket(nc->sock); + } else { + /* Only close outgoing UDP sockets or listeners. */ + if (nc->listener == NULL) closesocket(nc->sock); + } + nc->sock = INVALID_SOCKET; +} + +static int mg_accept_conn(struct mg_connection *lc) { + struct mg_connection *nc; + union socket_address sa; + socklen_t sa_len = sizeof(sa); + /* NOTE(lsm): on Windows, sock is always > FD_SETSIZE */ + sock_t sock = accept(lc->sock, &sa.sa, &sa_len); + if (sock == INVALID_SOCKET) { + if (mg_is_error()) DBG(("%p: failed to accept: %d", lc, mg_get_errno())); + return 0; + } + nc = mg_if_accept_new_conn(lc); + if (nc == NULL) { + closesocket(sock); + return 0; + } + DBG(("%p conn from %s:%d", nc, inet_ntoa(sa.sin.sin_addr), + ntohs(sa.sin.sin_port))); + mg_sock_set(nc, sock); +#if MG_ENABLE_SSL + if (lc->flags & MG_F_SSL) { + if (mg_ssl_if_conn_accept(nc, lc) != MG_SSL_OK) mg_close_conn(nc); + } else +#endif + { + mg_if_accept_tcp_cb(nc, &sa, sa_len); + } + return 1; +} + +/* 'sa' must be an initialized address to bind to */ +static sock_t mg_open_listening_socket(union socket_address *sa, int type, + int proto) { + socklen_t sa_len = + (sa->sa.sa_family == AF_INET) ? sizeof(sa->sin) : sizeof(sa->sin6); + sock_t sock = INVALID_SOCKET; +#if !MG_LWIP + int on = 1; +#endif + + if ((sock = socket(sa->sa.sa_family, type, proto)) != INVALID_SOCKET && +#if !MG_LWIP /* LWIP doesn't support either */ +#if defined(_WIN32) && defined(SO_EXCLUSIVEADDRUSE) && !defined(WINCE) + /* "Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE" http://goo.gl/RmrFTm */ + !setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (void *) &on, + sizeof(on)) && +#endif + +#if !defined(_WIN32) || !defined(SO_EXCLUSIVEADDRUSE) + /* + * SO_RESUSEADDR is not enabled on Windows because the semantics of + * SO_REUSEADDR on UNIX and Windows is different. On Windows, + * SO_REUSEADDR allows to bind a socket to a port without error even if + * the port is already open by another program. This is not the behavior + * SO_REUSEADDR was designed for, and leads to hard-to-track failure + * scenarios. Therefore, SO_REUSEADDR was disabled on Windows unless + * SO_EXCLUSIVEADDRUSE is supported and set on a socket. + */ + !setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof(on)) && +#endif +#endif /* !MG_LWIP */ + + !bind(sock, &sa->sa, sa_len) && + (type == SOCK_DGRAM || listen(sock, SOMAXCONN) == 0)) { +#if !MG_LWIP + mg_set_non_blocking_mode(sock); + /* In case port was set to 0, get the real port number */ + (void) getsockname(sock, &sa->sa, &sa_len); +#endif + } else if (sock != INVALID_SOCKET) { + closesocket(sock); + sock = INVALID_SOCKET; + } + + return sock; +} + +static void mg_write_to_socket(struct mg_connection *nc) { + struct mbuf *io = &nc->send_mbuf; + int n = 0; + +#if MG_LWIP + /* With LWIP we don't know if the socket is ready */ + if (io->len == 0) return; +#endif + + assert(io->len > 0); + + if (nc->flags & MG_F_UDP) { + int n = + sendto(nc->sock, io->buf, io->len, 0, &nc->sa.sa, sizeof(nc->sa.sin)); + DBG(("%p %d %d %d %s:%hu", nc, nc->sock, n, mg_get_errno(), + inet_ntoa(nc->sa.sin.sin_addr), ntohs(nc->sa.sin.sin_port))); + mg_if_sent_cb(nc, n); + return; + } + +#if MG_ENABLE_SSL + if (nc->flags & MG_F_SSL) { + if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) { + n = mg_ssl_if_write(nc, io->buf, io->len); + DBG(("%p %d bytes -> %d (SSL)", nc, n, nc->sock)); + if (n < 0) { + if (n != MG_SSL_WANT_READ && n != MG_SSL_WANT_WRITE) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } + return; + } else { + /* Successful SSL operation, clear off SSL wait flags */ + nc->flags &= ~(MG_F_WANT_READ | MG_F_WANT_WRITE); + } + } else { + mg_ssl_begin(nc); + return; + } + } else +#endif + { + n = (int) MG_SEND_FUNC(nc->sock, io->buf, io->len, 0); + DBG(("%p %d bytes -> %d", nc, n, nc->sock)); + } + + mg_if_sent_cb(nc, n); +} + +MG_INTERNAL size_t recv_avail_size(struct mg_connection *conn, size_t max) { + size_t avail; + if (conn->recv_mbuf_limit < conn->recv_mbuf.len) return 0; + avail = conn->recv_mbuf_limit - conn->recv_mbuf.len; + return avail > max ? max : avail; +} + +static void mg_handle_tcp_read(struct mg_connection *conn) { + int n = 0; + char *buf = (char *) MG_MALLOC(MG_TCP_RECV_BUFFER_SIZE); + + if (buf == NULL) { + DBG(("OOM")); + return; + } + +#if MG_ENABLE_SSL + if (conn->flags & MG_F_SSL) { + if (conn->flags & MG_F_SSL_HANDSHAKE_DONE) { + /* SSL library may have more bytes ready to read than we ask to read. + * Therefore, read in a loop until we read everything. Without the loop, + * we skip to the next select() cycle which can just timeout. */ + while ((n = mg_ssl_if_read(conn, buf, MG_TCP_RECV_BUFFER_SIZE)) > 0) { + DBG(("%p %d bytes <- %d (SSL)", conn, n, conn->sock)); + mg_if_recv_tcp_cb(conn, buf, n, 1 /* own */); + buf = NULL; + if (conn->flags & MG_F_CLOSE_IMMEDIATELY) break; + /* buf has been freed, we need a new one. */ + buf = (char *) MG_MALLOC(MG_TCP_RECV_BUFFER_SIZE); + if (buf == NULL) break; + } + MG_FREE(buf); + if (n < 0 && n != MG_SSL_WANT_READ) conn->flags |= MG_F_CLOSE_IMMEDIATELY; + } else { + MG_FREE(buf); + mg_ssl_begin(conn); + return; + } + } else +#endif + { + n = (int) MG_RECV_FUNC(conn->sock, buf, + recv_avail_size(conn, MG_TCP_RECV_BUFFER_SIZE), 0); + DBG(("%p %d bytes (PLAIN) <- %d", conn, n, conn->sock)); + if (n > 0) { + mg_if_recv_tcp_cb(conn, buf, n, 1 /* own */); + } else { + MG_FREE(buf); + } + if (n == 0) { + /* Orderly shutdown of the socket, try flushing output. */ + conn->flags |= MG_F_SEND_AND_CLOSE; + } else if (n < 0 && mg_is_error()) { + conn->flags |= MG_F_CLOSE_IMMEDIATELY; + } + } +} + +static int mg_recvfrom(struct mg_connection *nc, union socket_address *sa, + socklen_t *sa_len, char **buf) { + int n; + *buf = (char *) MG_MALLOC(MG_UDP_RECV_BUFFER_SIZE); + if (*buf == NULL) { + DBG(("Out of memory")); + return -ENOMEM; + } + n = recvfrom(nc->sock, *buf, MG_UDP_RECV_BUFFER_SIZE, 0, &sa->sa, sa_len); + if (n <= 0) { + DBG(("%p recvfrom: %s", nc, strerror(mg_get_errno()))); + MG_FREE(*buf); + } + return n; +} + +static void mg_handle_udp_read(struct mg_connection *nc) { + char *buf = NULL; + union socket_address sa; + socklen_t sa_len = sizeof(sa); + int n = mg_recvfrom(nc, &sa, &sa_len, &buf); + DBG(("%p %d bytes from %s:%d", nc, n, inet_ntoa(nc->sa.sin.sin_addr), + ntohs(nc->sa.sin.sin_port))); + mg_if_recv_udp_cb(nc, buf, n, &sa, sa_len); +} + +#if MG_ENABLE_SSL +static void mg_ssl_begin(struct mg_connection *nc) { + int server_side = (nc->listener != NULL); + enum mg_ssl_if_result res = mg_ssl_if_handshake(nc); + DBG(("%p %d res %d", nc, server_side, res)); + + if (res == MG_SSL_OK) { + nc->flags |= MG_F_SSL_HANDSHAKE_DONE; + nc->flags &= ~(MG_F_WANT_READ | MG_F_WANT_WRITE); + + if (server_side) { + union socket_address sa; + socklen_t sa_len = sizeof(sa); + (void) getpeername(nc->sock, &sa.sa, &sa_len); + mg_if_accept_tcp_cb(nc, &sa, sa_len); + } else { + mg_if_connect_cb(nc, 0); + } + } else if (res != MG_SSL_WANT_READ && res != MG_SSL_WANT_WRITE) { + if (!server_side) { + mg_if_connect_cb(nc, res); + } + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } +} +#endif /* MG_ENABLE_SSL */ + +#define _MG_F_FD_CAN_READ 1 +#define _MG_F_FD_CAN_WRITE 1 << 1 +#define _MG_F_FD_ERROR 1 << 2 + +void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) { + int worth_logging = + fd_flags != 0 || (nc->flags & (MG_F_WANT_READ | MG_F_WANT_WRITE)); + if (worth_logging) { + DBG(("%p fd=%d fd_flags=%d nc_flags=%lu rmbl=%d smbl=%d", nc, nc->sock, + fd_flags, nc->flags, (int) nc->recv_mbuf.len, + (int) nc->send_mbuf.len)); + } + + if (nc->flags & MG_F_CONNECTING) { + if (fd_flags != 0) { + int err = 0; +#if !defined(MG_ESP8266) + if (!(nc->flags & MG_F_UDP)) { + socklen_t len = sizeof(err); + int ret = + getsockopt(nc->sock, SOL_SOCKET, SO_ERROR, (char *) &err, &len); + if (ret != 0) { + err = 1; + } else if (err == EAGAIN || err == EWOULDBLOCK) { + err = 0; + } + } +#else + /* + * On ESP8266 we use blocking connect. + */ + err = nc->err; +#endif +#if MG_ENABLE_SSL + if ((nc->flags & MG_F_SSL) && err == 0) { + mg_ssl_begin(nc); + } else { + mg_if_connect_cb(nc, err); + } +#else + mg_if_connect_cb(nc, err); +#endif + } else if (nc->err != 0) { + mg_if_connect_cb(nc, nc->err); + } + } + + if (fd_flags & _MG_F_FD_CAN_READ) { + if (nc->flags & MG_F_UDP) { + mg_handle_udp_read(nc); + } else { + if (nc->flags & MG_F_LISTENING) { + /* + * We're not looping here, and accepting just one connection at + * a time. The reason is that eCos does not respect non-blocking + * flag on a listening socket and hangs in a loop. + */ + mg_accept_conn(nc); + } else { + mg_handle_tcp_read(nc); + } + } + } + + if (!(nc->flags & MG_F_CLOSE_IMMEDIATELY)) { + if ((fd_flags & _MG_F_FD_CAN_WRITE) && nc->send_mbuf.len > 0) { + mg_write_to_socket(nc); + } + mg_if_poll(nc, (time_t) now); + mg_if_timer(nc, now); + } + + if (worth_logging) { + DBG(("%p after fd=%d nc_flags=%lu rmbl=%d smbl=%d", nc, nc->sock, nc->flags, + (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); + } +} + +#if MG_ENABLE_BROADCAST +static void mg_mgr_handle_ctl_sock(struct mg_mgr *mgr) { + struct ctl_msg ctl_msg; + int len = + (int) MG_RECV_FUNC(mgr->ctl[1], (char *) &ctl_msg, sizeof(ctl_msg), 0); + size_t dummy = MG_SEND_FUNC(mgr->ctl[1], ctl_msg.message, 1, 0); + DBG(("read %d from ctl socket", len)); + (void) dummy; /* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25509 */ + if (len >= (int) sizeof(ctl_msg.callback) && ctl_msg.callback != NULL) { + struct mg_connection *nc; + for (nc = mg_next(mgr, NULL); nc != NULL; nc = mg_next(mgr, nc)) { + ctl_msg.callback(nc, MG_EV_POLL, + ctl_msg.message MG_UD_ARG(nc->user_data)); + } + } +} +#endif + +/* Associate a socket to a connection. */ +void mg_socket_if_sock_set(struct mg_connection *nc, sock_t sock) { + mg_set_non_blocking_mode(sock); + mg_set_close_on_exec(sock); + nc->sock = sock; + DBG(("%p %d", nc, sock)); +} + +void mg_socket_if_init(struct mg_iface *iface) { + (void) iface; + DBG(("%p using select()", iface->mgr)); +#if MG_ENABLE_BROADCAST + mg_socketpair(iface->mgr->ctl, SOCK_DGRAM); +#endif +} + +void mg_socket_if_free(struct mg_iface *iface) { + (void) iface; +} + +void mg_socket_if_add_conn(struct mg_connection *nc) { + (void) nc; +} + +void mg_socket_if_remove_conn(struct mg_connection *nc) { + (void) nc; +} + +void mg_add_to_set(sock_t sock, fd_set *set, sock_t *max_fd) { + if (sock != INVALID_SOCKET +#ifdef __unix__ + && sock < (sock_t) FD_SETSIZE +#endif + ) { + FD_SET(sock, set); + if (*max_fd == INVALID_SOCKET || sock > *max_fd) { + *max_fd = sock; + } + } +} + +time_t mg_socket_if_poll(struct mg_iface *iface, int timeout_ms) { + struct mg_mgr *mgr = iface->mgr; + double now = mg_time(); + double min_timer; + struct mg_connection *nc, *tmp; + struct timeval tv; + fd_set read_set, write_set, err_set; + sock_t max_fd = INVALID_SOCKET; + int num_fds, num_ev, num_timers = 0; +#ifdef __unix__ + int try_dup = 1; +#endif + + FD_ZERO(&read_set); + FD_ZERO(&write_set); + FD_ZERO(&err_set); +#if MG_ENABLE_BROADCAST + mg_add_to_set(mgr->ctl[1], &read_set, &max_fd); +#endif + + /* + * Note: it is ok to have connections with sock == INVALID_SOCKET in the list, + * e.g. timer-only "connections". + */ + min_timer = 0; + for (nc = mgr->active_connections, num_fds = 0; nc != NULL; nc = tmp) { + tmp = nc->next; + + if (nc->sock != INVALID_SOCKET) { + num_fds++; + +#ifdef __unix__ + /* A hack to make sure all our file descriptos fit into FD_SETSIZE. */ + if (nc->sock >= (sock_t) FD_SETSIZE && try_dup) { + int new_sock = dup(nc->sock); + if (new_sock >= 0) { + if (new_sock < (sock_t) FD_SETSIZE) { + closesocket(nc->sock); + DBG(("new sock %d -> %d", nc->sock, new_sock)); + nc->sock = new_sock; + } else { + closesocket(new_sock); + DBG(("new sock is still larger than FD_SETSIZE, disregard")); + try_dup = 0; + } + } else { + try_dup = 0; + } + } +#endif + + if (!(nc->flags & MG_F_WANT_WRITE) && + nc->recv_mbuf.len < nc->recv_mbuf_limit && + (!(nc->flags & MG_F_UDP) || nc->listener == NULL)) { + mg_add_to_set(nc->sock, &read_set, &max_fd); + } + + if (((nc->flags & MG_F_CONNECTING) && !(nc->flags & MG_F_WANT_READ)) || + (nc->send_mbuf.len > 0 && !(nc->flags & MG_F_CONNECTING))) { + mg_add_to_set(nc->sock, &write_set, &max_fd); + mg_add_to_set(nc->sock, &err_set, &max_fd); + } + } + + if (nc->ev_timer_time > 0) { + if (num_timers == 0 || nc->ev_timer_time < min_timer) { + min_timer = nc->ev_timer_time; + } + num_timers++; + } + } + + /* + * If there is a timer to be fired earlier than the requested timeout, + * adjust the timeout. + */ + if (num_timers > 0) { + double timer_timeout_ms = (min_timer - mg_time()) * 1000 + 1 /* rounding */; + if (timer_timeout_ms < timeout_ms) { + timeout_ms = (int) timer_timeout_ms; + } + } + if (timeout_ms < 0) timeout_ms = 0; + + tv.tv_sec = timeout_ms / 1000; + tv.tv_usec = (timeout_ms % 1000) * 1000; + + num_ev = select((int) max_fd + 1, &read_set, &write_set, &err_set, &tv); + now = mg_time(); +#if 0 + DBG(("select @ %ld num_ev=%d of %d, timeout=%d", (long) now, num_ev, num_fds, + timeout_ms)); +#endif + +#if MG_ENABLE_BROADCAST + if (num_ev > 0 && mgr->ctl[1] != INVALID_SOCKET && + FD_ISSET(mgr->ctl[1], &read_set)) { + mg_mgr_handle_ctl_sock(mgr); + } +#endif + + for (nc = mgr->active_connections; nc != NULL; nc = tmp) { + int fd_flags = 0; + if (nc->sock != INVALID_SOCKET) { + if (num_ev > 0) { + fd_flags = (FD_ISSET(nc->sock, &read_set) && + (!(nc->flags & MG_F_UDP) || nc->listener == NULL) + ? _MG_F_FD_CAN_READ + : 0) | + (FD_ISSET(nc->sock, &write_set) ? _MG_F_FD_CAN_WRITE : 0) | + (FD_ISSET(nc->sock, &err_set) ? _MG_F_FD_ERROR : 0); + } +#if MG_LWIP + /* With LWIP socket emulation layer, we don't get write events for UDP */ + if ((nc->flags & MG_F_UDP) && nc->listener == NULL) { + fd_flags |= _MG_F_FD_CAN_WRITE; + } +#endif + } + tmp = nc->next; + mg_mgr_handle_conn(nc, fd_flags, now); + } + + for (nc = mgr->active_connections; nc != NULL; nc = tmp) { + tmp = nc->next; + if ((nc->flags & MG_F_CLOSE_IMMEDIATELY) || + (nc->send_mbuf.len == 0 && (nc->flags & MG_F_SEND_AND_CLOSE))) { + mg_close_conn(nc); + } + } + + return (time_t) now; +} + +#if MG_ENABLE_BROADCAST +MG_INTERNAL void mg_socketpair_close(sock_t *sock) { + while (1) { + if (closesocket(*sock) == -1 && errno == EINTR) continue; + break; + } + *sock = INVALID_SOCKET; +} + +MG_INTERNAL sock_t +mg_socketpair_accept(sock_t sock, union socket_address *sa, socklen_t sa_len) { + sock_t rc; + while (1) { + if ((rc = accept(sock, &sa->sa, &sa_len)) == INVALID_SOCKET && + errno == EINTR) + continue; + break; + } + return rc; +} + +int mg_socketpair(sock_t sp[2], int sock_type) { + union socket_address sa; + sock_t sock; + socklen_t len = sizeof(sa.sin); + int ret = 0; + + sock = sp[0] = sp[1] = INVALID_SOCKET; + + (void) memset(&sa, 0, sizeof(sa)); + sa.sin.sin_family = AF_INET; + sa.sin.sin_port = htons(0); + sa.sin.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */ + + if ((sock = socket(AF_INET, sock_type, 0)) == INVALID_SOCKET) { + } else if (bind(sock, &sa.sa, len) != 0) { + } else if (sock_type == SOCK_STREAM && listen(sock, 1) != 0) { + } else if (getsockname(sock, &sa.sa, &len) != 0) { + } else if ((sp[0] = socket(AF_INET, sock_type, 0)) == INVALID_SOCKET) { + } else if (connect(sp[0], &sa.sa, len) != 0) { + } else if (sock_type == SOCK_DGRAM && + (getsockname(sp[0], &sa.sa, &len) != 0 || + connect(sock, &sa.sa, len) != 0)) { + } else if ((sp[1] = (sock_type == SOCK_DGRAM ? sock : mg_socketpair_accept( + sock, &sa, len))) == + INVALID_SOCKET) { + } else { + mg_set_close_on_exec(sp[0]); + mg_set_close_on_exec(sp[1]); + if (sock_type == SOCK_STREAM) mg_socketpair_close(&sock); + ret = 1; + } + + if (!ret) { + if (sp[0] != INVALID_SOCKET) mg_socketpair_close(&sp[0]); + if (sp[1] != INVALID_SOCKET) mg_socketpair_close(&sp[1]); + if (sock != INVALID_SOCKET) mg_socketpair_close(&sock); + } + + return ret; +} +#endif /* MG_ENABLE_BROADCAST */ + +static void mg_sock_get_addr(sock_t sock, int remote, + union socket_address *sa) { + socklen_t slen = sizeof(*sa); + memset(sa, 0, slen); + if (remote) { + getpeername(sock, &sa->sa, &slen); + } else { + getsockname(sock, &sa->sa, &slen); + } +} + +void mg_sock_to_str(sock_t sock, char *buf, size_t len, int flags) { + union socket_address sa; + mg_sock_get_addr(sock, flags & MG_SOCK_STRINGIFY_REMOTE, &sa); + mg_sock_addr_to_str(&sa, buf, len, flags); +} + +void mg_socket_if_get_conn_addr(struct mg_connection *nc, int remote, + union socket_address *sa) { + if ((nc->flags & MG_F_UDP) && remote) { + memcpy(sa, &nc->sa, sizeof(*sa)); + return; + } + mg_sock_get_addr(nc->sock, remote, sa); +} + +/* clang-format off */ +#define MG_SOCKET_IFACE_VTABLE \ + { \ + mg_socket_if_init, \ + mg_socket_if_free, \ + mg_socket_if_add_conn, \ + mg_socket_if_remove_conn, \ + mg_socket_if_poll, \ + mg_socket_if_listen_tcp, \ + mg_socket_if_listen_udp, \ + mg_socket_if_connect_tcp, \ + mg_socket_if_connect_udp, \ + mg_socket_if_tcp_send, \ + mg_socket_if_udp_send, \ + mg_socket_if_recved, \ + mg_socket_if_create_conn, \ + mg_socket_if_destroy_conn, \ + mg_socket_if_sock_set, \ + mg_socket_if_get_conn_addr, \ + } +/* clang-format on */ + +const struct mg_iface_vtable mg_socket_iface_vtable = MG_SOCKET_IFACE_VTABLE; +#if MG_NET_IF == MG_NET_IF_SOCKET +const struct mg_iface_vtable mg_default_iface_vtable = MG_SOCKET_IFACE_VTABLE; +#endif + +#endif /* MG_ENABLE_NET_IF_SOCKET */ diff --git a/src/mongoose-6.11/src/mg_net_if_socket.h b/src/mongoose-6.11/src/mg_net_if_socket.h new file mode 100644 index 0000000..744fc0f --- /dev/null +++ b/src/mongoose-6.11/src/mg_net_if_socket.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_MONGOOSE_SRC_NET_IF_SOCKET_H_ +#define CS_MONGOOSE_SRC_NET_IF_SOCKET_H_ + +#include "mg_net_if.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef MG_ENABLE_NET_IF_SOCKET +#define MG_ENABLE_NET_IF_SOCKET MG_NET_IF == MG_NET_IF_SOCKET +#endif + +extern const struct mg_iface_vtable mg_socket_iface_vtable; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CS_MONGOOSE_SRC_NET_IF_SOCKET_H_ */ diff --git a/src/mongoose-6.11/src/mg_net_if_socks.c b/src/mongoose-6.11/src/mg_net_if_socks.c new file mode 100644 index 0000000..c5661e9 --- /dev/null +++ b/src/mongoose-6.11/src/mg_net_if_socks.c @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_SOCKS + +struct socksdata { + char *proxy_addr; /* HOST:PORT of the socks5 proxy server */ + struct mg_connection *s; /* Respective connection to the server */ + struct mg_connection *c; /* Connection to the client */ + struct mbuf tmp; /* Temporary buffer for sent data */ +}; + +static void socks_if_disband(struct socksdata *d) { + LOG(LL_DEBUG, ("disbanding proxy %p %p", d->c, d->s)); + if (d->c) d->c->flags |= MG_F_SEND_AND_CLOSE; + if (d->s) d->s->flags |= MG_F_SEND_AND_CLOSE; + d->c = d->s = NULL; +} + +static void socks_if_handler(struct mg_connection *c, int ev, void *ev_data) { + struct socksdata *d = (struct socksdata *) c->user_data; + if (ev == MG_EV_CONNECT) { + int res = *(int *) ev_data; + if (res == 0) { + /* Send handshake to the proxy server */ + unsigned char buf[] = {MG_SOCKS_VERSION, 1, MG_SOCKS_HANDSHAKE_NOAUTH}; + mg_send(d->s, buf, sizeof(buf)); + LOG(LL_DEBUG, ("Sent handshake to %s", d->proxy_addr)); + } else { + LOG(LL_ERROR, ("Cannot connect to %s: %d", d->proxy_addr, res)); + d->c->flags |= MG_F_CLOSE_IMMEDIATELY; + } + } else if (ev == MG_EV_CLOSE) { + socks_if_disband(d); + } else if (ev == MG_EV_RECV) { + /* Handle handshake reply */ + if (!(c->flags & MG_SOCKS_HANDSHAKE_DONE)) { + /* TODO(lsm): process IPv6 too */ + unsigned char buf[10] = {MG_SOCKS_VERSION, MG_SOCKS_CMD_CONNECT, 0, + MG_SOCKS_ADDR_IPV4}; + if (c->recv_mbuf.len < 2) return; + if ((unsigned char) c->recv_mbuf.buf[1] == MG_SOCKS_HANDSHAKE_FAILURE) { + LOG(LL_ERROR, ("Server kicked us out")); + socks_if_disband(d); + return; + } + mbuf_remove(&c->recv_mbuf, 2); + c->flags |= MG_SOCKS_HANDSHAKE_DONE; + + /* Send connect request */ + memcpy(buf + 4, &d->c->sa.sin.sin_addr, 4); + memcpy(buf + 8, &d->c->sa.sin.sin_port, 2); + mg_send(c, buf, sizeof(buf)); + } + /* Process connect request */ + if ((c->flags & MG_SOCKS_HANDSHAKE_DONE) && + !(c->flags & MG_SOCKS_CONNECT_DONE)) { + if (c->recv_mbuf.len < 10) return; + if (c->recv_mbuf.buf[1] != MG_SOCKS_SUCCESS) { + LOG(LL_ERROR, ("Socks connection error: %d", c->recv_mbuf.buf[1])); + socks_if_disband(d); + return; + } + mbuf_remove(&c->recv_mbuf, 10); + c->flags |= MG_SOCKS_CONNECT_DONE; + /* Connected. Move sent data from client, if any, to server */ + if (d->s && d->c) { + mbuf_append(&d->s->send_mbuf, d->tmp.buf, d->tmp.len); + mbuf_free(&d->tmp); + } + } + /* All flags are set, we're in relay mode */ + if ((c->flags & MG_SOCKS_CONNECT_DONE) && d->c && d->s) { + mbuf_append(&d->c->recv_mbuf, d->s->recv_mbuf.buf, d->s->recv_mbuf.len); + mbuf_remove(&d->s->recv_mbuf, d->s->recv_mbuf.len); + } + } +} + +static void mg_socks_if_connect_tcp(struct mg_connection *c, + const union socket_address *sa) { + struct socksdata *d = (struct socksdata *) c->iface->data; + d->c = c; + d->s = mg_connect(c->mgr, d->proxy_addr, socks_if_handler); + d->s->user_data = d; + LOG(LL_DEBUG, ("%p %s", c, d->proxy_addr)); + (void) sa; +} + +static void mg_socks_if_connect_udp(struct mg_connection *c) { + (void) c; +} + +static int mg_socks_if_listen_tcp(struct mg_connection *c, + union socket_address *sa) { + (void) c; + (void) sa; + return 0; +} + +static int mg_socks_if_listen_udp(struct mg_connection *c, + union socket_address *sa) { + (void) c; + (void) sa; + return -1; +} + +static void mg_socks_if_tcp_send(struct mg_connection *c, const void *buf, + size_t len) { + struct socksdata *d = (struct socksdata *) c->iface->data; + LOG(LL_DEBUG, ("%p -> %p %d %d", c, buf, (int) len, (int) c->send_mbuf.len)); + if (d && d->s && d->s->flags & MG_SOCKS_CONNECT_DONE) { + mbuf_append(&d->s->send_mbuf, d->tmp.buf, d->tmp.len); + mbuf_append(&d->s->send_mbuf, buf, len); + mbuf_free(&d->tmp); + } else { + mbuf_append(&d->tmp, buf, len); + } +} + +static void mg_socks_if_udp_send(struct mg_connection *c, const void *buf, + size_t len) { + (void) c; + (void) buf; + (void) len; +} + +static void mg_socks_if_recved(struct mg_connection *c, size_t len) { + (void) c; + (void) len; +} + +static int mg_socks_if_create_conn(struct mg_connection *c) { + (void) c; + return 1; +} + +static void mg_socks_if_destroy_conn(struct mg_connection *c) { + c->iface->vtable->free(c->iface); + MG_FREE(c->iface); + c->iface = NULL; + LOG(LL_DEBUG, ("%p", c)); +} + +static void mg_socks_if_sock_set(struct mg_connection *c, sock_t sock) { + (void) c; + (void) sock; +} + +static void mg_socks_if_init(struct mg_iface *iface) { + (void) iface; +} + +static void mg_socks_if_free(struct mg_iface *iface) { + struct socksdata *d = (struct socksdata *) iface->data; + LOG(LL_DEBUG, ("%p", iface)); + if (d != NULL) { + socks_if_disband(d); + mbuf_free(&d->tmp); + MG_FREE(d->proxy_addr); + MG_FREE(d); + iface->data = NULL; + } +} + +static void mg_socks_if_add_conn(struct mg_connection *c) { + c->sock = INVALID_SOCKET; +} + +static void mg_socks_if_remove_conn(struct mg_connection *c) { + (void) c; +} + +static time_t mg_socks_if_poll(struct mg_iface *iface, int timeout_ms) { + LOG(LL_DEBUG, ("%p", iface)); + (void) iface; + (void) timeout_ms; + return (time_t) cs_time(); +} + +static void mg_socks_if_get_conn_addr(struct mg_connection *c, int remote, + union socket_address *sa) { + LOG(LL_DEBUG, ("%p", c)); + (void) c; + (void) remote; + (void) sa; +} + +const struct mg_iface_vtable mg_socks_iface_vtable = { + mg_socks_if_init, mg_socks_if_free, + mg_socks_if_add_conn, mg_socks_if_remove_conn, + mg_socks_if_poll, mg_socks_if_listen_tcp, + mg_socks_if_listen_udp, mg_socks_if_connect_tcp, + mg_socks_if_connect_udp, mg_socks_if_tcp_send, + mg_socks_if_udp_send, mg_socks_if_recved, + mg_socks_if_create_conn, mg_socks_if_destroy_conn, + mg_socks_if_sock_set, mg_socks_if_get_conn_addr, +}; + +struct mg_iface *mg_socks_mk_iface(struct mg_mgr *mgr, const char *proxy_addr) { + struct mg_iface *iface = mg_if_create_iface(&mg_socks_iface_vtable, mgr); + iface->data = MG_CALLOC(1, sizeof(struct socksdata)); + ((struct socksdata *) iface->data)->proxy_addr = strdup(proxy_addr); + return iface; +} + +#endif diff --git a/src/mongoose-6.11/src/mg_net_if_socks.h b/src/mongoose-6.11/src/mg_net_if_socks.h new file mode 100644 index 0000000..80e3107 --- /dev/null +++ b/src/mongoose-6.11/src/mg_net_if_socks.h @@ -0,0 +1,22 @@ +/* +* Copyright (c) 2014-2017 Cesanta Software Limited +* All rights reserved +*/ + +#ifndef CS_MONGOOSE_SRC_NET_IF_SOCKS_H_ +#define CS_MONGOOSE_SRC_NET_IF_SOCKS_H_ + +#if MG_ENABLE_SOCKS +#include "mg_net_if.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +extern const struct mg_iface_vtable mg_socks_iface_vtable; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* MG_ENABLE_SOCKS */ +#endif /* CS_MONGOOSE_SRC_NET_IF_SOCKS_H_ */ diff --git a/src/mongoose-6.11/src/mg_resolv.c b/src/mongoose-6.11/src/mg_resolv.c new file mode 100644 index 0000000..e445ac5 --- /dev/null +++ b/src/mongoose-6.11/src/mg_resolv.c @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_ASYNC_RESOLVER + +#include "mg_internal.h" +#include "mg_resolv.h" + +#ifndef MG_DEFAULT_NAMESERVER +#define MG_DEFAULT_NAMESERVER "8.8.8.8" +#endif + +struct mg_resolve_async_request { + char name[1024]; + int query; + mg_resolve_callback_t callback; + void *data; + time_t timeout; + int max_retries; + enum mg_resolve_err err; + + /* state */ + time_t last_time; + int retries; +}; + +/* + * Find what nameserver to use. + * + * Return 0 if OK, -1 if error + */ +static int mg_get_ip_address_of_nameserver(char *name, size_t name_len) { + int ret = -1; + +#ifdef _WIN32 + int i; + LONG err; + HKEY hKey, hSub; + wchar_t subkey[512], value[128], + *key = L"SYSTEM\\ControlSet001\\Services\\Tcpip\\Parameters\\Interfaces"; + + if ((err = RegOpenKeyExW(HKEY_LOCAL_MACHINE, key, 0, KEY_READ, &hKey)) != + ERROR_SUCCESS) { + fprintf(stderr, "cannot open reg key %S: %ld\n", key, err); + ret = -1; + } else { + for (ret = -1, i = 0; 1; i++) { + DWORD subkey_size = sizeof(subkey), type, len = sizeof(value); + if (RegEnumKeyExW(hKey, i, subkey, &subkey_size, NULL, NULL, NULL, + NULL) != ERROR_SUCCESS) { + break; + } + if (RegOpenKeyExW(hKey, subkey, 0, KEY_READ, &hSub) == ERROR_SUCCESS && + ((RegQueryValueExW(hSub, L"NameServer", 0, &type, (void *) value, + &len) == ERROR_SUCCESS && + value[0] != '\0') || + (RegQueryValueExW(hSub, L"DhcpNameServer", 0, &type, (void *) value, + &len) == ERROR_SUCCESS && + value[0] != '\0'))) { + /* + * See https://github.com/cesanta/mongoose/issues/176 + * The value taken from the registry can be empty, a single + * IP address, or multiple IP addresses separated by comma. + * If it's empty, check the next interface. + * If it's multiple IP addresses, take the first one. + */ + wchar_t *comma = wcschr(value, ','); + if (comma != NULL) { + *comma = '\0'; + } + /* %S will convert wchar_t -> char */ + snprintf(name, name_len, "%S", value); + ret = 0; + RegCloseKey(hSub); + break; + } + } + RegCloseKey(hKey); + } +#elif MG_ENABLE_FILESYSTEM && defined(MG_RESOLV_CONF_FILE_NAME) + FILE *fp; + char line[512]; + + if ((fp = mg_fopen(MG_RESOLV_CONF_FILE_NAME, "r")) == NULL) { + ret = -1; + } else { + /* Try to figure out what nameserver to use */ + for (ret = -1; fgets(line, sizeof(line), fp) != NULL;) { + unsigned int a, b, c, d; + if (sscanf(line, "nameserver %u.%u.%u.%u", &a, &b, &c, &d) == 4) { + snprintf(name, name_len, "%u.%u.%u.%u", a, b, c, d); + ret = 0; + break; + } + } + (void) fclose(fp); + } +#else + snprintf(name, name_len, "%s", MG_DEFAULT_NAMESERVER); +#endif /* _WIN32 */ + + return ret; +} + +int mg_resolve_from_hosts_file(const char *name, union socket_address *usa) { +#if MG_ENABLE_FILESYSTEM && defined(MG_HOSTS_FILE_NAME) + /* TODO(mkm) cache /etc/hosts */ + FILE *fp; + char line[1024]; + char *p; + char alias[256]; + unsigned int a, b, c, d; + int len = 0; + + if ((fp = mg_fopen(MG_HOSTS_FILE_NAME, "r")) == NULL) { + return -1; + } + + for (; fgets(line, sizeof(line), fp) != NULL;) { + if (line[0] == '#') continue; + + if (sscanf(line, "%u.%u.%u.%u%n", &a, &b, &c, &d, &len) == 0) { + /* TODO(mkm): handle ipv6 */ + continue; + } + for (p = line + len; sscanf(p, "%s%n", alias, &len) == 1; p += len) { + if (strcmp(alias, name) == 0) { + usa->sin.sin_addr.s_addr = htonl(a << 24 | b << 16 | c << 8 | d); + fclose(fp); + return 0; + } + } + } + + fclose(fp); +#else + (void) name; + (void) usa; +#endif + + return -1; +} + +static void mg_resolve_async_eh(struct mg_connection *nc, int ev, + void *data MG_UD_ARG(void *user_data)) { + time_t now = (time_t) mg_time(); + struct mg_resolve_async_request *req; + struct mg_dns_message *msg; + int first = 0; +#if !MG_ENABLE_CALLBACK_USERDATA + void *user_data = nc->user_data; +#endif + + if (ev != MG_EV_POLL) DBG(("ev=%d user_data=%p", ev, user_data)); + + req = (struct mg_resolve_async_request *) user_data; + + if (req == NULL) { + return; + } + + switch (ev) { + case MG_EV_CONNECT: + /* don't depend on timer not being at epoch for sending out first req */ + first = 1; + /* fallthrough */ + case MG_EV_POLL: + if (req->retries > req->max_retries) { + req->err = MG_RESOLVE_EXCEEDED_RETRY_COUNT; + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + break; + } + if (first || now - req->last_time >= req->timeout) { + mg_send_dns_query(nc, req->name, req->query); + req->last_time = now; + req->retries++; + } + break; + case MG_EV_RECV: + msg = (struct mg_dns_message *) MG_MALLOC(sizeof(*msg)); + if (mg_parse_dns(nc->recv_mbuf.buf, *(int *) data, msg) == 0 && + msg->num_answers > 0) { + req->callback(msg, req->data, MG_RESOLVE_OK); + nc->user_data = NULL; + MG_FREE(req); + } else { + req->err = MG_RESOLVE_NO_ANSWERS; + } + MG_FREE(msg); + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + break; + case MG_EV_SEND: + /* + * If a send error occurs, prevent closing of the connection by the core. + * We will retry after timeout. + */ + nc->flags &= ~MG_F_CLOSE_IMMEDIATELY; + mbuf_remove(&nc->send_mbuf, nc->send_mbuf.len); + break; + case MG_EV_TIMER: + req->err = MG_RESOLVE_TIMEOUT; + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + break; + case MG_EV_CLOSE: + /* If we got here with request still not done, fire an error callback. */ + if (req != NULL) { + char addr[32]; + mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr), MG_SOCK_STRINGIFY_IP); +#ifdef MG_LOG_DNS_FAILURES + LOG(LL_ERROR, ("Failed to resolve '%s', server %s", req->name, addr)); +#endif + req->callback(NULL, req->data, req->err); + nc->user_data = NULL; + MG_FREE(req); + } + break; + } +} + +int mg_resolve_async(struct mg_mgr *mgr, const char *name, int query, + mg_resolve_callback_t cb, void *data) { + struct mg_resolve_async_opts opts; + memset(&opts, 0, sizeof(opts)); + return mg_resolve_async_opt(mgr, name, query, cb, data, opts); +} + +int mg_resolve_async_opt(struct mg_mgr *mgr, const char *name, int query, + mg_resolve_callback_t cb, void *data, + struct mg_resolve_async_opts opts) { + struct mg_resolve_async_request *req; + struct mg_connection *dns_nc; + const char *nameserver = opts.nameserver; + char dns_server_buff[17], nameserver_url[26]; + + if (nameserver == NULL) { + nameserver = mgr->nameserver; + } + + DBG(("%s %d %p", name, query, opts.dns_conn)); + + /* resolve with DNS */ + req = (struct mg_resolve_async_request *) MG_CALLOC(1, sizeof(*req)); + if (req == NULL) { + return -1; + } + + strncpy(req->name, name, sizeof(req->name)); + req->name[sizeof(req->name) - 1] = '\0'; + + req->query = query; + req->callback = cb; + req->data = data; + /* TODO(mkm): parse defaults out of resolve.conf */ + req->max_retries = opts.max_retries ? opts.max_retries : 2; + req->timeout = opts.timeout ? opts.timeout : 5; + + /* Lazily initialize dns server */ + if (nameserver == NULL) { + if (mg_get_ip_address_of_nameserver(dns_server_buff, + sizeof(dns_server_buff)) != -1) { + nameserver = dns_server_buff; + } else { + nameserver = MG_DEFAULT_NAMESERVER; + } + } + + snprintf(nameserver_url, sizeof(nameserver_url), "udp://%s:53", nameserver); + + dns_nc = mg_connect(mgr, nameserver_url, MG_CB(mg_resolve_async_eh, NULL)); + if (dns_nc == NULL) { + MG_FREE(req); + return -1; + } + dns_nc->user_data = req; + if (opts.dns_conn != NULL) { + *opts.dns_conn = dns_nc; + } + + return 0; +} + +void mg_set_nameserver(struct mg_mgr *mgr, const char *nameserver) { + MG_FREE((char *) mgr->nameserver); + mgr->nameserver = NULL; + if (nameserver != NULL) { + mgr->nameserver = strdup(nameserver); + } +} + +#endif /* MG_ENABLE_ASYNC_RESOLVER */ diff --git a/src/mongoose-6.11/src/mg_resolv.h b/src/mongoose-6.11/src/mg_resolv.h new file mode 100644 index 0000000..a604c17 --- /dev/null +++ b/src/mongoose-6.11/src/mg_resolv.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +/* + * === API reference + */ + +#ifndef CS_MONGOOSE_SRC_RESOLV_H_ +#define CS_MONGOOSE_SRC_RESOLV_H_ + +#include "mg_dns.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +enum mg_resolve_err { + MG_RESOLVE_OK = 0, + MG_RESOLVE_NO_ANSWERS = 1, + MG_RESOLVE_EXCEEDED_RETRY_COUNT = 2, + MG_RESOLVE_TIMEOUT = 3 +}; + +typedef void (*mg_resolve_callback_t)(struct mg_dns_message *dns_message, + void *user_data, enum mg_resolve_err); + +/* Options for `mg_resolve_async_opt`. */ +struct mg_resolve_async_opts { + const char *nameserver; + int max_retries; /* defaults to 2 if zero */ + int timeout; /* in seconds; defaults to 5 if zero */ + int accept_literal; /* pseudo-resolve literal ipv4 and ipv6 addrs */ + int only_literal; /* only resolves literal addrs; sync cb invocation */ + struct mg_connection **dns_conn; /* return DNS connection */ +}; + +/* See `mg_resolve_async_opt()` */ +int mg_resolve_async(struct mg_mgr *mgr, const char *name, int query, + mg_resolve_callback_t cb, void *data); + +/* Set default DNS server */ +void mg_set_nameserver(struct mg_mgr *mgr, const char *nameserver); + +/* + * Resolved a DNS name asynchronously. + * + * Upon successful resolution, the user callback will be invoked + * with the full DNS response message and a pointer to the user's + * context `data`. + * + * In case of timeout while performing the resolution the callback + * will receive a NULL `msg`. + * + * The DNS answers can be extracted with `mg_next_record` and + * `mg_dns_parse_record_data`: + * + * [source,c] + * ---- + * struct in_addr ina; + * struct mg_dns_resource_record *rr = mg_next_record(msg, MG_DNS_A_RECORD, + * NULL); + * mg_dns_parse_record_data(msg, rr, &ina, sizeof(ina)); + * ---- + */ +int mg_resolve_async_opt(struct mg_mgr *mgr, const char *name, int query, + mg_resolve_callback_t cb, void *data, + struct mg_resolve_async_opts opts); + +/* + * Resolve a name from `/etc/hosts`. + * + * Returns 0 on success, -1 on failure. + */ +int mg_resolve_from_hosts_file(const char *host, union socket_address *usa); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* CS_MONGOOSE_SRC_RESOLV_H_ */ diff --git a/src/mongoose-6.11/src/mg_sntp.c b/src/mongoose-6.11/src/mg_sntp.c new file mode 100644 index 0000000..5d40e1a --- /dev/null +++ b/src/mongoose-6.11/src/mg_sntp.c @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2016 Cesanta Software Limited + * All rights reserved + */ + +#include "mg_internal.h" +#include "mg_sntp.h" +#include "mg_util.h" + +#if MG_ENABLE_SNTP + +#define SNTP_TIME_OFFSET 2208988800 + +#ifndef SNTP_TIMEOUT +#define SNTP_TIMEOUT 10 +#endif + +#ifndef SNTP_ATTEMPTS +#define SNTP_ATTEMPTS 3 +#endif + +static uint64_t mg_get_sec(uint64_t val) { + return (val & 0xFFFFFFFF00000000) >> 32; +} + +static uint64_t mg_get_usec(uint64_t val) { + uint64_t tmp = (val & 0x00000000FFFFFFFF); + tmp *= 1000000; + tmp >>= 32; + return tmp; +} + +static void mg_ntp_to_tv(uint64_t val, struct timeval *tv) { + uint64_t tmp; + tmp = mg_get_sec(val); + tmp -= SNTP_TIME_OFFSET; + tv->tv_sec = tmp; + tv->tv_usec = mg_get_usec(val); +} + +static void mg_get_ntp_ts(const char *ntp, uint64_t *val) { + uint32_t tmp; + memcpy(&tmp, ntp, sizeof(tmp)); + tmp = ntohl(tmp); + *val = (uint64_t) tmp << 32; + memcpy(&tmp, ntp + 4, sizeof(tmp)); + tmp = ntohl(tmp); + *val |= tmp; +} + +void mg_sntp_send_request(struct mg_connection *c) { + uint8_t buf[48] = {0}; + /* + * header - 8 bit: + * LI (2 bit) - 3 (not in sync), VN (3 bit) - 4 (version), + * mode (3 bit) - 3 (client) + */ + buf[0] = (3 << 6) | (4 << 3) | 3; + +/* + * Next fields should be empty in client request + * stratum, 8 bit + * poll interval, 8 bit + * rrecision, 8 bit + * root delay, 32 bit + * root dispersion, 32 bit + * ref id, 32 bit + * ref timestamp, 64 bit + * originate Timestamp, 64 bit + * receive Timestamp, 64 bit +*/ + +/* + * convert time to sntp format (sntp starts from 00:00:00 01.01.1900) + * according to rfc868 it is 2208988800L sec + * this information is used to correct roundtrip delay + * but if local clock is absolutely broken (and doesn't work even + * as simple timer), it is better to disable it +*/ +#ifndef MG_SNTP_NO_DELAY_CORRECTION + uint32_t sec; + sec = htonl((uint32_t)(mg_time() + SNTP_TIME_OFFSET)); + memcpy(&buf[40], &sec, sizeof(sec)); +#endif + + mg_send(c, buf, sizeof(buf)); +} + +#ifndef MG_SNTP_NO_DELAY_CORRECTION +static uint64_t mg_calculate_delay(uint64_t t1, uint64_t t2, uint64_t t3) { + /* roundloop delay = (T4 - T1) - (T3 - T2) */ + uint64_t d1 = ((mg_time() + SNTP_TIME_OFFSET) * 1000000) - + (mg_get_sec(t1) * 1000000 + mg_get_usec(t1)); + uint64_t d2 = (mg_get_sec(t3) * 1000000 + mg_get_usec(t3)) - + (mg_get_sec(t2) * 1000000 + mg_get_usec(t2)); + + return (d1 > d2) ? d1 - d2 : 0; +} +#endif + +MG_INTERNAL int mg_sntp_parse_reply(const char *buf, int len, + struct mg_sntp_message *msg) { + uint8_t hdr; + uint64_t trsm_ts_T3, delay = 0; + int mode; + struct timeval tv; + + if (len < 48) { + return -1; + } + + hdr = buf[0]; + + if ((hdr & 0x38) >> 3 != 4) { + /* Wrong version */ + return -1; + } + + mode = hdr & 0x7; + if (mode != 4 && mode != 5) { + /* Not a server reply */ + return -1; + } + + memset(msg, 0, sizeof(*msg)); + + msg->kiss_of_death = (buf[1] == 0); /* Server asks to not send requests */ + + mg_get_ntp_ts(&buf[40], &trsm_ts_T3); + +#ifndef MG_SNTP_NO_DELAY_CORRECTION + { + uint64_t orig_ts_T1, recv_ts_T2; + mg_get_ntp_ts(&buf[24], &orig_ts_T1); + mg_get_ntp_ts(&buf[32], &recv_ts_T2); + delay = mg_calculate_delay(orig_ts_T1, recv_ts_T2, trsm_ts_T3); + } +#endif + + mg_ntp_to_tv(trsm_ts_T3, &tv); + + msg->time = (double) tv.tv_sec + (((double) tv.tv_usec + delay) / 1000000.0); + + return 0; +} + +static void mg_sntp_handler(struct mg_connection *c, int ev, + void *ev_data MG_UD_ARG(void *user_data)) { + struct mbuf *io = &c->recv_mbuf; + struct mg_sntp_message msg; + + c->handler(c, ev, ev_data MG_UD_ARG(user_data)); + + switch (ev) { + case MG_EV_RECV: { + if (mg_sntp_parse_reply(io->buf, io->len, &msg) < 0) { + DBG(("Invalid SNTP packet received (%d)", (int) io->len)); + c->handler(c, MG_SNTP_MALFORMED_REPLY, NULL MG_UD_ARG(user_data)); + } else { + c->handler(c, MG_SNTP_REPLY, (void *) &msg MG_UD_ARG(user_data)); + } + + mbuf_remove(io, io->len); + break; + } + } +} + +int mg_set_protocol_sntp(struct mg_connection *c) { + if ((c->flags & MG_F_UDP) == 0) { + return -1; + } + + c->proto_handler = mg_sntp_handler; + + return 0; +} + +struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, + MG_CB(mg_event_handler_t event_handler, + void *user_data), + const char *sntp_server_name) { + struct mg_connection *c = NULL; + char url[100], *p_url = url; + const char *proto = "", *port = "", *tmp; + + /* If port is not specified, use default (123) */ + tmp = strchr(sntp_server_name, ':'); + if (tmp != NULL && *(tmp + 1) == '/') { + tmp = strchr(tmp + 1, ':'); + } + + if (tmp == NULL) { + port = ":123"; + } + + /* Add udp:// if needed */ + if (strncmp(sntp_server_name, "udp://", 6) != 0) { + proto = "udp://"; + } + + mg_asprintf(&p_url, sizeof(url), "%s%s%s", proto, sntp_server_name, port); + + c = mg_connect(mgr, p_url, event_handler MG_UD_ARG(user_data)); + + if (c == NULL) { + goto cleanup; + } + + mg_set_protocol_sntp(c); + +cleanup: + if (p_url != url) { + MG_FREE(p_url); + } + + return c; +} + +struct sntp_data { + mg_event_handler_t hander; + int count; +}; + +static void mg_sntp_util_ev_handler(struct mg_connection *c, int ev, + void *ev_data MG_UD_ARG(void *user_data)) { +#if !MG_ENABLE_CALLBACK_USERDATA + void *user_data = c->user_data; +#endif + struct sntp_data *sd = (struct sntp_data *) user_data; + + switch (ev) { + case MG_EV_CONNECT: + if (*(int *) ev_data != 0) { + mg_call(c, sd->hander, c->user_data, MG_SNTP_FAILED, NULL); + break; + } + /* fallthrough */ + case MG_EV_TIMER: + if (sd->count <= SNTP_ATTEMPTS) { + mg_sntp_send_request(c); + mg_set_timer(c, mg_time() + 10); + sd->count++; + } else { + mg_call(c, sd->hander, c->user_data, MG_SNTP_FAILED, NULL); + c->flags |= MG_F_CLOSE_IMMEDIATELY; + } + break; + case MG_SNTP_MALFORMED_REPLY: + mg_call(c, sd->hander, c->user_data, MG_SNTP_FAILED, NULL); + c->flags |= MG_F_CLOSE_IMMEDIATELY; + break; + case MG_SNTP_REPLY: + mg_call(c, sd->hander, c->user_data, MG_SNTP_REPLY, ev_data); + c->flags |= MG_F_CLOSE_IMMEDIATELY; + break; + case MG_EV_CLOSE: + MG_FREE(user_data); + c->user_data = NULL; + break; + } +} + +struct mg_connection *mg_sntp_get_time(struct mg_mgr *mgr, + mg_event_handler_t event_handler, + const char *sntp_server_name) { + struct mg_connection *c; + struct sntp_data *sd = (struct sntp_data *) MG_CALLOC(1, sizeof(*sd)); + if (sd == NULL) { + return NULL; + } + + c = mg_sntp_connect(mgr, MG_CB(mg_sntp_util_ev_handler, sd), + sntp_server_name); + if (c == NULL) { + MG_FREE(sd); + return NULL; + } + + sd->hander = event_handler; +#if !MG_ENABLE_CALLBACK_USERDATA + c->user_data = sd; +#endif + + return c; +} + +#endif /* MG_ENABLE_SNTP */ diff --git a/src/mongoose-6.11/src/mg_sntp.h b/src/mongoose-6.11/src/mg_sntp.h new file mode 100644 index 0000000..5cdff2e --- /dev/null +++ b/src/mongoose-6.11/src/mg_sntp.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_MONGOOSE_SRC_SNTP_H_ +#define CS_MONGOOSE_SRC_SNTP_H_ + +#if MG_ENABLE_SNTP + +#define MG_SNTP_EVENT_BASE 500 + +/* + * Received reply from time server. Event handler parameter contains + * pointer to mg_sntp_message structure + */ +#define MG_SNTP_REPLY (MG_SNTP_EVENT_BASE + 1) + +/* Received malformed SNTP packet */ +#define MG_SNTP_MALFORMED_REPLY (MG_SNTP_EVENT_BASE + 2) + +/* Failed to get time from server (timeout etc) */ +#define MG_SNTP_FAILED (MG_SNTP_EVENT_BASE + 3) + +struct mg_sntp_message { + /* if server sends this flags, user should not send requests to it */ + int kiss_of_death; + /* usual mg_time */ + double time; +}; + +/* Establishes connection to given sntp server */ +struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, + MG_CB(mg_event_handler_t event_handler, + void *user_data), + const char *sntp_server_name); + +/* Sends time request to given connection */ +void mg_sntp_send_request(struct mg_connection *c); + +/* + * Helper function + * Establishes connection to time server, tries to send request + * repeats sending SNTP_ATTEMPTS times every SNTP_TIMEOUT sec + * (if needed) + * See sntp_client example + */ +struct mg_connection *mg_sntp_get_time(struct mg_mgr *mgr, + mg_event_handler_t event_handler, + const char *sntp_server_name); + +#endif + +#endif /* CS_MONGOOSE_SRC_SNTP_H_ */ diff --git a/src/mongoose-6.11/src/mg_socks.c b/src/mongoose-6.11/src/mg_socks.c new file mode 100644 index 0000000..56b2d5c --- /dev/null +++ b/src/mongoose-6.11/src/mg_socks.c @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2017 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_SOCKS + +#include "mg_socks.h" +#include "mg_internal.h" + +/* + * https://www.ietf.org/rfc/rfc1928.txt paragraph 3, handle client handshake + * + * +----+----------+----------+ + * |VER | NMETHODS | METHODS | + * +----+----------+----------+ + * | 1 | 1 | 1 to 255 | + * +----+----------+----------+ + */ +static void mg_socks5_handshake(struct mg_connection *c) { + struct mbuf *r = &c->recv_mbuf; + if (r->buf[0] != MG_SOCKS_VERSION) { + c->flags |= MG_F_CLOSE_IMMEDIATELY; + } else if (r->len > 2 && (size_t) r->buf[1] + 2 <= r->len) { + /* https://www.ietf.org/rfc/rfc1928.txt paragraph 3 */ + unsigned char reply[2] = {MG_SOCKS_VERSION, MG_SOCKS_HANDSHAKE_FAILURE}; + int i; + for (i = 2; i < r->buf[1] + 2; i++) { + /* TODO(lsm): support other auth methods */ + if (r->buf[i] == MG_SOCKS_HANDSHAKE_NOAUTH) reply[1] = r->buf[i]; + } + mbuf_remove(r, 2 + r->buf[1]); + mg_send(c, reply, sizeof(reply)); + c->flags |= MG_SOCKS_HANDSHAKE_DONE; /* Mark handshake done */ + } +} + +static void disband(struct mg_connection *c) { + struct mg_connection *c2 = (struct mg_connection *) c->user_data; + if (c2 != NULL) { + c2->flags |= MG_F_SEND_AND_CLOSE; + c2->user_data = NULL; + } + c->flags |= MG_F_SEND_AND_CLOSE; + c->user_data = NULL; +} + +static void relay_data(struct mg_connection *c) { + struct mg_connection *c2 = (struct mg_connection *) c->user_data; + if (c2 != NULL) { + mg_send(c2, c->recv_mbuf.buf, c->recv_mbuf.len); + mbuf_remove(&c->recv_mbuf, c->recv_mbuf.len); + } else { + c->flags |= MG_F_SEND_AND_CLOSE; + } +} + +static void serv_ev_handler(struct mg_connection *c, int ev, void *ev_data) { + if (ev == MG_EV_CLOSE) { + disband(c); + } else if (ev == MG_EV_RECV) { + relay_data(c); + } else if (ev == MG_EV_CONNECT) { + int res = *(int *) ev_data; + if (res != 0) LOG(LL_ERROR, ("connect error: %d", res)); + } +} + +static void mg_socks5_connect(struct mg_connection *c, const char *addr) { + struct mg_connection *serv = mg_connect(c->mgr, addr, serv_ev_handler); + serv->user_data = c; + c->user_data = serv; +} + +/* + * Request, https://www.ietf.org/rfc/rfc1928.txt paragraph 4 + * + * +----+-----+-------+------+----------+----------+ + * |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | + * +----+-----+-------+------+----------+----------+ + * | 1 | 1 | X'00' | 1 | Variable | 2 | + * +----+-----+-------+------+----------+----------+ + */ +static void mg_socks5_handle_request(struct mg_connection *c) { + struct mbuf *r = &c->recv_mbuf; + unsigned char *p = (unsigned char *) r->buf; + unsigned char addr_len = 4, reply = MG_SOCKS_SUCCESS; + int ver, cmd, atyp; + char addr[300]; + + if (r->len < 8) return; /* return if not fully buffered. min DST.ADDR is 2 */ + ver = p[0]; + cmd = p[1]; + atyp = p[3]; + + /* TODO(lsm): support other commands */ + if (ver != MG_SOCKS_VERSION || cmd != MG_SOCKS_CMD_CONNECT) { + reply = MG_SOCKS_CMD_NOT_SUPPORTED; + } else if (atyp == MG_SOCKS_ADDR_IPV4) { + addr_len = 4; + if (r->len < (size_t) addr_len + 6) return; /* return if not buffered */ + snprintf(addr, sizeof(addr), "%d.%d.%d.%d:%d", p[4], p[5], p[6], p[7], + p[8] << 8 | p[9]); + mg_socks5_connect(c, addr); + } else if (atyp == MG_SOCKS_ADDR_IPV6) { + addr_len = 16; + if (r->len < (size_t) addr_len + 6) return; /* return if not buffered */ + snprintf(addr, sizeof(addr), "[%x:%x:%x:%x:%x:%x:%x:%x]:%d", + p[4] << 8 | p[5], p[6] << 8 | p[7], p[8] << 8 | p[9], + p[10] << 8 | p[11], p[12] << 8 | p[13], p[14] << 8 | p[15], + p[16] << 8 | p[17], p[18] << 8 | p[19], p[20] << 8 | p[21]); + mg_socks5_connect(c, addr); + } else if (atyp == MG_SOCKS_ADDR_DOMAIN) { + addr_len = p[4] + 1; + if (r->len < (size_t) addr_len + 6) return; /* return if not buffered */ + snprintf(addr, sizeof(addr), "%.*s:%d", p[4], p + 5, + p[4 + addr_len] << 8 | p[4 + addr_len + 1]); + mg_socks5_connect(c, addr); + } else { + reply = MG_SOCKS_ADDR_NOT_SUPPORTED; + } + + /* + * Reply, https://www.ietf.org/rfc/rfc1928.txt paragraph 5 + * + * +----+-----+-------+------+----------+----------+ + * |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | + * +----+-----+-------+------+----------+----------+ + * | 1 | 1 | X'00' | 1 | Variable | 2 | + * +----+-----+-------+------+----------+----------+ + */ + { + unsigned char buf[] = {MG_SOCKS_VERSION, reply, 0}; + mg_send(c, buf, sizeof(buf)); + } + mg_send(c, r->buf + 3, addr_len + 1 + 2); + + mbuf_remove(r, 6 + addr_len); /* Remove request from the input stream */ + c->flags |= MG_SOCKS_CONNECT_DONE; /* Mark ourselves as connected */ +} + +static void socks_handler(struct mg_connection *c, int ev, void *ev_data) { + if (ev == MG_EV_RECV) { + if (!(c->flags & MG_SOCKS_HANDSHAKE_DONE)) mg_socks5_handshake(c); + if (c->flags & MG_SOCKS_HANDSHAKE_DONE && + !(c->flags & MG_SOCKS_CONNECT_DONE)) { + mg_socks5_handle_request(c); + } + if (c->flags & MG_SOCKS_CONNECT_DONE) relay_data(c); + } else if (ev == MG_EV_CLOSE) { + disband(c); + } + (void) ev_data; +} + +void mg_set_protocol_socks(struct mg_connection *c) { + c->proto_handler = socks_handler; +} +#endif diff --git a/src/mongoose-6.11/src/mg_socks.h b/src/mongoose-6.11/src/mg_socks.h new file mode 100644 index 0000000..e7b231a --- /dev/null +++ b/src/mongoose-6.11/src/mg_socks.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2017 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_MONGOOSE_SRC_SOCKS_H_ +#define CS_MONGOOSE_SRC_SOCKS_H_ + +#if MG_ENABLE_SOCKS + +#define MG_SOCKS_VERSION 5 + +#define MG_SOCKS_HANDSHAKE_DONE MG_F_USER_1 +#define MG_SOCKS_CONNECT_DONE MG_F_USER_2 + +/* SOCKS5 handshake methods */ +enum mg_socks_handshake_method { + MG_SOCKS_HANDSHAKE_NOAUTH = 0, /* Handshake method - no authentication */ + MG_SOCKS_HANDSHAKE_GSSAPI = 1, /* Handshake method - GSSAPI auth */ + MG_SOCKS_HANDSHAKE_USERPASS = 2, /* Handshake method - user/password auth */ + MG_SOCKS_HANDSHAKE_FAILURE = 0xff, /* Handshake method - failure */ +}; + +/* SOCKS5 commands */ +enum mg_socks_command { + MG_SOCKS_CMD_CONNECT = 1, /* Command: CONNECT */ + MG_SOCKS_CMD_BIND = 2, /* Command: BIND */ + MG_SOCKS_CMD_UDP_ASSOCIATE = 3, /* Command: UDP ASSOCIATE */ +}; + +/* SOCKS5 address types */ +enum mg_socks_address_type { + MG_SOCKS_ADDR_IPV4 = 1, /* Address type: IPv4 */ + MG_SOCKS_ADDR_DOMAIN = 3, /* Address type: Domain name */ + MG_SOCKS_ADDR_IPV6 = 4, /* Address type: IPv6 */ +}; + +/* SOCKS5 response codes */ +enum mg_socks_response { + MG_SOCKS_SUCCESS = 0, /* Response: success */ + MG_SOCKS_FAILURE = 1, /* Response: failure */ + MG_SOCKS_NOT_ALLOWED = 2, /* Response: connection not allowed */ + MG_SOCKS_NET_UNREACHABLE = 3, /* Response: network unreachable */ + MG_SOCKS_HOST_UNREACHABLE = 4, /* Response: network unreachable */ + MG_SOCKS_CONN_REFUSED = 5, /* Response: network unreachable */ + MG_SOCKS_TTL_EXPIRED = 6, /* Response: network unreachable */ + MG_SOCKS_CMD_NOT_SUPPORTED = 7, /* Response: network unreachable */ + MG_SOCKS_ADDR_NOT_SUPPORTED = 8, /* Response: network unreachable */ +}; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Turn the connection into the SOCKS server */ +void mg_set_protocol_socks(struct mg_connection *c); + +/* Create socks tunnel for the client connection */ +struct mg_iface *mg_socks_mk_iface(struct mg_mgr *, const char *proxy_addr); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif +#endif diff --git a/src/mongoose-6.11/src/mg_ssl_if.h b/src/mongoose-6.11/src/mg_ssl_if.h new file mode 100644 index 0000000..62f24b5 --- /dev/null +++ b/src/mongoose-6.11/src/mg_ssl_if.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_MONGOOSE_SRC_SSL_IF_H_ +#define CS_MONGOOSE_SRC_SSL_IF_H_ + +#if MG_ENABLE_SSL + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +struct mg_ssl_if_ctx; +struct mg_connection; + +void mg_ssl_if_init(); + +enum mg_ssl_if_result { + MG_SSL_OK = 0, + MG_SSL_WANT_READ = -1, + MG_SSL_WANT_WRITE = -2, + MG_SSL_ERROR = -3, +}; + +struct mg_ssl_if_conn_params { + const char *cert; + const char *key; + const char *ca_cert; + const char *server_name; + const char *cipher_suites; + const char *psk_identity; + const char *psk_key; +}; + +enum mg_ssl_if_result mg_ssl_if_conn_init( + struct mg_connection *nc, const struct mg_ssl_if_conn_params *params, + const char **err_msg); +enum mg_ssl_if_result mg_ssl_if_conn_accept(struct mg_connection *nc, + struct mg_connection *lc); +void mg_ssl_if_conn_close_notify(struct mg_connection *nc); +void mg_ssl_if_conn_free(struct mg_connection *nc); + +enum mg_ssl_if_result mg_ssl_if_handshake(struct mg_connection *nc); +int mg_ssl_if_read(struct mg_connection *nc, void *buf, size_t buf_size); +int mg_ssl_if_write(struct mg_connection *nc, const void *data, size_t len); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* MG_ENABLE_SSL */ + +#endif /* CS_MONGOOSE_SRC_SSL_IF_H_ */ diff --git a/src/mongoose-6.11/src/mg_ssl_if_mbedtls.c b/src/mongoose-6.11/src/mg_ssl_if_mbedtls.c new file mode 100644 index 0000000..4c1c1db --- /dev/null +++ b/src/mongoose-6.11/src/mg_ssl_if_mbedtls.c @@ -0,0 +1,478 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_MBEDTLS + +#include +#include +#include +#include +#include + +static void mg_ssl_mbed_log(void *ctx, int level, const char *file, int line, + const char *str) { + enum cs_log_level cs_level; + switch (level) { + case 1: + cs_level = LL_ERROR; + break; + case 2: + case 3: + cs_level = LL_DEBUG; + break; + default: + cs_level = LL_VERBOSE_DEBUG; + } + /* mbedTLS passes strings with \n at the end, strip it. */ + LOG(cs_level, ("%p %.*s", ctx, (int) (strlen(str) - 1), str)); + (void) file; + (void) line; +} + +struct mg_ssl_if_ctx { + mbedtls_ssl_config *conf; + mbedtls_ssl_context *ssl; + mbedtls_x509_crt *cert; + mbedtls_pk_context *key; + mbedtls_x509_crt *ca_cert; + struct mbuf cipher_suites; +}; + +/* Must be provided by the platform. ctx is struct mg_connection. */ +extern int mg_ssl_if_mbed_random(void *ctx, unsigned char *buf, size_t len); + +void mg_ssl_if_init() { +} + +enum mg_ssl_if_result mg_ssl_if_conn_accept(struct mg_connection *nc, + struct mg_connection *lc) { + struct mg_ssl_if_ctx *ctx = + (struct mg_ssl_if_ctx *) MG_CALLOC(1, sizeof(*ctx)); + struct mg_ssl_if_ctx *lc_ctx = (struct mg_ssl_if_ctx *) lc->ssl_if_data; + nc->ssl_if_data = ctx; + if (ctx == NULL || lc_ctx == NULL) return MG_SSL_ERROR; + ctx->ssl = (mbedtls_ssl_context *) MG_CALLOC(1, sizeof(*ctx->ssl)); + if (mbedtls_ssl_setup(ctx->ssl, lc_ctx->conf) != 0) { + return MG_SSL_ERROR; + } + return MG_SSL_OK; +} + +static enum mg_ssl_if_result mg_use_cert(struct mg_ssl_if_ctx *ctx, + const char *cert, const char *key, + const char **err_msg); +static enum mg_ssl_if_result mg_use_ca_cert(struct mg_ssl_if_ctx *ctx, + const char *cert); +static enum mg_ssl_if_result mg_set_cipher_list(struct mg_ssl_if_ctx *ctx, + const char *ciphers); +static enum mg_ssl_if_result mg_ssl_if_mbed_set_psk(struct mg_ssl_if_ctx *ctx, + const char *identity, + const char *key); + +enum mg_ssl_if_result mg_ssl_if_conn_init( + struct mg_connection *nc, const struct mg_ssl_if_conn_params *params, + const char **err_msg) { + struct mg_ssl_if_ctx *ctx = + (struct mg_ssl_if_ctx *) MG_CALLOC(1, sizeof(*ctx)); + DBG(("%p %s,%s,%s", nc, (params->cert ? params->cert : ""), + (params->key ? params->key : ""), + (params->ca_cert ? params->ca_cert : ""))); + + if (ctx == NULL) { + MG_SET_PTRPTR(err_msg, "Out of memory"); + return MG_SSL_ERROR; + } + nc->ssl_if_data = ctx; + ctx->conf = (mbedtls_ssl_config *) MG_CALLOC(1, sizeof(*ctx->conf)); + mbuf_init(&ctx->cipher_suites, 0); + mbedtls_ssl_config_init(ctx->conf); + mbedtls_ssl_conf_dbg(ctx->conf, mg_ssl_mbed_log, nc); + if (mbedtls_ssl_config_defaults( + ctx->conf, (nc->flags & MG_F_LISTENING ? MBEDTLS_SSL_IS_SERVER + : MBEDTLS_SSL_IS_CLIENT), + MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT) != 0) { + MG_SET_PTRPTR(err_msg, "Failed to init SSL config"); + return MG_SSL_ERROR; + } + + /* TLS 1.2 and up */ + mbedtls_ssl_conf_min_version(ctx->conf, MBEDTLS_SSL_MAJOR_VERSION_3, + MBEDTLS_SSL_MINOR_VERSION_3); + mbedtls_ssl_conf_rng(ctx->conf, mg_ssl_if_mbed_random, nc); + + if (params->cert != NULL && + mg_use_cert(ctx, params->cert, params->key, err_msg) != MG_SSL_OK) { + return MG_SSL_ERROR; + } + + if (params->ca_cert != NULL && + mg_use_ca_cert(ctx, params->ca_cert) != MG_SSL_OK) { + MG_SET_PTRPTR(err_msg, "Invalid SSL CA cert"); + return MG_SSL_ERROR; + } + + if (mg_set_cipher_list(ctx, params->cipher_suites) != MG_SSL_OK) { + MG_SET_PTRPTR(err_msg, "Invalid cipher suite list"); + return MG_SSL_ERROR; + } + + if (mg_ssl_if_mbed_set_psk(ctx, params->psk_identity, params->psk_key) != + MG_SSL_OK) { + MG_SET_PTRPTR(err_msg, "Invalid PSK settings"); + return MG_SSL_ERROR; + } + + if (!(nc->flags & MG_F_LISTENING)) { + ctx->ssl = (mbedtls_ssl_context *) MG_CALLOC(1, sizeof(*ctx->ssl)); + mbedtls_ssl_init(ctx->ssl); + if (mbedtls_ssl_setup(ctx->ssl, ctx->conf) != 0) { + MG_SET_PTRPTR(err_msg, "Failed to create SSL session"); + return MG_SSL_ERROR; + } + if (params->server_name != NULL && + mbedtls_ssl_set_hostname(ctx->ssl, params->server_name) != 0) { + return MG_SSL_ERROR; + } + } + +#ifdef MG_SSL_IF_MBEDTLS_MAX_FRAG_LEN + if (mbedtls_ssl_conf_max_frag_len(ctx->conf, +#if MG_SSL_IF_MBEDTLS_MAX_FRAG_LEN == 512 + MBEDTLS_SSL_MAX_FRAG_LEN_512 +#elif MG_SSL_IF_MBEDTLS_MAX_FRAG_LEN == 1024 + MBEDTLS_SSL_MAX_FRAG_LEN_1024 +#elif MG_SSL_IF_MBEDTLS_MAX_FRAG_LEN == 2048 + MBEDTLS_SSL_MAX_FRAG_LEN_2048 +#elif MG_SSL_IF_MBEDTLS_MAX_FRAG_LEN == 4096 + MBEDTLS_SSL_MAX_FRAG_LEN_4096 +#else +#error Invalid MG_SSL_IF_MBEDTLS_MAX_FRAG_LEN +#endif + ) != 0) { + return MG_SSL_ERROR; + } +#endif + + nc->flags |= MG_F_SSL; + + return MG_SSL_OK; +} + +#if MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL +int ssl_socket_send(void *ctx, const unsigned char *buf, size_t len); +int ssl_socket_recv(void *ctx, unsigned char *buf, size_t len); +#else +static int ssl_socket_send(void *ctx, const unsigned char *buf, size_t len) { + struct mg_connection *nc = (struct mg_connection *) ctx; + int n = (int) MG_SEND_FUNC(nc->sock, buf, len, 0); + LOG(LL_DEBUG, ("%p %d -> %d", nc, (int) len, n)); + if (n >= 0) return n; + n = mg_get_errno(); + return ((n == EAGAIN || n == EINPROGRESS) ? MBEDTLS_ERR_SSL_WANT_WRITE : -1); +} + +static int ssl_socket_recv(void *ctx, unsigned char *buf, size_t len) { + struct mg_connection *nc = (struct mg_connection *) ctx; + int n = (int) MG_RECV_FUNC(nc->sock, buf, len, 0); + LOG(LL_DEBUG, ("%p %d <- %d", nc, (int) len, n)); + if (n >= 0) return n; + n = mg_get_errno(); + return ((n == EAGAIN || n == EINPROGRESS) ? MBEDTLS_ERR_SSL_WANT_READ : -1); +} +#endif + +static enum mg_ssl_if_result mg_ssl_if_mbed_err(struct mg_connection *nc, + int ret) { + if (ret == MBEDTLS_ERR_SSL_WANT_READ) return MG_SSL_WANT_READ; + if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) return MG_SSL_WANT_WRITE; + if (ret != + MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { /* CLOSE_NOTIFY = Normal shutdown */ + LOG(LL_ERROR, ("%p SSL error: %d", nc, ret)); + } + nc->err = ret; + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + return MG_SSL_ERROR; +} + +static void mg_ssl_if_mbed_free_certs_and_keys(struct mg_ssl_if_ctx *ctx) { + if (ctx->cert != NULL) { + mbedtls_x509_crt_free(ctx->cert); + MG_FREE(ctx->cert); + ctx->cert = NULL; + mbedtls_pk_free(ctx->key); + MG_FREE(ctx->key); + ctx->key = NULL; + } + if (ctx->ca_cert != NULL) { + mbedtls_ssl_conf_ca_chain(ctx->conf, NULL, NULL); +#ifdef MBEDTLS_X509_CA_CHAIN_ON_DISK + if (ctx->ca_cert->ca_chain_file != NULL) { + MG_FREE((void *) ctx->ca_cert->ca_chain_file); + ctx->ca_cert->ca_chain_file = NULL; + } +#endif + mbedtls_x509_crt_free(ctx->ca_cert); + MG_FREE(ctx->ca_cert); + ctx->ca_cert = NULL; + } +} + +enum mg_ssl_if_result mg_ssl_if_handshake(struct mg_connection *nc) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + int err; + /* If bio is not yet set, do it now. */ + if (ctx->ssl->p_bio == NULL) { + mbedtls_ssl_set_bio(ctx->ssl, nc, ssl_socket_send, ssl_socket_recv, NULL); + } + err = mbedtls_ssl_handshake(ctx->ssl); + if (err != 0) return mg_ssl_if_mbed_err(nc, err); +#ifdef MG_SSL_IF_MBEDTLS_FREE_CERTS + /* + * Free the peer certificate, we don't need it after handshake. + * Note that this effectively disables renegotiation. + */ + mbedtls_x509_crt_free(ctx->ssl->session->peer_cert); + mbedtls_free(ctx->ssl->session->peer_cert); + ctx->ssl->session->peer_cert = NULL; + /* On a client connection we can also free our own and CA certs. */ + if (nc->listener == NULL) { + if (ctx->conf->key_cert != NULL) { + /* Note that this assumes one key_cert entry, which matches our init. */ + MG_FREE(ctx->conf->key_cert); + ctx->conf->key_cert = NULL; + } + mbedtls_ssl_conf_ca_chain(ctx->conf, NULL, NULL); + mg_ssl_if_mbed_free_certs_and_keys(ctx); + } +#endif + return MG_SSL_OK; +} + +int mg_ssl_if_read(struct mg_connection *nc, void *buf, size_t buf_size) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + int n = mbedtls_ssl_read(ctx->ssl, (unsigned char *) buf, buf_size); + DBG(("%p %d -> %d", nc, (int) buf_size, n)); + if (n < 0) return mg_ssl_if_mbed_err(nc, n); + if (n == 0) nc->flags |= MG_F_CLOSE_IMMEDIATELY; + return n; +} + +int mg_ssl_if_write(struct mg_connection *nc, const void *data, size_t len) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + int n = mbedtls_ssl_write(ctx->ssl, (const unsigned char *) data, len); + DBG(("%p %d -> %d", nc, (int) len, n)); + if (n < 0) return mg_ssl_if_mbed_err(nc, n); + return n; +} + +void mg_ssl_if_conn_close_notify(struct mg_connection *nc) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + if (ctx == NULL) return; + mbedtls_ssl_close_notify(ctx->ssl); +} + +void mg_ssl_if_conn_free(struct mg_connection *nc) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + if (ctx == NULL) return; + nc->ssl_if_data = NULL; + if (ctx->ssl != NULL) { + mbedtls_ssl_free(ctx->ssl); + MG_FREE(ctx->ssl); + } + mg_ssl_if_mbed_free_certs_and_keys(ctx); + if (ctx->conf != NULL) { + mbedtls_ssl_config_free(ctx->conf); + MG_FREE(ctx->conf); + } + mbuf_free(&ctx->cipher_suites); + memset(ctx, 0, sizeof(*ctx)); + MG_FREE(ctx); +} + +static enum mg_ssl_if_result mg_use_ca_cert(struct mg_ssl_if_ctx *ctx, + const char *ca_cert) { + if (ca_cert == NULL || strcmp(ca_cert, "*") == 0) { + mbedtls_ssl_conf_authmode(ctx->conf, MBEDTLS_SSL_VERIFY_NONE); + return MG_SSL_OK; + } + ctx->ca_cert = (mbedtls_x509_crt *) MG_CALLOC(1, sizeof(*ctx->ca_cert)); + mbedtls_x509_crt_init(ctx->ca_cert); +#ifdef MBEDTLS_X509_CA_CHAIN_ON_DISK + ca_cert = strdup(ca_cert); + if (mbedtls_x509_crt_set_ca_chain_file(ctx->ca_cert, ca_cert) != 0) { + return MG_SSL_ERROR; + } +#else + if (mbedtls_x509_crt_parse_file(ctx->ca_cert, ca_cert) != 0) { + return MG_SSL_ERROR; + } +#endif + mbedtls_ssl_conf_ca_chain(ctx->conf, ctx->ca_cert, NULL); + mbedtls_ssl_conf_authmode(ctx->conf, MBEDTLS_SSL_VERIFY_REQUIRED); + return MG_SSL_OK; +} + +static enum mg_ssl_if_result mg_use_cert(struct mg_ssl_if_ctx *ctx, + const char *cert, const char *key, + const char **err_msg) { + if (key == NULL) key = cert; + if (cert == NULL || cert[0] == '\0' || key == NULL || key[0] == '\0') { + return MG_SSL_OK; + } + ctx->cert = (mbedtls_x509_crt *) MG_CALLOC(1, sizeof(*ctx->cert)); + mbedtls_x509_crt_init(ctx->cert); + ctx->key = (mbedtls_pk_context *) MG_CALLOC(1, sizeof(*ctx->key)); + mbedtls_pk_init(ctx->key); + if (mbedtls_x509_crt_parse_file(ctx->cert, cert) != 0) { + MG_SET_PTRPTR(err_msg, "Invalid SSL cert"); + return MG_SSL_ERROR; + } + if (mbedtls_pk_parse_keyfile(ctx->key, key, NULL) != 0) { + MG_SET_PTRPTR(err_msg, "Invalid SSL key"); + return MG_SSL_ERROR; + } + if (mbedtls_ssl_conf_own_cert(ctx->conf, ctx->cert, ctx->key) != 0) { + MG_SET_PTRPTR(err_msg, "Invalid SSL key or cert"); + return MG_SSL_ERROR; + } + return MG_SSL_OK; +} + +static const int mg_s_cipher_list[] = { +#if CS_PLATFORM != CS_P_ESP8266 + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA, +#else + /* + * ECDHE is way too slow on ESP8266 w/o cryptochip, this sometimes results + * in WiFi STA deauths. Use weaker but faster cipher suites. Sad but true. + * Disable DHE completely because it's just hopelessly slow. + */ + MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA, +#endif /* CS_PLATFORM != CS_P_ESP8266 */ + 0, +}; + +/* + * Ciphers can be specified as a colon-separated list of cipher suite names. + * These can be found in + * https://github.com/ARMmbed/mbedtls/blob/development/library/ssl_ciphersuites.c#L267 + * E.g.: TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-256-CCM + */ +static enum mg_ssl_if_result mg_set_cipher_list(struct mg_ssl_if_ctx *ctx, + const char *ciphers) { + if (ciphers != NULL) { + int l, id; + const char *s = ciphers, *e; + char tmp[50]; + while (s != NULL) { + e = strchr(s, ':'); + l = (e != NULL ? (e - s) : (int) strlen(s)); + strncpy(tmp, s, l); + tmp[l] = '\0'; + id = mbedtls_ssl_get_ciphersuite_id(tmp); + DBG(("%s -> %04x", tmp, id)); + if (id != 0) { + mbuf_append(&ctx->cipher_suites, &id, sizeof(id)); + } + s = (e != NULL ? e + 1 : NULL); + } + if (ctx->cipher_suites.len == 0) return MG_SSL_ERROR; + id = 0; + mbuf_append(&ctx->cipher_suites, &id, sizeof(id)); + mbuf_trim(&ctx->cipher_suites); + mbedtls_ssl_conf_ciphersuites(ctx->conf, + (const int *) ctx->cipher_suites.buf); + } else { + mbedtls_ssl_conf_ciphersuites(ctx->conf, mg_s_cipher_list); + } + return MG_SSL_OK; +} + +static enum mg_ssl_if_result mg_ssl_if_mbed_set_psk(struct mg_ssl_if_ctx *ctx, + const char *identity, + const char *key_str) { + unsigned char key[32]; + size_t key_len; + if (identity == NULL && key_str == NULL) return MG_SSL_OK; + if (identity == NULL || key_str == NULL) return MG_SSL_ERROR; + key_len = strlen(key_str); + if (key_len != 32 && key_len != 64) return MG_SSL_ERROR; + size_t i = 0; + memset(key, 0, sizeof(key)); + key_len = 0; + for (i = 0; key_str[i] != '\0'; i++) { + unsigned char c; + char hc = tolower((int) key_str[i]); + if (hc >= '0' && hc <= '9') { + c = hc - '0'; + } else if (hc >= 'a' && hc <= 'f') { + c = hc - 'a' + 0xa; + } else { + return MG_SSL_ERROR; + } + key_len = i / 2; + key[key_len] <<= 4; + key[key_len] |= c; + } + key_len++; + DBG(("identity = '%s', key = (%u)", identity, (unsigned int) key_len)); + /* mbedTLS makes copies of psk and identity. */ + if (mbedtls_ssl_conf_psk(ctx->conf, (const unsigned char *) key, key_len, + (const unsigned char *) identity, + strlen(identity)) != 0) { + return MG_SSL_ERROR; + } + return MG_SSL_OK; +} + +const char *mg_set_ssl(struct mg_connection *nc, const char *cert, + const char *ca_cert) { + const char *err_msg = NULL; + struct mg_ssl_if_conn_params params; + memset(¶ms, 0, sizeof(params)); + params.cert = cert; + params.ca_cert = ca_cert; + if (mg_ssl_if_conn_init(nc, ¶ms, &err_msg) != MG_SSL_OK) { + return err_msg; + } + return NULL; +} + +/* Lazy RNG. Warning: it would be a bad idea to do this in production! */ +#ifdef MG_SSL_MBED_DUMMY_RANDOM +int mg_ssl_if_mbed_random(void *ctx, unsigned char *buf, size_t len) { + (void) ctx; + while (len--) *buf++ = rand(); + return 0; +} +#endif + +#endif /* MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_MBEDTLS */ diff --git a/src/mongoose-6.11/src/mg_ssl_if_openssl.c b/src/mongoose-6.11/src/mg_ssl_if_openssl.c new file mode 100644 index 0000000..2f3da5e --- /dev/null +++ b/src/mongoose-6.11/src/mg_ssl_if_openssl.c @@ -0,0 +1,394 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_OPENSSL + +#ifdef __APPLE__ +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + +#include + +struct mg_ssl_if_ctx { + SSL *ssl; + SSL_CTX *ssl_ctx; + struct mbuf psk; + size_t identity_len; +}; + +void mg_ssl_if_init() { + SSL_library_init(); +} + +enum mg_ssl_if_result mg_ssl_if_conn_accept(struct mg_connection *nc, + struct mg_connection *lc) { + struct mg_ssl_if_ctx *ctx = + (struct mg_ssl_if_ctx *) MG_CALLOC(1, sizeof(*ctx)); + struct mg_ssl_if_ctx *lc_ctx = (struct mg_ssl_if_ctx *) lc->ssl_if_data; + nc->ssl_if_data = ctx; + if (ctx == NULL || lc_ctx == NULL) return MG_SSL_ERROR; + ctx->ssl_ctx = lc_ctx->ssl_ctx; + if ((ctx->ssl = SSL_new(ctx->ssl_ctx)) == NULL) { + return MG_SSL_ERROR; + } + return MG_SSL_OK; +} + +static enum mg_ssl_if_result mg_use_cert(SSL_CTX *ctx, const char *cert, + const char *key, const char **err_msg); +static enum mg_ssl_if_result mg_use_ca_cert(SSL_CTX *ctx, const char *cert); +static enum mg_ssl_if_result mg_set_cipher_list(SSL_CTX *ctx, const char *cl); +static enum mg_ssl_if_result mg_ssl_if_ossl_set_psk(struct mg_ssl_if_ctx *ctx, + const char *identity, + const char *key_str); + +enum mg_ssl_if_result mg_ssl_if_conn_init( + struct mg_connection *nc, const struct mg_ssl_if_conn_params *params, + const char **err_msg) { + struct mg_ssl_if_ctx *ctx = + (struct mg_ssl_if_ctx *) MG_CALLOC(1, sizeof(*ctx)); + DBG(("%p %s,%s,%s", nc, (params->cert ? params->cert : ""), + (params->key ? params->key : ""), + (params->ca_cert ? params->ca_cert : ""))); + if (ctx == NULL) { + MG_SET_PTRPTR(err_msg, "Out of memory"); + return MG_SSL_ERROR; + } + nc->ssl_if_data = ctx; + if (nc->flags & MG_F_LISTENING) { + ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method()); + } else { + ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method()); + } + if (ctx->ssl_ctx == NULL) { + MG_SET_PTRPTR(err_msg, "Failed to create SSL context"); + return MG_SSL_ERROR; + } + +#ifndef KR_VERSION + /* Disable deprecated protocols. */ + SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv2); + SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv3); + SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1); +#ifdef MG_SSL_OPENSSL_NO_COMPRESSION + SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_COMPRESSION); +#endif +#ifdef MG_SSL_OPENSSL_CIPHER_SERVER_PREFERENCE + SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); +#endif +#else +/* Krypton only supports TLSv1.2 anyway. */ +#endif + + if (params->cert != NULL && + mg_use_cert(ctx->ssl_ctx, params->cert, params->key, err_msg) != + MG_SSL_OK) { + return MG_SSL_ERROR; + } + + if (params->ca_cert != NULL && + mg_use_ca_cert(ctx->ssl_ctx, params->ca_cert) != MG_SSL_OK) { + MG_SET_PTRPTR(err_msg, "Invalid SSL CA cert"); + return MG_SSL_ERROR; + } + + if (params->server_name != NULL) { +#ifdef KR_VERSION + SSL_CTX_kr_set_verify_name(ctx->ssl_ctx, params->server_name); +#else +/* TODO(rojer): Implement server name verification on OpenSSL. */ +#endif + } + + if (mg_set_cipher_list(ctx->ssl_ctx, params->cipher_suites) != MG_SSL_OK) { + MG_SET_PTRPTR(err_msg, "Invalid cipher suite list"); + return MG_SSL_ERROR; + } + + mbuf_init(&ctx->psk, 0); + if (mg_ssl_if_ossl_set_psk(ctx, params->psk_identity, params->psk_key) != + MG_SSL_OK) { + MG_SET_PTRPTR(err_msg, "Invalid PSK settings"); + return MG_SSL_ERROR; + } + + if (!(nc->flags & MG_F_LISTENING) && + (ctx->ssl = SSL_new(ctx->ssl_ctx)) == NULL) { + MG_SET_PTRPTR(err_msg, "Failed to create SSL session"); + return MG_SSL_ERROR; + } + + nc->flags |= MG_F_SSL; + + return MG_SSL_OK; +} + +static enum mg_ssl_if_result mg_ssl_if_ssl_err(struct mg_connection *nc, + int res) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + int err = SSL_get_error(ctx->ssl, res); + if (err == SSL_ERROR_WANT_READ) return MG_SSL_WANT_READ; + if (err == SSL_ERROR_WANT_WRITE) return MG_SSL_WANT_WRITE; + DBG(("%p %p SSL error: %d %d", nc, ctx->ssl_ctx, res, err)); + nc->err = err; + return MG_SSL_ERROR; +} + +enum mg_ssl_if_result mg_ssl_if_handshake(struct mg_connection *nc) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + int server_side = (nc->listener != NULL); + int res; + /* If descriptor is not yet set, do it now. */ + if (SSL_get_fd(ctx->ssl) < 0) { + if (SSL_set_fd(ctx->ssl, nc->sock) != 1) return MG_SSL_ERROR; + } + res = server_side ? SSL_accept(ctx->ssl) : SSL_connect(ctx->ssl); + if (res != 1) return mg_ssl_if_ssl_err(nc, res); + return MG_SSL_OK; +} + +int mg_ssl_if_read(struct mg_connection *nc, void *buf, size_t buf_size) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + int n = SSL_read(ctx->ssl, buf, buf_size); + DBG(("%p %d -> %d", nc, (int) buf_size, n)); + if (n < 0) return mg_ssl_if_ssl_err(nc, n); + if (n == 0) nc->flags |= MG_F_CLOSE_IMMEDIATELY; + return n; +} + +int mg_ssl_if_write(struct mg_connection *nc, const void *data, size_t len) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + int n = SSL_write(ctx->ssl, data, len); + DBG(("%p %d -> %d", nc, (int) len, n)); + if (n <= 0) return mg_ssl_if_ssl_err(nc, n); + return n; +} + +void mg_ssl_if_conn_close_notify(struct mg_connection *nc) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + if (ctx == NULL) return; + SSL_shutdown(ctx->ssl); +} + +void mg_ssl_if_conn_free(struct mg_connection *nc) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + if (ctx == NULL) return; + nc->ssl_if_data = NULL; + if (ctx->ssl != NULL) SSL_free(ctx->ssl); + if (ctx->ssl_ctx != NULL && nc->listener == NULL) SSL_CTX_free(ctx->ssl_ctx); + mbuf_free(&ctx->psk); + memset(ctx, 0, sizeof(*ctx)); + MG_FREE(ctx); +} + +/* + * Cipher suite options used for TLS negotiation. + * https://wiki.mozilla.org/Security/Server_Side_TLS#Recommended_configurations + */ +static const char mg_s_cipher_list[] = +#if defined(MG_SSL_CRYPTO_MODERN) + "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:" + "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:" + "DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:" + "ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:" + "ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:" + "ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:" + "DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:" + "DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:" + "!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK" +#elif defined(MG_SSL_CRYPTO_OLD) + "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:" + "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:" + "DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:" + "ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:" + "ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:" + "ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:" + "DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:" + "DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:" + "ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:" + "AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:DES-CBC3-SHA:" + "HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:" + "!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA" +#else /* Default - intermediate. */ + "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:" + "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:" + "DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:" + "ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:" + "ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:" + "ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:" + "DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:" + "DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:" + "AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:" + "DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:" + "!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA" +#endif + ; + +/* + * Default DH params for PFS cipher negotiation. This is a 2048-bit group. + * Will be used if none are provided by the user in the certificate file. + */ +#if !MG_DISABLE_PFS && !defined(KR_VERSION) +static const char mg_s_default_dh_params[] = + "\ +-----BEGIN DH PARAMETERS-----\n\ +MIIBCAKCAQEAlvbgD/qh9znWIlGFcV0zdltD7rq8FeShIqIhkQ0C7hYFThrBvF2E\n\ +Z9bmgaP+sfQwGpVlv9mtaWjvERbu6mEG7JTkgmVUJrUt/wiRzwTaCXBqZkdUO8Tq\n\ ++E6VOEQAilstG90ikN1Tfo+K6+X68XkRUIlgawBTKuvKVwBhuvlqTGerOtnXWnrt\n\ +ym//hd3cd5PBYGBix0i7oR4xdghvfR2WLVu0LgdThTBb6XP7gLd19cQ1JuBtAajZ\n\ +wMuPn7qlUkEFDIkAZy59/Hue/H2Q2vU/JsvVhHWCQBL4F1ofEAt50il6ZxR1QfFK\n\ +9VGKDC4oOgm9DlxwwBoC2FjqmvQlqVV3kwIBAg==\n\ +-----END DH PARAMETERS-----\n"; +#endif + +static enum mg_ssl_if_result mg_use_ca_cert(SSL_CTX *ctx, const char *cert) { + if (cert == NULL || strcmp(cert, "*") == 0) { + return MG_SSL_OK; + } + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, 0); + return SSL_CTX_load_verify_locations(ctx, cert, NULL) == 1 ? MG_SSL_OK + : MG_SSL_ERROR; +} + +static enum mg_ssl_if_result mg_use_cert(SSL_CTX *ctx, const char *cert, + const char *key, + const char **err_msg) { + if (key == NULL) key = cert; + if (cert == NULL || cert[0] == '\0' || key == NULL || key[0] == '\0') { + return MG_SSL_OK; + } else if (SSL_CTX_use_certificate_file(ctx, cert, 1) == 0) { + MG_SET_PTRPTR(err_msg, "Invalid SSL cert"); + return MG_SSL_ERROR; + } else if (SSL_CTX_use_PrivateKey_file(ctx, key, 1) == 0) { + MG_SET_PTRPTR(err_msg, "Invalid SSL key"); + return MG_SSL_ERROR; + } else if (SSL_CTX_use_certificate_chain_file(ctx, cert) == 0) { + MG_SET_PTRPTR(err_msg, "Invalid CA bundle"); + return MG_SSL_ERROR; + } else { + SSL_CTX_set_mode(ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); +#if !MG_DISABLE_PFS && !defined(KR_VERSION) + BIO *bio = NULL; + DH *dh = NULL; + + /* Try to read DH parameters from the cert/key file. */ + bio = BIO_new_file(cert, "r"); + if (bio != NULL) { + dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); + BIO_free(bio); + } + /* + * If there are no DH params in the file, fall back to hard-coded ones. + * Not ideal, but better than nothing. + */ + if (dh == NULL) { + bio = BIO_new_mem_buf((void *) mg_s_default_dh_params, -1); + dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); + BIO_free(bio); + } + if (dh != NULL) { + SSL_CTX_set_tmp_dh(ctx, dh); + SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE); + DH_free(dh); + } +#if OPENSSL_VERSION_NUMBER > 0x10002000L + SSL_CTX_set_ecdh_auto(ctx, 1); +#endif +#endif + } + return MG_SSL_OK; +} + +static enum mg_ssl_if_result mg_set_cipher_list(SSL_CTX *ctx, const char *cl) { + return (SSL_CTX_set_cipher_list(ctx, cl ? cl : mg_s_cipher_list) == 1 + ? MG_SSL_OK + : MG_SSL_ERROR); +} + +#ifndef KR_VERSION +static unsigned int mg_ssl_if_ossl_psk_cb(SSL *ssl, const char *hint, + char *identity, + unsigned int max_identity_len, + unsigned char *psk, + unsigned int max_psk_len) { + struct mg_ssl_if_ctx *ctx = + (struct mg_ssl_if_ctx *) SSL_CTX_get_app_data(SSL_get_SSL_CTX(ssl)); + size_t key_len = ctx->psk.len - ctx->identity_len - 1; + DBG(("hint: '%s'", (hint ? hint : ""))); + if (ctx->identity_len + 1 > max_identity_len) { + DBG(("identity too long")); + return 0; + } + if (key_len > max_psk_len) { + DBG(("key too long")); + return 0; + } + memcpy(identity, ctx->psk.buf, ctx->identity_len + 1); + memcpy(psk, ctx->psk.buf + ctx->identity_len + 1, key_len); + (void) ssl; + return key_len; +} + +static enum mg_ssl_if_result mg_ssl_if_ossl_set_psk(struct mg_ssl_if_ctx *ctx, + const char *identity, + const char *key_str) { + unsigned char key[32]; + size_t key_len; + size_t i = 0; + if (identity == NULL && key_str == NULL) return MG_SSL_OK; + if (identity == NULL || key_str == NULL) return MG_SSL_ERROR; + key_len = strlen(key_str); + if (key_len != 32 && key_len != 64) return MG_SSL_ERROR; + memset(key, 0, sizeof(key)); + key_len = 0; + for (i = 0; key_str[i] != '\0'; i++) { + unsigned char c; + char hc = tolower((int) key_str[i]); + if (hc >= '0' && hc <= '9') { + c = hc - '0'; + } else if (hc >= 'a' && hc <= 'f') { + c = hc - 'a' + 0xa; + } else { + return MG_SSL_ERROR; + } + key_len = i / 2; + key[key_len] <<= 4; + key[key_len] |= c; + } + key_len++; + DBG(("identity = '%s', key = (%u)", identity, (unsigned int) key_len)); + ctx->identity_len = strlen(identity); + mbuf_append(&ctx->psk, identity, ctx->identity_len + 1); + mbuf_append(&ctx->psk, key, key_len); + SSL_CTX_set_psk_client_callback(ctx->ssl_ctx, mg_ssl_if_ossl_psk_cb); + SSL_CTX_set_app_data(ctx->ssl_ctx, ctx); + return MG_SSL_OK; +} +#else +static enum mg_ssl_if_result mg_ssl_if_ossl_set_psk(struct mg_ssl_if_ctx *ctx, + const char *identity, + const char *key_str) { + (void) ctx; + (void) identity; + (void) key_str; + /* Krypton does not support PSK. */ + return MG_SSL_ERROR; +} +#endif /* defined(KR_VERSION) */ + +const char *mg_set_ssl(struct mg_connection *nc, const char *cert, + const char *ca_cert) { + const char *err_msg = NULL; + struct mg_ssl_if_conn_params params; + memset(¶ms, 0, sizeof(params)); + params.cert = cert; + params.ca_cert = ca_cert; + if (mg_ssl_if_conn_init(nc, ¶ms, &err_msg) != MG_SSL_OK) { + return err_msg; + } + return NULL; +} + +#endif /* MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_OPENSSL */ diff --git a/src/mongoose-6.11/src/mg_uri.c b/src/mongoose-6.11/src/mg_uri.c new file mode 100644 index 0000000..9b6228b --- /dev/null +++ b/src/mongoose-6.11/src/mg_uri.c @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#include "mg_internal.h" +#include "mg_uri.h" + +/* + * scan string until encountering one of `seps`, keeping track of component + * boundaries in `res`. + * + * `p` will point to the char after the separator or it will be `end`. + */ +static void parse_uri_component(const char **p, const char *end, + const char *seps, struct mg_str *res) { + const char *q; + res->p = *p; + for (; *p < end; (*p)++) { + for (q = seps; *q != '\0'; q++) { + if (**p == *q) break; + } + if (*q != '\0') break; + } + res->len = (*p) - res->p; + if (*p < end) (*p)++; +} + +int mg_parse_uri(const struct mg_str uri, struct mg_str *scheme, + struct mg_str *user_info, struct mg_str *host, + unsigned int *port, struct mg_str *path, struct mg_str *query, + struct mg_str *fragment) { + struct mg_str rscheme = {0, 0}, ruser_info = {0, 0}, rhost = {0, 0}, + rpath = {0, 0}, rquery = {0, 0}, rfragment = {0, 0}; + unsigned int rport = 0; + enum { + P_START, + P_SCHEME_OR_PORT, + P_USER_INFO, + P_HOST, + P_PORT, + P_REST + } state = P_START; + + const char *p = uri.p, *end = p + uri.len; + while (p < end) { + switch (state) { + case P_START: + /* + * expecting on of: + * - `scheme://xxxx` + * - `xxxx:port` + * - `[a:b:c]:port` + * - `xxxx/path` + */ + if (*p == '[') { + state = P_HOST; + break; + } + for (; p < end; p++) { + if (*p == ':') { + state = P_SCHEME_OR_PORT; + break; + } else if (*p == '/') { + state = P_REST; + break; + } + } + if (state == P_START || state == P_REST) { + rhost.p = uri.p; + rhost.len = p - uri.p; + } + break; + case P_SCHEME_OR_PORT: + if (end - p >= 3 && strncmp(p, "://", 3) == 0) { + rscheme.p = uri.p; + rscheme.len = p - uri.p; + state = P_USER_INFO; + p += 3; + } else { + rhost.p = uri.p; + rhost.len = p - uri.p; + state = P_PORT; + } + break; + case P_USER_INFO: + ruser_info.p = p; + for (; p < end; p++) { + if (*p == '@' || *p == '[' || *p == '/') { + break; + } + } + if (p == end || *p == '/' || *p == '[') { + /* backtrack and parse as host */ + p = ruser_info.p; + } + ruser_info.len = p - ruser_info.p; + state = P_HOST; + break; + case P_HOST: + if (*p == '@') p++; + rhost.p = p; + if (*p == '[') { + int found = 0; + for (; !found && p < end; p++) { + found = (*p == ']'); + } + if (!found) return -1; + } else { + for (; p < end; p++) { + if (*p == ':' || *p == '/') break; + } + } + rhost.len = p - rhost.p; + if (p < end) { + if (*p == ':') { + state = P_PORT; + break; + } else if (*p == '/') { + state = P_REST; + break; + } + } + break; + case P_PORT: + p++; + for (; p < end; p++) { + if (*p == '/') { + state = P_REST; + break; + } + rport *= 10; + rport += *p - '0'; + } + break; + case P_REST: + /* `p` points to separator. `path` includes the separator */ + parse_uri_component(&p, end, "?#", &rpath); + if (p < end && *(p - 1) == '?') { + parse_uri_component(&p, end, "#", &rquery); + } + parse_uri_component(&p, end, "", &rfragment); + break; + } + } + + if (scheme != 0) *scheme = rscheme; + if (user_info != 0) *user_info = ruser_info; + if (host != 0) *host = rhost; + if (port != 0) *port = rport; + if (path != 0) *path = rpath; + if (query != 0) *query = rquery; + if (fragment != 0) *fragment = rfragment; + + return 0; +} + +/* Normalize the URI path. Remove/resolve "." and "..". */ +int mg_normalize_uri_path(const struct mg_str *in, struct mg_str *out) { + const char *s = in->p, *se = s + in->len; + char *cp = (char *) out->p, *d; + + if (in->len == 0 || *s != '/') { + out->len = 0; + return 0; + } + + d = cp; + + while (s < se) { + const char *next = s; + struct mg_str component; + parse_uri_component(&next, se, "/", &component); + if (mg_vcmp(&component, ".") == 0) { + /* Yum. */ + } else if (mg_vcmp(&component, "..") == 0) { + /* Backtrack to previous slash. */ + if (d > cp + 1 && *(d - 1) == '/') d--; + while (d > cp && *(d - 1) != '/') d--; + } else { + memmove(d, s, next - s); + d += next - s; + } + s = next; + } + if (d == cp) *d++ = '/'; + + out->p = cp; + out->len = d - cp; + return 1; +} + +int mg_assemble_uri(const struct mg_str *scheme, const struct mg_str *user_info, + const struct mg_str *host, unsigned int port, + const struct mg_str *path, const struct mg_str *query, + const struct mg_str *fragment, int normalize_path, + struct mg_str *uri) { + int result = -1; + struct mbuf out; + mbuf_init(&out, 0); + + if (scheme != NULL && scheme->len > 0) { + mbuf_append(&out, scheme->p, scheme->len); + mbuf_append(&out, "://", 3); + } + + if (user_info != NULL && user_info->len > 0) { + mbuf_append(&out, user_info->p, user_info->len); + mbuf_append(&out, "@", 1); + } + + if (host != NULL && host->len > 0) { + mbuf_append(&out, host->p, host->len); + } + + if (port != 0) { + char port_str[20]; + int port_str_len = sprintf(port_str, ":%u", port); + mbuf_append(&out, port_str, port_str_len); + } + + if (path != NULL && path->len > 0) { + if (normalize_path) { + struct mg_str npath = mg_strdup(*path); + if (npath.len != path->len) goto out; + if (!mg_normalize_uri_path(path, &npath)) { + free((void *) npath.p); + goto out; + } + mbuf_append(&out, npath.p, npath.len); + free((void *) npath.p); + } else { + mbuf_append(&out, path->p, path->len); + } + } else if (normalize_path) { + mbuf_append(&out, "/", 1); + } + + if (query != NULL && query->len > 0) { + mbuf_append(&out, "?", 1); + mbuf_append(&out, query->p, query->len); + } + + if (fragment != NULL && fragment->len > 0) { + mbuf_append(&out, "#", 1); + mbuf_append(&out, fragment->p, fragment->len); + } + + result = 0; + +out: + if (result == 0) { + uri->p = out.buf; + uri->len = out.len; + } else { + mbuf_free(&out); + uri->p = NULL; + uri->len = 0; + } + return result; +} diff --git a/src/mongoose-6.11/src/mg_uri.h b/src/mongoose-6.11/src/mg_uri.h new file mode 100644 index 0000000..f79e547 --- /dev/null +++ b/src/mongoose-6.11/src/mg_uri.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +/* + * === URI + */ + +#ifndef CS_MONGOOSE_SRC_URI_H_ +#define CS_MONGOOSE_SRC_URI_H_ + +#include "mg_net.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * Parses an URI and fills string chunks with locations of the respective + * uri components within the input uri string. NULL pointers will be + * ignored. + * + * General syntax: + * + * [scheme://[user_info@]]host[:port][/path][?query][#fragment] + * + * Example: + * + * foo.com:80 + * tcp://foo.com:1234 + * http://foo.com:80/bar?baz=1 + * https://user:pw@foo.com:443/blah + * + * `path` will include the leading slash. `query` won't include the leading `?`. + * `host` can contain embedded colons if surrounded by square brackets in order + * to support IPv6 literal addresses. + * + * + * Returns 0 on success, -1 on error. + */ +int mg_parse_uri(const struct mg_str uri, struct mg_str *scheme, + struct mg_str *user_info, struct mg_str *host, + unsigned int *port, struct mg_str *path, struct mg_str *query, + struct mg_str *fragment); + +/* + * Assemble URI from parts. Any of the inputs can be NULL or zero-length mg_str. + * + * If normalize_path is true, path is normalized by resolving relative refs. + * + * Result is a heap-allocated string (uri->p must be free()d after use). + * + * Returns 0 on success, -1 on error. + */ +int mg_assemble_uri(const struct mg_str *scheme, const struct mg_str *user_info, + const struct mg_str *host, unsigned int port, + const struct mg_str *path, const struct mg_str *query, + const struct mg_str *fragment, int normalize_path, + struct mg_str *uri); + +int mg_normalize_uri_path(const struct mg_str *in, struct mg_str *out); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* CS_MONGOOSE_SRC_URI_H_ */ diff --git a/src/mongoose-6.11/src/mg_util.c b/src/mongoose-6.11/src/mg_util.c new file mode 100644 index 0000000..485ba27 --- /dev/null +++ b/src/mongoose-6.11/src/mg_util.c @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#include "common/cs_base64.h" +#include "mg_internal.h" +#include "mg_util.h" + +/* For platforms with limited libc */ +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +const char *mg_skip(const char *s, const char *end, const char *delims, + struct mg_str *v) { + v->p = s; + while (s < end && strchr(delims, *(unsigned char *) s) == NULL) s++; + v->len = s - v->p; + while (s < end && strchr(delims, *(unsigned char *) s) != NULL) s++; + return s; +} + +#if MG_ENABLE_FILESYSTEM && !defined(MG_USER_FILE_FUNCTIONS) +int mg_stat(const char *path, cs_stat_t *st) { +#ifdef _WIN32 + wchar_t wpath[MG_MAX_PATH]; + to_wchar(path, wpath, ARRAY_SIZE(wpath)); + DBG(("[%ls] -> %d", wpath, _wstati64(wpath, st))); + return _wstati64(wpath, st); +#else + return stat(path, st); +#endif +} + +FILE *mg_fopen(const char *path, const char *mode) { +#ifdef _WIN32 + wchar_t wpath[MG_MAX_PATH], wmode[10]; + to_wchar(path, wpath, ARRAY_SIZE(wpath)); + to_wchar(mode, wmode, ARRAY_SIZE(wmode)); + return _wfopen(wpath, wmode); +#else + return fopen(path, mode); +#endif +} + +int mg_open(const char *path, int flag, int mode) { /* LCOV_EXCL_LINE */ +#if defined(_WIN32) && !defined(WINCE) + wchar_t wpath[MG_MAX_PATH]; + to_wchar(path, wpath, ARRAY_SIZE(wpath)); + return _wopen(wpath, flag, mode); +#else + return open(path, flag, mode); /* LCOV_EXCL_LINE */ +#endif +} + +size_t mg_fread(void *ptr, size_t size, size_t count, FILE *f) { + return fread(ptr, size, count, f); +} + +size_t mg_fwrite(const void *ptr, size_t size, size_t count, FILE *f) { + return fwrite(ptr, size, count, f); +} +#endif + +void mg_base64_encode(const unsigned char *src, int src_len, char *dst) { + cs_base64_encode(src, src_len, dst); +} + +int mg_base64_decode(const unsigned char *s, int len, char *dst) { + return cs_base64_decode(s, len, dst, NULL); +} + +#if MG_ENABLE_THREADS +void *mg_start_thread(void *(*f)(void *), void *p) { +#ifdef WINCE + return (void *) CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) f, p, 0, NULL); +#elif defined(_WIN32) + return (void *) _beginthread((void(__cdecl *) (void *) ) f, 0, p); +#else + pthread_t thread_id = (pthread_t) 0; + pthread_attr_t attr; + + (void) pthread_attr_init(&attr); + (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + +#if defined(MG_STACK_SIZE) && MG_STACK_SIZE > 1 + (void) pthread_attr_setstacksize(&attr, MG_STACK_SIZE); +#endif + + pthread_create(&thread_id, &attr, f, p); + pthread_attr_destroy(&attr); + + return (void *) thread_id; +#endif +} +#endif /* MG_ENABLE_THREADS */ + +/* Set close-on-exec bit for a given socket. */ +void mg_set_close_on_exec(sock_t sock) { +#if defined(_WIN32) && !defined(WINCE) + (void) SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0); +#elif defined(__unix__) + fcntl(sock, F_SETFD, FD_CLOEXEC); +#else + (void) sock; +#endif +} + +int mg_sock_addr_to_str(const union socket_address *sa, char *buf, size_t len, + int flags) { + int is_v6; + if (buf == NULL || len <= 0) return 0; + memset(buf, 0, len); +#if MG_ENABLE_IPV6 + is_v6 = sa->sa.sa_family == AF_INET6; +#else + is_v6 = 0; +#endif + if (flags & MG_SOCK_STRINGIFY_IP) { +#if MG_ENABLE_IPV6 + const void *addr = NULL; + char *start = buf; + socklen_t capacity = len; + if (!is_v6) { + addr = &sa->sin.sin_addr; + } else { + addr = (void *) &sa->sin6.sin6_addr; + if (flags & MG_SOCK_STRINGIFY_PORT) { + *buf = '['; + start++; + capacity--; + } + } + if (inet_ntop(sa->sa.sa_family, addr, start, capacity) == NULL) { + goto cleanup; + } +#elif defined(_WIN32) || MG_LWIP || (MG_NET_IF == MG_NET_IF_PIC32) + /* Only Windoze Vista (and newer) have inet_ntop() */ + char *addr_str = inet_ntoa(sa->sin.sin_addr); + if (addr_str != NULL) { + strncpy(buf, inet_ntoa(sa->sin.sin_addr), len - 1); + } else { + goto cleanup; + } +#else + if (inet_ntop(AF_INET, (void *) &sa->sin.sin_addr, buf, len - 1) == NULL) { + goto cleanup; + } +#endif + } + if (flags & MG_SOCK_STRINGIFY_PORT) { + int port = ntohs(sa->sin.sin_port); + if (flags & MG_SOCK_STRINGIFY_IP) { + int buf_len = strlen(buf); + snprintf(buf + buf_len, len - (buf_len + 1), "%s:%d", (is_v6 ? "]" : ""), + port); + } else { + snprintf(buf, len, "%d", port); + } + } + + return strlen(buf); + +cleanup: + *buf = '\0'; + return 0; +} + +int mg_conn_addr_to_str(struct mg_connection *nc, char *buf, size_t len, + int flags) { + union socket_address sa; + memset(&sa, 0, sizeof(sa)); + mg_if_get_conn_addr(nc, flags & MG_SOCK_STRINGIFY_REMOTE, &sa); + return mg_sock_addr_to_str(&sa, buf, len, flags); +} + +#if MG_ENABLE_HEXDUMP +static int mg_hexdump_n(const void *buf, int len, char *dst, int dst_len, + int offset) { + const unsigned char *p = (const unsigned char *) buf; + char ascii[17] = ""; + int i, idx, n = 0; + + for (i = 0; i < len; i++) { + idx = i % 16; + if (idx == 0) { + if (i > 0) n += snprintf(dst + n, MAX(dst_len - n, 0), " %s\n", ascii); + n += snprintf(dst + n, MAX(dst_len - n, 0), "%04x ", i + offset); + } + if (dst_len - n < 0) { + return n; + } + n += snprintf(dst + n, MAX(dst_len - n, 0), " %02x", p[i]); + ascii[idx] = p[i] < 0x20 || p[i] > 0x7e ? '.' : p[i]; + ascii[idx + 1] = '\0'; + } + + while (i++ % 16) n += snprintf(dst + n, MAX(dst_len - n, 0), "%s", " "); + n += snprintf(dst + n, MAX(dst_len - n, 0), " %s\n", ascii); + + return n; +} + +int mg_hexdump(const void *buf, int len, char *dst, int dst_len) { + return mg_hexdump_n(buf, len, dst, dst_len, 0); +} + +void mg_hexdumpf(FILE *fp, const void *buf, int len) { + char tmp[80]; + int offset = 0, n; + while (len > 0) { + n = (len < 16 ? len : 16); + mg_hexdump_n(((const char *) buf) + offset, n, tmp, sizeof(tmp), offset); + fputs(tmp, fp); + offset += n; + len -= n; + } +} + +void mg_hexdump_connection(struct mg_connection *nc, const char *path, + const void *buf, int num_bytes, int ev) { + FILE *fp = NULL; + char src[60], dst[60]; + const char *tag = NULL; + switch (ev) { + case MG_EV_RECV: + tag = "<-"; + break; + case MG_EV_SEND: + tag = "->"; + break; + case MG_EV_ACCEPT: + tag = " 0) { + mg_hexdumpf(fp, buf, num_bytes); + } + if (fp != stdout && fp != stderr) fclose(fp); +} +#endif + +int mg_is_big_endian(void) { + static const int n = 1; + /* TODO(mkm) use compiletime check with 4-byte char literal */ + return ((char *) &n)[0] == 0; +} + +DO_NOT_WARN_UNUSED MG_INTERNAL int mg_get_errno(void) { +#ifndef WINCE + return errno; +#else + /* TODO(alashkin): translate error codes? */ + return GetLastError(); +#endif +} + +void mg_mbuf_append_base64_putc(char ch, void *user_data) { + struct mbuf *mbuf = (struct mbuf *) user_data; + mbuf_append(mbuf, &ch, sizeof(ch)); +} + +void mg_mbuf_append_base64(struct mbuf *mbuf, const void *data, size_t len) { + struct cs_base64_ctx ctx; + cs_base64_init(&ctx, mg_mbuf_append_base64_putc, mbuf); + cs_base64_update(&ctx, (const char *) data, len); + cs_base64_finish(&ctx); +} + +void mg_basic_auth_header(const struct mg_str user, const struct mg_str pass, + struct mbuf *buf) { + const char *header_prefix = "Authorization: Basic "; + const char *header_suffix = "\r\n"; + + struct cs_base64_ctx ctx; + cs_base64_init(&ctx, mg_mbuf_append_base64_putc, buf); + + mbuf_append(buf, header_prefix, strlen(header_prefix)); + + cs_base64_update(&ctx, user.p, user.len); + if (pass.len > 0) { + cs_base64_update(&ctx, ":", 1); + cs_base64_update(&ctx, pass.p, pass.len); + } + cs_base64_finish(&ctx); + mbuf_append(buf, header_suffix, strlen(header_suffix)); +} + +struct mg_str mg_url_encode(const struct mg_str src) { + static const char *dont_escape = "._-$,;~()/"; + static const char *hex = "0123456789abcdef"; + size_t i = 0; + struct mbuf mb; + mbuf_init(&mb, src.len); + + for (i = 0; i < src.len; i++) { + const unsigned char c = *((const unsigned char *) src.p + i); + if (isalnum(c) || strchr(dont_escape, c) != NULL) { + mbuf_append(&mb, &c, 1); + } else { + mbuf_append(&mb, "%", 1); + mbuf_append(&mb, &hex[c >> 4], 1); + mbuf_append(&mb, &hex[c & 15], 1); + } + } + mbuf_append(&mb, "", 1); + mbuf_trim(&mb); + return mg_mk_str_n(mb.buf, mb.len - 1); +} diff --git a/src/mongoose-6.11/src/mg_util.h b/src/mongoose-6.11/src/mg_util.h new file mode 100644 index 0000000..e6f2e48 --- /dev/null +++ b/src/mongoose-6.11/src/mg_util.h @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +/* + * === Utility API + */ + +#ifndef CS_MONGOOSE_SRC_UTIL_H_ +#define CS_MONGOOSE_SRC_UTIL_H_ + +#include + +#include "mg_common.h" +#include "mg_net_if.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef MG_MAX_PATH +#ifdef PATH_MAX +#define MG_MAX_PATH PATH_MAX +#else +#define MG_MAX_PATH 256 +#endif +#endif + +/* + * Fetches substring from input string `s`, `end` into `v`. + * Skips initial delimiter characters. Records first non-delimiter character + * at the beginning of substring `v`. Then scans the rest of the string + * until a delimiter character or end-of-string is found. + * `delimiters` is a 0-terminated string containing delimiter characters. + * Either one of `delimiters` or `end_string` terminates the search. + * Returns an `s` pointer, advanced forward where parsing has stopped. + */ +const char *mg_skip(const char *s, const char *end_string, + const char *delimiters, struct mg_str *v); + +/* + * Decodes base64-encoded string `s`, `len` into the destination `dst`. + * The destination has to have enough space to hold the decoded buffer. + * Decoding stops either when all strings have been decoded or invalid an + * character appeared. + * Destination is '\0'-terminated. + * Returns the number of decoded characters. On success, that should be equal + * to `len`. On error (invalid character) the return value is smaller then + * `len`. + */ +int mg_base64_decode(const unsigned char *s, int len, char *dst); + +/* + * Base64-encode chunk of memory `src`, `src_len` into the destination `dst`. + * Destination has to have enough space to hold encoded buffer. + * Destination is '\0'-terminated. + */ +void mg_base64_encode(const unsigned char *src, int src_len, char *dst); + +#if MG_ENABLE_FILESYSTEM +/* + * Performs a 64-bit `stat()` call against a given file. + * + * `path` should be UTF8 encoded. + * + * Return value is the same as for `stat()` syscall. + */ +int mg_stat(const char *path, cs_stat_t *st); + +/* + * Opens the given file and returns a file stream. + * + * `path` and `mode` should be UTF8 encoded. + * + * Return value is the same as for the `fopen()` call. + */ +FILE *mg_fopen(const char *path, const char *mode); + +/* + * Opens the given file and returns a file stream. + * + * `path` should be UTF8 encoded. + * + * Return value is the same as for the `open()` syscall. + */ +int mg_open(const char *path, int flag, int mode); + +/* + * Reads data from the given file stream. + * + * Return value is a number of bytes readen. + */ +size_t mg_fread(void *ptr, size_t size, size_t count, FILE *f); + +/* + * Writes data to the given file stream. + * + * Return value is a number of bytes wtitten. + */ +size_t mg_fwrite(const void *ptr, size_t size, size_t count, FILE *f); + +#endif /* MG_ENABLE_FILESYSTEM */ + +#if MG_ENABLE_THREADS +/* + * Starts a new detached thread. + * Arguments and semantics are the same as pthead's `pthread_create()`. + * `thread_func` is a thread function, `thread_func_param` is a parameter + * that is passed to the thread function. + */ +void *mg_start_thread(void *(*thread_func)(void *), void *thread_func_param); +#endif + +void mg_set_close_on_exec(sock_t); + +#define MG_SOCK_STRINGIFY_IP 1 +#define MG_SOCK_STRINGIFY_PORT 2 +#define MG_SOCK_STRINGIFY_REMOTE 4 +/* + * Converts a connection's local or remote address into string. + * + * The `flags` parameter is a bit mask that controls the behaviour, + * see `MG_SOCK_STRINGIFY_*` definitions. + * + * - MG_SOCK_STRINGIFY_IP - print IP address + * - MG_SOCK_STRINGIFY_PORT - print port number + * - MG_SOCK_STRINGIFY_REMOTE - print remote peer's IP/port, not local address + * + * If both port number and IP address are printed, they are separated by `:`. + * If compiled with `-DMG_ENABLE_IPV6`, IPv6 addresses are supported. + * Return length of the stringified address. + */ +int mg_conn_addr_to_str(struct mg_connection *c, char *buf, size_t len, + int flags); +#if MG_NET_IF == MG_NET_IF_SOCKET +/* Legacy interface. */ +void mg_sock_to_str(sock_t sock, char *buf, size_t len, int flags); +#endif + +/* + * Convert the socket's address into string. + * + * `flags` is MG_SOCK_STRINGIFY_IP and/or MG_SOCK_STRINGIFY_PORT. + */ +int mg_sock_addr_to_str(const union socket_address *sa, char *buf, size_t len, + int flags); + +#if MG_ENABLE_HEXDUMP +/* + * Generates a human-readable hexdump of memory chunk. + * + * Takes a memory buffer `buf` of length `len` and creates a hex dump of that + * buffer in `dst`. The generated output is a-la hexdump(1). + * Returns the length of generated string, excluding terminating `\0`. If + * returned length is bigger than `dst_len`, the overflow bytes are discarded. + */ +int mg_hexdump(const void *buf, int len, char *dst, int dst_len); + +/* Same as mg_hexdump, but with output going to file instead of a buffer. */ +void mg_hexdumpf(FILE *fp, const void *buf, int len); + +/* + * Generates human-readable hexdump of the data sent or received by the + * connection. `path` is a file name where hexdump should be written. + * `num_bytes` is a number of bytes sent/received. `ev` is one of the `MG_*` + * events sent to an event handler. This function is supposed to be called from + * the event handler. + */ +void mg_hexdump_connection(struct mg_connection *nc, const char *path, + const void *buf, int num_bytes, int ev); +#endif + +/* + * Returns true if target platform is big endian. + */ +int mg_is_big_endian(void); + +/* + * Use with cs_base64_init/update/finish in order to write out base64 in chunks. + */ +void mg_mbuf_append_base64_putc(char ch, void *user_data); + +/* + * Encode `len` bytes starting at `data` as base64 and append them to an mbuf. + */ +void mg_mbuf_append_base64(struct mbuf *mbuf, const void *data, size_t len); + +/* + * Generate a Basic Auth header and appends it to buf. + * If pass is NULL, then user is expected to contain the credentials pair + * already encoded as `user:pass`. + */ +void mg_basic_auth_header(const struct mg_str user, const struct mg_str pass, + struct mbuf *buf); + +/* + * URL-escape the specified string. + * All non-printable characters are escaped, plus `._-$,;~()/`. + * Input need not be NUL-terminated, but the returned string is. + * Returned string is heap-allocated and must be free()'d. + */ +struct mg_str mg_url_encode(const struct mg_str src); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* CS_MONGOOSE_SRC_UTIL_H_ */ diff --git a/src/mpd_client.c b/src/mpd_client.c index 0acf93e..ff44d8c 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -36,21 +36,21 @@ #include "json_encode.h" /* forward declaration */ -static int mpd_notify_callback(struct mg_connection *c, enum mg_event ev); +static int mpd_notify_callback(struct mg_connection *c, const char *param); const char * mpd_cmd_strs[] = { MPD_CMDS(GEN_STR) }; -char * get_arg1 (char *p) { +char * get_arg1 (const char *p) { return strchr(p, ',') + 1; } -char * get_arg2 (char *p) { +char * get_arg2 (const char *p) { return get_arg1(get_arg1(p)); } -static inline enum mpd_cmd_ids get_cmd_id(char *cmd) +static inline enum mpd_cmd_ids get_cmd_id(const char *cmd) { for(int i = 0; i < sizeof(mpd_cmd_strs)/sizeof(mpd_cmd_strs[0]); i++) if(!strncmp(cmd, mpd_cmd_strs[i], strlen(mpd_cmd_strs[i]))) @@ -59,9 +59,9 @@ static inline enum mpd_cmd_ids get_cmd_id(char *cmd) return -1; } -int callback_mpd(struct mg_connection *c) +int callback_mpd(struct mg_connection *c,const struct mg_str msg) { - enum mpd_cmd_ids cmd_id = get_cmd_id(c->content); + enum mpd_cmd_ids cmd_id = get_cmd_id(msg.p); size_t n = 0; unsigned int uint_buf, uint_buf_2; int int_buf; @@ -70,10 +70,10 @@ int callback_mpd(struct mg_connection *c) char *p_charbuf2 = NULL; char *searchstr = NULL; - fprintf(stdout,"Got request: %s:%d\n",c->content,cmd_id); + fprintf(stdout,"Got request: %s:%d\n",msg.p,cmd_id); if(cmd_id == -1) - return MG_TRUE; + return 0; switch(cmd_id) { @@ -99,15 +99,15 @@ int callback_mpd(struct mg_connection *c) mpd_run_clear(mpd.conn); break; case MPD_API_RM_TRACK: - if(sscanf(c->content, "MPD_API_RM_TRACK,%u", &uint_buf)) + if(sscanf(msg.p, "MPD_API_RM_TRACK,%u", &uint_buf)) mpd_run_delete_id(mpd.conn, uint_buf); break; case MPD_API_RM_RANGE: - if(sscanf(c->content, "MPD_API_RM_RANGE,%u,%u", &uint_buf, &uint_buf_2)) + if(sscanf(msg.p, "MPD_API_RM_RANGE,%u,%u", &uint_buf, &uint_buf_2)) mpd_run_delete_range(mpd.conn, uint_buf, uint_buf_2); break; case MPD_API_MOVE_TRACK: - if (sscanf(c->content, "MPD_API_MOVE_TRACK,%u,%u", &uint_buf, &uint_buf_2) == 2) + if (sscanf(msg.p, "MPD_API_MOVE_TRACK,%u,%u", &uint_buf, &uint_buf_2) == 2) { uint_buf -= 1; uint_buf_2 -= 1; @@ -115,44 +115,43 @@ int callback_mpd(struct mg_connection *c) } break; case MPD_API_PLAY_TRACK: - if(sscanf(c->content, "MPD_API_PLAY_TRACK,%u", &uint_buf)) + if(sscanf(msg.p, "MPD_API_PLAY_TRACK,%u", &uint_buf)) mpd_run_play_id(mpd.conn, uint_buf); break; case MPD_API_TOGGLE_RANDOM: - if(sscanf(c->content, "MPD_API_TOGGLE_RANDOM,%u", &uint_buf)) + if(sscanf(msg.p, "MPD_API_TOGGLE_RANDOM,%u", &uint_buf)) mpd_run_random(mpd.conn, uint_buf); break; case MPD_API_TOGGLE_REPEAT: - if(sscanf(c->content, "MPD_API_TOGGLE_REPEAT,%u", &uint_buf)) + if(sscanf(msg.p, "MPD_API_TOGGLE_REPEAT,%u", &uint_buf)) mpd_run_repeat(mpd.conn, uint_buf); break; case MPD_API_TOGGLE_CONSUME: - if(sscanf(c->content, "MPD_API_TOGGLE_CONSUME,%u", &uint_buf)) + if(sscanf(msg.p, "MPD_API_TOGGLE_CONSUME,%u", &uint_buf)) mpd_run_consume(mpd.conn, uint_buf); break; case MPD_API_TOGGLE_SINGLE: - if(sscanf(c->content, "MPD_API_TOGGLE_SINGLE,%u", &uint_buf)) + if(sscanf(msg.p, "MPD_API_TOGGLE_SINGLE,%u", &uint_buf)) mpd_run_single(mpd.conn, uint_buf); break; case MPD_API_SET_CROSSFADE: - if(sscanf(c->content, "MPD_API_SET_CROSSFADE,%u", &uint_buf)) + if(sscanf(msg.p, "MPD_API_SET_CROSSFADE,%u", &uint_buf)) mpd_run_crossfade(mpd.conn, uint_buf); break; case MPD_API_SET_MIXRAMPDB: - if(sscanf(c->content, "MPD_API_SET_MIXRAMPDB,%f", &float_buf)) + if(sscanf(msg.p, "MPD_API_SET_MIXRAMPDB,%f", &float_buf)) mpd_run_mixrampdb(mpd.conn, float_buf); break; case MPD_API_SET_MIXRAMPDELAY: - if(sscanf(c->content, "MPD_API_SET_MIXRAMPDELAY,%f", &float_buf)) + if(sscanf(msg.p, "MPD_API_SET_MIXRAMPDELAY,%f", &float_buf)) mpd_run_mixrampdelay(mpd.conn, float_buf); break; case MPD_API_GET_OUTPUTS: mpd.buf_size = mpd_put_outputs(mpd.buf, 1); - c->callback_param = NULL; - mpd_notify_callback(c, MG_POLL); + mpd_notify_callback(c, NULL); break; case MPD_API_TOGGLE_OUTPUT: - if (sscanf(c->content, "MPD_API_TOGGLE_OUTPUT,%u,%u", &uint_buf, &uint_buf_2)) { + if (sscanf(msg.p, "MPD_API_TOGGLE_OUTPUT,%u,%u", &uint_buf, &uint_buf_2)) { if (uint_buf_2) mpd_run_enable_output(mpd.conn, uint_buf); else @@ -160,15 +159,15 @@ int callback_mpd(struct mg_connection *c) } break; case MPD_API_SET_VOLUME: - if(sscanf(c->content, "MPD_API_SET_VOLUME,%ud", &uint_buf) && uint_buf <= 100) + if(sscanf(msg.p, "MPD_API_SET_VOLUME,%ud", &uint_buf) && uint_buf <= 100) mpd_run_set_volume(mpd.conn, uint_buf); break; case MPD_API_SET_SEEK: - if(sscanf(c->content, "MPD_API_SET_SEEK,%u,%u", &uint_buf, &uint_buf_2)) + if(sscanf(msg.p, "MPD_API_SET_SEEK,%u,%u", &uint_buf, &uint_buf_2)) mpd_run_seek_id(mpd.conn, uint_buf, uint_buf_2); break; case MPD_API_GET_QUEUE: - if(sscanf(c->content, "MPD_API_GET_QUEUE,%u", &uint_buf)) + if(sscanf(msg.p, "MPD_API_GET_QUEUE,%u", &uint_buf)) n = mpd_put_queue(mpd.buf, uint_buf); break; case MPD_API_GET_CURRENT_SONG: @@ -176,7 +175,7 @@ int callback_mpd(struct mg_connection *c) break; case MPD_API_GET_ARTISTS: - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); if(strcmp(strtok(p_charbuf, ","), "MPD_API_GET_ARTISTS")) goto out_artist; uint_buf = strtoul(strtok(NULL, ","), NULL, 10); @@ -192,7 +191,7 @@ out_artist: free(p_charbuf); break; case MPD_API_GET_ARTISTALBUMS: - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); if(strcmp(strtok(p_charbuf, ","), "MPD_API_GET_ARTISTALBUMS")) goto out_artistalbum; uint_buf = strtoul(strtok(NULL, ","), NULL, 10); @@ -216,7 +215,7 @@ out_artistalbum: free(p_charbuf); break; case MPD_API_GET_ARTISTALBUMTITLES: - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); if(strcmp(strtok(p_charbuf, ","), "MPD_API_GET_ARTISTALBUMTITLES")) goto out_artistalbumtitle; @@ -231,7 +230,7 @@ out_artistalbumtitle: free(p_charbuf); break; case MPD_API_GET_PLAYLISTS: - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); if(strcmp(strtok(p_charbuf, ","), "MPD_API_GET_PLAYLISTS")) goto out_artist; uint_buf = strtoul(strtok(NULL, ","), NULL, 10); @@ -247,7 +246,7 @@ out_playlists: free(p_charbuf); break; case MPD_API_GET_BROWSE: - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); if(strcmp(strtok(p_charbuf, ","), "MPD_API_GET_BROWSE")) goto out_browse; @@ -274,7 +273,7 @@ out_browse: break; case MPD_API_ADD_TRACK: - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); if(strcmp(strtok(p_charbuf, ","), "MPD_API_ADD_TRACK")) goto out_add_track; @@ -282,13 +281,13 @@ out_browse: goto out_add_track; free(p_charbuf); - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); mpd_run_add(mpd.conn, get_arg1(p_charbuf)); out_add_track: free(p_charbuf); break; case MPD_API_ADD_PLAY_TRACK: - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); if(strcmp(strtok(p_charbuf, ","), "MPD_API_ADD_PLAY_TRACK")) goto out_play_track; @@ -296,7 +295,7 @@ out_add_track: goto out_play_track; free(p_charbuf); - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); int_buf = mpd_run_add_id(mpd.conn, get_arg1(p_charbuf)); if(int_buf != -1) mpd_run_play_id(mpd.conn, int_buf); @@ -304,7 +303,7 @@ out_play_track: free(p_charbuf); break; case MPD_API_ADD_PLAYLIST: - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); if(strcmp(strtok(p_charbuf, ","), "MPD_API_ADD_PLAYLIST")) goto out_playlist; @@ -312,13 +311,13 @@ out_play_track: goto out_playlist; free(p_charbuf); - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); mpd_run_load(mpd.conn, get_arg1(p_charbuf)); out_playlist: free(p_charbuf); break; case MPD_API_SAVE_QUEUE: - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); if(strcmp(strtok(p_charbuf, ","), "MPD_API_SAVE_QUEUE")) goto out_save_queue; @@ -326,13 +325,13 @@ out_playlist: goto out_save_queue; free(p_charbuf); - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); mpd_run_save(mpd.conn, get_arg1(p_charbuf)); out_save_queue: free(p_charbuf); break; case MPD_API_SEARCH_QUEUE: - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); if(strcmp(strtok(p_charbuf, ","), "MPD_API_SEARCH_QUEUE")) goto out_search_queue; if((token = strtok(NULL, ",")) == NULL) { @@ -356,7 +355,7 @@ out_search_queue: free(p_charbuf); break; case MPD_API_SEARCH_ADD: - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); if(strcmp(strtok(p_charbuf, ","), "MPD_API_SEARCH_ADD")) goto out_search_add; if((token = strtok(NULL, ",")) == NULL) { @@ -378,7 +377,7 @@ out_search_add: free(p_charbuf); break; case MPD_API_SEARCH: - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); if(strcmp(strtok(p_charbuf, ","), "MPD_API_SEARCH")) goto out_search; if((token = strtok(NULL, ",")) == NULL) { @@ -405,7 +404,7 @@ out_search: mpd_run_shuffle(mpd.conn); break; case MPD_API_SEND_MESSAGE: - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); if(strcmp(strtok(p_charbuf, ","), "MPD_API_SEND_MESSAGE")) goto out_send_message; @@ -413,7 +412,7 @@ out_search: goto out_send_message; free(p_charbuf); - p_charbuf = strdup(get_arg1(c->content)); + p_charbuf = strdup(get_arg1(msg.p)); if ( strtok(p_charbuf, ",") == NULL ) goto out_send_message; @@ -426,7 +425,7 @@ out_send_message: free(p_charbuf); break; case MPD_API_RM_PLAYLIST: - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); if(strcmp(strtok(p_charbuf, ","), "MPD_API_RM_PLAYLIST")) goto out_rm_playlist; @@ -434,7 +433,7 @@ out_send_message: goto out_rm_playlist; free(p_charbuf); - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); mpd_run_rm(mpd.conn, get_arg1(p_charbuf)); out_rm_playlist: free(p_charbuf); @@ -446,7 +445,7 @@ out_rm_playlist: n = mympd_get_stats(mpd.buf); break; case MPD_API_SET_REPLAYGAIN: - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); if(strcmp(strtok(p_charbuf, ","), "MPD_API_SET_REPLAYGAIN")) goto out_set_replaygain; @@ -454,7 +453,7 @@ out_rm_playlist: goto out_set_replaygain; free(p_charbuf); - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); mpd_send_command(mpd.conn, "replay_gain_mode", get_arg1(p_charbuf), NULL); struct mpd_pair *pair; while ((pair = mpd_recv_pair(mpd.conn)) != NULL) { @@ -477,34 +476,33 @@ out_set_replaygain: if(n > 0) { //fprintf(stdout,"Send response:\n %s\n",mpd.buf); - mg_websocket_write(c, 1, mpd.buf, n); + mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, strlen(mpd.buf)); } - return MG_TRUE; + return 0; } int mpd_close_handler(struct mg_connection *c) { /* Cleanup session data */ - if(c->connection_param) - free(c->connection_param); +// if(c->connection_param) +// free(c->connection_param); return 0; } -static int mpd_notify_callback(struct mg_connection *c, enum mg_event ev) { +static int mpd_notify_callback(struct mg_connection *c, const char *param) { size_t n; - if(!c->is_websocket) - return MG_TRUE; + if(is_websocket(c)) + return 0; - if(c->callback_param) + if(param) { /* error message? */ - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"error\",\"data\":\"%s\"}", - (const char *)c->callback_param); + n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"error\",\"data\":\"%s\"}",param); - mg_websocket_write(c, 1, mpd.buf, n); - return MG_TRUE; + mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, strlen(mpd.buf)); + return 0; } if(!c->connection_param) @@ -514,32 +512,32 @@ static int mpd_notify_callback(struct mg_connection *c, enum mg_event ev) { if(mpd.conn_state != MPD_CONNECTED) { n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"disconnected\"}"); - mg_websocket_write(c, 1, mpd.buf, n); + mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, strlen(mpd.buf)); } else { - mg_websocket_write(c, 1, mpd.buf, mpd.buf_size); - - if(s->song_id != mpd.song_id)// || s->queue_version != mpd.queue_version) + mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, strlen(mpd.buf)); + + if(s->song_id != mpd.song_id) { n = mpd_put_current_song(mpd.buf); - mg_websocket_write(c, 1, mpd.buf, n); + mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, strlen(mpd.buf)); s->song_id = mpd.song_id; } if(s->queue_version != mpd.queue_version) { n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"update_queue\"}"); - mg_websocket_write(c, 1, mpd.buf, n); + mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, strlen(mpd.buf)); s->queue_version = mpd.queue_version; } } - return MG_TRUE; + return 0; } -void mpd_poll(struct mg_server *s) +void mpd_poll(struct mg_mgr *s) { switch (mpd.conn_state) { case MPD_DISCONNECTED: @@ -556,8 +554,7 @@ void mpd_poll(struct mg_server *s) fprintf(stderr, "MPD connection: %s\n", mpd_connection_get_error_message(mpd.conn)); for (struct mg_connection *c = mg_next(s, NULL); c != NULL; c = mg_next(s, c)) { - c->callback_param = (void *)mpd_connection_get_error_message(mpd.conn); - mpd_notify_callback(c, MG_POLL); + mpd_notify_callback(c, mpd_connection_get_error_message(mpd.conn)); } mpd.conn_state = MPD_FAILURE; return; @@ -568,8 +565,7 @@ void mpd_poll(struct mg_server *s) fprintf(stderr, "MPD connection: %s\n", mpd_connection_get_error_message(mpd.conn)); for (struct mg_connection *c = mg_next(s, NULL); c != NULL; c = mg_next(s, c)) { - c->callback_param = (void *)mpd_connection_get_error_message(mpd.conn); - mpd_notify_callback(c, MG_POLL); + mpd_notify_callback(c, mpd_connection_get_error_message(mpd.conn)); } mpd.conn_state = MPD_FAILURE; return; @@ -582,8 +578,7 @@ void mpd_poll(struct mg_server *s) mpd.buf_size = mpd_put_outputs(mpd.buf, 1); for (struct mg_connection *c = mg_next(s, NULL); c != NULL; c = mg_next(s, c)) { - c->callback_param = NULL; - mpd_notify_callback(c, MG_POLL); + mpd_notify_callback(c, NULL); } break; @@ -602,14 +597,12 @@ void mpd_poll(struct mg_server *s) mpd.buf_size = mpd_put_state(mpd.buf, &mpd.song_id, &mpd.next_song_id, &mpd.queue_version); for (struct mg_connection *c = mg_next(s, NULL); c != NULL; c = mg_next(s, c)) { - c->callback_param = NULL; - mpd_notify_callback(c, MG_POLL); + mpd_notify_callback(c, NULL); } mpd.buf_size = mpd_put_outputs(mpd.buf, 0); for (struct mg_connection *c = mg_next(s, NULL); c != NULL; c = mg_next(s, c)) { - c->callback_param = NULL; - mpd_notify_callback(c, MG_POLL); + mpd_notify_callback(c, NULL); } break; } diff --git a/src/mpd_client.h b/src/mpd_client.h index 36ebd53..770dbbf 100644 --- a/src/mpd_client.h +++ b/src/mpd_client.h @@ -120,14 +120,18 @@ struct t_mpd { int streamport; char coverimage[40]; +static int is_websocket(const struct mg_connection *nc) { + return nc->flags & MG_F_IS_WEBSOCKET; +}; + struct t_mpd_client_session { int song_id; int next_song_id; unsigned queue_version; }; -void mpd_poll(struct mg_server *s); -int callback_mpd(struct mg_connection *c); +void mpd_poll(struct mg_mgr *s); +int callback_mpd(struct mg_connection *c,const struct mg_str msg); int mpd_close_handler(struct mg_connection *c); int mpd_put_state(char *buffer, int *current_song_id, int *next_song_id, unsigned *queue_version); int mpd_put_outputs(char *buffer, int putnames); diff --git a/src/mympd.c b/src/mympd.c index 8ddf691..935fdf4 100644 --- a/src/mympd.c +++ b/src/mympd.c @@ -31,68 +31,57 @@ #include #include "mongoose/mongoose.h" -//#include "http_server.h" #include "mpd_client.h" #include "config.h" extern char *optarg; +static sig_atomic_t s_signal_received = 0; +static struct mg_serve_http_opts s_http_server_opts; -int force_exit = 0; - -void bye() -{ - force_exit = 1; +static void signal_handler(int sig_num) { + signal(sig_num, signal_handler); // Reinstantiate signal handler + s_signal_received = sig_num; } - -static int is_websocket(const struct mg_connection *nc) { - return nc->flags & MG_F_IS_WEBSOCKET; -} - -static void server_callback(struct mg_connection *c, int ev, void *p) { -// int result = MG_EV_FALSE; - FILE *fp = NULL; - +static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { switch(ev) { - case MG_EV_CLOSE: - mpd_close_handler(c); - break; -// return MG_TRUE; - case MG_EV_HTTP_REQUEST: - if (is_websocket(c)) { - c->content[c->content_len] = '\0'; - if(c->content_len) - callback_mpd(c); - break; - } else + case MG_EV_CLOSE: { + mpd_close_handler(nc); break; - case MG_EV_AUTH: - // no auth for websockets since mobile safari does not support it - if ( (mpd.gpass == NULL) || (c->is_websocket) || ((mpd.local_port > 0) && (c->local_port == mpd.local_port)) ) - break; - else { - if ( (fp = fopen(mpd.gpass, "r")) != NULL ) { - mg_authorize_digest(c, fp); - fclose(fp); - } - } + } + case MG_EV_WEBSOCKET_FRAME: { + struct websocket_message *wm = (struct websocket_message *) ev_data; + struct mg_str d = {(char *) wm->data, wm->size}; + callback_mpd(nc, d); break; + } + case MG_EV_HTTP_REQUEST: { + mg_serve_http(nc, (struct http_message *) ev_data, s_http_server_opts); + break; + } } } int main(int argc, char **argv) { int n, option_index = 0; - struct mg_server *server = mg_create_server(NULL, server_callback); + + struct mg_mgr mgr; + struct mg_connection *nc; + unsigned int current_timer = 0, last_timer = 0; + + signal(SIGTERM, signal_handler); + signal(SIGINT, signal_handler); + setvbuf(stdout, NULL, _IOLBF, 0); + setvbuf(stderr, NULL, _IOLBF, 0); + + mg_mgr_init(&mgr, NULL); + char *run_as_user = NULL; char const *error_msg = NULL; char *webport = "8080"; - atexit(bye); - mg_set_option(server, "document_root", SRC_PATH); - - mg_set_option(server, "auth_domain", "mympd"); mpd.port = 6600; mpd.local_port = 0; mpd.gpass = NULL; @@ -171,42 +160,23 @@ int main(int argc, char **argv) return EXIT_FAILURE; } - if(error_msg) - { - fprintf(stderr, "Mongoose error: %s\n", error_msg); - return EXIT_FAILURE; - } } - error_msg = mg_set_option(server, "listening_port", webport); - if(error_msg) { - fprintf(stderr, "Mongoose error: %s\n", error_msg); - return EXIT_FAILURE; - } + nc = mg_bind(&mgr, webport, ev_handler); + mg_set_protocol_http_websocket(nc); + s_http_server_opts.document_root = SRC_PATH; - /* drop privilges at last to ensure proper port binding */ - if(run_as_user != NULL) { - error_msg = mg_set_option(server, "run_as_user", run_as_user); - free(run_as_user); - if(error_msg) - { - fprintf(stderr, "Mongoose error: %s\n", error_msg); - return EXIT_FAILURE; - } - } - - while (!force_exit) { - mg_poll_server(server, 200); + printf("Started on port %s\n", webport); + while (s_signal_received == 0) { + mg_mgr_poll(&mgr, 200); current_timer = time(NULL); if(current_timer - last_timer) { last_timer = current_timer; - mpd_poll(server); + mpd_poll(&mgr); } } - + mg_mgr_free(&mgr); mpd_disconnect(); - mg_destroy_server(&server); - return EXIT_SUCCESS; }