diff --git a/README.md b/README.md index 0967275fa0c351bd4e6aed25bf06480390b84f07..acc01972447fcba07a57097b8ff1a3cf7d5dbf41 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ 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 Mongoose Source Code here](https://www.cesanta.com/download.html) Looking for a complete IoT firmware solution? @@ -17,9 +17,9 @@ Check out [Mongoose OS](https://mongoose-os.com) - open source embedded operatin # Support - [Study mongoose example code](https://github.com/cesanta/mongoose/tree/master/examples) - [Read User Guide and API reference](https://docs.cesanta.com/mongoose) -- [Support Forum - ask your technical questions here] (http://forum.cesanta.com/index.php?p=/categories/mongoose) -- [Commercial licensing and support available] (https://www.cesanta.com/services-support) -- [Check our latest releases] (https://github.com/cesanta/mongoose/releases) +- [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 @@ -47,7 +47,7 @@ Check out [Mongoose OS](https://mongoose-os.com) - open source embedded operatin 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) +Commercial Projects: [Contact us for commercial license.](https://www.cesanta.com/contact.html) # Dashboard Example @@ -55,18 +55,14 @@ Mongoose is often used to implement device dashboards and real-time data exchange over Websocket. Here is a dashboard example that illustrates the functionality: - + -[Developing a new product? Contact us today to discuss how Mongoose can help. -](https://www.cesanta.com/contact) +[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://docs.cesanta.com/contributors_la.shtml) +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/products/binary) - -[](https://github.com/cesanta/mongoose) +- [Download pre-compiled Mongoose web server binary.](https://www.cesanta.com/binary.html) diff --git a/docs/c-api/coap.h/mg_coap_free_options.md b/docs/c-api/coap.h/mg_coap_free_options.md index 9dcc816f471af97be5a1e1721d341ff3f202c864..d429cf49edc124368edf89041941001b1c89e249 100644 --- a/docs/c-api/coap.h/mg_coap_free_options.md +++ b/docs/c-api/coap.h/mg_coap_free_options.md @@ -7,5 +7,5 @@ signature: | --- Frees the memory allocated for options. -If the cm paramater doesn't contain any option it does nothing. +If the cm parameter doesn't contain any option it does nothing. diff --git a/docs/c-api/dns.h/mg_dns_encode_record.md b/docs/c-api/dns.h/mg_dns_encode_record.md index c52cf26f352974afec84aa7f500f73bbc46570c7..7c9f2cf2bf9316c8433bf88917e7fd5babc7ce6d 100644 --- a/docs/c-api/dns.h/mg_dns_encode_record.md +++ b/docs/c-api/dns.h/mg_dns_encode_record.md @@ -21,5 +21,5 @@ 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 appened or -1 in case of error. +Returns the number of bytes appended or -1 in case of error. diff --git a/docs/c-api/http.h/intro.md b/docs/c-api/http.h/intro.md index 8ef19dd1facab0e43aaa7ed35718c7c762680ca6..8459b601893357ce97cef36745d279fd2c41fbaf 100644 --- a/docs/c-api/http.h/intro.md +++ b/docs/c-api/http.h/intro.md @@ -11,6 +11,7 @@ items: - { name: mg_send_websocket_handshake.md } - { name: mg_send_websocket_handshake2.md } - { name: mg_send_websocket_handshake3.md } + - { name: mg_send_websocket_handshake3v.md } - { name: mg_set_protocol_http_websocket.md } - { name: mg_url_decode.md } - { name: struct_http_message.md } diff --git a/docs/c-api/http.h/mg_connect_ws.md b/docs/c-api/http.h/mg_connect_ws.md index b0f38d74895f3a5c6222457a6189a10ac345c160..7a5b517d34f0064642b5abe094d5f526906a0f63 100644 --- a/docs/c-api/http.h/mg_connect_ws.md +++ b/docs/c-api/http.h/mg_connect_ws.md @@ -4,9 +4,8 @@ decl_name: "mg_connect_ws" symbol_kind: "func" signature: | struct mg_connection *mg_connect_ws(struct mg_mgr *mgr, - mg_event_handler_t event_handler, - const char *url, const char *protocol, - const char *extra_headers); + MG_CB(mg_event_handler_t event_handler, + void *user_data); --- Helper function that creates an outbound WebSocket connection. diff --git a/docs/c-api/http.h/mg_connect_ws_opt.md b/docs/c-api/http.h/mg_connect_ws_opt.md index 57956ddbd30fbb22ef7582589574f9b40240ce8a..e2203cff69636a5e9c837b87c74f719eb1df899c 100644 --- a/docs/c-api/http.h/mg_connect_ws_opt.md +++ b/docs/c-api/http.h/mg_connect_ws_opt.md @@ -3,11 +3,8 @@ title: "mg_connect_ws_opt()" decl_name: "mg_connect_ws_opt" symbol_kind: "func" signature: | - struct mg_connection *mg_connect_ws_opt(struct mg_mgr *mgr, - mg_event_handler_t ev_handler, - struct mg_connect_opts opts, - const char *url, const char *protocol, - const char *extra_headers); + struct mg_connection *mg_connect_ws_opt( + struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data); --- Helper function that creates an outbound WebSocket connection diff --git a/docs/c-api/http.h/mg_send_websocket_framev.md b/docs/c-api/http.h/mg_send_websocket_framev.md index 005a7916527a68b4d1d592c8cc42cd7e76b4bb7c..06e094601efd1e9887613cb9aebd28e2a036978a 100644 --- a/docs/c-api/http.h/mg_send_websocket_framev.md +++ b/docs/c-api/http.h/mg_send_websocket_framev.md @@ -9,5 +9,6 @@ signature: | Sends multiple websocket frames. -Like `mg_send_websocket_frame()`, but composes a frame from multiple buffers. +Like `mg_send_websocket_frame()`, but composes a frame from multiple +*buffers. diff --git a/docs/c-api/http.h/mg_send_websocket_handshake3v.md b/docs/c-api/http.h/mg_send_websocket_handshake3v.md new file mode 100644 index 0000000000000000000000000000000000000000..acf8c1e6777ecae4c87c85886462cd7cc0023bbd --- /dev/null +++ b/docs/c-api/http.h/mg_send_websocket_handshake3v.md @@ -0,0 +1,17 @@ +--- +title: "mg_send_websocket_handshake3v()" +decl_name: "mg_send_websocket_handshake3v" +symbol_kind: "func" +signature: | + 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); +--- + +Same as mg_send_websocket_handshake3 but with strings not necessarily +NUL-temrinated + diff --git a/docs/c-api/http.h/mg_url_decode.md b/docs/c-api/http.h/mg_url_decode.md index 2343adfa2c434f207dc10e0b29802ecb25ce42dd..454acd03ce01a6eb62aff59eccc6a343d56be52e 100644 --- a/docs/c-api/http.h/mg_url_decode.md +++ b/docs/c-api/http.h/mg_url_decode.md @@ -13,6 +13,7 @@ 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, +then the source string is partially decoded and `-1` is returned. +*Otherwise, a length of the decoded string is returned, not counting final NUL. diff --git a/docs/c-api/http.h/struct_http_message.md b/docs/c-api/http.h/struct_http_message.md index 60eae685f96f1d2310246a52da29e66a31f09e4f..9380496d453d060d3dba6c430135ca30afa431d6 100644 --- a/docs/c-api/http.h/struct_http_message.md +++ b/docs/c-api/http.h/struct_http_message.md @@ -5,6 +5,7 @@ symbol_kind: "struct" signature: | 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" */ @@ -28,9 +29,6 @@ signature: | /* Headers */ struct mg_str header_names[MG_MAX_HTTP_HEADERS]; struct mg_str header_values[MG_MAX_HTTP_HEADERS]; - - /* Message body */ - struct mg_str body; /* Zero-length for requests with no body */ }; --- diff --git a/docs/c-api/http_client.h/mg_connect_http.md b/docs/c-api/http_client.h/mg_connect_http.md index fcf5f04687585192eade1234648ae79dd0217480..4664fcd9040d75cf8c167b3135e6c87417c68660 100644 --- a/docs/c-api/http_client.h/mg_connect_http.md +++ b/docs/c-api/http_client.h/mg_connect_http.md @@ -3,11 +3,9 @@ title: "mg_connect_http()" decl_name: "mg_connect_http" symbol_kind: "func" signature: | - struct mg_connection *mg_connect_http(struct mg_mgr *mgr, - mg_event_handler_t event_handler, - const char *url, - const char *extra_headers, - const char *post_data); + struct mg_connection *mg_connect_http( + struct mg_mgr *mgr, + MG_CB(mg_event_handler_t event_handler, void *user_data); --- Helper function that creates an outbound HTTP connection. diff --git a/docs/c-api/http_client.h/mg_connect_http_opt.md b/docs/c-api/http_client.h/mg_connect_http_opt.md index 2454cb10ee06712997b4a9e6c088d12ff7b5e3f4..29df1d753db7e0ac5e9565a7266b2444379c8d34 100644 --- a/docs/c-api/http_client.h/mg_connect_http_opt.md +++ b/docs/c-api/http_client.h/mg_connect_http_opt.md @@ -3,12 +3,8 @@ title: "mg_connect_http_opt()" decl_name: "mg_connect_http_opt" symbol_kind: "func" signature: | - struct mg_connection *mg_connect_http_opt(struct mg_mgr *mgr, - mg_event_handler_t ev_handler, - struct mg_connect_opts opts, - const char *url, - const char *extra_headers, - const char *post_data); + struct mg_connection *mg_connect_http_opt( + struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data); --- Helper function that creates an outbound HTTP connection. diff --git a/docs/c-api/http_server.h/intro.md b/docs/c-api/http_server.h/intro.md index 98402e96a7685670bf6e2dad0c775ae1eab73e1e..0fc609dbc0717b3381f4efdfeefa128fd80eb6c4 100644 --- a/docs/c-api/http_server.h/intro.md +++ b/docs/c-api/http_server.h/intro.md @@ -3,6 +3,7 @@ title: "Server API reference" symbol_kind: "intro" decl_name: "http_server.h" items: + - { name: mg_check_digest_auth.md } - { name: mg_file_upload_handler.md } - { name: mg_get_http_basic_auth.md } - { name: mg_get_http_header.md } diff --git a/docs/c-api/http_server.h/mg_check_digest_auth.md b/docs/c-api/http_server.h/mg_check_digest_auth.md new file mode 100644 index 0000000000000000000000000000000000000000..189f0c4a902f4228ee85910395e5092e240406ec --- /dev/null +++ b/docs/c-api/http_server.h/mg_check_digest_auth.md @@ -0,0 +1,17 @@ +--- +title: "mg_check_digest_auth()" +decl_name: "mg_check_digest_auth" +symbol_kind: "func" +signature: | + 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); +--- + +Authenticates given response params against an opened password file. +Returns 1 if authenticated, 0 otherwise. + +It's used by mg_http_check_digest_auth(). + diff --git a/docs/c-api/http_server.h/mg_file_upload_handler.md b/docs/c-api/http_server.h/mg_file_upload_handler.md index 444c106ff2b4a6713099ff455e961575da006f6b..a2c9e016f6ea34beb257c9dacfb271cfb813bb4d 100644 --- a/docs/c-api/http_server.h/mg_file_upload_handler.md +++ b/docs/c-api/http_server.h/mg_file_upload_handler.md @@ -4,7 +4,8 @@ decl_name: "mg_file_upload_handler" symbol_kind: "func" signature: | void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data, - mg_fu_fname_fn local_name_fn); + mg_fu_fname_fn local_name_fn + MG_UD_ARG(void *user_data); --- File upload handler. diff --git a/docs/c-api/http_server.h/mg_get_http_var.md b/docs/c-api/http_server.h/mg_get_http_var.md index 55ee3e6fdc0eb5b35a1477866e469df1f1033577..8ca1d915d33325b4f597224bf8c524b6ddcb262f 100644 --- a/docs/c-api/http_server.h/mg_get_http_var.md +++ b/docs/c-api/http_server.h/mg_get_http_var.md @@ -12,5 +12,6 @@ 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, `-1` is returned. +url-encoded buffer. If destination is too small or an error occured, +negative number is returned. diff --git a/docs/c-api/http_server.h/mg_http_send_redirect.md b/docs/c-api/http_server.h/mg_http_send_redirect.md index 8d7d3b2e2c21c5c91776d810c2f1f8a47f7f6001..00d704e0e6ff58c4f351145e8b011ce1980f214c 100644 --- a/docs/c-api/http_server.h/mg_http_send_redirect.md +++ b/docs/c-api/http_server.h/mg_http_send_redirect.md @@ -12,7 +12,7 @@ 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 reponse line. `extra_headers` must NOT end end with new line. +after the response line. `extra_headers` must NOT end end with new line. Example: diff --git a/docs/c-api/http_server.h/mg_register_http_endpoint.md b/docs/c-api/http_server.h/mg_register_http_endpoint.md index 752eaae6b02ef138c3db4b8f794928abbd169705..394bbb51e1b7d839b97346faf5181d2b07fd4cf4 100644 --- a/docs/c-api/http_server.h/mg_register_http_endpoint.md +++ b/docs/c-api/http_server.h/mg_register_http_endpoint.md @@ -4,7 +4,8 @@ decl_name: "mg_register_http_endpoint" symbol_kind: "func" signature: | void mg_register_http_endpoint(struct mg_connection *nc, const char *uri_path, - mg_event_handler_t handler); + MG_CB(mg_event_handler_t handler, + void *user_data); --- Registers a callback for a specified http endpoint @@ -20,7 +21,7 @@ static void handle_hello1(struct mg_connection *nc, int ev, void *ev_data) { nc->flags |= MG_F_SEND_AND_CLOSE; } -static void handle_hello1(struct mg_connection *nc, int ev, void *ev_data) { +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; diff --git a/docs/c-api/http_server.h/mg_send_http_chunk.md b/docs/c-api/http_server.h/mg_send_http_chunk.md index 2ce64a1a92664c1dd10a88ec84256ae2eb3c3bbc..6a2a4d838fd7bba7fb5403abf578f92b051fea12 100644 --- a/docs/c-api/http_server.h/mg_send_http_chunk.md +++ b/docs/c-api/http_server.h/mg_send_http_chunk.md @@ -9,7 +9,7 @@ signature: | 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)` whill append the `3\r\nfoo\r\n` string +`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 diff --git a/docs/c-api/http_server.h/mg_send_response_line.md b/docs/c-api/http_server.h/mg_send_response_line.md index 4bd22639a2649e2310a2fd387d3b272c668c07df..5daa74fe8fe23e3b4c9e5fda94aeaf67d0406c3b 100644 --- a/docs/c-api/http_server.h/mg_send_response_line.md +++ b/docs/c-api/http_server.h/mg_send_response_line.md @@ -9,7 +9,7 @@ signature: | Sends the response status line. If `extra_headers` is not NULL, then `extra_headers` are also sent -after the reponse line. `extra_headers` must NOT end end with new line. +after the response line. `extra_headers` must NOT end end with new line. Example: mg_send_response_line(nc, 200, "Access-Control-Allow-Origin: *"); diff --git a/docs/c-api/mqtt.h/intro.md b/docs/c-api/mqtt.h/intro.md index 5ae849aa34821e14e1d74073b164380149594c87..bb5d31b43a2d96dcf4aa7f8e51b5ec56566acdb8 100644 --- a/docs/c-api/mqtt.h/intro.md +++ b/docs/c-api/mqtt.h/intro.md @@ -5,6 +5,7 @@ decl_name: "mqtt.h" items: - { name: mg_mqtt_connack.md } - { name: mg_mqtt_disconnect.md } + - { name: mg_mqtt_match_topic_expression.md } - { name: mg_mqtt_next_subscribe_topic.md } - { name: mg_mqtt_ping.md } - { name: mg_mqtt_pong.md } @@ -17,6 +18,7 @@ items: - { name: mg_mqtt_subscribe.md } - { name: mg_mqtt_unsuback.md } - { name: mg_mqtt_unsubscribe.md } + - { name: mg_mqtt_vmatch_topic_expression.md } - { name: mg_send_mqtt_handshake.md } - { name: mg_send_mqtt_handshake_opt.md } - { name: mg_set_protocol_mqtt.md } diff --git a/docs/c-api/mqtt.h/mg_mqtt_match_topic_expression.md b/docs/c-api/mqtt.h/mg_mqtt_match_topic_expression.md new file mode 100644 index 0000000000000000000000000000000000000000..81575b47f5dc597371262e99fa226cbbf1621945 --- /dev/null +++ b/docs/c-api/mqtt.h/mg_mqtt_match_topic_expression.md @@ -0,0 +1,12 @@ +--- +title: "mg_mqtt_match_topic_expression()" +decl_name: "mg_mqtt_match_topic_expression" +symbol_kind: "func" +signature: | + int mg_mqtt_match_topic_expression(struct mg_str exp, struct mg_str topic); +--- + +Matches a topic against a topic expression + +Returns 1 if it matches; 0 otherwise. + diff --git a/docs/c-api/mqtt.h/mg_mqtt_vmatch_topic_expression.md b/docs/c-api/mqtt.h/mg_mqtt_vmatch_topic_expression.md new file mode 100644 index 0000000000000000000000000000000000000000..4b0a423157a1fd60d7e8e2cde9b1b28e9036d104 --- /dev/null +++ b/docs/c-api/mqtt.h/mg_mqtt_vmatch_topic_expression.md @@ -0,0 +1,11 @@ +--- +title: "mg_mqtt_vmatch_topic_expression()" +decl_name: "mg_mqtt_vmatch_topic_expression" +symbol_kind: "func" +signature: | + int mg_mqtt_vmatch_topic_expression(const char *exp, struct mg_str topic); +--- + +Same as `mg_mqtt_match_topic_expression()`, but takes `exp` as a +NULL-terminated string. + diff --git a/docs/c-api/mqtt.h/struct_mg_mqtt_proto_data.md b/docs/c-api/mqtt.h/struct_mg_mqtt_proto_data.md index 8ae54fcfcb96499a569b7ff25e76cbf06737399d..f095589d2c471c6772e03b6808ea553754f84891 100644 --- a/docs/c-api/mqtt.h/struct_mg_mqtt_proto_data.md +++ b/docs/c-api/mqtt.h/struct_mg_mqtt_proto_data.md @@ -5,6 +5,7 @@ symbol_kind: "struct" signature: | struct mg_mqtt_proto_data { uint16_t keep_alive; + double last_control_time; }; --- diff --git a/docs/c-api/net.h/intro.md b/docs/c-api/net.h/intro.md index fc0bb22f143f26a455f55d33164ed695a8112714..1534ceb03ea7ef2fcc577fc911494dfda89ed3f6 100644 --- a/docs/c-api/net.h/intro.md +++ b/docs/c-api/net.h/intro.md @@ -11,7 +11,6 @@ items: - { name: mg_check_ip_acl.md } - { name: mg_connect.md } - { name: mg_connect_opt.md } - - { name: mg_enable_javascript.md } - { name: mg_mgr_free.md } - { name: mg_mgr_init.md } - { name: mg_mgr_init_opt.md } diff --git a/docs/c-api/net.h/mg_add_sock.md b/docs/c-api/net.h/mg_add_sock.md index 511ae84100b786819ba93153a00e67e9b8de98ef..e63ce2e48212c987cca7ae576bd97ca769b42c58 100644 --- a/docs/c-api/net.h/mg_add_sock.md +++ b/docs/c-api/net.h/mg_add_sock.md @@ -3,7 +3,9 @@ title: "mg_add_sock()" decl_name: "mg_add_sock" symbol_kind: "func" signature: | - struct mg_connection *mg_add_sock(struct mg_mgr *, sock_t, mg_event_handler_t); + 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 diff --git a/docs/c-api/net.h/mg_add_sock_opt.md b/docs/c-api/net.h/mg_add_sock_opt.md index 678a91cbb8fdfa55a295f88b11d84f6ef4ce6ac5..de2a22e9ffd766b45206cf28f707a63d9b17160c 100644 --- a/docs/c-api/net.h/mg_add_sock_opt.md +++ b/docs/c-api/net.h/mg_add_sock_opt.md @@ -3,9 +3,9 @@ title: "mg_add_sock_opt()" decl_name: "mg_add_sock_opt" symbol_kind: "func" signature: | - struct mg_connection *mg_add_sock_opt(struct mg_mgr *, sock_t, - mg_event_handler_t, - struct mg_add_sock_opts); + struct mg_connection *mg_add_sock_opt(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 diff --git a/docs/c-api/net.h/mg_bind.md b/docs/c-api/net.h/mg_bind.md index 800bba9547f2bb7d7341a64d9fd455bbeacb6f5b..9507d51c585e828db6fee531ac9c9cf0ed326be5 100644 --- a/docs/c-api/net.h/mg_bind.md +++ b/docs/c-api/net.h/mg_bind.md @@ -3,8 +3,9 @@ title: "mg_bind()" decl_name: "mg_bind" symbol_kind: "func" signature: | - struct mg_connection *mg_bind(struct mg_mgr *, const char *, - mg_event_handler_t); + 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. diff --git a/docs/c-api/net.h/mg_bind_opt.md b/docs/c-api/net.h/mg_bind_opt.md index a9cbf953e0d248bcb3cbc62dc6c376c9e9e6f884..823dd0c0aef50fb18bcc8ab48673c1d376e3f2ba 100644 --- a/docs/c-api/net.h/mg_bind_opt.md +++ b/docs/c-api/net.h/mg_bind_opt.md @@ -4,8 +4,8 @@ decl_name: "mg_bind_opt" symbol_kind: "func" signature: | struct mg_connection *mg_bind_opt(struct mg_mgr *mgr, const char *address, - mg_event_handler_t handler, - struct mg_bind_opts opts); + MG_CB(mg_event_handler_t handler, + void *user_data); --- Creates a listening connection. @@ -15,7 +15,7 @@ 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` paramer has following +prefix, e.g. `udp://:8000`. To summarize, `address` parameter has following format: `[PROTO://][IP_ADDRESS]:PORT`, where `PROTO` could be `tcp` or `udp`. diff --git a/docs/c-api/net.h/mg_broadcast.md b/docs/c-api/net.h/mg_broadcast.md index 4eda7f3cd833e4049017d0524e99dee84444a361..cef0b379ceacac570d2167b475d1a5d81957af7c 100644 --- a/docs/c-api/net.h/mg_broadcast.md +++ b/docs/c-api/net.h/mg_broadcast.md @@ -3,7 +3,8 @@ title: "mg_broadcast()" decl_name: "mg_broadcast" symbol_kind: "func" signature: | - void mg_broadcast(struct mg_mgr *, mg_event_handler_t func, void *, size_t); + void mg_broadcast(struct mg_mgr *mgr, mg_event_handler_t cb, void *data, + size_t len); --- Passes a message of a given length to all connections. diff --git a/docs/c-api/net.h/mg_check_ip_acl.md b/docs/c-api/net.h/mg_check_ip_acl.md index e3087e2b2f2c060dfc29f5be4e84ec0ba03b5fe3..83c2c7ba22837595ea5c2d534ddf764afa314b41 100644 --- a/docs/c-api/net.h/mg_check_ip_acl.md +++ b/docs/c-api/net.h/mg_check_ip_acl.md @@ -18,7 +18,7 @@ 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 acccesses, only allow 192.168/16 subnet +`-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]. diff --git a/docs/c-api/net.h/mg_connect.md b/docs/c-api/net.h/mg_connect.md index bbacd8f4c9faac91271151c4a9a0bfbf859733f0..788be84e663c6f86bea11ea248d7fa46098aeed4 100644 --- a/docs/c-api/net.h/mg_connect.md +++ b/docs/c-api/net.h/mg_connect.md @@ -4,7 +4,8 @@ decl_name: "mg_connect" symbol_kind: "func" signature: | struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *address, - mg_event_handler_t handler); + MG_CB(mg_event_handler_t handler, + void *user_data); --- Connects to a remote host. diff --git a/docs/c-api/net.h/mg_connect_opt.md b/docs/c-api/net.h/mg_connect_opt.md index de7e71392fdc279bea3001376481b2faf2863a30..3ae818a4436731c7d48eabc38f3a4c136cd5870e 100644 --- a/docs/c-api/net.h/mg_connect_opt.md +++ b/docs/c-api/net.h/mg_connect_opt.md @@ -4,8 +4,8 @@ decl_name: "mg_connect_opt" symbol_kind: "func" signature: | struct mg_connection *mg_connect_opt(struct mg_mgr *mgr, const char *address, - mg_event_handler_t handler, - struct mg_connect_opts opts); + MG_CB(mg_event_handler_t handler, + void *user_data); --- Connects to a remote host. diff --git a/docs/c-api/net.h/mg_enable_javascript.md b/docs/c-api/net.h/mg_enable_javascript.md deleted file mode 100644 index 763151e02bc46ff18cc856cc897c5f5d4b844601..0000000000000000000000000000000000000000 --- a/docs/c-api/net.h/mg_enable_javascript.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: "mg_enable_javascript()" -decl_name: "mg_enable_javascript" -symbol_kind: "func" -signature: | - enum v7_err mg_enable_javascript(struct mg_mgr *m, struct v7 *v7, - const char *init_js_file_name); ---- - -Enables server-side JavaScript scripting. -Requires a `-DMG_ENABLE_JAVASCRIPT` compilation flag and V7 engine sources. -V7 instance must not be destroyed during manager's lifetime. -Returns a V7 error. - diff --git a/docs/c-api/net.h/mg_event_handler_t.md b/docs/c-api/net.h/mg_event_handler_t.md index 191805c8f74dba67a81ae4881d8b05b2fea0ca02..5a4822246d2475a84ac186e4554681b26025a82d 100644 --- a/docs/c-api/net.h/mg_event_handler_t.md +++ b/docs/c-api/net.h/mg_event_handler_t.md @@ -4,7 +4,7 @@ decl_name: "mg_event_handler_t" symbol_kind: "typedef" signature: | typedef void (*mg_event_handler_t)(struct mg_connection *nc, int ev, - void *ev_data); + void *ev_data MG_UD_ARG(void *user_data)); --- Callback function (event handler) prototype. Must be defined by the user. diff --git a/docs/c-api/net.h/mg_next.md b/docs/c-api/net.h/mg_next.md index fcd5e1d17b92348206810fcbaba11c444dbdaa92..423af6fdbc591de9b59c4806ecaf56055bdb06dd 100644 --- a/docs/c-api/net.h/mg_next.md +++ b/docs/c-api/net.h/mg_next.md @@ -3,7 +3,7 @@ title: "mg_next()" decl_name: "mg_next" symbol_kind: "func" signature: | - struct mg_connection *mg_next(struct mg_mgr *, struct mg_connection *); + struct mg_connection *mg_next(struct mg_mgr *mgr, struct mg_connection *c); --- Iterates over all active connections. diff --git a/docs/c-api/net.h/struct_mg_connect_opts.md b/docs/c-api/net.h/struct_mg_connect_opts.md index f4ef502d7ca1e27393f17ccdfac59addc587753a..f05bf25f482d8771af81d883845cf2f0b4990b7a 100644 --- a/docs/c-api/net.h/struct_mg_connect_opts.md +++ b/docs/c-api/net.h/struct_mg_connect_opts.md @@ -8,6 +8,7 @@ signature: | 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. diff --git a/docs/c-api/net.h/struct_mg_mgr.md b/docs/c-api/net.h/struct_mg_mgr.md index dcc2b523dfdab4a5b4d7167a8a911b6c72139087..d33727c38d2314ec252096ed8a560d0a59b02f4e 100644 --- a/docs/c-api/net.h/struct_mg_mgr.md +++ b/docs/c-api/net.h/struct_mg_mgr.md @@ -14,9 +14,7 @@ signature: | void *user_data; /* User data */ int num_ifaces; struct mg_iface **ifaces; /* network interfaces */ - #if MG_ENABLE_JAVASCRIPT - struct v7 *v7; - #endif + const char *nameserver; /* DNS server to use */ }; --- diff --git a/docs/c-api/net.h/struct_mg_mgr_init_opts.md b/docs/c-api/net.h/struct_mg_mgr_init_opts.md index da3e49516593452736fb75b5194512cca397ba69..7db23de5bbd8cf1a39fde09b76471174ded345b8 100644 --- a/docs/c-api/net.h/struct_mg_mgr_init_opts.md +++ b/docs/c-api/net.h/struct_mg_mgr_init_opts.md @@ -4,9 +4,10 @@ decl_name: "struct mg_mgr_init_opts" symbol_kind: "struct" signature: | struct mg_mgr_init_opts { - struct mg_iface_vtable *main_iface; + const struct mg_iface_vtable *main_iface; int num_ifaces; - struct mg_iface_vtable **ifaces; + const struct mg_iface_vtable **ifaces; + const char *nameserver; }; --- diff --git a/docs/c-api/resolv.h/intro.md b/docs/c-api/resolv.h/intro.md index 3fc5fc069f796ebaf1ff3939e7290805f2ea1d58..f77184f9e5df71c3163d13d9c22ce9d6245bba92 100644 --- a/docs/c-api/resolv.h/intro.md +++ b/docs/c-api/resolv.h/intro.md @@ -6,6 +6,7 @@ items: - { name: mg_resolve_async.md } - { name: mg_resolve_async_opt.md } - { name: mg_resolve_from_hosts_file.md } + - { name: mg_set_nameserver.md } - { name: struct_mg_resolve_async_opts.md } --- diff --git a/docs/c-api/resolv.h/mg_set_nameserver.md b/docs/c-api/resolv.h/mg_set_nameserver.md new file mode 100644 index 0000000000000000000000000000000000000000..2e33c629c54056eba705bedce1c85f0a54ccc9b4 --- /dev/null +++ b/docs/c-api/resolv.h/mg_set_nameserver.md @@ -0,0 +1,10 @@ +--- +title: "mg_set_nameserver()" +decl_name: "mg_set_nameserver" +symbol_kind: "func" +signature: | + void mg_set_nameserver(struct mg_mgr *mgr, const char *nameserver); +--- + +Set default DNS server + diff --git a/docs/c-api/resolv.h/struct_mg_resolve_async_opts.md b/docs/c-api/resolv.h/struct_mg_resolve_async_opts.md index 6c39e2f1a2c09644a56c0aa884530f53d448122b..8a836b9a0953ad8eb914247364b429852cf3d092 100644 --- a/docs/c-api/resolv.h/struct_mg_resolve_async_opts.md +++ b/docs/c-api/resolv.h/struct_mg_resolve_async_opts.md @@ -4,7 +4,7 @@ decl_name: "struct mg_resolve_async_opts" symbol_kind: "struct" signature: | struct mg_resolve_async_opts { - const char *nameserver_url; + 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 */ diff --git a/docs/c-api/uri.h/intro.md b/docs/c-api/uri.h/intro.md index 154aea63c0cea2a85116944a648df50adf6ef92b..f266b27ec39bc275fdf5f7d14839116585af84ef 100644 --- a/docs/c-api/uri.h/intro.md +++ b/docs/c-api/uri.h/intro.md @@ -3,6 +3,7 @@ title: "URI" symbol_kind: "intro" decl_name: "uri.h" items: + - { name: mg_assemble_uri.md } - { name: mg_parse_uri.md } --- diff --git a/docs/c-api/uri.h/mg_assemble_uri.md b/docs/c-api/uri.h/mg_assemble_uri.md new file mode 100644 index 0000000000000000000000000000000000000000..2006514834514225f39eb4e1d4d88068e8c5b2dd --- /dev/null +++ b/docs/c-api/uri.h/mg_assemble_uri.md @@ -0,0 +1,20 @@ +--- +title: "mg_assemble_uri()" +decl_name: "mg_assemble_uri" +symbol_kind: "func" +signature: | + 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); +--- + +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. + diff --git a/docs/c-api/uri.h/mg_parse_uri.md b/docs/c-api/uri.h/mg_parse_uri.md index 48cb6d943b29ba9849e58a29e0b8f329e82a6b43..2d330f18e71d630b64f89172afefac6bfd714b9a 100644 --- a/docs/c-api/uri.h/mg_parse_uri.md +++ b/docs/c-api/uri.h/mg_parse_uri.md @@ -3,7 +3,7 @@ title: "mg_parse_uri()" decl_name: "mg_parse_uri" symbol_kind: "func" signature: | - int mg_parse_uri(struct mg_str uri, struct mg_str *scheme, + 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); diff --git a/docs/c-api/util.h/intro.md b/docs/c-api/util.h/intro.md index 9b30d680091764b2d58fe6f3c7cbf72788314b1d..73c4f792229377241116200a7f7b5a4c9706d83b 100644 --- a/docs/c-api/util.h/intro.md +++ b/docs/c-api/util.h/intro.md @@ -8,20 +8,21 @@ items: - { name: mg_basic_auth_header.md } - { name: mg_conn_addr_to_str.md } - { name: mg_fopen.md } + - { name: mg_fread.md } + - { name: mg_fwrite.md } - { name: mg_hexdump.md } - { name: mg_hexdump_connection.md } - { name: mg_hexdumpf.md } - { name: mg_is_big_endian.md } - - { name: mg_match_prefix.md } - { name: mg_mbuf_append_base64.md } - { name: mg_mbuf_append_base64_putc.md } - - { name: mg_next_comma_list_entry.md } - { name: mg_open.md } - { name: mg_skip.md } - { name: mg_sock_addr_to_str.md } - { name: mg_sock_to_str.md } - { name: mg_start_thread.md } - { name: mg_stat.md } + - { name: mg_url_encode.md } --- diff --git a/docs/c-api/util.h/mg_basic_auth_header.md b/docs/c-api/util.h/mg_basic_auth_header.md index d8b582a5a622b839903e74d1fc6617502e08e881..724ec05391cc6b6a09a9dea8ecf6aeda7b9186aa 100644 --- a/docs/c-api/util.h/mg_basic_auth_header.md +++ b/docs/c-api/util.h/mg_basic_auth_header.md @@ -3,7 +3,8 @@ title: "mg_basic_auth_header()" decl_name: "mg_basic_auth_header" symbol_kind: "func" signature: | - void mg_basic_auth_header(const char *user, const char *pass, struct mbuf *buf); + void mg_basic_auth_header(const struct mg_str user, const struct mg_str pass, + struct mbuf *buf); --- Generate a Basic Auth header and appends it to buf. diff --git a/docs/c-api/util.h/mg_conn_addr_to_str.md b/docs/c-api/util.h/mg_conn_addr_to_str.md index e623734fbf0b5286de40fbe6c20dbb1d548c7c04..b7603fb29aa76fcfb32d8700f2a79c880464768a 100644 --- a/docs/c-api/util.h/mg_conn_addr_to_str.md +++ b/docs/c-api/util.h/mg_conn_addr_to_str.md @@ -3,8 +3,8 @@ title: "mg_conn_addr_to_str()" decl_name: "mg_conn_addr_to_str" symbol_kind: "func" signature: | - void mg_conn_addr_to_str(struct mg_connection *nc, char *buf, size_t len, - int flags); + int mg_conn_addr_to_str(struct mg_connection *c, char *buf, size_t len, + int flags); --- Converts a connection's local or remote address into string. @@ -17,5 +17,6 @@ see `MG_SOCK_STRINGIFY_*` definitions. - 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. +If compiled with `-DMG_ENABLE_IPV6`, IPv6 addresses are supported. +Return length of the stringified address. diff --git a/docs/c-api/util.h/mg_fread.md b/docs/c-api/util.h/mg_fread.md new file mode 100644 index 0000000000000000000000000000000000000000..0ff4994ad470bc9d79e7745fcbaf8213e9bf6145 --- /dev/null +++ b/docs/c-api/util.h/mg_fread.md @@ -0,0 +1,12 @@ +--- +title: "mg_fread()" +decl_name: "mg_fread" +symbol_kind: "func" +signature: | + size_t mg_fread(void *ptr, size_t size, size_t count, FILE *f); +--- + +Reads data from the given file stream. + +Return value is a number of bytes readen. + diff --git a/docs/c-api/util.h/mg_fwrite.md b/docs/c-api/util.h/mg_fwrite.md new file mode 100644 index 0000000000000000000000000000000000000000..5ddbbbcea9b6030e56cc7b3ca993416b469c88ea --- /dev/null +++ b/docs/c-api/util.h/mg_fwrite.md @@ -0,0 +1,12 @@ +--- +title: "mg_fwrite()" +decl_name: "mg_fwrite" +symbol_kind: "func" +signature: | + size_t mg_fwrite(const 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. + diff --git a/docs/c-api/util.h/mg_match_prefix.md b/docs/c-api/util.h/mg_match_prefix.md deleted file mode 100644 index 15ddc9ef1fc17e80f343bec5b6fbeb665f377ad2..0000000000000000000000000000000000000000 --- a/docs/c-api/util.h/mg_match_prefix.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: "mg_match_prefix()" -decl_name: "mg_match_prefix" -symbol_kind: "func" -signature: | - int mg_match_prefix(const char *pattern, int pattern_len, const char *str); ---- - -Matches 0-terminated string (mg_match_prefix) or string with given length -mg_match_prefix_n against a glob pattern. - -Match is case-insensitive. Returns number of bytes matched, or -1 if no -match. - diff --git a/docs/c-api/util.h/mg_next_comma_list_entry.md b/docs/c-api/util.h/mg_next_comma_list_entry.md deleted file mode 100644 index a96103936f5463494dfd5e0dda46da5354f1dc0d..0000000000000000000000000000000000000000 --- a/docs/c-api/util.h/mg_next_comma_list_entry.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: "mg_next_comma_list_entry()" -decl_name: "mg_next_comma_list_entry" -symbol_kind: "func" -signature: | - const char *mg_next_comma_list_entry(const char *list, struct mg_str *val, - struct mg_str *eq_val); ---- - -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. - diff --git a/docs/c-api/util.h/mg_sock_addr_to_str.md b/docs/c-api/util.h/mg_sock_addr_to_str.md index 3502e135e44da7e64a51d0b2d6b9fbab944fe234..b5cb6fddd17267e36c470bf08b3f57fca583dcee 100644 --- a/docs/c-api/util.h/mg_sock_addr_to_str.md +++ b/docs/c-api/util.h/mg_sock_addr_to_str.md @@ -3,8 +3,8 @@ title: "mg_sock_addr_to_str()" decl_name: "mg_sock_addr_to_str" symbol_kind: "func" signature: | - void mg_sock_addr_to_str(const union socket_address *sa, char *buf, size_t len, - int flags); + int mg_sock_addr_to_str(const union socket_address *sa, char *buf, size_t len, + int flags); --- Convert the socket's address into string. diff --git a/docs/c-api/util.h/mg_url_encode.md b/docs/c-api/util.h/mg_url_encode.md new file mode 100644 index 0000000000000000000000000000000000000000..105ce9473eb7b005411b04081361e92461aab443 --- /dev/null +++ b/docs/c-api/util.h/mg_url_encode.md @@ -0,0 +1,13 @@ +--- +title: "mg_url_encode()" +decl_name: "mg_url_encode" +symbol_kind: "func" +signature: | + struct mg_str mg_url_encode(const struct mg_str src); +--- + +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. + diff --git a/docs/http/cgi.md b/docs/http/cgi.md index 8e2309db58223b754923078da640c110a5823ab1..86b0a4f3db50201d58aac217450e56bfedc2a9fe 100644 --- a/docs/http/cgi.md +++ b/docs/http/cgi.md @@ -16,7 +16,7 @@ CGI file must be executable. Mongoose honours the shebang line - see http://en.wikipedia.org/wiki/Shebang_(Unix). For example, if both PHP and Perl CGIs are used, then -``#!/path/to/php-cgi.exe` and ``#!/path/to/perl.exe` must be the first lines +`#!/path/to/php-cgi.exe` and `#!/path/to/perl.exe` must be the first lines of the respective CGI scripts. It is possible to hardcode the path to the CGI interpreter for all @@ -29,3 +29,7 @@ Example: ```c opts.cgi_interpreter = "C:\\ruby\\ruby.exe"; ``` +NOTE: In the CGI handler we don't use explicitly a system call waitpid() for +reaping zombie processes. Instead, we set the SIGCHLD handler to SIG_IGN. +It will cause zombie processes to be reaped automatically. +CAUTION: not all OSes (e.g. QNX) reap zombies if SIGCHLD is ignored. \ No newline at end of file diff --git a/docs/http/client_example.md b/docs/http/client_example.md index 9f7e06e0a72936bace89140f2d130921beff7ad6..2b159a6594d157ca6772aa4333b8f10f0fdecf95 100644 --- a/docs/http/client_example.md +++ b/docs/http/client_example.md @@ -18,8 +18,9 @@ static int exit_flag = 0; static void ev_handler(struct mg_connection *c, int ev, void *p) { if (ev == MG_EV_HTTP_REPLY) { + struct http_message *hm = (struct http_message *)p; c->flags |= MG_F_CLOSE_IMMEDIATELY; - fwrite(hm->message.p, 1, hm->message.len, stdout); + fwrite(hm->message.p, 1, (int)hm->message.len, stdout); putchar('\n'); exit_flag = 1; } else if (ev == MG_EV_CLOSE) { @@ -31,7 +32,7 @@ int main(void) { struct mg_mgr mgr; mg_mgr_init(&mgr, NULL); - mg_connect_http(mgr, ev_handler, url, NULL, NULL); + mg_connect_http(&mgr, ev_handler, url, NULL, NULL); while (exit_flag == 0) { diff --git a/docs/http/server_example.md b/docs/http/server_example.md index a244d647d2acd09c4bade5d847579aac00a5dd03..225beb406be55ed5fb63fb2d4de0884e5ddac9c9 100644 --- a/docs/http/server_example.md +++ b/docs/http/server_example.md @@ -30,8 +30,8 @@ static void ev_handler(struct mg_connection *c, int ev, void *p) { // We have received an HTTP request. Parsed request is contained in `hm`. // Send HTTP reply to the client which shows full original request. - mg_send_head(c, 200, hm.message.len, "Content-Type: text/plain"); - mg_printf(c, "%.*s", hm.message.len, hm.message.p); + mg_send_head(c, 200, hm->message.len, "Content-Type: text/plain"); + mg_printf(c, "%.*s", (int)hm->message.len, hm->message.p); } } diff --git a/docs/http/ssi.md b/docs/http/ssi.md index 2fcfbc4db5a07e6c80b6798089dc5ac4403ec9c2..23c390e117cec0369cc664206f53d2486d0a76fa 100644 --- a/docs/http/ssi.md +++ b/docs/http/ssi.md @@ -35,7 +35,7 @@ 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 PARAMS -->` directive, +On each occurrence of `<!--#call PARAMS -->` directive, Mongoose calls a registered event handler with `MG_EV_SSI_CALL` event. Event parameter will point to the `PARAMS` string. An event handler can output any text, for example by calling diff --git a/docs/http/ssl.md b/docs/http/ssl.md index 9908f1906c996f0366ddfbf97c6e37b71eda6218..c317e57a6a6a22fa2ae46fadd9f085967dd1f871 100644 --- a/docs/http/ssl.md +++ b/docs/http/ssl.md @@ -30,4 +30,4 @@ int main(void) { } ``` -For the full example, please see the [Simplest HTTPS server example](https://github.com/cesanta/dev/tree/master/mongoose/examples/simplest_web_server_ssl). +For the full example, please see the [Simplest HTTPS server example](https://github.com/cesanta/mongoose/tree/master/examples/simplest_web_server_ssl). diff --git a/docs/overview/build-options/disabling-flags.md b/docs/overview/build-options/disabling-flags.md index 8633083f3a13213c4b401a86ce0a41d2a6b92c2d..767438fe4afcb86c422703c04b7095481a3efe07 100644 --- a/docs/overview/build-options/disabling-flags.md +++ b/docs/overview/build-options/disabling-flags.md @@ -3,6 +3,6 @@ title: Disabling flags --- - `MG_DISABLE_HTTP_DIGEST_AUTH` disable HTTP Digest (MD5) authorisation support -- `MG_DISABLE_SHA1` disable SHA1 support (used by WebSocket) -- `MG_DISABLE_MD5` disable MD5 support (used by HTTP auth) +- `CS_DISABLE_SHA1` disable SHA1 support (used by WebSocket) +- `CS_DISABLE_MD5` disable MD5 support (used by HTTP auth) - `MG_DISABLE_HTTP_KEEP_ALIVE` useful for embedded systems to save resources diff --git a/docs/overview/build-options/tunables.md b/docs/overview/build-options/tunables.md index e1ca4c63c1c7702dac15e07c57f774f3d1c1cc44..c9944c4c26e162dc99a3513b175f0b23472952dd 100644 --- a/docs/overview/build-options/tunables.md +++ b/docs/overview/build-options/tunables.md @@ -9,3 +9,4 @@ title: Tunables - `MG_SSL_CRYPTO_MODERN`, `MG_SSL_CRYPTO_OLD` - choose either "Modern" or "Old" ciphers instead of the default "Intermediate" setting. See [this article](https://wiki.mozilla.org/Security/Server_Side_TLS#Recommended_configurations) for details. +- `MG_USER_FILE_FUNCTIONS` allow you to use custom file operation, by defining you own `mg_stat`, `mg_fopen`, `mg_open`, `mg_fread` and `mg_fwrite` functions \ No newline at end of file diff --git a/examples/CC3200/Makefile.build b/examples/CC3200/Makefile.build index 7d567d0f1f65deb53e98e1cb46b65050847c9f3f..6e16c3cef587fcb586d14293540463a1c4c0e23a 100644 --- a/examples/CC3200/Makefile.build +++ b/examples/CC3200/Makefile.build @@ -22,7 +22,7 @@ IPATH = . ../.. $(REPO_PATH) VPATH = ../.. -MONGOOSE_FEATURES = -DMG_ENABLE_SSL -DMG_ENABLE_HTTP_STREAMING_MULTIPART +MONGOOSE_FEATURES = -DMG_ENABLE_SSL -DMG_ENABLE_HTTP_STREAMING_MULTIPART -DMG_MODULE_LINES SDK_FLAGS = -DUSE_FREERTOS -DSL_PLATFORM_MULTI_THREADED # -DTARGET_IS_CC3200 would reduce code size by using functions in ROM @@ -58,11 +58,11 @@ $(FW_DIR): $(FW_ZIP): $(FW_ELF) $(FW_BIN) $(SLFS_FILES) @echo " Code size: $(shell ls -l $(FW_BIN) | awk '{print $$5}')" @echo " GEN $(FW_MANIFEST)" - @fw_meta gen_build_info \ + @fw_meta.py gen_build_info \ --json_output=$(BUILD_INFO_JSON) @cp -v $(SLFS_FILES) out/ @cp $(CC3200_SP_FILE)* $(FW_DIR) - @fw_meta create_manifest \ + @fw_meta.py create_manifest \ --name=$(PROG) --platform=$(PLATFORM) \ --build_info=$(BUILD_INFO_JSON) \ --output=$(FW_MANIFEST) \ @@ -72,14 +72,14 @@ $(FW_ZIP): $(FW_ELF) $(FW_BIN) $(SLFS_FILES) /sys/mcuimg.bin:type=app,src=$(notdir $(FW_BIN)) \ $(foreach f,$(SLFS_FILES), $(notdir $(f)):type=slfile,src=$(notdir $(f))) @echo " ZIP $@" - @fw_meta create_fw \ + @fw_meta.py create_fw \ --manifest=$(FW_MANIFEST) \ --src_dir=$(FW_DIR) \ --output=$@ FREERTOS_SRCS = timers.c list.c queue.c tasks.c port.c heap_3.c osi_freertos.c DRIVER_SRCS = cpu.c gpio.c gpio_if.c i2c.c i2c_if.c interrupt.c pin.c prcm.c spi.c uart.c udma.c utils.c -SL_SRCS = socket.c wlan.c driver.c device.c netapp.c netcfg.c network_common.c cc_pal.c fs.c +SL_SRCS = socket.c wlan.c driver.c device.c netapp.c netcfg.c network_common.c timer.c cc_pal.c fs.c SDK_SRCS = startup_gcc.c $(FREERTOS_SRCS) $(DRIVER_SRCS) $(SL_SRCS) IPATH += $(SDK_PATH) $(SDK_PATH)/inc $(SDK_PATH)/driverlib \ $(SDK_PATH)/example/common $(SDK_PATH)/oslib \ diff --git a/examples/CC3200/cs_dbg.h b/examples/CC3200/cs_dbg.h index 8e86f92d1b780116c1dd1f242671d7123c3f84e5..e987c427a440dbf43c8d660df7a82936d3f2f27b 100644 --- a/examples/CC3200/cs_dbg.h +++ b/examples/CC3200/cs_dbg.h @@ -40,22 +40,22 @@ void cs_log_set_level(enum cs_log_level level); void cs_log_set_file(FILE *file); -extern enum cs_log_level cs_log_level; +extern enum cs_log_level cs_log_threshold; void cs_log_print_prefix(const char *func); void cs_log_printf(const char *fmt, ...); #define LOG(l, x) \ - if (cs_log_level >= l) { \ + if (cs_log_threshold >= l) { \ cs_log_print_prefix(__func__); \ cs_log_printf x; \ } #ifndef CS_NDEBUG -#define DBG(x) \ - if (cs_log_level >= LL_VERBOSE_DEBUG) { \ - cs_log_print_prefix(__func__); \ - cs_log_printf x; \ +#define DBG(x) \ + if (cs_log_threshold >= LL_VERBOSE_DEBUG) { \ + cs_log_print_prefix(__func__); \ + cs_log_printf x; \ } #else /* NDEBUG */ diff --git a/examples/CC3200/main.c b/examples/CC3200/main.c index 24eea055e31a9bec6d2f995b3b2996e3bad9ce87..dbda74ae665611ecb5395d2837140d4be0b448e0 100644 --- a/examples/CC3200/main.c +++ b/examples/CC3200/main.c @@ -294,3 +294,16 @@ void SimpleLinkGeneralEventHandler(SlDeviceEvent_t *e) { LOG(LL_ERROR, ("status %d sender %d", e->EventData.deviceEvent.status, e->EventData.deviceEvent.sender)); } + +#ifndef __TI_COMPILER_VERSION__ +int _gettimeofday_r(struct _reent *r, struct timeval *tp, void *tz) { +#else +int gettimeofday(struct timeval *tp, void *tz) { +#endif + unsigned long sec; + unsigned short msec; + MAP_PRCMRTCGet(&sec, &msec); + tp->tv_sec = sec; + tp->tv_usec = ((unsigned long) msec) * 1000; + return 0; +} diff --git a/examples/CC3200/sdk.version b/examples/CC3200/sdk.version index 8f4f18a30b67a9bbf9efc7087af37e858101910c..f8bd0daecd6033a3fdf563fde2e1e337ac7c818a 100644 --- a/examples/CC3200/sdk.version +++ b/examples/CC3200/sdk.version @@ -1 +1 @@ -docker.cesanta.com/cc3200-build:1.2.0-r8 +docker.cesanta.com/cc3200-build:1.3.0-r2 diff --git a/examples/ESP32_IDF/Makefile b/examples/ESP32_IDF/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..e40fd4c0df83a2546c07f713d3feba9b60235e4c --- /dev/null +++ b/examples/ESP32_IDF/Makefile @@ -0,0 +1,16 @@ +SDK_VER=$(shell cat sdk.version) + +.PHONY: all + +MG_PATH = $(realpath $(PWD)/../../) + +all: + docker run --rm -it -v $(MG_PATH):/esp32_idf \ + $(SDK_VER) \ + bash -c "cd esp32_idf/examples/ESP32_IDF && \ + make -f Makefile.build defconfig && make -f Makefile.build" + +clean: + docker run --rm -it -v $(MG_PATH):/esp32_idf \ + $(SDK_VER) \ + bash -c "rm -rf esp32_idf/examples/ESP32_IDF/build" diff --git a/examples/ESP32_IDF/Makefile.build b/examples/ESP32_IDF/Makefile.build new file mode 100644 index 0000000000000000000000000000000000000000..5a6de248d87e1cdbe69988a0da43b148c498b48c --- /dev/null +++ b/examples/ESP32_IDF/Makefile.build @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := esp32_idf + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/ESP32_IDF/README.md b/examples/ESP32_IDF/README.md new file mode 100644 index 0000000000000000000000000000000000000000..4af679a09625b4a97ac1fb331b79c092b845de1a --- /dev/null +++ b/examples/ESP32_IDF/README.md @@ -0,0 +1,18 @@ +This is a Mongoose "Hello, world" that can be compiled under Espressif IoT Development Framework for ESP32 + +It connects to WiFi network and serves a "hello world" page. + +Most of the the boilerplate comes from [project_template](https://github.com/espressif/esp-idf-template) with minimal changes. + +For building the example, you need to have [Docker](https://www.docker.com/products/docker) and use our pre-built SDK container. +To build just run in the example directory +``` +$ make +``` + +Note: before building, change `WIFI_SSID` and `WIFI_PASS` macros in main/main.c file + +Once built, use [esptool](https://github.com/espressif/esptool) for flashing +``` +$ python esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 115200 --before default_reset --after hard_reset write_flash -u --flash_mode dio --flash_freq 40m --flash_size detect 0x1000 build/bootloader/bootloader.bin 0x10000 build/esp32_idf.bin 0x8000 build/partitions_singleapp.bin +``` diff --git a/examples/ESP32_IDF/main/component.mk b/examples/ESP32_IDF/main/component.mk new file mode 100644 index 0000000000000000000000000000000000000000..f8659a72c3b04f2fe5ea09a77e6c250606057df7 --- /dev/null +++ b/examples/ESP32_IDF/main/component.mk @@ -0,0 +1,17 @@ +# +# Main component makefile. +# +# This Makefile can be left empty. By default, it will take the sources in the +# src/ directory, compile them and link them into lib(subdirectory_name).a +# in the build directory. This behaviour is entirely configurable, +# please read the ESP-IDF documents if you need to do this. +# + +COMPONENT_OBJS = main.o mongoose.o + +mongoose.o: ../../../../mongoose.c + $(summary) "CC $@" + $(CC) $(CFLAGS) $(CPPFLAGS) \ + $(addprefix -I ,$(COMPONENT_INCLUDES)) \ + $(addprefix -I ,$(COMPONENT_EXTRA_INCLUDES)) \ + -c $< -o $@ diff --git a/examples/ESP32_IDF/main/main.c b/examples/ESP32_IDF/main/main.c new file mode 100644 index 0000000000000000000000000000000000000000..3b9a0ee74e9be7b553f0c275c0a44e03e6a617dc --- /dev/null +++ b/examples/ESP32_IDF/main/main.c @@ -0,0 +1,93 @@ +#include <string.h> + +#include "freertos/FreeRTOS.h" + +#include "esp_event.h" +#include "esp_event_loop.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "nvs_flash.h" + +#include "./../../../mongoose.h" + +#define WIFI_SSID "ssid" +#define WIFI_PASS "pass" + +#define MG_LISTEN_ADDR "80" + +static esp_err_t event_handler(void *ctx, system_event_t *event) { + (void) ctx; + (void) event; + return ESP_OK; +} + +static void mg_ev_handler(struct mg_connection *nc, int ev, void *p) { + static const char *reply_fmt = + "HTTP/1.0 200 OK\r\n" + "Connection: close\r\n" + "Content-Type: text/plain\r\n" + "\r\n" + "Hello %s\n"; + + switch (ev) { + case MG_EV_ACCEPT: { + char addr[32]; + mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr), + MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT); + printf("Connection %p from %s\n", nc, addr); + break; + } + case MG_EV_HTTP_REQUEST: { + char addr[32]; + struct http_message *hm = (struct http_message *) p; + mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr), + MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT); + printf("HTTP request from %s: %.*s %.*s\n", addr, (int) hm->method.len, + hm->method.p, (int) hm->uri.len, hm->uri.p); + mg_printf(nc, reply_fmt, addr); + nc->flags |= MG_F_SEND_AND_CLOSE; + break; + } + case MG_EV_CLOSE: { + printf("Connection %p closed\n", nc); + break; + } + } +} + +void app_main(void) { + nvs_flash_init(); + tcpip_adapter_init(); + ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL)); + + /* Initializing WiFi */ + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + wifi_config_t sta_config = { + .sta = {.ssid = WIFI_SSID, .password = WIFI_PASS, .bssid_set = false}}; + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &sta_config)); + ESP_ERROR_CHECK(esp_wifi_start()); + ESP_ERROR_CHECK(esp_wifi_connect()); + + /* Starting Mongoose */ + struct mg_mgr mgr; + struct mg_connection *nc; + + printf("Starting web-server on port %s\n", MG_LISTEN_ADDR); + + mg_mgr_init(&mgr, NULL); + + nc = mg_bind(&mgr, MG_LISTEN_ADDR, mg_ev_handler); + if (nc == NULL) { + printf("Error setting up listener!\n"); + return; + } + mg_set_protocol_http_websocket(nc); + + /* Processing events */ + while (1) { + mg_mgr_poll(&mgr, 1000); + } +} diff --git a/examples/ESP32_IDF/sdk.version b/examples/ESP32_IDF/sdk.version new file mode 100644 index 0000000000000000000000000000000000000000..174eac89b428df5e04b7284448dc25604d673da4 --- /dev/null +++ b/examples/ESP32_IDF/sdk.version @@ -0,0 +1 @@ +docker.cesanta.com/esp32-build:1.0-r13 diff --git a/examples/ESP8266_RTOS/build.sh b/examples/ESP8266_RTOS/build.sh index 749d1f156958949a32e256c0beb922e168d17fbf..a7d1e0ec997c52cf94074cdc6bc08d56b93ebb90 100755 --- a/examples/ESP8266_RTOS/build.sh +++ b/examples/ESP8266_RTOS/build.sh @@ -1,7 +1,8 @@ #!/bin/bash +REPO=$(cd ../.. && pwd) docker run \ - --rm -i -v $(realpath ${PWD}/../..):/src \ + --rm -i -v $REPO:/src \ --entrypoint=/bin/bash $(cat sdk.version) -l -c -x ' export SDK_PATH=/opt/Espressif/ESP8266_RTOS_SDK; export BIN_PATH=./bin; diff --git a/examples/Makefile b/examples/Makefile index a6c96ddc5ca6c7589e2a286f098b18ad5d8a518b..9f364d2e2f2a6d579bec7c9bfc8e33b7b985bcfe 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -3,7 +3,7 @@ # `wildcard ./*/` works in both linux and linux/wine, while `wildcard */` enumerates nothing under wine SUBDIRS = $(sort $(dir $(wildcard ./*/))) -SUBDIRS:=$(filter-out ./ ./CC3200/ ./ESP8266_RTOS/ ./mbed/ ./MSP432/ ./nRF51/ ./nRF52/ ./NXP_K64/ ./NXP_LPC4088/ ./PIC32/ ./STM32F4_CC3100/ ./TM4C129/ ./WinCE/, $(SUBDIRS)) +SUBDIRS:=$(filter-out ./ ./CC3200/ ./ESP32_IDF/ ./ESP8266_RTOS/ ./mbed/ ./MSP432/ ./nRF51/ ./nRF52/ ./NXP_K64/ ./NXP_LPC4088/ ./PIC32/ ./STM32F4_CC3100/ ./TM4C129/ ./WinCE/, $(SUBDIRS)) ifeq ($(OS), Windows_NT) SUBDIRS:=$(filter-out ./netcat/ ./raspberry_pi_mjpeg_led/ ./captive_dns_server/, $(SUBDIRS)) diff --git a/examples/big_upload/big_upload.c b/examples/big_upload/big_upload.c index 8bd3cc5a519c3ee7e3d6b6f442747a0582745a26..563436503e797b8319a11a14a7240ff067c37298 100644 --- a/examples/big_upload/big_upload.c +++ b/examples/big_upload/big_upload.c @@ -10,28 +10,13 @@ #include "mongoose.h" static const char *s_http_port = "8000"; +static struct mg_serve_http_opts s_http_server_opts; struct file_writer_data { FILE *fp; size_t bytes_written; }; -static void handle_request(struct mg_connection *nc) { - // This handler gets for all endpoints but /upload - mg_printf(nc, "%s", - "HTTP/1.1 200 OK\r\n" - "Content-Type: text/html\r\n" - "Connection: close\r\n" - "\r\n" - "<html><body>Upload example." - "<form method=\"POST\" action=\"/upload\" " - " enctype=\"multipart/form-data\">" - "<input type=\"file\" name=\"file\" /> <br/>" - "<input type=\"submit\" value=\"Upload\" />" - "</form></body></html>"); - nc->flags |= MG_F_SEND_AND_CLOSE; -} - static void handle_upload(struct mg_connection *nc, int ev, void *p) { struct file_writer_data *data = (struct file_writer_data *) nc->user_data; struct mg_http_multipart_part *mp = (struct mg_http_multipart_part *) p; @@ -82,25 +67,27 @@ static void handle_upload(struct mg_connection *nc, int ev, void *p) { } static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { - (void) ev_data; - switch (ev) { - case MG_EV_HTTP_REQUEST: - // Invoked when the full HTTP request is in the buffer (including body). - handle_request(nc); - break; + if (ev == MG_EV_HTTP_REQUEST) { + mg_serve_http(nc, ev_data, s_http_server_opts); } } int main(void) { struct mg_mgr mgr; - struct mg_connection *nc; + struct mg_connection *c; mg_mgr_init(&mgr, NULL); - nc = mg_bind(&mgr, s_http_port, ev_handler); + c = mg_bind(&mgr, s_http_port, ev_handler); + if (c == NULL) { + fprintf(stderr, "Cannot start server on port %s\n", s_http_port); + exit(EXIT_FAILURE); + } + + s_http_server_opts.document_root = "."; // Serve current directory + mg_register_http_endpoint(c, "/upload", handle_upload MG_UD_ARG(NULL)); - mg_register_http_endpoint(nc, "/upload", handle_upload); // Set up HTTP server parameters - mg_set_protocol_http_websocket(nc); + mg_set_protocol_http_websocket(c); printf("Starting web server on port %s\n", s_http_port); for (;;) { diff --git a/examples/big_upload/index.html b/examples/big_upload/index.html new file mode 100644 index 0000000000000000000000000000000000000000..c162c653aba05759e0a62591bc99fa640021b9c4 --- /dev/null +++ b/examples/big_upload/index.html @@ -0,0 +1,54 @@ +<!DOCTYPE html> +<html> +<head> + <title>AJAX Upload Example</title> + <script src="//code.jquery.com/jquery-1.9.1.js"></script> + <script type="text/javascript"> + function updateProgress(evt) { + if (evt.lengthComputable) { + document.getElementById("output").textContent = + "Uploaded " + evt.loaded + " of " + evt.total + " bytes"; + } + } + function uploadFile() { + var file_data = new FormData(document.getElementById('filename')); + $.ajax({ + url: "/upload", + type: "POST", + data: file_data, + processData: false, + contentType: false, + cache: false, + xhr: function() { + myXhr = $.ajaxSettings.xhr(); + if(myXhr.upload){ + myXhr.upload.addEventListener('progress',updateProgress, false); // for handling the progress of the upload + } + return myXhr; + }, + }).done(function(data) { + document.getElementById("output").textContent = "Result: " + data; + }); + return false; + } + </script> +</head> + +<body> + <h1>Upload file using standard form upload</h1> + + <form method="POST" action="/upload" enctype="multipart/form-data"> + <label>Select a file:</label><br> + <input type="file" name="file" /> + <input type="submit" value="Upload" /> + </form> + + <h1>Upload file using Ajax - that also gives progress report</h1> + <form method="post" id="filename" name="filename" onsubmit="return uploadFile();"> + <label>Select a file:</label><br> + <input type="file" id="file" name="file" required /> + <input type="submit" value="Upload" /> + </form> + <br><br><div id="output"></div> +</body> +</html> diff --git a/examples/mqtt_broker/Makefile b/examples/mqtt_broker/Makefile index f0c17a0e0daaed2a74d8425a185f4953a76e6f12..86f0b09b0fb24c5f7286336e421ce33089a4a3a0 100644 --- a/examples/mqtt_broker/Makefile +++ b/examples/mqtt_broker/Makefile @@ -1,4 +1,4 @@ PROG = mqtt_broker MODULE_CFLAGS = -DMG_ENABLE_MQTT_BROKER -DMG_ENABLE_HTTP=0 -SSL_LIB=openssl +#SSL_LIB=openssl include ../examples.mk diff --git a/examples/mqtt_broker/mqtt_broker.c b/examples/mqtt_broker/mqtt_broker.c index 4c02f9b613b5dbc4f314fb196b698e7cf922d58d..4cc5185b5088afad3a6d71e04a1697d911a2dec8 100644 --- a/examples/mqtt_broker/mqtt_broker.c +++ b/examples/mqtt_broker/mqtt_broker.c @@ -17,22 +17,31 @@ #include "../../mongoose.h" +static const char *s_listening_address = "0.0.0.0:1883"; + +static void ev_handler(struct mg_connection *c, int ev, void *ev_data) { + if (ev != MG_EV_POLL) printf("USER HANDLER GOT EVENT %d\n", ev); + /* Do your custom event processing here */ + mg_mqtt_broker(c, ev, ev_data); +} + int main(void) { struct mg_mgr mgr; - const char *address = "0.0.0.0:1883"; - struct mg_connection *nc; + struct mg_connection *c; struct mg_mqtt_broker brk; mg_mgr_init(&mgr, NULL); mg_mqtt_broker_init(&brk, NULL); - if ((nc = mg_bind(&mgr, address, mg_mqtt_broker)) == NULL) { - fprintf(stderr, "mg_bind(%s) failed\n", address); + if ((c = mg_bind(&mgr, s_listening_address, ev_handler)) == NULL) { + fprintf(stderr, "mg_bind(%s) failed\n", s_listening_address); exit(EXIT_FAILURE); } - nc->user_data = &brk; + mg_mqtt_broker_init(&brk, NULL); + c->user_data = &brk; + mg_set_protocol_mqtt(c); - printf("MQTT broker started on %s\n", address); + printf("MQTT broker started on %s\n", s_listening_address); /* * TODO: Add a HTTP status page that shows current sessions diff --git a/examples/mqtt_client/mqtt_client.c b/examples/mqtt_client/mqtt_client.c index da0282bb832129ef60c5f60f3e1283c04a3f3917..20090efe6327beafc06c49d37fd4ae622c004d2d 100644 --- a/examples/mqtt_client/mqtt_client.c +++ b/examples/mqtt_client/mqtt_client.c @@ -27,10 +27,7 @@ static void ev_handler(struct mg_connection *nc, int ev, void *p) { struct mg_mqtt_message *msg = (struct mg_mqtt_message *) p; (void) nc; -#if 0 - if (ev != MG_EV_POLL) - printf("USER HANDLER GOT %d\n", ev); -#endif + if (ev != MG_EV_POLL) printf("USER HANDLER GOT EVENT %d\n", ev); switch (ev) { case MG_EV_CONNECT: { diff --git a/examples/mqtt_over_websocket_server/Makefile b/examples/mqtt_over_websocket_server/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..7f6be7c6cb9b1bdcfaa1a0407ef67c4865ee2edf --- /dev/null +++ b/examples/mqtt_over_websocket_server/Makefile @@ -0,0 +1,4 @@ +PROG = mqtt_over_websocket_server +MODULE_CFLAGS = -DMG_ENABLE_MQTT_BROKER=1 +#SSL_LIB=mbedtls +include ../examples.mk diff --git a/examples/mqtt_over_websocket_server/mqtt_over_websocket_server.c b/examples/mqtt_over_websocket_server/mqtt_over_websocket_server.c new file mode 100644 index 0000000000000000000000000000000000000000..885921ce93a7213c2ec16868c39a422b0c825e83 --- /dev/null +++ b/examples/mqtt_over_websocket_server/mqtt_over_websocket_server.c @@ -0,0 +1,124 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + * + * 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 <https://www.cesanta.com/license>. + */ + +#include "mongoose.h" + +static const char *s_mqtt_address = "0.0.0.0:1883"; +static const char *s_http_address = "0.0.0.0:8080"; + +static void unproxy(struct mg_connection *c) { + struct mg_connection *pc = (struct mg_connection *) c->user_data; + if (pc != NULL) { + pc->flags |= MG_F_CLOSE_IMMEDIATELY; + pc->user_data = NULL; + c->user_data = NULL; + } + printf("Closing connection %p\n", c); +} + +static void proxy_handler(struct mg_connection *c, int ev, void *ev_data) { + if (ev == MG_EV_POLL) return; + printf("%p %s EVENT %d %p\n", c, __func__, ev, ev_data); + switch (ev) { + case MG_EV_CLOSE: { + unproxy(c); + break; + } + case MG_EV_RECV: { + struct mg_connection *pc = (struct mg_connection *) c->user_data; + if (pc != NULL) { + mg_send_websocket_frame(pc, WEBSOCKET_OP_BINARY, c->recv_mbuf.buf, + c->recv_mbuf.len); + mbuf_remove(&c->recv_mbuf, c->recv_mbuf.len); + } + break; + } + } +} + +static void http_handler(struct mg_connection *c, int ev, void *ev_data) { + struct mg_connection *pc = (struct mg_connection *) c->user_data; + if (ev == MG_EV_POLL) return; + printf("%p %s EVENT %d %p\n", c, __func__, ev, ev_data); + /* Do your custom event processing here */ + switch (ev) { + case MG_EV_WEBSOCKET_HANDSHAKE_DONE: { + pc = mg_connect(c->mgr, s_mqtt_address, proxy_handler); + pc->user_data = c; + c->user_data = pc; + printf("Created proxy connection %p\n", pc); + break; + } + case MG_EV_WEBSOCKET_FRAME: { + struct websocket_message *wm = (struct websocket_message *) ev_data; + if (pc != NULL) { + printf("Forwarding %d bytes\n", (int) wm->size); + mg_send(pc, wm->data, wm->size); + } + break; + } + case MG_EV_CLOSE: { + unproxy(c); + break; + } + } +} + +static void mqtt_handler(struct mg_connection *c, int ev, void *ev_data) { + if (ev == MG_EV_POLL) return; + printf("%p %s EVENT %d %p\n", c, __func__, ev, ev_data); + /* Do your custom event processing here */ + switch (ev) { + case MG_EV_CLOSE: + printf("Closing MQTT connection %p\n", c); + break; + } + mg_mqtt_broker(c, ev, ev_data); +} + +static void start_mqtt_server(struct mg_mgr *mgr, const char *addr) { + struct mg_connection *c; + static struct mg_mqtt_broker brk; // static is important - must not perish + if ((c = mg_bind(mgr, addr, mqtt_handler)) == NULL) { + fprintf(stderr, "Cannot start MQTT server on port [%s]\n", addr); + exit(EXIT_FAILURE); + } + mg_mqtt_broker_init(&brk, NULL); + c->user_data = &brk; + mg_set_protocol_mqtt(c); + printf("MQTT server started on %s\n", addr); +} + +static void start_http_server(struct mg_mgr *mgr, const char *addr) { + struct mg_connection *c; + if ((c = mg_bind(mgr, addr, http_handler)) == NULL) { + fprintf(stderr, "Cannot start HTTP server on port [%s]\n", addr); + exit(EXIT_FAILURE); + } + mg_set_protocol_http_websocket(c); + printf("HTTP server started on %s\n", addr); +} + +int main(void) { + struct mg_mgr mgr; + mg_mgr_init(&mgr, NULL); + start_http_server(&mgr, s_http_address); + start_mqtt_server(&mgr, s_mqtt_address); + for (;;) { + mg_mgr_poll(&mgr, 1000); + } +} diff --git a/examples/multithreaded/Makefile b/examples/multithreaded/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..c41017da655797681c1e64bf8487ab7a930c115d --- /dev/null +++ b/examples/multithreaded/Makefile @@ -0,0 +1,3 @@ +PROG = multithreaded +MODULE_CFLAGS=-DMG_ENABLE_THREADS +include ../examples.mk diff --git a/examples/multithreaded/multithreaded.c b/examples/multithreaded/multithreaded.c new file mode 100644 index 0000000000000000000000000000000000000000..a3053c81478f1f08397ce923911d76c7b827d94e --- /dev/null +++ b/examples/multithreaded/multithreaded.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2014-2017 Cesanta Software Limited + * All rights reserved + */ + +#include "mongoose.h" + +static sig_atomic_t s_received_signal = 0; +static const char *s_http_port = "8000"; +static const int s_num_worker_threads = 5; +static unsigned long s_next_id = 0; + +static void signal_handler(int sig_num) { + signal(sig_num, signal_handler); + s_received_signal = sig_num; +} +static struct mg_serve_http_opts s_http_server_opts; +static sock_t sock[2]; + +// This info is passed to the worker thread +struct work_request { + unsigned long conn_id; // needed to identify the connection where to send the reply + // optionally, more data that could be required by worker +}; + +// This info is passed by the worker thread to mg_broadcast +struct work_result { + unsigned long conn_id; + int sleep_time; +}; + +static void on_work_complete(struct mg_connection *nc, int ev, void *ev_data) { + (void) ev; + char s[32]; + struct mg_connection *c; + for (c = mg_next(nc->mgr, NULL); c != NULL; c = mg_next(nc->mgr, c)) { + if (c->user_data != NULL) { + struct work_result *res = (struct work_result *)ev_data; + if ((unsigned long)c->user_data == res->conn_id) { + sprintf(s, "conn_id:%lu sleep:%d", res->conn_id, res->sleep_time); + mg_send_head(c, 200, strlen(s), "Content-Type: text/plain"); + mg_printf(c, "%s", s); + } + } + } +} + +void *worker_thread_proc(void *param) { + struct mg_mgr *mgr = (struct mg_mgr *) param; + struct work_request req = {0}; + + while (s_received_signal == 0) { + if (read(sock[1], &req, sizeof(req)) < 0) + perror("Reading worker sock"); + int r = rand() % 10; + sleep(r); + struct work_result res = {req.conn_id, r}; + mg_broadcast(mgr, on_work_complete, (void *)&res, sizeof(res)); + } + return NULL; +} + +static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { + (void) nc; + (void) ev_data; + + switch (ev) { + case MG_EV_ACCEPT: + nc->user_data = (void *)++s_next_id; + break; + case MG_EV_HTTP_REQUEST: { + struct work_request req = {(unsigned long)nc->user_data}; + + if (write(sock[0], &req, sizeof(req)) < 0) + perror("Writing worker sock"); + break; + } + case MG_EV_CLOSE: { + if (nc->user_data) nc->user_data = NULL; + } + } +} + +int main(void) { + struct mg_mgr mgr; + struct mg_connection *nc; + int i; + + if (mg_socketpair(sock, SOCK_STREAM) == 0) { + perror("Opening socket pair"); + exit(1); + } + + signal(SIGTERM, signal_handler); + signal(SIGINT, signal_handler); + + mg_mgr_init(&mgr, NULL); + + nc = mg_bind(&mgr, s_http_port, ev_handler); + if (nc == NULL) { + printf("Failed to create listener\n"); + return 1; + } + + mg_set_protocol_http_websocket(nc); + s_http_server_opts.document_root = "."; // Serve current directory + s_http_server_opts.enable_directory_listing = "no"; + + for (i = 0; i < s_num_worker_threads; i++) { + mg_start_thread(worker_thread_proc, &mgr); + } + + printf("Started on port %s\n", s_http_port); + while (s_received_signal == 0) { + mg_mgr_poll(&mgr, 200); + } + + mg_mgr_free(&mgr); + + closesocket(sock[0]); + closesocket(sock[1]); + + return 0; +} diff --git a/examples/nRF51/http/boards/pca10028/armgcc/Makefile b/examples/nRF51/http/boards/pca10028/armgcc/Makefile index d2cde039711b826ffe44fa5e66e2705bab1fb5cf..07534aa2cd52891870bdb44ad96af35f06e608de 100644 --- a/examples/nRF51/http/boards/pca10028/armgcc/Makefile +++ b/examples/nRF51/http/boards/pca10028/armgcc/Makefile @@ -3,7 +3,7 @@ PROJECT_NAME := iot_lwip_tcp_server_pca10028 export OUTPUT_FILENAME #MAKEFILE_NAME := $(CURDIR)/$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)) MAKEFILE_NAME := $(MAKEFILE_LIST) -MAKEFILE_DIR := $(dir $(MAKEFILE_NAME) ) +MAKEFILE_DIR := $(dir $(MAKEFILE_NAME) ) TEMPLATE_PATH = ../../../../nrf51_iot_sdk/components/toolchain/gcc ifeq ($(OS),Windows_NT) @@ -17,7 +17,7 @@ RM := rm -rf #echo suspend ifeq ("$(VERBOSE)","1") -NO_ECHO := +NO_ECHO := else NO_ECHO := @ endif @@ -118,7 +118,7 @@ BUILD_DIRECTORIES := $(sort $(OBJECT_DIRECTORY) $(OUTPUT_BINARY_DIRECTORY) $(LIS # Mongoose features MG_FEATURES_TINY = \ -DMG_DISABLE_HTTP_DIGEST_AUTH \ - -DMG_DISABLE_MD5 \ + -DCS_DISABLE_MD5 \ -DMG_DISABLE_HTTP_KEEP_ALIVE \ -DMG_ENABLE_HTTP_SSI=0 \ -DMG_ENABLE_HTTP_STREAMING_MULTIPART @@ -251,7 +251,7 @@ genbin: $(NO_ECHO)$(OBJCOPY) -O binary $(OUTPUT_BINARY_DIRECTORY)/$(OUTPUT_FILENAME).out $(OUTPUT_BINARY_DIRECTORY)/$(OUTPUT_FILENAME).bin ## Create binary .hex file from the .out file -genhex: +genhex: @echo Preparing: $(OUTPUT_FILENAME).hex $(NO_ECHO)$(OBJCOPY) -O ihex $(OUTPUT_BINARY_DIRECTORY)/$(OUTPUT_FILENAME).out $(OUTPUT_BINARY_DIRECTORY)/$(OUTPUT_FILENAME).hex diff --git a/examples/nRF52/http/boards/pca10040/armgcc/Makefile b/examples/nRF52/http/boards/pca10040/armgcc/Makefile index 073af2b84f92841607c4ba15b5bd950ecb41fab7..bd2aa6cc69911415b7d42d627f53d103d45a81b0 100644 --- a/examples/nRF52/http/boards/pca10040/armgcc/Makefile +++ b/examples/nRF52/http/boards/pca10040/armgcc/Makefile @@ -3,7 +3,7 @@ PROJECT_NAME := iot_lwip_tcp_server_pca10040 export OUTPUT_FILENAME #MAKEFILE_NAME := $(CURDIR)/$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)) MAKEFILE_NAME := $(MAKEFILE_LIST) -MAKEFILE_DIR := $(dir $(MAKEFILE_NAME) ) +MAKEFILE_DIR := $(dir $(MAKEFILE_NAME) ) TEMPLATE_PATH = ../../../../nrf5_iot_sdk/components/toolchain/gcc ifeq ($(OS),Windows_NT) @@ -17,7 +17,7 @@ RM := rm -rf #echo suspend ifeq ("$(VERBOSE)","1") -NO_ECHO := +NO_ECHO := else NO_ECHO := @ endif @@ -165,7 +165,7 @@ BUILD_DIRECTORIES := $(sort $(OBJECT_DIRECTORY) $(OUTPUT_BINARY_DIRECTORY) $(LIS # Mongoose features MG_FEATURES_TINY = \ -DMG_DISABLE_HTTP_DIGEST_AUTH \ - -DMG_DISABLE_MD5 \ + -DCS_DISABLE_MD5 \ -DMG_DISABLE_HTTP_KEEP_ALIVE \ -DMG_ENABLE_HTTP_SSI=0 \ -DMG_ENABLE_HTTP_STREAMING_MULTIPART @@ -297,7 +297,7 @@ genbin: $(NO_ECHO)$(OBJCOPY) -O binary $(OUTPUT_BINARY_DIRECTORY)/$(OUTPUT_FILENAME).out $(OUTPUT_BINARY_DIRECTORY)/$(OUTPUT_FILENAME).bin ## Create binary .hex file from the .out file -genhex: +genhex: @echo Preparing: $(OUTPUT_FILENAME).hex $(NO_ECHO)$(OBJCOPY) -O ihex $(OUTPUT_BINARY_DIRECTORY)/$(OUTPUT_FILENAME).out $(OUTPUT_BINARY_DIRECTORY)/$(OUTPUT_FILENAME).hex echosize: diff --git a/examples/websocket_chat/Makefile b/examples/websocket_chat/Makefile index b6244834cf4da21a84bd7223680b45017214cf9e..2fbb6947977473c3e45b5946ec348fd99081a8c5 100644 --- a/examples/websocket_chat/Makefile +++ b/examples/websocket_chat/Makefile @@ -1,3 +1,3 @@ PROG = websocket_chat -MODULE_CFLAGS = -DMG_ENABLE_FILESYSTEM=0 +MODULE_CFLAGS = -DMG_ENABLE_FILESYSTEM=1 include ../examples.mk diff --git a/examples/websocket_chat/websocket_chat.c b/examples/websocket_chat/websocket_chat.c index 3d2ad6116c25fa5c37de74805dd6273b17a94397..265ea0798a37635fe061d6fb99b299d89dfcbf89 100644 --- a/examples/websocket_chat/websocket_chat.c +++ b/examples/websocket_chat/websocket_chat.c @@ -7,6 +7,7 @@ static sig_atomic_t s_signal_received = 0; static const char *s_http_port = "8000"; +static struct mg_serve_http_opts s_http_server_opts; static void signal_handler(int sig_num) { signal(sig_num, signal_handler); // Reinstantiate signal handler @@ -46,6 +47,10 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { broadcast(nc, d); break; } + case MG_EV_HTTP_REQUEST: { + mg_serve_http(nc, (struct http_message *) ev_data, s_http_server_opts); + break; + } case MG_EV_CLOSE: { /* Disconnect. Tell everybody. */ if (is_websocket(nc)) { @@ -69,6 +74,8 @@ int main(void) { nc = mg_bind(&mgr, s_http_port, ev_handler); mg_set_protocol_http_websocket(nc); + s_http_server_opts.document_root = "."; // Serve current directory + s_http_server_opts.enable_directory_listing = "yes"; printf("Started on port %s\n", s_http_port); while (s_signal_received == 0) { diff --git a/mongoose.c b/mongoose.c index 3a7ed941a585eeb486a4450af855104f4be06d4d..b3461bf72ac9a46f2bb9a91b7e4017635a2ff514 100644 --- a/mongoose.c +++ b/mongoose.c @@ -10,21 +10,7 @@ #ifndef CS_MONGOOSE_SRC_INTERNAL_H_ #define CS_MONGOOSE_SRC_INTERNAL_H_ -#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 +/* Amalgamated: #include "common/mg_mem.h" */ #ifndef MBUF_REALLOC #define MBUF_REALLOC MG_REALLOC @@ -48,9 +34,9 @@ #define MG_DISABLE_PFS #endif -/* Amalgamated: #include "mongoose/src/net.h" */ -/* Amalgamated: #include "mongoose/src/http.h" */ /* Amalgamated: #include "common/cs_dbg.h" */ +/* Amalgamated: #include "mongoose/src/http.h" */ +/* Amalgamated: #include "mongoose/src/net.h" */ #define MG_CTL_MSG_MESSAGE_SIZE 8192 @@ -62,7 +48,8 @@ MG_INTERNAL struct mg_connection *mg_do_connect(struct mg_connection *nc, 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, int ev, void *ev_data); + 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); @@ -110,11 +97,6 @@ MG_INTERNAL size_t mg_handle_chunked(struct mg_connection *nc, struct http_message *hm, char *buf, size_t blen); -MG_INTERNAL int mg_http_common_url_parse(const char *url, const char *schema, - const char *schema_tls, int *use_ssl, - char **user, char **pass, char **addr, - int *port_i, const char **path); - #if MG_ENABLE_FILESYSTEM MG_INTERNAL int mg_uri_to_local_path(struct http_message *hm, const struct mg_serve_http_opts *opts, @@ -155,9 +137,11 @@ 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_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); + const struct mg_str *key, + struct http_message *); #endif #endif /* MG_ENABLE_HTTP */ @@ -165,11 +149,6 @@ MG_INTERNAL int mg_get_errno(void); MG_INTERNAL void mg_close_conn(struct mg_connection *conn); -MG_INTERNAL int mg_http_common_url_parse(const char *url, const char *schema, - const char *schema_tls, int *use_ssl, - char **user, char **pass, char **addr, - int *port_i, const char **path); - #if MG_ENABLE_SNTP MG_INTERNAL int mg_sntp_parse_reply(const char *buf, int len, struct mg_sntp_message *msg); @@ -177,6 +156,42 @@ MG_INTERNAL int mg_sntp_parse_reply(const char *buf, int len, #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_dbg.h" #endif /* @@ -217,32 +232,34 @@ enum cs_log_level { _LL_MAX = 5, }; +/* Set log level. */ void cs_log_set_level(enum cs_log_level level); +/* Set log filter. NULL (a default) logs everything. */ +void cs_log_set_filter(char *source_file_name); + +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 void cs_log_set_file(FILE *file); -extern enum cs_log_level cs_log_level; -void cs_log_print_prefix(const char *func); -void cs_log_printf(const char *fmt, ...); - -#define LOG(l, x) \ - do { \ - if (cs_log_level >= l) { \ - cs_log_print_prefix(__func__); \ - cs_log_printf x; \ - } \ +void cs_log_printf(const char *fmt, ...) +#ifdef __GNUC__ + __attribute__((format(printf, 1, 2))) +#endif + ; + +#define LOG(l, x) \ + do { \ + if (cs_log_print_prefix(l, __func__, __FILE__)) cs_log_printf x; \ } while (0) #ifndef CS_NDEBUG -#define DBG(x) \ - do { \ - if (cs_log_level >= LL_VERBOSE_DEBUG) { \ - cs_log_print_prefix(__func__); \ - cs_log_printf x; \ - } \ - } while (0) +#define DBG(x) LOG(LL_VERBOSE_DEBUG, x) #else /* NDEBUG */ @@ -274,16 +291,21 @@ void cs_log_printf(const char *fmt, ...); #include <stdarg.h> #include <stdio.h> +#include <string.h> /* Amalgamated: #include "common/cs_time.h" */ +/* Amalgamated: #include "common/str_util.h" */ -enum cs_log_level cs_log_level WEAK = +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; + #if CS_ENABLE_STDIO FILE *cs_log_file WEAK = NULL; @@ -292,12 +314,36 @@ FILE *cs_log_file WEAK = NULL; double cs_log_ts WEAK; #endif -void cs_log_print_prefix(const char *func) WEAK; -void cs_log_print_prefix(const char *func) { +enum cs_log_level cs_log_cur_msg_level WEAK = LL_NONE; + +void cs_log_set_filter(char *str) WEAK; +void cs_log_set_filter(char *str) { + free(s_filter_pattern); + if (str != NULL) { + s_filter_pattern = strdup(str); + s_filter_pattern_len = strlen(str); + } 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 { @@ -306,6 +352,7 @@ void cs_log_print_prefix(const char *func) { cs_log_ts = now; } #endif + return 1; } void cs_log_printf(const char *fmt, ...) WEAK; @@ -316,6 +363,7 @@ void cs_log_printf(const char *fmt, ...) { 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; @@ -327,7 +375,7 @@ void cs_log_set_file(FILE *file) { void cs_log_set_level(enum cs_log_level level) WEAK; void cs_log_set_level(enum cs_log_level level) { - cs_log_level = level; + cs_log_threshold = level; #if CS_LOG_ENABLE_TS_DIFF && CS_ENABLE_STDIO cs_log_ts = cs_time(); #endif @@ -592,6 +640,7 @@ struct dirent *readdir(DIR *dir); #ifndef EXCLUDE_COMMON +/* Amalgamated: #include "common/mg_mem.h" */ /* Amalgamated: #include "common/cs_dirent.h" */ /* @@ -599,14 +648,6 @@ struct dirent *readdir(DIR *dir); * for systems which do not natively support it (e.g. Windows). */ -#ifndef MG_FREE -#define MG_FREE free -#endif - -#ifndef MG_MALLOC -#define MG_MALLOC malloc -#endif - #ifdef _WIN32 struct win32_dir { DIR d; @@ -739,6 +780,41 @@ double cs_time(void) { #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 @@ -750,6 +826,10 @@ double cs_time(void) { #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 @@ -765,9 +845,13 @@ double cs_time(void) { #endif /* BIG_ENDIAN */ #endif /* BYTE_ORDER */ +#ifdef __cplusplus +} +#endif + #endif /* CS_COMMON_CS_ENDIAN_H_ */ #ifdef MG_MODULE_LINES -#line 1 "common/md5.c" +#line 1 "common/cs_md5.c" #endif /* * This code implements the MD5 message-digest algorithm. @@ -786,11 +870,11 @@ double cs_time(void) { * will fill a supplied 16-byte array with the digest. */ -/* Amalgamated: #include "common/md5.h" */ +/* Amalgamated: #include "common/cs_md5.h" */ /* Amalgamated: #include "common/str_util.h" */ #if !defined(EXCLUDE_COMMON) -#if !DISABLE_MD5 +#if !CS_DISABLE_MD5 /* Amalgamated: #include "common/cs_endian.h" */ @@ -821,7 +905,7 @@ static void byteReverse(unsigned char *buf, unsigned longs) { * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious * initialization constants. */ -void MD5_Init(MD5_CTX *ctx) { +void cs_md5_init(cs_md5_ctx *ctx) { ctx->buf[0] = 0x67452301; ctx->buf[1] = 0xefcdab89; ctx->buf[2] = 0x98badcfe; @@ -831,7 +915,7 @@ void MD5_Init(MD5_CTX *ctx) { ctx->bits[1] = 0; } -static void MD5Transform(uint32_t buf[4], uint32_t const in[16]) { +static void cs_md5_transform(uint32_t buf[4], uint32_t const in[16]) { register uint32_t a, b, c, d; a = buf[0]; @@ -913,7 +997,7 @@ static void MD5Transform(uint32_t buf[4], uint32_t const in[16]) { buf[3] += d; } -void MD5_Update(MD5_CTX *ctx, const unsigned char *buf, size_t len) { +void cs_md5_update(cs_md5_ctx *ctx, const unsigned char *buf, size_t len) { uint32_t t; t = ctx->bits[0]; @@ -932,7 +1016,7 @@ void MD5_Update(MD5_CTX *ctx, const unsigned char *buf, size_t len) { } memcpy(p, buf, t); byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (uint32_t *) ctx->in); + cs_md5_transform(ctx->buf, (uint32_t *) ctx->in); buf += t; len -= t; } @@ -940,7 +1024,7 @@ void MD5_Update(MD5_CTX *ctx, const unsigned char *buf, size_t len) { while (len >= 64) { memcpy(ctx->in, buf, 64); byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (uint32_t *) ctx->in); + cs_md5_transform(ctx->buf, (uint32_t *) ctx->in); buf += 64; len -= 64; } @@ -948,7 +1032,7 @@ void MD5_Update(MD5_CTX *ctx, const unsigned char *buf, size_t len) { memcpy(ctx->in, buf, len); } -void MD5_Final(unsigned char digest[16], MD5_CTX *ctx) { +void cs_md5_final(unsigned char digest[16], cs_md5_ctx *ctx) { unsigned count; unsigned char *p; uint32_t *a; @@ -961,7 +1045,7 @@ void MD5_Final(unsigned char digest[16], MD5_CTX *ctx) { if (count < 8) { memset(p, 0, count); byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (uint32_t *) ctx->in); + cs_md5_transform(ctx->buf, (uint32_t *) ctx->in); memset(ctx->in, 0, 56); } else { memset(p, 0, count - 8); @@ -972,235 +1056,23 @@ void MD5_Final(unsigned char digest[16], MD5_CTX *ctx) { a[14] = ctx->bits[0]; a[15] = ctx->bits[1]; - MD5Transform(ctx->buf, (uint32_t *) ctx->in); + 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 /* DISABLE_MD5 */ - -char *cs_md5(char buf[33], ...) { - unsigned char hash[16]; - const unsigned char *p; - va_list ap; - MD5_CTX ctx; - - MD5_Init(&ctx); - - va_start(ap, buf); - while ((p = va_arg(ap, const unsigned char *) ) != NULL) { - size_t len = va_arg(ap, size_t); - MD5_Update(&ctx, p, len); - } - va_end(ap); - - MD5_Final(hash, &ctx); - cs_to_hex(buf, hash, sizeof(hash)); - - return buf; -} +#endif /* CS_DISABLE_MD5 */ #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 <assert.h> -#include <string.h> -/* 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_str.h" */ - -#include <stdlib.h> -#include <string.h> - -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 = memcmp(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; -} - -struct mg_str mg_strdup(const struct mg_str s) WEAK; -struct mg_str mg_strdup(const struct mg_str s) { - struct mg_str r = {NULL, 0}; - if (s.len > 0 && s.p != NULL) { - r.p = (char *) malloc(s.len); - if (r.p != NULL) { - memcpy((char *) r.p, s.p, s.len); - r.len = s.len; - } - } - return r; -} - -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); -} -#ifdef MG_MODULE_LINES -#line 1 "common/sha1.c" +#line 1 "common/cs_sha1.c" #endif /* Copyright(c) By Steve Reid <steve@edmweb.com> */ /* 100% Public Domain */ -/* Amalgamated: #include "common/sha1.h" */ +/* Amalgamated: #include "common/cs_sha1.h" */ -#if !DISABLE_SHA1 && !defined(EXCLUDE_COMMON) +#if !CS_DISABLE_SHA1 && !defined(EXCLUDE_COMMON) /* Amalgamated: #include "common/cs_endian.h" */ @@ -1368,86 +1240,313 @@ void cs_sha1_init(cs_sha1_ctx *context) { 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_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 <assert.h> +#include <string.h> +/* 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 <stdlib.h> +#include <string.h> + +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; } -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); +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; } - c = 0200; - cs_sha1_update(context, &c, 1); - while ((context->count[0] & 504) != 448) { - c = 0000; - cs_sha1_update(context, &c, 1); + 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; } - 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); + 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; + } } - memset(context, '\0', sizeof(*context)); - memset(&finalcount, '\0', sizeof(finalcount)); + return r; } -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; +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 */); +} - 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); - } +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 */); +} - memset(buf1, 0, sizeof(buf1)); - memset(buf2, 0, sizeof(buf2)); - memcpy(buf1, key, keylen); - memcpy(buf2, key, keylen); +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; +} - for (i = 0; i < sizeof(buf1); i++) { - buf1[i] ^= 0x36; - buf2[i] ^= 0x5c; +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; +} - cs_sha1_init(&ctx); - cs_sha1_update(&ctx, buf1, sizeof(buf1)); - cs_sha1_update(&ctx, data, datalen); - cs_sha1_final(out, &ctx); +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; - cs_sha1_init(&ctx); - cs_sha1_update(&ctx, buf2, sizeof(buf2)); - cs_sha1_update(&ctx, out, 20); - cs_sha1_final(out, &ctx); + if (s1.len > n) { + s1.len = n; + } + if (s2.len > n) { + s2.len = n; + } + return mg_strcmp(s1, s2); } -#endif /* EXCLUDE_COMMON */ +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 @@ -1458,6 +1557,7 @@ void cs_hmac_sha1(const unsigned char *key, size_t keylen, #ifndef EXCLUDE_COMMON +/* Amalgamated: #include "common/mg_mem.h" */ /* Amalgamated: #include "common/platform.h" */ /* Amalgamated: #include "common/str_util.h" */ @@ -1465,13 +1565,7 @@ void cs_hmac_sha1(const unsigned char *key, size_t keylen, #define C_DISABLE_BUILTIN_SNPRINTF 0 #endif -#ifndef MG_MALLOC -#define MG_MALLOC malloc -#endif - -#ifndef MG_FREE -#define MG_FREE free -#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) { @@ -1737,7 +1831,7 @@ const char *c_strnstr(const char *s, const char *find, size_t slen) { char *strdup(const char *src) WEAK; char *strdup(const char *src) { size_t len = strlen(src) + 1; - char *ret = malloc(len); + char *ret = MG_MALLOC(len); if (ret != NULL) { strcpy(ret, src); } @@ -1863,6 +1957,109 @@ int mg_avprintf(char **buf, size_t size, const char *fmt, va_list ap) { 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; +} + +int mg_match_prefix_n(const struct mg_str, const struct mg_str) WEAK; +int mg_match_prefix_n(const struct mg_str pattern, const struct mg_str str) { + const char *or_str; + size_t len, i = 0, j = 0; + int res; + + 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; i++, j++) { + if (pattern.p[i] == '?' && j != str.len) { + continue; + } else if (pattern.p[i] == '$') { + return j == str.len ? (int) j : -1; + } 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) { + 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 == -1 && len-- > 0); + return res == -1 ? -1 : (int) (j + res + len); + } else if (str_util_lowercase(&pattern.p[i]) != + str_util_lowercase(&str.p[j])) { + return -1; + } + } + return j; +} + +int mg_match_prefix(const char *, int, const char *) WEAK; +int 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/tun.h" @@ -1933,7 +2130,8 @@ extern "C" { struct mg_connection *mg_tun_bind_opt(struct mg_mgr *mgr, const char *dispatcher, - mg_event_handler_t handler, + MG_CB(mg_event_handler_t handler, + void *user_data), struct mg_bind_opts opts); int mg_tun_parse_frame(void *data, size_t len, struct mg_tun_frame *frame); @@ -2018,7 +2216,8 @@ MG_INTERNAL void mg_remove_conn(struct mg_connection *conn) { } MG_INTERNAL void mg_call(struct mg_connection *nc, - mg_event_handler_t ev_handler, int ev, void *ev_data) { + mg_event_handler_t ev_handler, void *user_data, int ev, + void *ev_data) { if (ev_handler == NULL) { /* * If protocol handler is specified, call it. Otherwise, call user-specified @@ -2033,22 +2232,15 @@ MG_INTERNAL void mg_call(struct mg_connection *nc, } #if !defined(NO_LIBC) && MG_ENABLE_HEXDUMP - /* LCOV_EXCL_START */ - if (nc->mgr->hexdump_file != NULL && ev != MG_EV_POLL && + if (nc->mgr->hexdump_file != NULL && ev != MG_EV_POLL && ev != MG_EV_RECV && ev != MG_EV_SEND /* handled separately */) { - if (ev == MG_EV_RECV) { - mg_hexdump_connection(nc, nc->mgr->hexdump_file, nc->recv_mbuf.buf, - *(int *) ev_data, ev); - } else { - mg_hexdump_connection(nc, nc->mgr->hexdump_file, NULL, 0, ev); - } + mg_hexdump_connection(nc, nc->mgr->hexdump_file, NULL, 0, ev); } -/* LCOV_EXCL_STOP */ #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); + 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) { @@ -2064,12 +2256,15 @@ MG_INTERNAL void mg_call(struct mg_connection *nc, ev_handler == nc->handler ? "user" : "proto", nc->flags, (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); } +#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; - mg_call(c, NULL, MG_EV_TIMER, &now); + mg_call(c, NULL, c->user_data, MG_EV_TIMER, &now); /* * To prevent timer firing all the time, reset the timer after delivery. * However, in case user sets it to new value, do not reset. @@ -2082,11 +2277,11 @@ void mg_if_timer(struct mg_connection *c, double now) { 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, MG_EV_POLL, &now); + mg_call(nc, NULL, nc->user_data, MG_EV_POLL, &now); } } -static void mg_destroy_conn(struct mg_connection *conn, int destroy_if) { +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); @@ -2103,9 +2298,14 @@ static void mg_destroy_conn(struct mg_connection *conn, int destroy_if) { 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, MG_EV_CLOSE, NULL); + mg_call(conn, NULL, conn->user_data, MG_EV_CLOSE, NULL); mg_destroy_conn(conn, 0 /* destroy_if */); } @@ -2160,36 +2360,13 @@ void mg_mgr_init_opt(struct mg_mgr *m, void *user_data, m->ifaces[i]->vtable->init(m->ifaces[i]); } } + if (opts.nameserver != NULL) { + m->nameserver = strdup(opts.nameserver); + } DBG(("==================================")); DBG(("init mgr=%p", m)); } -#if MG_ENABLE_JAVASCRIPT -static enum v7_err mg_send_js(struct v7 *v7, v7_val_t *res) { - v7_val_t arg0 = v7_arg(v7, 0); - v7_val_t arg1 = v7_arg(v7, 1); - struct mg_connection *c = (struct mg_connection *) v7_get_ptr(v7, arg0); - size_t len = 0; - - if (v7_is_string(arg1)) { - const char *data = v7_get_string(v7, &arg1, &len); - mg_send(c, data, len); - } - - *res = v7_mk_number(v7, len); - - return V7_OK; -} - -enum v7_err mg_enable_javascript(struct mg_mgr *m, struct v7 *v7, - const char *init_file_name) { - v7_val_t v; - m->v7 = v7; - v7_set_method(v7, v7_get_global(v7), "mg_send", mg_send_js); - return v7_exec_file(v7, init_file_name, &v); -} -#endif - void mg_mgr_free(struct mg_mgr *m) { struct mg_connection *conn, *tmp_conn; @@ -2217,6 +2394,8 @@ void mg_mgr_free(struct mg_mgr *m) { } MG_FREE(m->ifaces); } + + MG_FREE((char *) m->nameserver); } time_t mg_mgr_poll(struct mg_mgr *m, int timeout_ms) { @@ -2342,7 +2521,7 @@ MG_INTERNAL struct mg_connection *mg_create_connection( * 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. Shoud be at least + * `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 * @@ -2449,7 +2628,7 @@ 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, MG_EV_ACCEPT, &nc->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) { @@ -2459,23 +2638,35 @@ void mg_send(struct mg_connection *nc, const void *buf, int 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) { - mg_hexdump_connection(nc, nc->mgr->hexdump_file, buf, len, MG_EV_SEND); + char *buf = nc->send_mbuf.buf; + mg_hexdump_connection(nc, nc->mgr->hexdump_file, buf, num_sent, MG_EV_SEND); } #endif -} - -void mg_if_sent_cb(struct mg_connection *nc, int num_sent) { 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, MG_EV_SEND, &num_sent); + 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)); /* @@ -2499,7 +2690,7 @@ MG_INTERNAL void mg_recv_common(struct mg_connection *nc, void *buf, int len, mbuf_append(&nc->recv_mbuf, buf, len); MG_FREE(buf); } - mg_call(nc, NULL, MG_EV_RECV, &len); + 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) { @@ -2547,7 +2738,7 @@ void mg_if_recv_udp_cb(struct mg_connection *nc, void *buf, int len, */ nc->flags |= MG_F_SEND_AND_CLOSE; mg_add_conn(lc->mgr, nc); - mg_call(nc, NULL, MG_EV_ACCEPT, &nc->sa); + 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 */ @@ -2591,7 +2782,7 @@ void mg_if_connect_cb(struct mg_connection *nc, int err) { if (err != 0) { nc->flags |= MG_F_CLOSE_IMMEDIATELY; } - mg_call(nc, NULL, MG_EV_CONNECT, &err); + mg_call(nc, NULL, nc->user_data, MG_EV_CONNECT, &err); } #if MG_ENABLE_ASYNC_RESOLVER @@ -2629,27 +2820,29 @@ static void resolve_cb(struct mg_dns_message *msg, void *data, if (e == MG_RESOLVE_TIMEOUT) { double now = mg_time(); - mg_call(nc, NULL, MG_EV_TIMER, &now); + 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, MG_EV_CONNECT, &failure); - mg_call(nc, NULL, MG_EV_CLOSE, NULL); + 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_event_handler_t callback) { + 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, callback, 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_event_handler_t callback, + MG_CB(mg_event_handler_t callback, + void *user_data), struct mg_connect_opts opts) { struct mg_connection *nc = NULL; int proto, rc; @@ -2672,7 +2865,11 @@ struct mg_connection *mg_connect_opt(struct mg_mgr *mgr, const char *address, 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 : "-"), @@ -2723,6 +2920,7 @@ struct mg_connection *mg_connect_opt(struct mg_mgr *mgr, const char *address, 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"); @@ -2744,14 +2942,16 @@ struct mg_connection *mg_connect_opt(struct mg_mgr *mgr, const char *address, } struct mg_connection *mg_bind(struct mg_mgr *srv, const char *address, - mg_event_handler_t event_handler) { + 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, event_handler, 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_event_handler_t callback, + MG_CB(mg_event_handler_t callback, + void *user_data), struct mg_bind_opts opts) { union socket_address sa; struct mg_connection *nc = NULL; @@ -2759,12 +2959,16 @@ struct mg_connection *mg_bind_opt(struct mg_mgr *mgr, const char *address, 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 + MG_COPY_COMMON_CONNECTION_OPTIONS(&add_sock_opts, &opts); #if MG_ENABLE_TUN if (mg_strncmp(mg_mk_str(address), mg_mk_str("ws://"), 5) == 0 || mg_strncmp(mg_mk_str(address), mg_mk_str("wss://"), 6) == 0) { - return mg_tun_bind_opt(mgr, address, callback, opts); + return mg_tun_bind_opt(mgr, address, MG_CB(callback, user_data), opts); } #endif @@ -2895,7 +3099,7 @@ int mg_check_ip_acl(const char *acl, uint32_t remote_ip) { } } - DBG(("%08x %c", remote_ip, allowed)); + DBG(("%08x %c", (unsigned int) remote_ip, allowed)); return allowed == '+'; } @@ -2913,7 +3117,7 @@ double mg_set_timer(struct mg_connection *c, double timestamp) { * 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, + 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; @@ -2933,8 +3137,13 @@ void mg_if_get_conn_addr(struct mg_connection *nc, int remote, } struct mg_connection *mg_add_sock_opt(struct mg_mgr *s, sock_t sock, - mg_event_handler_t callback, + 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); @@ -2944,10 +3153,11 @@ struct mg_connection *mg_add_sock_opt(struct mg_mgr *s, sock_t sock, } struct mg_connection *mg_add_sock(struct mg_mgr *s, sock_t sock, - mg_event_handler_t callback) { + 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, callback, opts); + return mg_add_sock_opt(s, sock, MG_CB(callback, user_data), opts); } double mg_time(void) { @@ -2974,7 +3184,7 @@ extern "C" { #define MG_ENABLE_NET_IF_SOCKET MG_NET_IF == MG_NET_IF_SOCKET #endif -extern struct mg_iface_vtable mg_socket_iface_vtable; +extern const struct mg_iface_vtable mg_socket_iface_vtable; #ifdef __cplusplus } @@ -3002,7 +3212,7 @@ struct mg_tun_client; extern "C" { #endif /* __cplusplus */ -extern struct mg_iface_vtable mg_tun_iface_vtable; +extern const struct mg_iface_vtable mg_tun_iface_vtable; struct mg_connection *mg_tun_if_find_conn(struct mg_tun_client *client, uint32_t stream_id); @@ -3022,18 +3232,18 @@ struct mg_connection *mg_tun_if_find_conn(struct mg_tun_client *client, /* Amalgamated: #include "mongoose/src/net_if_socket.h" */ /* Amalgamated: #include "mongoose/src/net_if_tun.h" */ -extern struct mg_iface_vtable mg_default_iface_vtable; +extern const struct mg_iface_vtable mg_default_iface_vtable; #if MG_ENABLE_TUN -struct mg_iface_vtable *mg_ifaces[] = {&mg_default_iface_vtable, - &mg_tun_iface_vtable}; +const struct mg_iface_vtable *mg_ifaces[] = {&mg_default_iface_vtable, + &mg_tun_iface_vtable}; #else -struct mg_iface_vtable *mg_ifaces[] = {&mg_default_iface_vtable}; +const struct mg_iface_vtable *mg_ifaces[] = {&mg_default_iface_vtable}; #endif int mg_num_ifaces = (int) (sizeof(mg_ifaces) / sizeof(mg_ifaces[0])); -struct mg_iface *mg_if_create_iface(struct mg_iface_vtable *vtable, +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; @@ -3043,7 +3253,7 @@ struct mg_iface *mg_if_create_iface(struct mg_iface_vtable *vtable, } struct mg_iface *mg_find_iface(struct mg_mgr *mgr, - struct mg_iface_vtable *vtable, + const struct mg_iface_vtable *vtable, struct mg_iface *from) { int i = 0; if (from != NULL) { @@ -3095,17 +3305,16 @@ void mg_set_non_blocking_mode(sock_t sock) { #endif } -static int mg_is_error(int n) { +static int mg_is_error(void) { int err = mg_get_errno(); - return (n < 0 && err != EINPROGRESS && err != EWOULDBLOCK + return err != EINPROGRESS && err != EWOULDBLOCK #ifndef WINCE - && err != EAGAIN && err != EINTR + && err != EAGAIN && err != EINTR #endif #ifdef _WIN32 - && WSAGetLastError() != WSAEINTR && - WSAGetLastError() != WSAEWOULDBLOCK + && WSAGetLastError() != WSAEINTR && WSAGetLastError() != WSAEWOULDBLOCK #endif - ); + ; } void mg_socket_if_connect_tcp(struct mg_connection *nc, @@ -3120,7 +3329,7 @@ void mg_socket_if_connect_tcp(struct mg_connection *nc, mg_set_non_blocking_mode(nc->sock); #endif rc = connect(nc->sock, &sa->sa, sizeof(sa->sin)); - nc->err = mg_is_error(rc) ? mg_get_errno() : 0; + 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)); } @@ -3196,7 +3405,7 @@ static int mg_accept_conn(struct mg_connection *lc) { /* 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(-1)) DBG(("%p: failed to accept: %d", lc, mg_get_errno())); + if (mg_is_error()) DBG(("%p: failed to accept: %d", lc, mg_get_errno())); return 0; } nc = mg_if_accept_new_conn(lc); @@ -3281,10 +3490,7 @@ static void mg_write_to_socket(struct mg_connection *nc) { 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))); - if (n > 0) { - mbuf_remove(io, n); - mg_if_sent_cb(nc, n); - } + mg_if_sent_cb(nc, n); return; } @@ -3310,18 +3516,10 @@ static void mg_write_to_socket(struct mg_connection *nc) { #endif { n = (int) MG_SEND_FUNC(nc->sock, io->buf, io->len, 0); - DBG(("%p %d bytes -> %d", nc, n, nc->sock)); - if (n < 0 && mg_is_error(n)) { - /* Something went wrong, drop the connection. */ - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - return; - } + DBG(("%p %d bytes -> %d", nc, n, nc->sock)); } - if (n > 0) { - mbuf_remove(io, n); - mg_if_sent_cb(nc, n); - } + mg_if_sent_cb(nc, n); } MG_INTERNAL size_t recv_avail_size(struct mg_connection *conn, size_t max) { @@ -3376,7 +3574,7 @@ static void mg_handle_tcp_read(struct mg_connection *conn) { if (n == 0) { /* Orderly shutdown of the socket, try flushing output. */ conn->flags |= MG_F_SEND_AND_CLOSE; - } else if (mg_is_error(n)) { + } else if (n < 0 && mg_is_error()) { conn->flags |= MG_F_CLOSE_IMMEDIATELY; } } @@ -3524,7 +3722,8 @@ static void mg_mgr_handle_ctl_sock(struct mg_mgr *mgr) { 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); + ctl_msg.callback(nc, MG_EV_POLL, + ctl_msg.message MG_UD_ARG(nc->user_data)); } } } @@ -3542,9 +3741,7 @@ void mg_socket_if_init(struct mg_iface *iface) { (void) iface; DBG(("%p using select()", iface->mgr)); #if MG_ENABLE_BROADCAST - do { - mg_socketpair(iface->mgr->ctl, SOCK_DGRAM); - } while (iface->mgr->ctl[0] == INVALID_SOCKET); + mg_socketpair(iface->mgr->ctl, SOCK_DGRAM); #endif } @@ -3702,6 +3899,26 @@ time_t mg_socket_if_poll(struct mg_iface *iface, int timeout_ms) { } #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; @@ -3724,21 +3941,20 @@ int mg_socketpair(sock_t sp[2], int sock_type) { } 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 - : accept(sock, &sa.sa, &len))) == + } 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) closesocket(sock); + if (sock_type == SOCK_STREAM) mg_socketpair_close(&sock); ret = 1; } if (!ret) { - if (sp[0] != INVALID_SOCKET) closesocket(sp[0]); - if (sp[1] != INVALID_SOCKET) closesocket(sp[1]); - if (sock != INVALID_SOCKET) closesocket(sock); - sock = sp[0] = sp[1] = INVALID_SOCKET; + 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; @@ -3764,6 +3980,10 @@ void mg_sock_to_str(sock_t sock, char *buf, size_t len, int 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); } @@ -3789,9 +4009,9 @@ void mg_socket_if_get_conn_addr(struct mg_connection *nc, int remote, } /* clang-format on */ -struct mg_iface_vtable mg_socket_iface_vtable = MG_SOCKET_IFACE_VTABLE; +const struct mg_iface_vtable mg_socket_iface_vtable = MG_SOCKET_IFACE_VTABLE; #if MG_NET_IF == MG_NET_IF_SOCKET -struct mg_iface_vtable mg_default_iface_vtable = MG_SOCKET_IFACE_VTABLE; +const struct mg_iface_vtable mg_default_iface_vtable = MG_SOCKET_IFACE_VTABLE; #endif #endif /* MG_ENABLE_NET_IF_SOCKET */ @@ -3844,7 +4064,7 @@ void mg_tun_if_tcp_send(struct mg_connection *nc, const void *buf, size_t len) { #if MG_ENABLE_HEXDUMP char hex[512]; mg_hexdump(buf, len, hex, sizeof(hex)); - LOG(LL_DEBUG, ("sending to stream %zu:\n%s", stream_id, hex)); + LOG(LL_DEBUG, ("sending to stream 0x%x:\n%s", (unsigned int) stream_id, hex)); #endif mg_tun_send_frame(client->disp, stream_id, MG_TUN_DATA_FRAME, 0, msg); @@ -3875,7 +4095,7 @@ void mg_tun_if_destroy_conn(struct mg_connection *nc) { uint32_t stream_id = (uint32_t)(uintptr_t) nc->mgr_data; struct mg_str msg = {NULL, 0}; - LOG(LL_DEBUG, ("closing %zu:", stream_id)); + LOG(LL_DEBUG, ("closing 0x%x:", (unsigned int) stream_id)); mg_tun_send_frame(client->disp, stream_id, MG_TUN_DATA_FRAME, MG_TUN_F_END_STREAM, msg); } @@ -3931,13 +4151,14 @@ struct mg_connection *mg_tun_if_find_conn(struct mg_tun_client *client, if (stream_id > client->last_stream_id) { /* create a new connection */ - LOG(LL_DEBUG, ("new stream 0x%lx, accepting", stream_id)); + LOG(LL_DEBUG, ("new stream 0x%x, accepting", (unsigned int) stream_id)); nc = mg_if_accept_new_conn(client->listener); nc->mgr_data = (void *) (uintptr_t) stream_id; client->last_stream_id = stream_id; } else { - LOG(LL_DEBUG, ("Ignoring stream 0x%lx (last_stream_id 0x%lx)", stream_id, - client->last_stream_id)); + LOG(LL_DEBUG, + ("Ignoring stream 0x%x (last_stream_id 0x%x)", (unsigned int) stream_id, + (unsigned int) client->last_stream_id)); } return nc; @@ -3965,7 +4186,7 @@ struct mg_connection *mg_tun_if_find_conn(struct mg_tun_client *client, } /* clang-format on */ -struct mg_iface_vtable mg_tun_iface_vtable = MG_TUN_IFACE_VTABLE; +const struct mg_iface_vtable mg_tun_iface_vtable = MG_TUN_IFACE_VTABLE; #endif /* MG_ENABLE_TUN */ #ifdef MG_MODULE_LINES @@ -4040,6 +4261,21 @@ enum mg_ssl_if_result mg_ssl_if_conn_init( 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) { @@ -4124,6 +4360,12 @@ int mg_ssl_if_write(struct mg_connection *nc, const void *data, size_t len) { 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; @@ -4266,7 +4508,7 @@ static unsigned int mg_ssl_if_ossl_psk_cb(SSL *ssl, const char *hint, unsigned char *psk, unsigned int max_psk_len) { struct mg_ssl_if_ctx *ctx = - (struct mg_ssl_if_ctx *) ssl->ctx->msg_callback_arg; + (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) { @@ -4315,8 +4557,7 @@ static enum mg_ssl_if_result mg_ssl_if_ossl_set_psk(struct mg_ssl_if_ctx *ctx, 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); - /* Hack: there is no field for us to keep this, so we use msg_callback_arg */ - ctx->ssl_ctx->msg_callback_arg = ctx; + SSL_CTX_set_app_data(ctx->ssl_ctx, ctx); return MG_SSL_OK; } #else @@ -4557,6 +4798,12 @@ static void mg_ssl_if_mbed_free_certs_and_keys(struct mg_ssl_if_ctx *ctx) { } 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; @@ -4611,6 +4858,12 @@ int mg_ssl_if_write(struct mg_connection *nc, const void *data, size_t len) { 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; @@ -4632,13 +4885,21 @@ void mg_ssl_if_conn_free(struct mg_connection *nc) { 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; @@ -4672,7 +4933,9 @@ static enum mg_ssl_if_result mg_use_cert(struct mg_ssl_if_ctx *ctx, static const int mg_s_cipher_list[] = { 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, @@ -4793,23 +5056,26 @@ int mg_ssl_if_mbed_random(void *ctx, unsigned char *buf, size_t len) { /* Amalgamated: #include "mongoose/src/uri.h" */ /* - * scan string until `sep`, keeping track of component boundaries in `res`. + * 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, char sep, - struct mg_str *res) { +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)++) { - if (**p == sep) { - break; + 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(struct mg_str uri, struct mg_str *scheme, +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) { @@ -4833,8 +5099,13 @@ int mg_parse_uri(struct mg_str uri, struct mg_str *scheme, * 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; @@ -4854,7 +5125,7 @@ int mg_parse_uri(struct mg_str uri, struct mg_str *scheme, rscheme.p = uri.p; rscheme.len = p - uri.p; state = P_USER_INFO; - p += 2; /* point to last separator char */ + p += 3; } else { rhost.p = uri.p; rhost.len = p - uri.p; @@ -4862,27 +5133,35 @@ int mg_parse_uri(struct mg_str uri, struct mg_str *scheme, } break; case P_USER_INFO: - p++; ruser_info.p = p; for (; p < end; p++) { - if (*p == '@') { - state = P_HOST; - break; - } else if (*p == '/') { + if (*p == '@' || *p == '[') { break; } } - if (p == end || *p == '/') { + if (p == end || *p == '/' || *p == '[') { /* backtrack and parse as host */ - state = P_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; - for (; p < end; 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; @@ -4891,7 +5170,6 @@ int mg_parse_uri(struct mg_str uri, struct mg_str *scheme, break; } } - rhost.len = p - rhost.p; break; case P_PORT: p++; @@ -4906,9 +5184,11 @@ int mg_parse_uri(struct mg_str uri, struct mg_str *scheme, break; case P_REST: /* `p` points to separator. `path` includes the separator */ - parse_uri_component(&p, end, '?', &rpath); - parse_uri_component(&p, end, '#', &rquery); - parse_uri_component(&p, end, '\0', &rfragment); + parse_uri_component(&p, end, "?#", &rpath); + if (p < end && *(p - 1) == '?') { + parse_uri_component(&p, end, "#", &rquery); + } + parse_uri_component(&p, end, "", &rfragment); break; } } @@ -4939,7 +5219,7 @@ int mg_normalize_uri_path(const struct mg_str *in, struct mg_str *out) { while (s < se) { const char *next = s; struct mg_str component; - parse_uri_component(&next, se, '/', &component); + parse_uri_component(&next, se, "/", &component); if (mg_vcmp(&component, ".") == 0) { /* Yum. */ } else if (mg_vcmp(&component, "..") == 0) { @@ -4958,6 +5238,76 @@ int mg_normalize_uri_path(const struct mg_str *in, struct mg_str *out) { 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/http.c" #endif @@ -4968,10 +5318,9 @@ int mg_normalize_uri_path(const struct mg_str *in, struct mg_str *out) { #if MG_ENABLE_HTTP +/* Amalgamated: #include "common/md5.h" */ /* Amalgamated: #include "mongoose/src/internal.h" */ /* Amalgamated: #include "mongoose/src/util.h" */ -/* Amalgamated: #include "common/sha1.h" */ -/* Amalgamated: #include "common/md5.h" */ static const char *mg_version_header = "Mongoose/" MG_VERSION; @@ -4997,9 +5346,14 @@ struct mg_http_proto_data_chuncked { struct mg_http_endpoint { struct mg_http_endpoint *next; - const char *name; - size_t name_len; + 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 { @@ -5027,6 +5381,10 @@ struct mg_reverse_proxy_data { struct mg_connection *linked_conn; }; +struct mg_ws_proto_data { + size_t reass_len; /* Defragmented size of the frame so far. */ +}; + struct mg_http_proto_data { #if MG_ENABLE_FILESYSTEM struct mg_http_proto_data_file file; @@ -5036,18 +5394,23 @@ struct mg_http_proto_data { #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_event_handler_t ev_handler, - struct mg_connect_opts opts, const char *schema, const char *schema_ssl, - const char *url, const char **path, char **user, char **pass, char **addr); + 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) { @@ -5062,9 +5425,9 @@ static struct mg_http_proto_data *mg_http_get_proto_data( #if MG_ENABLE_HTTP_STREAMING_MULTIPART static void mg_http_free_proto_data_mp_stream( struct mg_http_multipart_stream *mp) { - free((void *) mp->boundary); - free((void *) mp->var_name); - free((void *) mp->file_name); + MG_FREE((void *) mp->boundary); + MG_FREE((void *) mp->var_name); + MG_FREE((void *) mp->file_name); memset(mp, 0, sizeof(*mp)); } #endif @@ -5085,8 +5448,10 @@ static void mg_http_free_proto_data_endpoints(struct mg_http_endpoint **ep) { while (current != NULL) { struct mg_http_endpoint *tmp = current->next; - free((void *) current->name); - free(current); + MG_FREE((void *) current->uri_pattern.p); + MG_FREE((void *) current->auth_domain); + MG_FREE((void *) current->auth_file); + MG_FREE(current); current = tmp; } @@ -5122,7 +5487,7 @@ static void mg_http_conn_destructor(void *proto_data) { #endif mg_http_free_proto_data_endpoints(&pd->endpoints); mg_http_free_reverse_proxy_data(&pd->reverse_proxy_data); - free(proto_data); + MG_FREE(proto_data); } #if MG_ENABLE_FILESYSTEM @@ -5363,28 +5728,33 @@ static void mg_http_transfer_file_data(struct mg_connection *nc) { if (pd->file.type == DATA_FILE) { struct mbuf *io = &nc->send_mbuf; - if (io->len < sizeof(buf)) { - to_read = sizeof(buf) - io->len; + if (io->len >= MG_MAX_HTTP_SEND_MBUF) { + to_read = 0; + } else { + to_read = MG_MAX_HTTP_SEND_MBUF - io->len; } - - if (left > 0 && to_read > left) { + if (to_read > left) { to_read = left; } - - if (to_read == 0) { - /* Rate limiting. send_mbuf is too full, wait until it's drained. */ - } else if (pd->file.sent < pd->file.cl && - (n = fread(buf, 1, to_read, pd->file.fp)) > 0) { - mg_send(nc, buf, n); - pd->file.sent += n; + 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 = fwrite(io->buf, 1, to_write, pd->file.fp); + size_t n = mg_fwrite(io->buf, 1, to_write, pd->file.fp); if (n > 0) { mbuf_remove(io, n); pd->file.sent += n; @@ -5481,7 +5851,7 @@ MG_INTERNAL size_t mg_handle_chunked(struct mg_connection *nc, /* Send MG_EV_HTTP_CHUNK event */ nc->flags &= ~MG_F_DELETE_CHUNK; - mg_call(nc, nc->handler, MG_EV_HTTP_CHUNK, hm); + 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) { @@ -5502,10 +5872,10 @@ MG_INTERNAL size_t mg_handle_chunked(struct mg_connection *nc, return body_len; } -static mg_event_handler_t mg_http_get_endpoint_handler( - struct mg_connection *nc, struct mg_str *uri_path) { +struct mg_http_endpoint *mg_http_get_endpoint_handler(struct mg_connection *nc, + struct mg_str *uri_path) { struct mg_http_proto_data *pd; - mg_event_handler_t ret = NULL; + struct mg_http_endpoint *ret = NULL; int matched, matched_max = 0; struct mg_http_endpoint *ep; @@ -5517,11 +5887,10 @@ static mg_event_handler_t mg_http_get_endpoint_handler( ep = pd->endpoints; while (ep != NULL) { - const struct mg_str name_s = {ep->name, ep->name_len}; - if ((matched = mg_match_prefix_n(name_s, *uri_path)) != -1) { + if ((matched = mg_match_prefix_n(ep->uri_pattern, *uri_path)) != -1) { if (matched > matched_max) { /* Looking for the longest suitable handler */ - ret = ep->handler; + ret = ep; matched_max = matched; } } @@ -5532,20 +5901,6 @@ static mg_event_handler_t mg_http_get_endpoint_handler( return ret; } -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); - - if (pd->endpoint_handler == NULL || ev == MG_EV_HTTP_REQUEST) { - pd->endpoint_handler = - ev == MG_EV_HTTP_REQUEST - ? mg_http_get_endpoint_handler(nc->listener, &hm->uri) - : NULL; - } - mg_call(nc, pd->endpoint_handler ? pd->endpoint_handler : nc->handler, ev, - hm); -} - #if MG_ENABLE_HTTP_STREAMING_MULTIPART static void mg_http_multipart_continue(struct mg_connection *nc); @@ -5554,26 +5909,42 @@ static void mg_http_multipart_begin(struct mg_connection *nc, #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, +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) { +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, &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, +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) { - struct http_message shm; - struct http_message *hm = &shm; +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; @@ -5602,14 +5973,15 @@ void mg_http_handler(struct mg_connection *nc, int ev, void *ev_data) { 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), - MG_EV_HTTP_PART_END, &mp); + 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), - MG_EV_HTTP_MULTIPART_REQUEST_END, &mp); + nc->user_data, MG_EV_HTTP_MULTIPART_REQUEST_END, &mp); } else #endif - if (io->len > 0 && mg_parse_http(io->buf, io->len, hm, is_req) > 0) { + 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. @@ -5617,8 +5989,10 @@ void mg_http_handler(struct mg_connection *nc, int ev, void *ev_data) { 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 @@ -5627,10 +6001,11 @@ void mg_http_handler(struct mg_connection *nc, int ev, void *ev_data) { } #endif - mg_call(nc, nc->handler, ev, ev_data); + 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) { @@ -5672,11 +6047,12 @@ void mg_http_handler(struct mg_connection *nc, int ev, void *ev_data) { mbuf_remove(io, req_len); nc->proto_handler = mg_ws_handler; nc->flags |= MG_F_IS_WEBSOCKET; - mg_call(nc, nc->handler, MG_EV_WEBSOCKET_HANDSHAKE_DONE, NULL); - mg_ws_handler(nc, MG_EV_RECV, ev_data); + 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) { - mg_event_handler_t handler; + struct mg_http_endpoint *ep; /* This is a websocket request. Switch protocol handlers. */ mbuf_remove(io, req_len); @@ -5688,79 +6064,45 @@ void mg_http_handler(struct mg_connection *nc, int ev, void *ev_data) { * deliver subsequent websocket events to this handler after the * protocol switch. */ - handler = mg_http_get_endpoint_handler(nc->listener, &hm->uri); - if (handler != NULL) { - nc->handler = handler; + 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, MG_EV_WEBSOCKET_HANDSHAKE_REQUEST, hm); + 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); + mg_ws_handshake(nc, vec, hm); } - mg_call(nc, nc->handler, MG_EV_WEBSOCKET_HANDSHAKE_DONE, NULL); - mg_ws_handler(nc, MG_EV_RECV, ev_data); + 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 <= io->len) { + else if (hm->message.len > pd->rcvd) { + /* Not yet received all HTTP body, deliver MG_EV_HTTP_CHUNK */ + deliver_chunk(nc, hm, req_len); + } else { + /* We did receive all HTTP body. */ int trigger_ev = nc->listener ? MG_EV_HTTP_REQUEST : MG_EV_HTTP_REPLY; - -/* Whole HTTP message is fully buffered, call event handler */ - -#if MG_ENABLE_JAVASCRIPT - v7_val_t v1, v2, headers, req, args, res; - struct v7 *v7 = nc->mgr->v7; - const char *ev_name = trigger_ev == MG_EV_HTTP_REPLY ? "onsnd" : "onrcv"; - int i, js_callback_handled_request = 0; - - if (v7 != NULL) { - /* Lookup JS callback */ - v1 = v7_get(v7, v7_get_global(v7), "Http", ~0); - v2 = v7_get(v7, v1, ev_name, ~0); - - /* Create callback params. TODO(lsm): own/disown those */ - args = v7_mk_array(v7); - req = v7_mk_object(v7); - headers = v7_mk_object(v7); - - /* Populate request object */ - v7_set(v7, req, "method", ~0, - v7_mk_string(v7, hm->method.p, hm->method.len, 1)); - v7_set(v7, req, "uri", ~0, v7_mk_string(v7, hm->uri.p, hm->uri.len, 1)); - v7_set(v7, req, "body", ~0, - v7_mk_string(v7, hm->body.p, hm->body.len, 1)); - v7_set(v7, req, "headers", ~0, headers); - for (i = 0; hm->header_names[i].len > 0; i++) { - const struct mg_str *name = &hm->header_names[i]; - const struct mg_str *value = &hm->header_values[i]; - v7_set(v7, headers, name->p, name->len, - v7_mk_string(v7, value->p, value->len, 1)); - } - - /* Invoke callback. TODO(lsm): report errors */ - v7_array_push(v7, args, v7_mk_foreign(v7, nc)); - v7_array_push(v7, args, req); - if (v7_apply(v7, v2, V7_UNDEFINED, args, &res) == V7_OK && - v7_is_truthy(v7, res)) { - js_callback_handled_request++; - } - } - - /* If JS callback returns true, stop request processing */ - if (js_callback_handled_request) { - nc->flags |= MG_F_SEND_AND_CLOSE; - } else { - mg_http_call_endpoint_handler(nc, trigger_ev, hm); - } -#else + 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); -#endif mbuf_remove(io, hm->message.len); + pd->rcvd = 0; } } - (void) pd; } static size_t mg_get_line_len(const char *buf, size_t buf_len) { @@ -5811,17 +6153,19 @@ static void mg_http_multipart_begin(struct mg_connection *nc, */ 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; - pd->endpoint_handler = mg_http_get_endpoint_handler(nc->listener, &hm->uri); - if (pd->endpoint_handler == 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_call(nc, pd->endpoint_handler, MG_EV_HTTP_MULTIPART_REQUEST, hm); + mg_http_call_endpoint_handler(nc, MG_EV_HTTP_MULTIPART_REQUEST, hm); mbuf_remove(io, req_len); } @@ -5842,7 +6186,7 @@ static void mg_http_multipart_call_handler(struct mg_connection *c, int ev, mp.user_data = pd->mp_stream.user_data; mp.data.p = data; mp.data.len = data_len; - mg_call(c, pd->endpoint_handler, ev, &mp); + mg_call(c, pd->endpoint_handler, c->user_data, ev, &mp); pd->mp_stream.user_data = mp.user_data; } @@ -5863,9 +6207,9 @@ 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); - free((void *) pd->mp_stream.file_name); + MG_FREE((void *) pd->mp_stream.file_name); pd->mp_stream.file_name = NULL; - free((void *) pd->mp_stream.var_name); + 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); @@ -5943,9 +6287,9 @@ static int mg_http_multipart_process_boundary(struct mg_connection *c) { mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_END, NULL, 0); } - free((void *) pd->mp_stream.file_name); + MG_FREE((void *) pd->mp_stream.file_name); pd->mp_stream.file_name = strdup(file_name); - free((void *) pd->mp_stream.var_name); + MG_FREE((void *) pd->mp_stream.var_name); pd->mp_stream.var_name = strdup(var_name); mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_BEGIN, NULL, 0); @@ -6416,6 +6760,13 @@ int mg_get_http_var(const struct mg_str *buf, const char *name, char *dst, 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 + */ if (dst == NULL || dst_len == 0) { len = -2; } else if (buf->p == NULL || name == NULL || buf->len == 0) { @@ -6424,7 +6775,7 @@ int mg_get_http_var(const struct mg_str *buf, const char *name, char *dst, } else { name_len = strlen(name); e = buf->p + buf->len; - len = -1; + len = 0; dst[0] = '\0'; for (p = buf->p; p + name_len < e; p++) { @@ -6436,8 +6787,9 @@ int mg_get_http_var(const struct mg_str *buf, const char *name, char *dst, 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 = -2; + len = -3; } break; } @@ -6590,6 +6942,42 @@ static int mg_is_file_hidden(const char *path, } #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, @@ -6598,7 +6986,6 @@ static void mg_mkmd5resp(const char *method, size_t method_len, const char *uri, 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, @@ -6642,14 +7029,13 @@ static int mg_check_nonce(const char *nonce) { int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain, FILE *fp) { struct mg_str *hdr; - char buf[128], f_user[sizeof(buf)], f_ha1[sizeof(buf)], f_domain[sizeof(buf)]; - char user[50], cnonce[33], response[40], uri[200], qop[20], nc[20], nonce[30]; - char expected_response[33]; + char username[50], cnonce[64], response[40], uri[200], qop[20], nc[20], + nonce[30]; /* Parse "Authorization:" header, fail fast on parse error */ if (hm == NULL || fp == NULL || (hdr = mg_get_http_header(hm, "Authorization")) == NULL || - mg_http_parse_header(hdr, "username", user, sizeof(user)) == 0 || + mg_http_parse_header(hdr, "username", username, sizeof(username)) == 0 || mg_http_parse_header(hdr, "cnonce", cnonce, sizeof(cnonce)) == 0 || mg_http_parse_header(hdr, "response", response, sizeof(response)) == 0 || mg_http_parse_header(hdr, "uri", uri, sizeof(uri)) == 0 || @@ -6660,6 +7046,26 @@ int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain, return 0; } + /* NOTE(lsm): due to a bug in MSIE, we do not compare URIs */ + + return 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); +} + +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: @@ -6667,16 +7073,16 @@ int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain, */ while (fgets(buf, sizeof(buf), fp) != NULL) { if (sscanf(buf, "%[^:]:%[^:]:%s", f_user, f_domain, f_ha1) == 3 && - strcmp(user, f_user) == 0 && - /* NOTE(lsm): due to a bug in MSIE, we do not compare URIs */ - strcmp(auth_domain, f_domain) == 0) { - /* User and domain matched, check the password */ - mg_mkmd5resp( - hm->method.p, hm->method.len, hm->uri.p, - hm->uri.len + (hm->query_string.len ? hm->query_string.len + 1 : 0), - f_ha1, strlen(f_ha1), nonce, strlen(nonce), nc, strlen(nc), cnonce, - strlen(cnonce), qop, strlen(qop), expected_response); - return mg_casecmp(response, expected_response) == 0; + 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; } } @@ -6684,10 +7090,10 @@ int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain, return 0; } -static int mg_is_authorized(struct http_message *hm, const char *path, - int is_directory, const char *domain, - const char *passwords_file, - int is_global_pass_file) { +static int mg_http_is_authorized(struct http_message *hm, struct mg_str path, + int is_directory, const char *domain, + const char *passwords_file, + int is_global_pass_file) { char buf[MG_MAX_PATH]; const char *p; FILE *fp; @@ -6697,12 +7103,13 @@ static int mg_is_authorized(struct http_message *hm, const char *path, if (is_global_pass_file) { fp = mg_fopen(passwords_file, "r"); } else if (is_directory) { - snprintf(buf, sizeof(buf), "%s%c%s", path, DIRSEP, passwords_file); + snprintf(buf, sizeof(buf), "%.*s%c%s", (int) path.len, path.p, DIRSEP, + passwords_file); fp = mg_fopen(buf, "r"); } else { - p = strrchr(path, DIRSEP); - if (p == NULL) p = path; - snprintf(buf, sizeof(buf), "%.*s%c%s", (int) (p - path), path, DIRSEP, + 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"); } @@ -6713,15 +7120,16 @@ static int mg_is_authorized(struct http_message *hm, const char *path, } } - LOG(LL_DEBUG, ("%s '%s' %d %d", path, passwords_file ? passwords_file : "", - is_global_pass_file, authorized)); + LOG(LL_DEBUG, + ("%.*s %s %d %d", (int) path.len, path.p, + passwords_file ? passwords_file : "", is_global_pass_file, authorized)); return authorized; } #else -static int mg_is_authorized(struct http_message *hm, const char *path, - int is_directory, const char *domain, - const char *passwords_file, - int is_global_pass_file) { +static int mg_http_is_authorized(struct http_message *hm, + const struct mg_str path, int is_directory, + const char *domain, const char *passwords_file, + int is_global_pass_file) { (void) hm; (void) path; (void) is_directory; @@ -6733,28 +7141,6 @@ static int mg_is_authorized(struct http_message *hm, const char *path, #endif #if MG_ENABLE_DIRECTORY_LISTING -static size_t mg_url_encode(const char *src, size_t s_len, char *dst, - size_t dst_len) { - static const char *dont_escape = "._-$,;~()/"; - static const char *hex = "0123456789abcdef"; - size_t i = 0, j = 0; - - for (i = j = 0; dst_len > 0 && i < s_len && j + 2 < dst_len - 1; i++, j++) { - if (isalnum(*(const unsigned char *) (src + i)) || - strchr(dont_escape, *(const unsigned char *) (src + i)) != NULL) { - dst[j] = src[i]; - } else if (j + 3 < dst_len) { - dst[j] = '%'; - dst[j + 1] = hex[(*(const unsigned char *) (src + i)) >> 4]; - dst[j + 2] = hex[(*(const unsigned char *) (src + i)) & 0xf]; - j += 2; - } - } - - dst[j] = '\0'; - return j; -} - static void mg_escape(const char *src, char *dst, size_t dst_len) { size_t n = 0; while (*src != '\0' && n + 5 < dst_len) { @@ -6770,10 +7156,11 @@ static void mg_escape(const char *src, char *dst, size_t dst_len) { static void mg_print_dir_entry(struct mg_connection *nc, const char *file_name, cs_stat_t *stp) { - char size[64], mod[64], href[MAX_PATH_SIZE * 3], path[MAX_PATH_SIZE]; + 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]"); @@ -6794,19 +7181,20 @@ static void mg_print_dir_entry(struct mg_connection *nc, const char *file_name, } strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", localtime(&stp->st_mtime)); mg_escape(file_name, path, sizeof(path)); - mg_url_encode(file_name, strlen(file_name), href, sizeof(href)); + href = mg_url_encode(mg_mk_str(file_name)); mg_printf_http_chunk(nc, "<tr><td><a href=\"%s%s\">%s%s</a></td>" "<td>%s</td><td name=%" INT64_FMT ">%s</td></tr>\n", - href, slash, path, slash, mod, is_dir ? -1 : fsize, + 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[MAX_PATH_SIZE]; + char path[MG_MAX_PATH]; cs_stat_t st; struct dirent *dp; DIR *dirp; @@ -6950,7 +7338,7 @@ static int mg_http_send_port_based_redirect( } static void mg_reverse_proxy_handler(struct mg_connection *nc, int ev, - void *ev_data) { + 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); @@ -6976,6 +7364,10 @@ static void mg_reverse_proxy_handler(struct mg_connection *nc, int ev, 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, @@ -6983,20 +7375,19 @@ void mg_http_reverse_proxy(struct mg_connection *nc, struct mg_str upstream) { struct mg_connection *be; char burl[256], *purl = burl; - char *addr = NULL; - const char *path = NULL; 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_reverse_proxy_handler, opts, "http://", - "https://", purl, &path, NULL /* user */, - NULL /* pass */, &addr); + 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)); @@ -7011,10 +7402,10 @@ void mg_http_reverse_proxy(struct mg_connection *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, - path); + 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", addr); + 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]; @@ -7064,7 +7455,7 @@ static int mg_http_handle_forwarding(struct mg_connection *nc, return 0; } -#endif +#endif /* MG_ENABLE_FILESYSTEM */ MG_INTERNAL int mg_uri_to_local_path(struct http_message *hm, const struct mg_serve_http_opts *opts, @@ -7162,7 +7553,7 @@ MG_INTERNAL int mg_uri_to_local_path(struct http_message *hm, } } if (u >= cp_end) break; - parse_uri_component((const char **) &next, cp_end, '/', &component); + parse_uri_component((const char **) &next, cp_end, "/", &component); if (component.len > 0) { int len; memmove(p + 1, component.p, component.len); @@ -7348,10 +7739,12 @@ MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path, if (is_dav && opts->dav_document_root == NULL) { mg_http_send_error(nc, 501, NULL); - } else if (!mg_is_authorized(hm, path, is_directory, opts->auth_domain, - opts->global_auth_file, 1) || - !mg_is_authorized(hm, path, is_directory, opts->auth_domain, - opts->per_directory_auth_file, 0)) { + } else if (!mg_http_is_authorized(hm, mg_mk_str(path), is_directory, + opts->auth_domain, opts->global_auth_file, + 1) || + !mg_http_is_authorized(hm, mg_mk_str(path), is_directory, + opts->auth_domain, + opts->per_directory_auth_file, 0)) { mg_http_send_digest_auth_request(nc, opts->auth_domain); } else if (is_cgi) { #if MG_ENABLE_HTTP_CGI @@ -7367,11 +7760,11 @@ MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path, } 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_is_authorized(hm, path, is_directory, opts->auth_domain, - opts->dav_auth_file, 1)))) { + } else if (is_dav && (opts->dav_auth_file == NULL || + (strcmp(opts->dav_auth_file, "-") != 0 && + !mg_http_is_authorized(hm, mg_mk_str(path), + is_directory, opts->auth_domain, + opts->dav_auth_file, 1)))) { mg_http_send_digest_auth_request(nc, opts->auth_domain); #endif } else if (!mg_vcmp(&hm->method, "MKCOL")) { @@ -7474,13 +7867,14 @@ void mg_serve_http(struct mg_connection *nc, struct http_message *hm, #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_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 *) calloc(1, sizeof(*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) { @@ -7494,10 +7888,10 @@ void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data, nc->flags |= MG_F_SEND_AND_CLOSE; return; } - fus->lfn = (char *) malloc(lfn.len + 1); + 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) free((char *) lfn.p); + 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"); @@ -7521,7 +7915,7 @@ void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data, struct file_upload_state *fus = (struct file_upload_state *) mp->user_data; if (fus == NULL || fus->fp == NULL) break; - if (fwrite(mp->data.p, 1, mp->data.len, fus->fp) != mp->data.len) { + 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 @@ -7579,115 +7973,60 @@ void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data, */ } if (fus->fp != NULL) fclose(fus->fp); - free(fus->lfn); - free(fus); + 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 */ -/* returns 0 on success, -1 on error */ -MG_INTERNAL int mg_http_common_url_parse(const char *url, const char *schema, - const char *schema_tls, int *use_ssl, - char **user, char **pass, char **addr, - int *port_i, const char **path) { - int addr_len = 0; - int auth_sep_pos = -1; - int user_sep_pos = -1; - int port_pos = -1; - (void) user; - (void) pass; - - if (strncmp(url, schema, strlen(schema)) == 0) { - url += strlen(schema); - } else if (strncmp(url, schema_tls, strlen(schema_tls)) == 0) { - url += strlen(schema_tls); - *use_ssl = 1; -#if !MG_ENABLE_SSL - return -1; /* SSL is not enabled, cannot do HTTPS URLs */ -#endif - } - - while (*url != '\0') { - *addr = (char *) MG_REALLOC(*addr, addr_len + 6 /* space for port too. */); - if (*addr == NULL) { - DBG(("OOM")); - return -1; - } - if (*url == '/') { - break; - } - if (*url == '@') { - auth_sep_pos = addr_len; - user_sep_pos = port_pos; - port_pos = -1; - } - if (*url == ':') port_pos = addr_len; - (*addr)[addr_len++] = *url; - (*addr)[addr_len] = '\0'; - url++; - } - - if (addr_len == 0) goto cleanup; - if (port_pos < 0) { - *port_i = addr_len; - addr_len += sprintf(*addr + addr_len, ":%d", *use_ssl ? 443 : 80); - } else { - *port_i = -1; - } - - if (*path == NULL) *path = url; - - if (**path == '\0') *path = "/"; - - if (user != NULL && pass != NULL) { - if (auth_sep_pos == -1) { - *user = NULL; - *pass = NULL; - } else { - /* user is from 0 to user_sep_pos */ - *user = (char *) MG_MALLOC(user_sep_pos + 1); - memcpy(*user, *addr, user_sep_pos); - (*user)[user_sep_pos] = '\0'; - /* pass is from user_sep_pos + 1 to auth_sep_pos */ - *pass = (char *) MG_MALLOC(auth_sep_pos - user_sep_pos - 1 + 1); - memcpy(*pass, *addr + user_sep_pos + 1, auth_sep_pos - user_sep_pos - 1); - (*pass)[auth_sep_pos - user_sep_pos - 1] = '\0'; - - /* move address proper to the front */ - memmove(*addr, *addr + auth_sep_pos + 1, addr_len - auth_sep_pos); - } - } - - DBG(("%s %s", *addr, *path)); - - return 0; - -cleanup: - MG_FREE(*addr); - return -1; -} - struct mg_connection *mg_connect_http_base( - struct mg_mgr *mgr, mg_event_handler_t ev_handler, - struct mg_connect_opts opts, const char *schema, const char *schema_ssl, - const char *url, const char **path, char **user, char **pass, char **addr) { + 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; - int port_i = -1; + 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_http_common_url_parse(url, schema, schema_ssl, &use_ssl, user, pass, - addr, &port_i, path) < 0) { + 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"); - return NULL; + 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; } - LOG(LL_DEBUG, ("%s use_ssl? %d", url, use_ssl)); + 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 /* @@ -7700,68 +8039,61 @@ struct mg_connection *mg_connect_http_base( } #else MG_SET_PTRPTR(opts.error_string, "ssl is disabled"); - if (user != NULL) MG_FREE(*user); - if (pass != NULL) MG_FREE(*pass); - MG_FREE(*addr); - return NULL; + goto out; #endif } - if ((nc = mg_connect_opt(mgr, *addr, ev_handler, opts)) != NULL) { + if ((nc = mg_connect_opt(mgr, conn_addr, MG_CB(ev_handler, user_data), + opts)) != NULL) { mg_set_protocol_http_websocket(nc); - /* If the port was addred by us, restore the original host. */ - if (port_i >= 0) (*addr)[port_i] = '\0'; } +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_event_handler_t ev_handler, - struct mg_connect_opts opts, - const char *url, - const char *extra_headers, - const char *post_data) { - char *user = NULL, *pass = NULL, *addr = NULL; - const char *path = NULL; +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, ev_handler, opts, "http://", "https://", url, - &path, &user, &pass, &addr); + 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 != NULL) { - mg_basic_auth_header(user, pass, &auth); + if (user.len > 0) { + mg_basic_auth_header(user, null_str, &auth); } - mg_printf(nc, "%s %s HTTP/1.1\r\nHost: %s\r\nContent-Length: %" SIZE_T_FMT + if (post_data == NULL) post_data = ""; + if (extra_headers == NULL) extra_headers = ""; + if (path.len == 0) path = 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 == NULL ? "GET" : "POST", path, addr, - post_data == NULL ? 0 : strlen(post_data), (int) auth.len, - (auth.buf == NULL ? "" : auth.buf), - extra_headers == NULL ? "" : extra_headers, - post_data == NULL ? "" : post_data); + (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); - MG_FREE(user); - MG_FREE(pass); - MG_FREE(addr); return nc; } -struct mg_connection *mg_connect_http(struct mg_mgr *mgr, - mg_event_handler_t ev_handler, - const char *url, - const char *extra_headers, - const char *post_data) { +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, ev_handler, opts, url, extra_headers, - post_data); + 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, @@ -7802,23 +8134,73 @@ size_t mg_parse_multipart(const char *buf, size_t buf_len, char *var_name, return 0; } -void mg_register_http_endpoint(struct mg_connection *nc, const char *uri_path, - mg_event_handler_t handler) { +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 *) calloc(1, sizeof(*new_ep)); + 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->name = strdup(uri_path); - new_ep->name_len = strlen(new_ep->name); + 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, 0 /* is_directory */, + ep->auth_domain, ep->auth_file, + 1 /* 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/http_cgi.c" @@ -7828,6 +8210,10 @@ void mg_register_http_endpoint(struct mg_connection *nc, const char *uri_path, * All rights reserved */ +#ifndef _WIN32 +#include <signal.h> +#endif + #if MG_ENABLE_HTTP && MG_ENABLE_HTTP_CGI #ifndef MG_MAX_CGI_ENVIR_VARS @@ -7920,7 +8306,7 @@ static void mg_spawn_stdio_thread(sock_t sock, HANDLE hPipe, } static void mg_abs_path(const char *utf8_path, char *abs_path, size_t len) { - wchar_t buf[MAX_PATH_SIZE], buf2[MAX_PATH_SIZE]; + 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); @@ -7932,9 +8318,9 @@ static int mg_start_process(const char *interp, const char *cmd, STARTUPINFOW si; PROCESS_INFORMATION pi; HANDLE a[2], b[2], me = GetCurrentProcess(); - wchar_t wcmd[MAX_PATH_SIZE], full_dir[MAX_PATH_SIZE]; - char buf[MAX_PATH_SIZE], buf2[MAX_PATH_SIZE], buf5[MAX_PATH_SIZE], - buf4[MAX_PATH_SIZE], cmdline[MAX_PATH_SIZE]; + 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; @@ -8195,8 +8581,11 @@ static void mg_prepare_cgi_environment(struct mg_connection *nc, } static void mg_cgi_ev_handler(struct mg_connection *cgi_nc, int ev, - void *ev_data) { - struct mg_connection *nc = (struct mg_connection *) cgi_nc->user_data; + 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) { @@ -8257,7 +8646,7 @@ MG_INTERNAL void mg_handle_cgi(struct mg_connection *nc, const char *prog, const struct http_message *hm, const struct mg_serve_http_opts *opts) { struct mg_cgi_env_block blk; - char dir[MAX_PATH_SIZE]; + char dir[MG_MAX_PATH]; const char *p; sock_t fds[2]; @@ -8275,23 +8664,30 @@ MG_INTERNAL void mg_handle_cgi(struct mg_connection *nc, const char *prog, prog = p + 1; } - /* - * Try to create socketpair in a loop until success. mg_socketpair() - * can be interrupted by a signal and fail. - * TODO(lsm): use sigaction to restart interrupted syscall - */ - do { - mg_socketpair(fds, SOCK_STREAM); - } while (fds[0] == INVALID_SOCKET); + 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_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_USER_1; /* Push POST data to the CGI */ if (n > 0 && n < nc->recv_mbuf.len) { @@ -8333,7 +8729,7 @@ static void mg_send_ssi_file(struct mg_connection *nc, struct http_message *hm, static void mg_send_file_data(struct mg_connection *nc, FILE *fp) { char buf[BUFSIZ]; size_t n; - while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) { + while ((n = mg_fread(buf, 1, sizeof(buf), fp)) > 0) { mg_send(nc, buf, n); } } @@ -8341,7 +8737,7 @@ static void mg_send_file_data(struct mg_connection *nc, FILE *fp) { 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[BUFSIZ], path[MAX_PATH_SIZE], *p; + char file_name[MG_MAX_PATH], path[MG_MAX_PATH], *p; FILE *fp; /* @@ -8444,9 +8840,9 @@ static void mg_send_ssi_file(struct mg_connection *nc, struct http_message *hm, cctx.req = hm; cctx.file = mg_mk_str(path); cctx.arg = mg_mk_str(p + d_call.len + 1); - mg_call(nc, NULL, MG_EV_SSI_CALL, + mg_call(nc, NULL, nc->user_data, MG_EV_SSI_CALL, (void *) cctx.arg.p); /* NUL added above */ - mg_call(nc, NULL, MG_EV_SSI_CALL_CTX, &cctx); + 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); @@ -8557,10 +8953,10 @@ static int mg_mkdir(const char *path, uint32_t mode) { static void mg_print_props(struct mg_connection *nc, const char *name, cs_stat_t *stp) { - char mtime[64], buf[MAX_PATH_SIZE * 3]; + 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_url_encode(name, strlen(name), buf, sizeof(buf)); mg_printf(nc, "<d:response>" "<d:href>%s</d:href>" @@ -8574,8 +8970,9 @@ static void mg_print_props(struct mg_connection *nc, const char *name, "<d:status>HTTP/1.1 200 OK</d:status>" "</d:propstat>" "</d:response>\n", - buf, S_ISDIR(stp->st_mode) ? "<d:collection/>" : "", + name_esc.p, S_ISDIR(stp->st_mode) ? "<d:collection/>" : "", (int64_t) stp->st_size, mtime); + free((void *) name_esc.p); } MG_INTERNAL void mg_handle_propfind(struct mg_connection *nc, const char *path, @@ -8595,7 +8992,7 @@ MG_INTERNAL void mg_handle_propfind(struct mg_connection *nc, const char *path, 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[MAX_PATH_SIZE]; + 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); @@ -8662,7 +9059,7 @@ MG_INTERNAL void mg_handle_mkcol(struct mg_connection *nc, const char *path, static int mg_remove_directory(const struct mg_serve_http_opts *opts, const char *dir) { - char path[MAX_PATH_SIZE]; + char path[MG_MAX_PATH]; struct dirent *dp; cs_stat_t st; DIR *dirp; @@ -8697,7 +9094,7 @@ MG_INTERNAL void mg_handle_move(struct mg_connection *c, 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[MAX_PATH_SIZE]; + 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) { @@ -8734,7 +9131,7 @@ static int mg_create_itermediate_directories(const char *path) { /* Create intermediate directories if they do not exist */ for (s = path + 1; *s != '\0'; s++) { if (*s == '/') { - char buf[MAX_PATH_SIZE]; + char buf[MG_MAX_PATH]; cs_stat_t st; snprintf(buf, sizeof(buf), "%.*s", (int) (s - path), path); buf[sizeof(buf) - 1] = '\0'; @@ -8793,12 +9190,12 @@ MG_INTERNAL void mg_handle_put(struct mg_connection *nc, const char *path, #if MG_ENABLE_HTTP && MG_ENABLE_HTTP_WEBSOCKET +/* Amalgamated: #include "common/sha1.h" */ + #ifndef MG_WEBSOCKET_PING_INTERVAL_SECONDS #define MG_WEBSOCKET_PING_INTERVAL_SECONDS 5 #endif -#define MG_WS_NO_HOST_HEADER_MAGIC ((char *) 0x1) - static int mg_is_ws_fragment(unsigned char flags) { return (flags & 0x80) == 0 || (flags & 0x0f) == 0; } @@ -8810,32 +9207,38 @@ static int mg_is_ws_first_fragment(unsigned char flags) { static void mg_handle_incoming_websocket_frame(struct mg_connection *nc, struct websocket_message *wsm) { if (wsm->flags & 0x8) { - mg_call(nc, nc->handler, MG_EV_WEBSOCKET_CONTROL_FRAME, wsm); + mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_CONTROL_FRAME, wsm); } else { - mg_call(nc, nc->handler, MG_EV_WEBSOCKET_FRAME, wsm); + 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); +}; + 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, buf_len = nc->recv_mbuf.len, len, mask_len = 0, header_len = 0; unsigned char *p = (unsigned char *) nc->recv_mbuf.buf, *buf = p, *e = p + buf_len; - unsigned *sizep = (unsigned *) &p[1]; /* Size ptr for defragmented frames */ - int ok, reass = buf_len > 0 && mg_is_ws_fragment(p[0]) && - !(nc->flags & MG_F_WEBSOCKET_NO_DEFRAG); + struct mg_ws_proto_data *wsd = mg_ws_get_proto_data(nc); + int ok; + int reass = buf_len > 0 && mg_is_ws_fragment(p[0]) && + !(nc->flags & MG_F_WEBSOCKET_NO_DEFRAG); /* If that's a continuation frame that must be reassembled, handle it */ - if (reass && !mg_is_ws_first_fragment(p[0]) && - buf_len >= 1 + sizeof(*sizep) && buf_len >= 1 + sizeof(*sizep) + *sizep) { - buf += 1 + sizeof(*sizep) + *sizep; - buf_len -= 1 + sizeof(*sizep) + *sizep; + if (reass && !mg_is_ws_first_fragment(p[0]) && buf_len >= 1 && + buf_len >= 1 + wsd->reass_len) { + buf += 1 + wsd->reass_len; + buf_len -= 1 + wsd->reass_len; } if (buf_len >= 2) { - len = buf[1] & 127; - mask_len = buf[1] & 128 ? 4 : 0; + len = buf[1] & 0x7f; + mask_len = buf[1] & 0x80 ? 4 : 0; if (len < 126 && buf_len >= mask_len) { data_len = len; header_len = 2 + mask_len; @@ -8850,7 +9253,13 @@ static int mg_deliver_websocket_data(struct mg_connection *nc) { } frame_len = header_len + data_len; - ok = frame_len > 0 && frame_len <= buf_len; + ok = (frame_len > 0 && frame_len <= buf_len); + + /* Check for overflow */ + if (frame_len < header_len || frame_len < data_len) { + ok = 0; + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } if (ok) { struct websocket_message wsm; @@ -8869,32 +9278,33 @@ static int mg_deliver_websocket_data(struct mg_connection *nc) { if (reass) { /* On first fragmented frame, nullify size */ if (mg_is_ws_first_fragment(wsm.flags)) { - mbuf_resize(&nc->recv_mbuf, nc->recv_mbuf.size + sizeof(*sizep)); p[0] &= ~0x0f; /* Next frames will be treated as continuation */ - buf = p + 1 + sizeof(*sizep); - *sizep = 0; /* TODO(lsm): fix. this can stomp over frame data */ + buf = p + 1; + wsd->reass_len = 0; } /* Append this frame to the reassembled buffer */ memmove(buf, wsm.data, e - wsm.data); - (*sizep) += wsm.size; + wsd->reass_len += wsm.size; nc->recv_mbuf.len -= wsm.data - buf; /* On last fragmented frame - call user handler and remove data */ if (wsm.flags & 0x80) { - wsm.data = p + 1 + sizeof(*sizep); - wsm.size = *sizep; + wsm.data = p + 1; + wsm.size = wsd->reass_len; mg_handle_incoming_websocket_frame(nc, &wsm); - mbuf_remove(&nc->recv_mbuf, 1 + sizeof(*sizep) + *sizep); + mbuf_remove(&nc->recv_mbuf, 1 + wsd->reass_len); + wsd->reass_len = 0; } } else { /* TODO(lsm): properly handle OOB control frames during defragmentation */ mg_handle_incoming_websocket_frame(nc, &wsm); mbuf_remove(&nc->recv_mbuf, (size_t) frame_len); /* Cleanup frame */ + wsd->reass_len = 0; } - /* If client closes, close too */ - if ((buf[0] & 0x0f) == WEBSOCKET_OP_CLOSE) { + /* If the frame is not reassembled - client closes and close too */ + if (!reass && (buf[0] & 0x0f) == WEBSOCKET_OP_CLOSE) { nc->flags |= MG_F_SEND_AND_CLOSE; } } @@ -9034,8 +9444,8 @@ void mg_printf_websocket_frame(struct mg_connection *nc, int op, } MG_INTERNAL void mg_ws_handler(struct mg_connection *nc, int ev, - void *ev_data) { - mg_call(nc, nc->handler, ev, ev_data); + 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: @@ -9055,11 +9465,14 @@ MG_INTERNAL void mg_ws_handler(struct mg_connection *nc, int ev, default: break; } +#if MG_ENABLE_CALLBACK_USERDATA + (void) user_data; +#endif } #ifndef MG_EXT_SHA1 -static void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[], - const size_t *msg_lens, uint8_t *digest) { +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); @@ -9074,21 +9487,28 @@ extern void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[], #endif MG_INTERNAL void mg_ws_handshake(struct mg_connection *nc, - const struct mg_str *key) { + 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%s%s", + mg_printf(nc, "%s", "HTTP/1.1 101 Switching Protocols\r\n" "Upgrade: websocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Accept: ", - b64_sha, "\r\n\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)); } @@ -9103,6 +9523,18 @@ 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]; @@ -9113,7 +9545,7 @@ void mg_send_websocket_handshake3(struct mg_connection *nc, const char *path, mg_base64_encode((unsigned char *) &nonce, sizeof(nonce), key); mbuf_init(&auth, 0); - if (user != NULL) { + if (user.len > 0) { mg_basic_auth_header(user, pass, &auth); } @@ -9124,23 +9556,26 @@ void mg_send_websocket_handshake3(struct mg_connection *nc, const char *path, * because it handles NULL specially (and incorrectly). */ mg_printf(nc, - "GET %s HTTP/1.1\r\n" + "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", - path, (int) auth.len, (auth.buf == NULL ? "" : auth.buf), key); + (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 != MG_WS_NO_HOST_HEADER_MAGIC) { - mg_printf(nc, "Host: %s\r\n", host); + 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 != NULL) { - mg_printf(nc, "Sec-WebSocket-Protocol: %s\r\n", protocol); + if (protocol.len > 0) { + mg_printf(nc, "Sec-WebSocket-Protocol: %.*s\r\n", (int) protocol.len, + protocol.p); } - if (extra_headers != NULL) { - mg_printf(nc, "%s", extra_headers); + if (extra_headers.len > 0) { + mg_printf(nc, "%.*s", (int) extra_headers.len, extra_headers.p); } mg_printf(nc, "\r\n"); @@ -9149,39 +9584,36 @@ void mg_send_websocket_handshake3(struct mg_connection *nc, const char *path, void mg_send_websocket_handshake(struct mg_connection *nc, const char *path, const char *extra_headers) { - mg_send_websocket_handshake2(nc, path, MG_WS_NO_HOST_HEADER_MAGIC, NULL, - extra_headers); -} - -struct mg_connection *mg_connect_ws_opt(struct mg_mgr *mgr, - mg_event_handler_t ev_handler, - struct mg_connect_opts opts, - const char *url, const char *protocol, - const char *extra_headers) { - char *user = NULL, *pass = NULL, *addr = NULL; - const char *path = NULL; + 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, ev_handler, opts, "ws://", "wss://", url, &path, - &user, &pass, &addr); - + 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_handshake3(nc, path, addr, protocol, extra_headers, user, - pass); + mg_send_websocket_handshake3v(nc, path, host, mg_mk_str(protocol), + mg_mk_str(extra_headers), user_info, + null_str); } - - MG_FREE(addr); - MG_FREE(user); - MG_FREE(pass); return nc; } -struct mg_connection *mg_connect_ws(struct mg_mgr *mgr, - mg_event_handler_t ev_handler, - const char *url, const char *protocol, - const char *extra_headers) { +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, ev_handler, opts, url, protocol, extra_headers); + 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 @@ -9210,14 +9642,10 @@ const char *mg_skip(const char *s, const char *end, const char *delims, return s; } -static int lowercase(const char *s) { - return tolower(*(const unsigned char *) s); -} - -#if MG_ENABLE_FILESYSTEM +#if MG_ENABLE_FILESYSTEM && !defined(MG_USER_FILE_FUNCTIONS) int mg_stat(const char *path, cs_stat_t *st) { #ifdef _WIN32 - wchar_t wpath[MAX_PATH_SIZE]; + wchar_t wpath[MG_MAX_PATH]; to_wchar(path, wpath, ARRAY_SIZE(wpath)); DBG(("[%ls] -> %d", wpath, _wstati64(wpath, st))); return _wstati64(wpath, st); @@ -9228,7 +9656,7 @@ int mg_stat(const char *path, cs_stat_t *st) { FILE *mg_fopen(const char *path, const char *mode) { #ifdef _WIN32 - wchar_t wpath[MAX_PATH_SIZE], wmode[10]; + 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); @@ -9239,13 +9667,21 @@ FILE *mg_fopen(const char *path, const char *mode) { int mg_open(const char *path, int flag, int mode) { /* LCOV_EXCL_LINE */ #if defined(_WIN32) && !defined(WINCE) - wchar_t wpath[MAX_PATH_SIZE]; + 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) { @@ -9292,10 +9728,10 @@ void mg_set_close_on_exec(sock_t sock) { #endif } -void mg_sock_addr_to_str(const union socket_address *sa, char *buf, size_t len, - int flags) { +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; + if (buf == NULL || len <= 0) return 0; memset(buf, 0, len); #if MG_ENABLE_IPV6 is_v6 = sa->sa.sa_family == AF_INET6; @@ -9345,18 +9781,19 @@ void mg_sock_addr_to_str(const union socket_address *sa, char *buf, size_t len, } } - return; + return strlen(buf); cleanup: *buf = '\0'; + return 0; } -void mg_conn_addr_to_str(struct mg_connection *nc, char *buf, size_t len, - int flags) { +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); - mg_sock_addr_to_str(&sa, buf, len, flags); + return mg_sock_addr_to_str(&sa, buf, len, flags); } #if MG_ENABLE_HEXDUMP @@ -9405,8 +9842,26 @@ void mg_hexdumpf(FILE *fp, const void *buf, int len) { void mg_hexdump_connection(struct mg_connection *nc, const char *path, const void *buf, int num_bytes, int ev) { FILE *fp = NULL; - char *hexbuf, src[60], dst[60]; - int buf_size = num_bytes * 5 + 100; + 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 = "<A"; + break; + case MG_EV_CONNECT: + tag = "C>"; + break; + case MG_EV_CLOSE: + tag = "XX"; + break; + } + if (tag == NULL) return; /* Don't log MG_EV_TIMER, etc */ if (strcmp(path, "-") == 0) { fp = stdout; @@ -9424,20 +9879,12 @@ void mg_hexdump_connection(struct mg_connection *nc, const char *path, mg_conn_addr_to_str(nc, dst, sizeof(dst), MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT | MG_SOCK_STRINGIFY_REMOTE); - fprintf( - fp, "%lu %p %s %s %s %d\n", (unsigned long) mg_time(), (void *) nc, src, - ev == MG_EV_RECV ? "<-" : ev == MG_EV_SEND - ? "->" - : ev == MG_EV_ACCEPT - ? "<A" - : ev == MG_EV_CONNECT ? "C>" : "XX", - dst, num_bytes); - if (num_bytes > 0 && (hexbuf = (char *) MG_MALLOC(buf_size)) != NULL) { - mg_hexdump(buf, num_bytes, hexbuf, buf_size); - fprintf(fp, "%s", hexbuf); - MG_FREE(hexbuf); + fprintf(fp, "%lu %p %s %s %s %d\n", (unsigned long) mg_time(), (void *) nc, + src, tag, dst, (int) num_bytes); + if (num_bytes > 0) { + mg_hexdumpf(fp, buf, num_bytes); } - if (fp != stdin && fp != stdout) fclose(fp); + if (fp != stdout && fp != stderr) fclose(fp); } #endif @@ -9447,90 +9894,6 @@ int mg_is_big_endian(void) { return ((char *) &n)[0] == 0; } -const char *mg_next_comma_list_entry(const char *list, struct mg_str *val, - struct mg_str *eq_val) { - if (list == NULL || *list == '\0') { - /* End of the list */ - list = NULL; - } else { - val->p = list; - if ((list = strchr(val->p, ',')) != NULL) { - /* Comma found. Store length and shift the list ptr */ - val->len = list - val->p; - list++; - } else { - /* This value is the last one */ - list = val->p + strlen(val->p); - val->len = list - val->p; - } - - 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; -} - -int mg_match_prefix_n(const struct mg_str pattern, const struct mg_str str) { - const char *or_str; - size_t len, i = 0, j = 0; - int res; - - if ((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; i++, j++) { - if (pattern.p[i] == '?' && j != str.len) { - continue; - } else if (pattern.p[i] == '$') { - return j == str.len ? (int) j : -1; - } else if (pattern.p[i] == '*') { - i++; - if (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) { - 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 == -1 && len-- > 0); - return res == -1 ? -1 : (int) (j + res + len); - } else if (lowercase(&pattern.p[i]) != lowercase(&str.p[j])) { - return -1; - } - } - return j; -} - -int mg_match_prefix(const char *pattern, int pattern_len, const char *str) { - const struct mg_str pstr = {pattern, (size_t) pattern_len}; - return mg_match_prefix_n(pstr, mg_mk_str(str)); -} - DO_NOT_WARN_UNUSED MG_INTERNAL int mg_get_errno(void) { #ifndef WINCE return errno; @@ -9552,7 +9915,7 @@ void mg_mbuf_append_base64(struct mbuf *mbuf, const void *data, size_t len) { cs_base64_finish(&ctx); } -void mg_basic_auth_header(const char *user, const char *pass, +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"; @@ -9562,14 +9925,36 @@ void mg_basic_auth_header(const char *user, const char *pass, mbuf_append(buf, header_prefix, strlen(header_prefix)); - cs_base64_update(&ctx, user, strlen(user)); - if (pass != NULL) { + cs_base64_update(&ctx, user.p, user.len); + if (pass.len > 0) { cs_base64_update(&ctx, ":", 1); - cs_base64_update(&ctx, pass, strlen(pass)); + 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/mqtt.c" #endif @@ -9598,21 +9983,28 @@ static const char *scanto(const char *p, struct mg_str *s) { MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm) { uint8_t header; - size_t len = 0; + size_t len = 0, len_len = 0; + const char *p, *end; + unsigned char lc = 0; int cmd; - const char *p = &io->buf[1], *end; if (io->len < 2) return -1; header = io->buf[0]; cmd = header >> 4; /* decode mqtt variable length */ - do { - len += (*p & 127) << 7 * (p - &io->buf[1]); - } while ((*p++ & 128) != 0 && ((size_t)(p - io->buf) <= io->len)); + 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 -2; + } end = p + len; - if (end > io->buf + io->len + 1) { + if (lc & 0x80 || len > (io->len - (p - io->buf))) { return -1; } @@ -9622,24 +10014,36 @@ MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm) { switch (cmd) { case MG_MQTT_CMD_CONNECT: { p = scanto(p, &mm->protocol_name); + if (p > end - 4) return -2; mm->protocol_version = *(uint8_t *) p++; mm->connect_flags = *(uint8_t *) p++; mm->keep_alive_timer = getu16(p); p += 2; - if (p < end) p = scanto(p, &mm->client_id); - if (p < end && (mm->connect_flags & MG_MQTT_HAS_WILL)) + if (p >= end) return -2; + p = scanto(p, &mm->client_id); + if (p > end) return -2; + if (mm->connect_flags & MG_MQTT_HAS_WILL) { + if (p >= end) return -2; p = scanto(p, &mm->will_topic); - if (p < end && (mm->connect_flags & MG_MQTT_HAS_WILL)) + } + if (mm->connect_flags & MG_MQTT_HAS_WILL) { + if (p >= end) return -2; p = scanto(p, &mm->will_message); - if (p < end && (mm->connect_flags & MG_MQTT_HAS_USER_NAME)) + } + if (mm->connect_flags & MG_MQTT_HAS_USER_NAME) { + if (p >= end) return -2; p = scanto(p, &mm->user_name); - if (p < end && (mm->connect_flags & MG_MQTT_HAS_PASSWORD)) + } + if (mm->connect_flags & MG_MQTT_HAS_PASSWORD) { + if (p >= end) return -2; p = scanto(p, &mm->password); + } + if (p != end) return -2; LOG(LL_DEBUG, ("%d %2x %d proto [%.*s] client_id [%.*s] will_topic [%.*s] " "will_msg [%.*s] user_name [%.*s] password [%.*s]", - len, (int) mm->connect_flags, (int) mm->keep_alive_timer, + (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, @@ -9648,6 +10052,7 @@ MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm) { break; } case MG_MQTT_CMD_CONNACK: + if (end - p < 2) return -2; mm->connack_ret_code = p[1]; break; case MG_MQTT_CMD_PUBACK: @@ -9658,17 +10063,19 @@ MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm) { mm->message_id = getu16(p); break; case MG_MQTT_CMD_PUBLISH: { - if (MG_MQTT_GET_QOS(header) > 0) { + p = scanto(p, &mm->topic); + if (p > end) return -2; + if (mm->qos > 0) { + if (end - p < 2) return -2; mm->message_id = getu16(p); p += 2; } - p = scanto(p, &mm->topic); - mm->payload.p = p; mm->payload.len = end - p; break; } case MG_MQTT_CMD_SUBSCRIBE: + if (end - p < 2) return -2; mm->message_id = getu16(p); p += 2; /* @@ -9683,24 +10090,48 @@ MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm) { break; } - return end - io->buf; + mm->len = end - io->buf; + return mm->len; } -static void mqtt_handler(struct mg_connection *nc, int ev, void *ev_data) { - int 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); + nc->handler(nc, ev, ev_data MG_UD_ARG(user_data)); switch (ev) { - case MG_EV_RECV: - len = parse_mqtt(io, &mm); - if (len == -1) break; /* not fully buffered */ - nc->handler(nc, MG_MQTT_EVENT_BASE + mm.cmd, &mm); - mbuf_remove(io, len); + 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 == -1) break; /* not fully buffered */ + /* Protocol error. */ + 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; + } } } @@ -9708,12 +10139,63 @@ 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); @@ -9721,98 +10203,74 @@ void mg_send_mqtt_handshake(struct mg_connection *nc, const char *client_id) { void mg_send_mqtt_handshake_opt(struct mg_connection *nc, const char *client_id, struct mg_send_mqtt_handshake_opts opts) { - uint8_t header = MG_MQTT_CMD_CONNECT << 4; - uint8_t rem_len; - uint16_t keep_alive; - uint16_t len; + uint16_t hlen, nlen, rem_len = 0; struct mg_mqtt_proto_data *pd = (struct mg_mqtt_proto_data *) nc->proto_data; - /* - * 9: version_header(len, magic_string, version_number), 1: flags, 2: - * keep-alive timer, - * 2: client_identifier_len, n: client_id - */ - rem_len = 9 + 1 + 2 + 2 + (uint8_t) strlen(client_id); + mg_send(nc, "\00\04MQTT\04", 7); + rem_len += 7; if (opts.user_name != NULL) { opts.flags |= MG_MQTT_HAS_USER_NAME; - rem_len += (uint8_t) strlen(opts.user_name) + 2; } if (opts.password != NULL) { opts.flags |= MG_MQTT_HAS_PASSWORD; - rem_len += (uint8_t) strlen(opts.password) + 2; } if (opts.will_topic != NULL && opts.will_message != NULL) { opts.flags |= MG_MQTT_HAS_WILL; - rem_len += (uint8_t) strlen(opts.will_topic) + 2; - rem_len += (uint8_t) strlen(opts.will_message) + 2; } - - mg_send(nc, &header, 1); - mg_send(nc, &rem_len, 1); - mg_send(nc, "\00\06MQIsdp\03", 9); - mg_send(nc, &opts.flags, 1); - if (opts.keep_alive == 0) { opts.keep_alive = 60; } - keep_alive = htons(opts.keep_alive); - mg_send(nc, &keep_alive, 2); + mg_send(nc, &opts.flags, 1); + rem_len += 1; + + nlen = htons(opts.keep_alive); + mg_send(nc, &nlen, 2); + rem_len += 2; - len = htons((uint16_t) strlen(client_id)); - mg_send(nc, &len, 2); - mg_send(nc, client_id, strlen(client_id)); + 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) { - len = htons((uint16_t) strlen(opts.will_topic)); - mg_send(nc, &len, 2); - mg_send(nc, opts.will_topic, strlen(opts.will_topic)); + 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; - len = htons((uint16_t) strlen(opts.will_message)); - mg_send(nc, &len, 2); - mg_send(nc, opts.will_message, strlen(opts.will_message)); + 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) { - len = htons((uint16_t) strlen(opts.user_name)); - mg_send(nc, &len, 2); - mg_send(nc, opts.user_name, strlen(opts.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) { - len = htons((uint16_t) strlen(opts.password)); - mg_send(nc, &len, 2); - mg_send(nc, opts.password, strlen(opts.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; } } -static void mg_mqtt_prepend_header(struct mg_connection *nc, uint8_t cmd, - uint8_t flags, size_t len) { - 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); -} - void mg_mqtt_publish(struct mg_connection *nc, const char *topic, uint16_t message_id, int flags, const void *data, size_t len) { @@ -9855,15 +10313,16 @@ void mg_mqtt_subscribe(struct mg_connection *nc, 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; - } + 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 pos + 2 + topic->len + 1; + return new_pos; } void mg_mqtt_unsubscribe(struct mg_connection *nc, char **topics, @@ -9899,8 +10358,9 @@ void mg_mqtt_connack(struct mg_connection *nc, uint8_t return_code) { 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, MG_MQTT_QOS(1), 2); + mg_mqtt_prepend_header(nc, cmd, flags, 2 /* len */); } void mg_mqtt_puback(struct mg_connection *nc, uint16_t message_id) { @@ -9998,7 +10458,8 @@ void mg_mqtt_broker_init(struct mg_mqtt_broker *brk, void *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 *) calloc(1, sizeof *s); + 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); @@ -10019,8 +10480,8 @@ static void mg_mqtt_broker_handle_connect(struct mg_mqtt_broker *brk, 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->user_data; - uint8_t qoss[512]; - size_t qoss_len = 0; + uint8_t qoss[MG_MQTT_MAX_SESSION_SUBSCRIPTIONS]; + size_t num_subs = 0; struct mg_str topic; uint8_t qos; int pos; @@ -10028,41 +10489,33 @@ static void mg_mqtt_broker_handle_subscribe(struct mg_connection *nc, for (pos = 0; (pos = mg_mqtt_next_subscribe_topic(msg, &topic, &qos, pos)) != -1;) { - qoss[qoss_len++] = qos; + 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; } - ss->subscriptions = (struct mg_mqtt_topic_expression *) realloc( - ss->subscriptions, sizeof(*ss->subscriptions) * qoss_len); + 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 = mg_mqtt_next_subscribe_topic(msg, &topic, &qos, pos)) != -1; ss->num_subscriptions++) { te = &ss->subscriptions[ss->num_subscriptions]; - te->topic = (char *) malloc(topic.len + 1); + te->topic = (char *) MG_MALLOC(topic.len + 1); te->qos = qos; strncpy((char *) te->topic, topic.p, topic.len + 1); } - mg_mqtt_suback(nc, qoss, qoss_len, msg->message_id); -} - -/* - * Matches a topic against a topic expression - * - * See http://goo.gl/iWk21X - * - * Returns 1 if it matches; 0 otherwise. - */ -static int mg_mqtt_match_topic_expression(const char *exp, - const struct mg_str *topic) { - /* TODO(mkm): implement real matching */ - size_t len = strlen(exp); - if (strchr(exp, '#')) { - len -= 2; - if (topic->len < len) { - len = topic->len; - } - } - return strncmp(topic->p, exp, len) == 0; + mg_mqtt_suback(nc, qoss, num_subs, msg->message_id); } static void mg_mqtt_broker_handle_publish(struct mg_mqtt_broker *brk, @@ -10072,8 +10525,8 @@ static void mg_mqtt_broker_handle_publish(struct mg_mqtt_broker *brk, 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_match_topic_expression(s->subscriptions[i].topic, - &msg->topic)) { + 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); @@ -10102,17 +10555,32 @@ void mg_mqtt_broker(struct mg_connection *nc, int ev, void *data) { switch (ev) { case MG_EV_ACCEPT: - mg_set_protocol_mqtt(nc); + if (nc->proto_data == NULL) mg_set_protocol_mqtt(nc); nc->user_data = NULL; /* Clear up the inherited pointer to broker */ break; case MG_EV_MQTT_CONNECT: - mg_mqtt_broker_handle_connect(brk, nc); + if (nc->user_data == NULL) { + mg_mqtt_broker_handle_connect(brk, nc); + } else { + /* Repeated CONNECT */ + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } break; case MG_EV_MQTT_SUBSCRIBE: - mg_mqtt_broker_handle_subscribe(nc, msg); + if (nc->user_data != NULL) { + mg_mqtt_broker_handle_subscribe(nc, msg); + } else { + /* Subscribe before CONNECT */ + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } break; case MG_EV_MQTT_PUBLISH: - mg_mqtt_broker_handle_publish(brk, msg); + if (nc->user_data != 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->user_data != NULL) { @@ -10417,7 +10885,7 @@ int mg_parse_dns(const char *buf, int len, struct mg_dns_message *msg) { size_t mg_dns_uncompress_name(struct mg_dns_message *msg, struct mg_str *name, char *dst, int dst_len) { - int chunk_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; @@ -10432,14 +10900,21 @@ size_t mg_dns_uncompress_name(struct mg_dns_message *msg, struct mg_str *name, return 0; } - if (chunk_len & 0xc0) { + 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; } @@ -10464,12 +10939,13 @@ size_t mg_dns_uncompress_name(struct mg_dns_message *msg, struct mg_str *name, return dst - old_dst; } -static void dns_handler(struct mg_connection *nc, int ev, void *ev_data) { +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); + nc->handler(nc, ev, ev_data MG_UD_ARG(user_data)); switch (ev) { case MG_EV_RECV: @@ -10488,7 +10964,7 @@ static void dns_handler(struct mg_connection *nc, int ev, void *ev_data) { mg_send(nc, io->buf, io->len); } else { /* Call user handler with parsed message */ - nc->handler(nc, MG_DNS_MESSAGE, &msg); + nc->handler(nc, MG_DNS_MESSAGE, &msg MG_UD_ARG(user_data)); } mbuf_remove(io, io->len); break; @@ -10591,10 +11067,6 @@ int mg_dns_reply_record(struct mg_dns_reply *reply, #define MG_DEFAULT_NAMESERVER "8.8.8.8" #endif -static const char *mg_default_dns_server = "udp://" MG_DEFAULT_NAMESERVER ":53"; - -MG_INTERNAL char mg_dns_server[256]; - struct mg_resolve_async_request { char name[1024]; int query; @@ -10636,10 +11108,12 @@ static int mg_get_ip_address_of_nameserver(char *name, size_t name_len) { break; } if (RegOpenKeyExW(hKey, subkey, 0, KEY_READ, &hSub) == ERROR_SUCCESS && - (RegQueryValueExW(hSub, L"NameServer", 0, &type, (void *) value, - &len) == ERROR_SUCCESS || - RegQueryValueExW(hSub, L"DhcpNameServer", 0, &type, (void *) value, - &len) == 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 @@ -10648,13 +11122,11 @@ static int mg_get_ip_address_of_nameserver(char *name, size_t name_len) { * If it's multiple IP addresses, take the first one. */ wchar_t *comma = wcschr(value, ','); - if (value[0] == '\0') { - continue; - } if (comma != NULL) { *comma = '\0'; } - snprintf(name, name_len, "udp://%S:53", value); + /* %S will convert wchar_t -> char */ + snprintf(name, name_len, "%S", value); ret = 0; RegCloseKey(hSub); break; @@ -10662,18 +11134,18 @@ static int mg_get_ip_address_of_nameserver(char *name, size_t name_len) { } RegCloseKey(hKey); } -#elif MG_ENABLE_FILESYSTEM +#elif MG_ENABLE_FILESYSTEM && defined(MG_RESOLV_CONF_FILE_NAME) FILE *fp; char line[512]; - if ((fp = mg_fopen("/etc/resolv.conf", "r")) == NULL) { + 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, "udp://%u.%u.%u.%u:53", a, b, c, d); + snprintf(name, name_len, "%u.%u.%u.%u", a, b, c, d); ret = 0; break; } @@ -10681,14 +11153,14 @@ static int mg_get_ip_address_of_nameserver(char *name, size_t name_len) { (void) fclose(fp); } #else - snprintf(name, name_len, "%s", mg_default_dns_server); + 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 +#if MG_ENABLE_FILESYSTEM && defined(MG_HOSTS_FILE_NAME) /* TODO(mkm) cache /etc/hosts */ FILE *fp; char line[1024]; @@ -10697,7 +11169,7 @@ int mg_resolve_from_hosts_file(const char *name, union socket_address *usa) { unsigned int a, b, c, d; int len = 0; - if ((fp = mg_fopen("/etc/hosts", "r")) == NULL) { + if ((fp = mg_fopen(MG_HOSTS_FILE_NAME, "r")) == NULL) { return -1; } @@ -10726,15 +11198,19 @@ int mg_resolve_from_hosts_file(const char *name, union socket_address *usa) { return -1; } -static void mg_resolve_async_eh(struct mg_connection *nc, int ev, void *data) { +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, nc->user_data)); + if (ev != MG_EV_POLL) DBG(("ev=%d user_data=%p", ev, user_data)); - req = (struct mg_resolve_async_request *) nc->user_data; + req = (struct mg_resolve_async_request *) user_data; if (req == NULL) { return; @@ -10785,6 +11261,11 @@ static void mg_resolve_async_eh(struct mg_connection *nc, int ev, void *data) { 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); @@ -10805,7 +11286,12 @@ int mg_resolve_async_opt(struct mg_mgr *mgr, const char *name, int query, struct mg_resolve_async_opts opts) { struct mg_resolve_async_request *req; struct mg_connection *dns_nc; - const char *nameserver = opts.nameserver_url; + 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)); @@ -10824,19 +11310,20 @@ int mg_resolve_async_opt(struct mg_mgr *mgr, const char *name, int query, req->timeout = opts.timeout ? opts.timeout : 5; /* Lazily initialize dns server */ - if (nameserver == NULL && mg_dns_server[0] == '\0' && - mg_get_ip_address_of_nameserver(mg_dns_server, sizeof(mg_dns_server)) == - -1) { - strncpy(mg_dns_server, mg_default_dns_server, sizeof(mg_dns_server)); - } - if (nameserver == NULL) { - nameserver = mg_dns_server; + if (mg_get_ip_address_of_nameserver(dns_server_buff, + sizeof(dns_server_buff)) != -1) { + nameserver = dns_server_buff; + } else { + nameserver = MG_DEFAULT_NAMESERVER; + } } - dns_nc = mg_connect(mgr, nameserver, mg_resolve_async_eh); + 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) { - free(req); + MG_FREE(req); return -1; } dns_nc->user_data = req; @@ -10847,6 +11334,14 @@ int mg_resolve_async_opt(struct mg_mgr *mgr, const char *name, int query, 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/coap.c" @@ -11312,7 +11807,7 @@ uint32_t mg_coap_compose(struct mg_coap_message *cm, struct mbuf *io) { /* saving previous lenght to handle non-empty mbuf */ prev_io_len = io->len; - mbuf_append(io, NULL, packet_size); + if (mbuf_append(io, NULL, packet_size) == 0) return MG_COAP_ERROR; ptr = io->buf + prev_io_len; /* @@ -11396,14 +11891,15 @@ uint32_t mg_coap_send_ack(struct mg_connection *nc, uint16_t msg_id) { return mg_coap_send_message(nc, &cm); } -static void coap_handler(struct mg_connection *nc, int ev, void *ev_data) { +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); + nc->handler(nc, ev, ev_data MG_UD_ARG(user_data)); switch (ev) { case MG_EV_RECV: @@ -11416,7 +11912,8 @@ static void coap_handler(struct mg_connection *nc, int ev, void *ev_data) { */ cm.flags |= MG_COAP_FORMAT_ERROR; /* LCOV_EXCL_LINE */ } /* LCOV_EXCL_LINE */ - nc->handler(nc, MG_COAP_EVENT_BASE + cm.msg_type, &cm); + nc->handler(nc, MG_COAP_EVENT_BASE + cm.msg_type, + &cm MG_UD_ARG(user_data)); } mg_coap_free_options(&cm); @@ -11481,9 +11978,10 @@ static void mg_tun_init_client(struct mg_tun_client *client, struct mg_mgr *mgr, } void mg_tun_log_frame(struct mg_tun_frame *frame) { - LOG(LL_DEBUG, ("Got TUN frame: type=0x%x, flags=0x%x stream_id=0x%lx, " - "len=%zu", - frame->type, frame->flags, frame->stream_id, frame->body.len)); + LOG(LL_DEBUG, ("Got TUN frame: type=0x%x, flags=0x%x stream_id=0x%x, " + "len=%d", + frame->type, frame->flags, (unsigned int) frame->stream_id, + (int) frame->body.len)); #if MG_ENABLE_HEXDUMP { char hex[512]; @@ -11508,8 +12006,13 @@ static void mg_tun_close_all(struct mg_tun_client *client) { } static void mg_tun_client_handler(struct mg_connection *nc, int ev, - void *ev_data) { - struct mg_tun_client *client = (struct mg_tun_client *) nc->user_data; + void *ev_data MG_UD_ARG(void *user_data)) { +#if !MG_ENABLE_CALLBACK_USERDATA + void *user_data = nc->user_data; +#else + (void) nc; +#endif + struct mg_tun_client *client = (struct mg_tun_client *) user_data; switch (ev) { case MG_EV_CONNECT: { @@ -11541,7 +12044,7 @@ static void mg_tun_client_handler(struct mg_connection *nc, int ev, struct mg_tun_frame frame; if (mg_tun_parse_frame(wm->data, wm->size, &frame) == -1) { - LOG(LL_ERROR, ("Got invalid tun frame dropping", wm->size)); + LOG(LL_ERROR, ("Got invalid tun frame dropping")); break; } @@ -11596,21 +12099,28 @@ static void mg_tun_do_reconnect(struct mg_tun_client *client) { opts.ssl_ca_cert = client->ssl.ssl_ca_cert; #endif /* HTTP/Websocket listener */ - if ((dc = mg_connect_ws_opt(client->mgr, mg_tun_client_handler, opts, - client->disp_url, MG_TUN_PROTO_NAME, NULL)) == - NULL) { + if ((dc = mg_connect_ws_opt(client->mgr, MG_CB(mg_tun_client_handler, client), + opts, client->disp_url, MG_TUN_PROTO_NAME, + NULL)) == NULL) { LOG(LL_ERROR, ("Cannot connect to WS server on addr [%s]\n", client->disp_url)); return; } client->disp = dc; +#if !MG_ENABLE_CALLBACK_USERDATA dc->user_data = client; +#endif } void mg_tun_reconnect_ev_handler(struct mg_connection *nc, int ev, - void *ev_data) { - struct mg_tun_client *client = (struct mg_tun_client *) nc->user_data; + void *ev_data MG_UD_ARG(void *user_data)) { +#if !MG_ENABLE_CALLBACK_USERDATA + void *user_data = nc->user_data; +#else + (void) nc; +#endif + struct mg_tun_client *client = (struct mg_tun_client *) user_data; (void) ev_data; switch (ev) { @@ -11627,9 +12137,11 @@ void mg_tun_reconnect_ev_handler(struct mg_connection *nc, int ev, static void mg_tun_reconnect(struct mg_tun_client *client, int timeout) { if (client->reconnect == NULL) { - client->reconnect = - mg_add_sock(client->mgr, INVALID_SOCKET, mg_tun_reconnect_ev_handler); + client->reconnect = mg_add_sock(client->mgr, INVALID_SOCKET, + MG_CB(mg_tun_reconnect_ev_handler, client)); +#if !MG_ENABLE_CALLBACK_USERDATA client->reconnect->user_data = client; +#endif } client->reconnect->ev_timer_time = mg_time() + timeout; } @@ -11685,18 +12197,21 @@ void mg_tun_destroy_client(struct mg_tun_client *client) { } static struct mg_connection *mg_tun_do_bind(struct mg_tun_client *client, - mg_event_handler_t handler, + MG_CB(mg_event_handler_t handler, + void *user_data), struct mg_bind_opts opts) { struct mg_connection *lc; opts.iface = client->iface; - lc = mg_bind_opt(client->mgr, ":1234" /* dummy port */, handler, opts); + lc = mg_bind_opt(client->mgr, ":1234" /* dummy port */, + MG_CB(handler, user_data), opts); client->listener = lc; return lc; } struct mg_connection *mg_tun_bind_opt(struct mg_mgr *mgr, const char *dispatcher, - mg_event_handler_t handler, + MG_CB(mg_event_handler_t handler, + void *user_data), struct mg_bind_opts opts) { #if MG_ENABLE_SSL struct mg_tun_ssl_opts ssl = {opts.ssl_cert, opts.ssl_key, opts.ssl_ca_cert}; @@ -11713,7 +12228,7 @@ struct mg_connection *mg_tun_bind_opt(struct mg_mgr *mgr, opts.ssl_key = NULL; opts.ssl_ca_cert = NULL; #endif - return mg_tun_do_bind(client, handler, opts); + return mg_tun_do_bind(client, MG_CB(handler, user_data), opts); } int mg_tun_parse_frame(void *data, size_t len, struct mg_tun_frame *frame) { @@ -11800,7 +12315,7 @@ static void mg_get_ntp_ts(const char *ntp, uint64_t *val) { } void mg_sntp_send_request(struct mg_connection *c) { - char buf[48] = {0}; + uint8_t buf[48] = {0}; /* * header - 8 bit: * LI (2 bit) - 3 (not in sync), VN (3 bit) - 4 (version), @@ -11828,16 +12343,16 @@ void mg_sntp_send_request(struct mg_connection *c) { * but if local clock is absolutely broken (and doesn't work even * as simple timer), it is better to disable it */ -#ifndef MG_SNMP_NO_DELAY_CORRECTION +#ifndef MG_SNTP_NO_DELAY_CORRECTION uint32_t sec; - sec = htonl(mg_time() + SNTP_TIME_OFFSET); + sec = htonl((uint32_t)(mg_time() + SNTP_TIME_OFFSET)); memcpy(&buf[40], &sec, sizeof(sec)); #endif mg_send(c, buf, sizeof(buf)); } -#ifndef MG_SNMP_NO_DELAY_CORRECTION +#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) - @@ -11852,12 +12367,10 @@ static uint64_t mg_calculate_delay(uint64_t t1, uint64_t t2, uint64_t t3) { MG_INTERNAL int mg_sntp_parse_reply(const char *buf, int len, struct mg_sntp_message *msg) { uint8_t hdr; - uint64_t orig_ts_T1, recv_ts_T2, trsm_ts_T3, delay = 0; + uint64_t trsm_ts_T3, delay = 0; int mode; struct timeval tv; - (void) orig_ts_T1; - (void) recv_ts_T2; if (len < 48) { return -1; } @@ -11881,10 +12394,13 @@ MG_INTERNAL int mg_sntp_parse_reply(const char *buf, int len, mg_get_ntp_ts(&buf[40], &trsm_ts_T3); -#ifndef MG_SNMP_NO_DELAY_CORRECTION - 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); +#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); @@ -11894,19 +12410,20 @@ MG_INTERNAL int mg_sntp_parse_reply(const char *buf, int len, return 0; } -static void mg_sntp_handler(struct mg_connection *c, int ev, void *ev_data) { +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); + 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); + c->handler(c, MG_SNTP_MALFORMED_REPLY, NULL MG_UD_ARG(user_data)); } else { - c->handler(c, MG_SNTP_REPLY, (void *) &msg); + c->handler(c, MG_SNTP_REPLY, (void *) &msg MG_UD_ARG(user_data)); } mbuf_remove(io, io->len); @@ -11926,7 +12443,8 @@ int mg_set_protocol_sntp(struct mg_connection *c) { } struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, - mg_event_handler_t event_handler, + 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; @@ -11949,7 +12467,7 @@ struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, mg_asprintf(&p_url, sizeof(url), "%s%s%s", proto, sntp_server_name, port); - c = mg_connect(mgr, p_url, event_handler); + c = mg_connect(mgr, p_url, event_handler MG_UD_ARG(user_data)); if (c == NULL) { goto cleanup; @@ -11971,13 +12489,16 @@ struct sntp_data { }; static void mg_sntp_util_ev_handler(struct mg_connection *c, int ev, - void *ev_data) { - struct sntp_data *sd = (struct sntp_data *) c->user_data; + 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, MG_SNTP_FAILED, NULL); + mg_call(c, sd->hander, c->user_data, MG_SNTP_FAILED, NULL); break; } /* fallthrough */ @@ -11987,20 +12508,20 @@ static void mg_sntp_util_ev_handler(struct mg_connection *c, int ev, mg_set_timer(c, mg_time() + 10); sd->count++; } else { - mg_call(c, sd->hander, MG_SNTP_FAILED, NULL); + 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, MG_SNTP_FAILED, NULL); + 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, MG_SNTP_REPLY, ev_data); + 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(c->user_data); + MG_FREE(user_data); c->user_data = NULL; break; } @@ -12015,14 +12536,17 @@ struct mg_connection *mg_sntp_get_time(struct mg_mgr *mgr, return NULL; } - c = mg_sntp_connect(mgr, mg_sntp_util_ev_handler, sntp_server_name); + 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; } @@ -12038,6 +12562,7 @@ struct mg_connection *mg_sntp_get_time(struct mg_mgr *mgr, #if CS_PLATFORM == CS_P_CC3200 +/* Amalgamated: #include "common/mg_mem.h" */ #include <stdio.h> #include <string.h> @@ -12063,7 +12588,7 @@ int asprintf(char **strp, const char *fmt, ...) { va_list ap; int len; - *strp = malloc(BUFSIZ); + *strp = MG_MALLOC(BUFSIZ); if (*strp == NULL) return -1; va_start(ap, fmt); @@ -12071,7 +12596,7 @@ int asprintf(char **strp, const char *fmt, ...) { va_end(ap); if (len > 0) { - *strp = realloc(*strp, len + 1); + *strp = MG_REALLOC(*strp, len + 1); if (*strp == NULL) return -1; } @@ -12094,25 +12619,6 @@ time_t HOSTtime() { #endif /* __TI_COMPILER_VERSION__ */ -#ifndef __TI_COMPILER_VERSION__ -int _gettimeofday_r(struct _reent *r, struct timeval *tp, void *tzp) { -#else -int gettimeofday(struct timeval *tp, void *tzp) { -#endif - unsigned long long r1 = 0, r2; - /* Achieve two consecutive reads of the same value. */ - do { - r2 = r1; - r1 = PRCMSlowClkCtrFastGet(); - } while (r1 != r2); - /* This is a 32768 Hz counter. */ - tp->tv_sec = (r1 >> 15); - /* 1/32768-th of a second is 30.517578125 microseconds, approx. 31, - * but we round down so it doesn't overflow at 32767 */ - tp->tv_usec = (r1 & 0x7FFF) * 30; - return 0; -} - void fprint_str(FILE *fp, const char *str) { while (*str != '\0') { if (*str == '\n') MAP_UARTCharPut(CONSOLE_UART, '\r'); @@ -12246,14 +12752,25 @@ void fs_slfs_set_new_file_size(const char *name, size_t size); #if CS_PLATFORM == CS_P_CC3200 #include <inc/hw_types.h> #endif -#include <simplelink/include/simplelink.h> -#include <simplelink/include/fs.h> /* 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 */ -extern int set_errno(int e); -static const char *drop_dir(const char *fname, bool *is_slfs); +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. @@ -12282,18 +12799,18 @@ static int sl_fs_to_errno(_i32 r) { switch (r) { case SL_FS_OK: return 0; - case SL_FS_FILE_NAME_EXIST: + case SL_ERROR_FS_FILE_NAME_EXIST: return EEXIST; - case SL_FS_WRONG_FILE_NAME: + case SL_ERROR_FS_WRONG_FILE_NAME: return EINVAL; - case SL_FS_ERR_NO_AVAILABLE_NV_INDEX: - case SL_FS_ERR_NO_AVAILABLE_BLOCKS: + case SL_ERROR_FS_NO_AVAILABLE_NV_INDEX: + case SL_ERROR_FS_NOT_ENOUGH_STORAGE_SPACE: return ENOSPC; - case SL_FS_ERR_FAILED_TO_ALLOCATE_MEM: + case SL_ERROR_FS_FAILED_TO_ALLOCATE_MEM: return ENOMEM; - case SL_FS_ERR_FILE_NOT_EXISTS: + case SL_ERROR_FS_FILE_NOT_EXISTS: return ENOENT; - case SL_FS_ERR_NOT_SUPPORTED: + case SL_ERROR_FS_NOT_SUPPORTED: return ENOTSUP; } return ENXIO; @@ -12316,13 +12833,14 @@ int fs_slfs_open(const char *pathname, int flags, mode_t mode) { _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.FileLen; + fi->size = SL_FI_FILE_SIZE(sl_fi); } - am = FS_MODE_OPEN_READ; + am = SL_FS_READ; } else { if (!(flags & O_TRUNC) || (flags & O_APPEND)) { // FailFS files cannot be opened for append and will be truncated @@ -12330,31 +12848,30 @@ int fs_slfs_open(const char *pathname, int flags, mode_t mode) { return set_errno(ENOTSUP); } if (flags & O_CREAT) { - size_t i, size = FS_SLFS_MAX_FILE_SIZE; + 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) { - size = s_sl_file_size_hints[i].size; - free(s_sl_file_size_hints[i].name); + 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; } } - DBG(("creating %s with max size %d", pathname, (int) size)); - am = FS_MODE_OPEN_CREATE(size, 0); + am = FS_MODE_OPEN_CREATE(new_size, 0); } else { - am = FS_MODE_OPEN_WRITE; + am = SL_FS_WRITE; } } - _i32 r = sl_FsOpen((_u8 *) pathname, am, NULL, &fi->fh); - DBG(("sl_FsOpen(%s, 0x%x) = %d, %d", pathname, (int) am, (int) r, - (int) fi->fh)); - if (r == SL_FS_OK) { + 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 { - fi->fh = -1; - r = set_errno(sl_fs_to_errno(r)); + r = set_errno(sl_fs_to_errno(fi->fh)); } return r; } @@ -12363,7 +12880,7 @@ 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); - DBG(("sl_FsClose(%d) = %d", (int) fi->fh, (int) r)); + 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)); } @@ -12408,7 +12925,7 @@ int fs_slfs_stat(const char *pathname, struct stat *s) { if (r == SL_FS_OK) { s->st_mode = S_IFREG | 0666; s->st_nlink = 1; - s->st_size = sl_fi.FileLen; + s->st_size = SL_FI_FILE_SIZE(sl_fi); return 0; } return set_errno(sl_fs_to_errno(r)); @@ -12476,6 +12993,32 @@ void fs_slfs_set_new_file_size(const char *name, size_t size) { #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 <errno.h> #include <stdbool.h> #include <stdio.h> @@ -12500,7 +13043,7 @@ void fs_slfs_set_new_file_size(const char *name, size_t size) { #define SPIFFS_FD_BASE 10 #define SLFS_FD_BASE 100 -#ifndef MG_UART_CHAR_PUT +#if !defined(MG_UART_CHAR_PUT) && !defined(MG_UART_WRITE) #if CS_PLATFORM == CS_P_CC3200 #include <inc/hw_types.h> #include <inc/hw_memmap.h> @@ -12509,34 +13052,10 @@ void fs_slfs_set_new_file_size(const char *name, size_t size) { #include <driverlib/uart.h> #define MG_UART_CHAR_PUT(fd, c) MAP_UARTCharPut(UARTA0_BASE, c); #else -#define MG_UART_CHAR_PUT(fd, c) +#define MG_UART_WRITE(fd, buf, len) #endif /* CS_PLATFORM == CS_P_CC3200 */ #endif /* !MG_UART_CHAR_PUT */ -int set_errno(int e) { - errno = e; - return (e == 0 ? 0 : -1); -} - -static 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; -} - enum fd_type { FD_INVALID, FD_SYS, @@ -12738,7 +13257,6 @@ int write(int fd, const char *buf, unsigned count) { ssize_t _write(int fd, const void *buf, size_t count) { #endif int r = -1; - size_t i = 0; switch (fd_type(fd)) { case FD_INVALID: r = set_errno(EBADF); @@ -12748,11 +13266,18 @@ ssize_t _write(int fd, const void *buf, size_t count) { r = set_errno(EACCES); break; } - 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); +#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; } @@ -12868,6 +13393,7 @@ int sl_fs_init(void) { 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 @@ -12889,7 +13415,7 @@ 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 = EAFNOSUPPORT; + errno = ENOTSUP; return NULL; } res = snprintf(dst, size, "%lu.%lu.%lu.%lu", SL_IPV4_BYTE(in->s_addr, 0), @@ -12907,7 +13433,7 @@ 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 = EAFNOSUPPORT; + errno = ENOTSUP; return 0; } if (sscanf(src, "%lu.%lu.%lu.%lu", &a0, &a1, &a2, &a3) != 4) { @@ -12997,7 +13523,7 @@ extern "C" { #define MG_ENABLE_NET_IF_SIMPLELINK MG_NET_IF == MG_NET_IF_SIMPLELINK #endif -extern struct mg_iface_vtable mg_simplelink_iface_vtable; +extern const struct mg_iface_vtable mg_simplelink_iface_vtable; #ifdef __cplusplus } @@ -13029,12 +13555,16 @@ int sl_set_ssl_opts(struct mg_connection *nc); 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_EALREADY && n != SL_EAGAIN); + return (n < 0 && n != SL_ERROR_BSD_EALREADY && n != SL_ERROR_BSD_EAGAIN); } void mg_sl_if_connect_tcp(struct mg_connection *nc, @@ -13171,7 +13701,6 @@ static void mg_write_to_socket(struct mg_connection *nc) { } if (n > 0) { - mbuf_remove(io, n); mg_if_sent_cb(nc, n); } else if (n < 0 && mg_is_error(n)) { /* Something went wrong, drop the connection. */ @@ -13236,7 +13765,7 @@ void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) { 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_EALREADY) { + 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 @@ -13245,9 +13774,17 @@ void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) { 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_ESECSNOVERIFY || + if (nc->err == SL_ERROR_BSD_ESECSNOVERIFY || /* TODO(rojer): Provide API to set the date for verification. */ - nc->err == SL_ESECDATEERROR) { + 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) { @@ -13321,9 +13858,9 @@ time_t mg_sl_if_poll(struct mg_iface *iface, int timeout_ms) { sock_t max_fd = INVALID_SOCKET; int num_fds, num_ev = 0, num_timers = 0; - SL_FD_ZERO(&read_set); - SL_FD_ZERO(&write_set); - SL_FD_ZERO(&err_set); + 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, @@ -13339,14 +13876,14 @@ time_t mg_sl_if_poll(struct mg_iface *iface, int timeout_ms) { if (!(nc->flags & MG_F_WANT_WRITE) && nc->recv_mbuf.len < nc->recv_mbuf_limit && (!(nc->flags & MG_F_UDP) || nc->listener == NULL)) { - SL_FD_SET(nc->sock, &read_set); + 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_FD_SET(nc->sock, &write_set); - SL_FD_SET(nc->sock, &err_set); + 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; } } @@ -13387,14 +13924,15 @@ time_t mg_sl_if_poll(struct mg_iface *iface, int timeout_ms) { if (nc->sock != INVALID_SOCKET) { if (num_ev > 0) { fd_flags = - (SL_FD_ISSET(nc->sock, &read_set) && + (SL_SOCKET_FD_ISSET(nc->sock, &read_set) && (!(nc->flags & MG_F_UDP) || nc->listener == NULL) ? _MG_F_FD_CAN_READ : 0) | - (SL_FD_ISSET(nc->sock, &write_set) ? _MG_F_FD_CAN_WRITE : 0) | - (SL_FD_ISSET(nc->sock, &err_set) ? _MG_F_FD_ERROR : 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 writeable. */ + /* 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; } @@ -13417,7 +13955,7 @@ time_t mg_sl_if_poll(struct mg_iface *iface, int timeout_ms) { 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 hould have been preserved in the connection, + * 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)); } @@ -13469,9 +14007,9 @@ void sl_restart_cb(struct mg_mgr *mgr) { } /* clang-format on */ -struct mg_iface_vtable mg_simplelink_iface_vtable = MG_SL_IFACE_VTABLE; +const struct mg_iface_vtable mg_simplelink_iface_vtable = MG_SL_IFACE_VTABLE; #if MG_NET_IF == MG_NET_IF_SIMPLELINK -struct mg_iface_vtable mg_default_iface_vtable = MG_SL_IFACE_VTABLE; +const struct mg_iface_vtable mg_default_iface_vtable = MG_SL_IFACE_VTABLE; #endif #endif /* MG_ENABLE_NET_IF_SIMPLELINK */ @@ -13485,6 +14023,15 @@ struct mg_iface_vtable mg_default_iface_vtable = MG_SL_IFACE_VTABLE; #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; @@ -13525,6 +14072,11 @@ enum mg_ssl_if_result mg_ssl_if_conn_init( 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; @@ -13544,7 +14096,8 @@ bool pem_to_der(const char *pem_file, const char *der_file) { pf = fopen(pem_file, "r"); if (pf == NULL) goto clean; remove(der_file); - fs_slfs_set_new_file_size(der_file + 3, 2048); + 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) { @@ -13586,8 +14139,8 @@ static char *sl_pem2der(const char *pem_file) { } char *der_file = NULL; /* DER file must be located on SLFS, add prefix. */ - int l = mg_asprintf(&der_file, 0, "SL:%.*s.der", (int) (pem_ext - pem_file), - pem_file); + 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; @@ -13600,9 +14153,10 @@ static char *sl_pem2der(const char *pem_file) { } if (result) { /* Strip the SL: prefix we added since NWP does not expect it. */ - memmove(der_file, der_file + 3, l - 2 /* including \0 */); + memmove(der_file, der_file + MG_SSL_IF_SIMPLELINK_SLFS_PREFIX_LEN, + l - 2 /* including \0 */); } else { - free(der_file); + MG_FREE(der_file); der_file = NULL; } return der_file; @@ -13638,8 +14192,8 @@ int sl_set_ssl_opts(struct mg_connection *nc) { } else { err = -1; } - free(ssl_cert); - free(ssl_key); + MG_FREE(ssl_cert); + MG_FREE(ssl_key); if (err != 0) return err; } if (ctx->ssl_ca_cert != NULL) { @@ -13653,19 +14207,20 @@ int sl_set_ssl_opts(struct mg_connection *nc) { } else { err = -1; } - free(ssl_ca_cert); + MG_FREE(ssl_ca_cert); if (err != 0) return err; } } if (ctx->ssl_server_name != NULL) { err = sl_SetSockOpt(nc->sock, SL_SOL_SOCKET, - SO_SECURE_DOMAIN_NAME_VERIFICATION, + 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_ENOPROTOOPT. There isn't much we can do about it, + * 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_ENOPROTOOPT) return err; + if (err != 0 && err != SL_ERROR_BSD_ENOPROTOOPT) return err; } } return 0; @@ -13691,7 +14246,7 @@ int sl_set_ssl_opts(struct mg_connection *nc) { #include <stdint.h> -extern struct mg_iface_vtable mg_lwip_iface_vtable; +extern const struct mg_iface_vtable mg_lwip_iface_vtable; struct mg_lwip_conn_state { struct mg_connection *nc; @@ -13713,10 +14268,9 @@ struct mg_lwip_conn_state { enum mg_sig_type { MG_SIG_CONNECT_RESULT = 1, MG_SIG_RECV = 2, - MG_SIG_SENT_CB = 3, - MG_SIG_CLOSE_CONN = 4, - MG_SIG_TOMBSTONE = 5, - MG_SIG_ACCEPT = 6, + 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); @@ -13737,15 +14291,33 @@ void mg_lwip_mgr_schedule_poll(struct mg_mgr *mgr); #if MG_ENABLE_NET_IF_LWIP_LOW_LEVEL +/* Amalgamated: #include "common/mg_mem.h" */ + +#include <lwip/init.h> #include <lwip/pbuf.h> #include <lwip/tcp.h> -#if CS_PLATFORM != CS_P_STM32 +#include <lwip/tcpip.h> +#if LWIP_VERSION >= 0x01050000 +#include <lwip/priv/tcp_priv.h> /* For tcp_seg */ +#else #include <lwip/tcp_impl.h> #endif #include <lwip/udp.h> /* 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 @@ -13763,16 +14335,12 @@ void mg_lwip_mgr_schedule_poll(struct mg_mgr *mgr); #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 = GET_IPV4(src) +#define SET_ADDR(dst, src) (dst)->sin.sin_addr.s_addr = ip_2_ip4(src)->addr #endif -/* - * If lwip is compiled with ipv6 support, then API changes even for ipv4 - */ -#if !defined(LWIP_IPV6) || !LWIP_IPV6 -#define GET_IPV4(ipX_addr) ((ipX_addr)->addr) -#else -#define GET_IPV4(ipX_addr) ((ipX_addr)->ip4.addr) +#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); @@ -13785,7 +14353,7 @@ 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); -#ifdef RTOS_SDK +#if defined(RTOS_SDK) || defined(ESP_PLATFORM) extern void mgos_lock(); extern void mgos_unlock(); #else @@ -13852,7 +14420,7 @@ static err_t mg_lwip_tcp_recv_cb(void *arg, struct tcp_pcb *tpcb, 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) { + if (nc != NULL && !(nc->flags & MG_F_CLOSE_IMMEDIATELY)) { mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); } else { /* Tombstoned connection, do nothing. */ @@ -13906,22 +14474,22 @@ static void mg_lwip_handle_recv_tcp(struct mg_connection *nc) { while (cs->rx_chain != NULL) { struct pbuf *seg = cs->rx_chain; size_t len = (seg->len - cs->rx_offset); - char *data = (char *) malloc(len); + char *data = (char *) MG_MALLOC(len); if (data == NULL) { mgos_unlock(); DBG(("OOM")); return; } pbuf_copy_partial(seg, data, len, cs->rx_offset); - mgos_unlock(); - mg_if_recv_tcp_cb(nc, data, len, 1 /* own */); - mgos_lock(); 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(); @@ -13933,20 +14501,26 @@ static void mg_lwip_handle_recv_tcp(struct mg_connection *nc) { 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", nc, tpcb, num_sent)); - if (nc == NULL) { - tcp_abort(tpcb); - return ERR_ABRT; + 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); } - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - cs->num_sent += num_sent; - - mg_lwip_post_signal(MG_SIG_SENT_CB, nc); return ERR_OK; } -void mg_lwip_if_connect_tcp(struct mg_connection *nc, - const union socket_address *sa) { +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; @@ -13970,12 +14544,17 @@ void mg_lwip_if_connect_tcp(struct mg_connection *nc, } } +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 CS_PLATFORM == CS_P_NRF51 || CS_PLATFORM == CS_P_NRF52 || \ - CS_PLATFORM == CS_P_STM32 +#if LWIP_VERSION >= 0x01050000 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 @@ -13993,7 +14572,11 @@ static void mg_lwip_udp_recv_cb(void *arg, struct udp_pcb *pcb, struct pbuf *p, return; } union socket_address *sa = (union socket_address *) sap->payload; +#if LWIP_VERSION >= 0x01050000 + 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); @@ -14028,7 +14611,7 @@ static void mg_lwip_handle_recv_udp(struct mg_connection *nc) { struct pbuf *p = sap->next; cs->rx_chain = pbuf_dechain(p); size_t data_len = p->len; - char *data = (char *) malloc(data_len); + char *data = (char *) MG_MALLOC(data_len); if (data != NULL) { pbuf_copy_partial(p, data, data_len, 0); pbuf_free(p); @@ -14042,7 +14625,8 @@ static void mg_lwip_handle_recv_udp(struct mg_connection *nc) { } } -void mg_lwip_if_connect_udp(struct mg_connection *nc) { +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 */); @@ -14056,6 +14640,10 @@ void mg_lwip_if_connect_udp(struct mg_connection *nc) { 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); @@ -14063,13 +14651,18 @@ void mg_lwip_accept_conn(struct mg_connection *nc, struct tcp_pcb *tpcb) { 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")); - tcp_close(cs->pcb.tcp); + tcpip_callback(tcp_close_tcpip, cs->pcb.tcp); } } else #endif @@ -14079,15 +14672,27 @@ void mg_lwip_handle_accept(struct mg_connection *nc) { } static err_t mg_lwip_accept_cb(void *arg, struct tcp_pcb *newtpcb, err_t err) { - struct mg_connection *lc = (struct mg_connection *) arg; - DBG(("%p conn %p from %s:%u", lc, newtpcb, + 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)); - struct mg_connection *nc = mg_if_accept_new_conn(lc); + 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; } - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + 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 @@ -14101,10 +14706,20 @@ static err_t mg_lwip_accept_cb(void *arg, struct tcp_pcb *newtpcb, err_t err) { #endif mg_lwip_post_signal(MG_SIG_ACCEPT, nc); (void) err; + (void) lpcb; return ERR_OK; } -int mg_lwip_if_listen_tcp(struct mg_connection *nc, union socket_address *sa) { +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; @@ -14113,16 +14728,26 @@ int mg_lwip_if_listen_tcp(struct mg_connection *nc, union socket_address *sa) { DBG(("%p tcp_bind(%s:%u) = %d", nc, IPADDR_NTOA(ip), port, cs->err)); if (cs->err != ERR_OK) { tcp_close(tpcb); - return -1; + ctx->ret = -1; + return; } tcp_arg(tpcb, nc); tpcb = tcp_listen(tpcb); cs->pcb.tcp = tpcb; tcp_accept(tpcb, mg_lwip_accept_cb); - return 0; + ctx->ret = 0; } -int mg_lwip_if_listen_udp(struct mg_connection *nc, union socket_address *sa) { +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; @@ -14131,24 +14756,47 @@ int mg_lwip_if_listen_udp(struct mg_connection *nc, union socket_address *sa) { DBG(("%p udb_bind(%s:%u) = %d", nc, IPADDR_NTOA(ip), port, cs->err)); if (cs->err != ERR_OK) { udp_remove(upcb); - return -1; + ctx->ret = -1; + } else { + udp_recv(upcb, mg_lwip_udp_recv_cb, nc); + cs->pcb.udp = upcb; + ctx->ret = 0; } - udp_recv(upcb, mg_lwip_udp_recv_cb, nc); - cs->pcb.udp = upcb; - return 0; } -int mg_lwip_tcp_write(struct mg_connection *nc, const void *data, - uint16_t len) { +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; - len = MIN(tpcb->mss, MIN(len, tpcb->snd_buf)); + 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 %u %p %p", tpcb, tpcb->acked, tpcb->snd_buf, tpcb->snd_queuelen, tpcb->unsent, tpcb->unacked)); - tcp_output(tpcb); - return 0; + 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, @@ -14156,79 +14804,124 @@ int mg_lwip_tcp_write(struct mg_connection *nc, const void *data, * reduce footprint. */ #if CS_PLATFORM == CS_P_ESP8266 - if (tpcb->unacked != NULL) { - return 0; - } - if (tpcb->unsent != NULL) { - len = MIN(len, (TCP_MSS - tpcb->unsent->len)); + if (unacked > 0) { + ctx->ret = 0; + return; } + len = MIN(len, (TCP_MSS - unsent)); #endif - err_t err = tcp_write(tpcb, data, len, TCP_WRITE_FLAG_COPY); - DBG(("%p tcp_write %u = %d", tpcb, len, err)); - if (err != ERR_OK) { + 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. */ - return (err == ERR_MEM ? 0 : -1); + ctx->ret = (cs->err == ERR_MEM ? 0 : -1); + return; } - return len; + ctx->ret = len; } -static void mg_lwip_send_more(struct mg_connection *nc) { +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; - if (nc->sock == INVALID_SOCKET || cs->pcb.tcp == NULL) { - DBG(("%p invalid socket", nc)); - return; - } - int num_written = 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_written)); - if (num_written == 0) return; - if (num_written < 0) { - mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); + struct tcp_pcb *tpcb = cs->pcb.tcp; + if (tpcb == NULL) { + return -1; } - mbuf_remove(&nc->send_mbuf, num_written); - mbuf_trim(&nc->send_mbuf); + tcpip_callback(mg_lwip_tcp_write_tcpip, &ctx); + return ctx.ret; } -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); +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); } -void mg_lwip_if_udp_send(struct mg_connection *nc, const void *buf, - size_t len) { +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 (nc->sock == INVALID_SOCKET || cs->pcb.udp == NULL) { + 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; + return -1; } struct udp_pcb *upcb = cs->pcb.udp; struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM); - ip_addr_t *ip = (ip_addr_t *) &nc->sa.sin.sin_addr.s_addr; +#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; + return 0; } - memcpy(p->payload, buf, len); - cs->err = udp_sendto(upcb, p, (ip_addr_t *) ip, port); - DBG(("%p udp_sendto = %d", nc, cs->err)); + 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); - if (cs->err != ERR_OK) { - mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); + 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 { - cs->num_sent += len; - mg_lwip_post_signal(MG_SIG_SENT_CB, nc); + 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; @@ -14237,27 +14930,33 @@ void mg_lwip_if_recved(struct mg_connection *nc, size_t len) { return; } DBG(("%p %p %u", nc, cs->pcb.tcp, len)); -/* Currently SSL acknowledges data immediately. - * TODO(rojer): Find a way to propagate mg_lwip_if_recved. */ + struct tcp_recved_ctx ctx = {.tpcb = cs->pcb.tcp, .len = len}; #if MG_ENABLE_SSL if (!(nc->flags & MG_F_SSL)) { - tcp_recved(cs->pcb.tcp, len); + tcpip_callback(tcp_recved_tcpip, &ctx); + } else { + /* Currently SSL acknowledges data immediately. + * TODO(rojer): Find a way to propagate mg_lwip_if_recved. */ } #else - tcp_recved(cs->pcb.tcp, len); + 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 *) calloc(1, sizeof(*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; @@ -14267,7 +14966,7 @@ void mg_lwip_if_destroy_conn(struct mg_connection *nc) { tcp_arg(tpcb, NULL); DBG(("%p tcp_close %p", nc, tpcb)); tcp_arg(tpcb, NULL); - tcp_close(tpcb); + tcpip_callback(tcp_close_tcpip, tpcb); } while (cs->rx_chain != NULL) { struct pbuf *seg = cs->rx_chain; @@ -14275,16 +14974,16 @@ void mg_lwip_if_destroy_conn(struct mg_connection *nc) { pbuf_free(seg); } memset(cs, 0, sizeof(*cs)); - free(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)); - udp_remove(upcb); + tcpip_callback(udp_remove_tcpip, upcb); } memset(cs, 0, sizeof(*cs)); - free(cs); + MG_FREE(cs); } nc->sock = INVALID_SOCKET; } @@ -14292,22 +14991,21 @@ void mg_lwip_if_destroy_conn(struct mg_connection *nc) { void mg_lwip_if_get_conn_addr(struct mg_connection *nc, int remote, union socket_address *sa) { memset(sa, 0, sizeof(*sa)); - if (nc->sock == INVALID_SOCKET) return; + 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 { + } 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) { - sa->sin.sin_port = htons(tpcb->remote_port); - SET_ADDR(sa, &tpcb->remote_ip); - } else { + memcpy(sa, &nc->sa, sizeof(*sa)); + } else if (tpcb != NULL) { sa->sin.sin_port = htons(tpcb->local_port); SET_ADDR(sa, &tpcb->local_ip); } @@ -14340,9 +15038,9 @@ void mg_lwip_if_sock_set(struct mg_connection *nc, sock_t sock) { } /* clang-format on */ -struct mg_iface_vtable mg_lwip_iface_vtable = MG_LWIP_IFACE_VTABLE; +const struct mg_iface_vtable mg_lwip_iface_vtable = MG_LWIP_IFACE_VTABLE; #if MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL -struct mg_iface_vtable mg_default_iface_vtable = MG_LWIP_IFACE_VTABLE; +const struct mg_iface_vtable mg_default_iface_vtable = MG_LWIP_IFACE_VTABLE; #endif #endif /* MG_ENABLE_NET_IF_LWIP_LOW_LEVEL */ @@ -14426,17 +15124,6 @@ void mg_ev_mgr_lwip_process_signals(struct mg_mgr *mgr) { } break; } - case MG_SIG_SENT_CB: { - if (cs->num_sent > 0) mg_if_sent_cb(nc, cs->num_sent); - cs->num_sent = 0; - - if (nc->send_mbuf.len == 0 && (nc->flags & MG_F_SEND_AND_CLOSE) && - !(nc->flags & MG_F_WANT_WRITE)) { - mg_close_conn(nc); - } - - break; - } case MG_SIG_TOMBSTONE: { break; } @@ -14449,7 +15136,7 @@ void mg_ev_mgr_lwip_process_signals(struct mg_mgr *mgr) { } void mg_lwip_if_init(struct mg_iface *iface) { - LOG(LL_INFO, ("%p Mongoose init")); + LOG(LL_INFO, ("%p Mongoose init", iface)); iface->data = MG_CALLOC(1, sizeof(struct mg_ev_mgr_lwip_data)); } @@ -14520,14 +15207,14 @@ time_t mg_lwip_if_poll(struct mg_iface *iface, int timeout_ms) { } else #endif /* MG_ENABLE_SSL */ { - if (!(nc->flags & (MG_F_CONNECTING | MG_F_UDP))) { - if (nc->send_mbuf.len > 0) mg_lwip_send_more(nc); + 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) { - tcp_output(cs->pcb.tcp); + tcpip_callback(tcp_output_tcpip, cs->pcb.tcp); } if (nc->ev_timer_time > 0) { if (num_timers == 0 || nc->ev_timer_time < min_timer) { @@ -14547,7 +15234,7 @@ time_t mg_lwip_if_poll(struct mg_iface *iface, int timeout_ms) { uint32_t mg_lwip_get_poll_delay_ms(struct mg_mgr *mgr) { struct mg_connection *nc; - double now = mg_time(); + double now; double min_timer = 0; int num_timers = 0; mg_ev_mgr_lwip_process_signals(mgr); @@ -14559,7 +15246,11 @@ uint32_t mg_lwip_get_poll_delay_ms(struct mg_mgr *mgr) { } num_timers++; } - if (nc->send_mbuf.len > 0) { + 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) { @@ -14573,7 +15264,10 @@ uint32_t mg_lwip_get_poll_delay_ms(struct mg_mgr *mgr) { } } 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; @@ -14593,6 +15287,7 @@ uint32_t mg_lwip_get_poll_delay_ms(struct mg_mgr *mgr) { #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 <lwip/pbuf.h> @@ -14620,7 +15315,7 @@ void mg_lwip_ssl_do_hs(struct mg_connection *nc) { enum mg_ssl_if_result res; if (nc->flags & MG_F_CLOSE_IMMEDIATELY) return; res = mg_ssl_if_handshake(nc); - DBG(("%p %d %d %d", nc, nc->flags, server_side, res)); + 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; @@ -14668,10 +15363,9 @@ void mg_lwip_ssl_send(struct mg_connection *nc) { 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, %d", nc, len, ret)); + DBG(("%p SSL_write %u = %d", nc, len, ret)); if (ret > 0) { - mbuf_remove(&nc->send_mbuf, ret); - mbuf_trim(&nc->send_mbuf); + 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 @@ -14692,12 +15386,12 @@ void mg_lwip_ssl_recv(struct mg_connection *nc) { /* Don't deliver data before connect callback */ if (nc->flags & MG_F_CONNECTING) return; while (nc->recv_mbuf.len < MG_LWIP_SSL_RECV_MBUF_LIMIT) { - char *buf = (char *) malloc(MG_LWIP_SSL_IO_SIZE); + 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) { - free(buf); + MG_FREE(buf); if (ret == MG_SSL_WANT_WRITE) { nc->flags |= MG_F_WANT_WRITE; return; @@ -14756,8 +15450,8 @@ 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); - LOG(LL_DEBUG, ("%p %d -> %d", nc, len, ret)); if (ret == 0) ret = MBEDTLS_ERR_SSL_WANT_WRITE; + LOG(LL_DEBUG, ("%p %d -> %d", nc, len, ret)); return ret; } @@ -14887,7 +15581,7 @@ extern "C" { #define MG_ENABLE_NET_IF_PIC32 MG_NET_IF == MG_NET_IF_PIC32 #endif -extern struct mg_iface_vtable mg_pic32_iface_vtable; +extern const struct mg_iface_vtable mg_pic32_iface_vtable; #ifdef __cplusplus } @@ -15055,10 +15749,7 @@ static void mg_handle_send(struct mg_connection *nc) { } } - if (bytes_written != 0) { - mbuf_remove(&nc->send_mbuf, bytes_written); - mg_if_sent_cb(nc, bytes_written); - } + mg_if_sent_cb(nc, bytes_written); } static void mg_handle_recv(struct mg_connection *nc) { @@ -15186,9 +15877,29 @@ void mg_pic32_if_connect_udp(struct mg_connection *nc) { } /* clang-format on */ -struct mg_iface_vtable mg_pic32_iface_vtable = MG_PIC32_IFACE_VTABLE; +const struct mg_iface_vtable mg_pic32_iface_vtable = MG_PIC32_IFACE_VTABLE; #if MG_NET_IF == MG_NET_IF_PIC32 -struct mg_iface_vtable mg_default_iface_vtable = MG_PIC32_IFACE_VTABLE; +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/mongoose.h b/mongoose.h index 1ec11e5126bfee9e44cac6aabb8353e878620697..73176345ed96df15144188df06226aa0b566a519 100644 --- a/mongoose.h +++ b/mongoose.h @@ -23,7 +23,7 @@ #ifndef CS_MONGOOSE_SRC_COMMON_H_ #define CS_MONGOOSE_SRC_COMMON_H_ -#define MG_VERSION "6.7" +#define MG_VERSION "6.8" /* Local tweaks, applied before any of Mongoose's own headers. */ #ifdef MG_LOCALS @@ -46,9 +46,10 @@ #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_CC3100 6 #define CS_P_TM4C129 14 #define CS_P_MBED 7 #define CS_P_WINCE 8 @@ -58,15 +59,17 @@ #define CS_P_NRF52 10 #define CS_P_PIC32 11 #define CS_P_STM32 16 -/* Next id: 17 */ +/* 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) +#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) @@ -111,8 +114,9 @@ /* 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_cc3200.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" */ @@ -195,6 +199,7 @@ #include <stdlib.h> #include <sys/stat.h> #include <time.h> +#include <ctype.h> #ifdef _MSC_VER #pragma comment(lib, "ws2_32.lib") /* Linking with winsock library */ @@ -221,15 +226,13 @@ #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) #if !defined(__MINGW32__) && !defined(__MINGW64__) #define popen(x, y) _popen((x), (y)) #define pclose(x) _pclose(x) +#define fileno _fileno #endif -#define rmdir _rmdir #if defined(_MSC_VER) && _MSC_VER >= 1400 #define fseeko(x, y, z) _fseeki64((x), (y), (z)) #else @@ -320,6 +323,9 @@ typedef struct _stati64 cs_stat_t; #define MG_NET_IF MG_NET_IF_SOCKET #endif +int rmdir(const char *dirname); +unsigned int sleep(unsigned int seconds); + #endif /* CS_PLATFORM == CS_P_WINDOWS */ #endif /* CS_COMMON_PLATFORMS_PLATFORM_WINDOWS_H_ */ #ifdef MG_MODULE_LINES @@ -459,6 +465,14 @@ typedef struct stat cs_stat_t; #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 @@ -529,7 +543,9 @@ typedef struct stat cs_stat_t; #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 @@ -537,7 +553,7 @@ typedef struct stat cs_stat_t; #define __cdecl #define _FILE_OFFSET_BITS 32 -#ifndef RTOS_SDK +#if !defined(RTOS_SDK) && !defined(__cplusplus) #define fileno(x) -1 #endif @@ -549,9 +565,9 @@ typedef struct stat cs_stat_t; #ifndef MG_NET_IF #include <lwip/opt.h> #if LWIP_SOCKET /* RTOS SDK has LWIP sockets */ -# define MG_NET_IF MG_NET_IF_SOCKET +#define MG_NET_IF MG_NET_IF_SOCKET #else -# define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL +#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL #endif #endif @@ -592,7 +608,7 @@ typedef struct stat cs_stat_t; #include <simplelink.h> #include <netapp.h> -#undef timeval +#undef timeval typedef int sock_t; #define INVALID_SOCKET (-1) @@ -667,6 +683,7 @@ extern "C" { 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, ...); @@ -690,7 +707,7 @@ struct stat { }; int _stat(const char *pathname, struct stat *st); -#define stat(a, b) _stat(a, b) +int stat(const char *pathname, struct stat *st); #define __S_IFMT 0170000 @@ -706,8 +723,10 @@ int _stat(const char *pathname, struct stat *st); #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. */ +/* 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__ */ @@ -828,7 +847,8 @@ int _stat(const char *pathname, struct stat *st); #define CS_ENABLE_STDIO 1 #endif -#if (defined(CC3200_FS_SPIFFS) || defined(CC3200_FS_SLFS)) && !defined(MG_ENABLE_FILESYSTEM) +#if (defined(CC3200_FS_SPIFFS) || defined(CC3200_FS_SLFS)) && \ + !defined(MG_ENABLE_FILESYSTEM) #define MG_ENABLE_FILESYSTEM 1 #endif @@ -872,15 +892,15 @@ typedef struct stat cs_stat_t; #define __cdecl #ifndef MG_NET_IF -# include <lwip/opt.h> -# 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 +#include <lwip/opt.h> +#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" +/* Amalgamated: #include "common/platforms/simplelink/cs_simplelink.h" */ #endif #ifndef CS_ENABLE_STDIO @@ -1002,16 +1022,16 @@ in_addr_t inet_addr(const char *cp); #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 +#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 +#define LWIP_TIMEVAL_PRIVATE 0 #else struct timeval; int gettimeofday(struct timeval *tp, void *tzp); @@ -1048,12 +1068,12 @@ int gettimeofday(struct timeval *tp, void *tzp); #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 +#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 */ +#define ENOSPC 28 /* No space left on device */ #endif /* @@ -1061,7 +1081,7 @@ int gettimeofday(struct timeval *tp, void *tzp); * compilers, suppress it. */ #if !defined(__ARMCC_VERSION) -# define LWIP_TIMEVAL_PRIVATE 0 +#define LWIP_TIMEVAL_PRIVATE 0 #endif #define INT64_FMT PRId64 @@ -1085,9 +1105,10 @@ int gettimeofday(struct timeval *tp, void *tzp); #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(MG_NET_IF) && MG_NET_IF == MG_NET_IF_SIMPLELINK && \ - !defined(__SIMPLELINK_H__) +#if !defined(__SIMPLELINK_H__) #include <stdbool.h> @@ -1101,6 +1122,12 @@ int gettimeofday(struct timeval *tp, void *tzp); #undef fd_set #endif +#if CS_PLATFORM == CS_P_CC3220 +#include <ti/drivers/net/wifi/porting/user.h> +#include <ti/drivers/net/wifi/simplelink.h> +#include <ti/drivers/net/wifi/sl_socket.h> +#include <ti/drivers/net/wifi/netapp.h> +#else /* We want to disable SL_INC_STD_BSD_API_NAMING, so we include user.h ourselves * and undef it. */ #define PROVISIONING_API_H_ @@ -1110,6 +1137,7 @@ int gettimeofday(struct timeval *tp, void *tzp); #include <simplelink/include/simplelink.h> #include <simplelink/include/netapp.h> +#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. */ @@ -1178,7 +1206,59 @@ int sl_set_ssl_opts(struct mg_connection *nc); } #endif -#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK && !defined(__SIMPLELINK_H__) */ +#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 @@ -1235,6 +1315,10 @@ int sl_set_ssl_opts(struct mg_connection *nc); #define EWOULDBLOCK WSAEWOULDBLOCK #endif +#ifndef EAGAIN +#define EAGAIN EWOULDBLOCK +#endif + #ifndef __func__ #define STRX(x) #x #define STR(x) STRX(x) @@ -1361,18 +1445,18 @@ typedef struct _stati64 { #endif #ifndef _UINTPTR_T_DEFINED -typedef unsigned int* uintptr_t; +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) +#define S_ISDIR(x) (((x) &_S_IFDIR) != 0) #endif #ifndef S_ISREG -#define S_ISREG(x) (((x) & _S_IFREG) != 0) +#define S_ISREG(x) (((x) &_S_IFREG) != 0) #endif int open(const char *filename, int oflag, int pmode); @@ -1409,7 +1493,8 @@ typedef struct stat cs_stat_t; #define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL /* - * LPCXpress comes with 3 C library implementations: Newlib, NewlibNano and Redlib. + * 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. @@ -1505,7 +1590,7 @@ typedef TCP_SOCKET sock_t; #define CS_ENABLE_STDIO 1 #endif -char* inet_ntoa(struct in_addr in); +char *inet_ntoa(struct in_addr in); #endif /* CS_PLATFORM == CS_P_PIC32 */ @@ -1624,6 +1709,84 @@ void mg_lwip_set_keepalive_params(struct mg_connection *nc, int idle, #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 /* @@ -1634,6 +1797,8 @@ void mg_lwip_set_keepalive_params(struct mg_connection *nc, int idle, #ifndef CS_COMMON_CS_TIME_H_ #define CS_COMMON_CS_TIME_H_ +#include <time.h> + /* Amalgamated: #include "common/platform.h" */ #ifdef __cplusplus @@ -1643,6 +1808,12 @@ extern "C" { /* 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 */ @@ -1665,7 +1836,7 @@ double cs_time(void); #ifdef __cplusplus extern "C" { -#endif /* __cplusplus */ +#endif /* Describes chunk of memory */ struct mg_str { @@ -1683,6 +1854,8 @@ 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 @@ -1696,13 +1869,28 @@ int mg_vcmp(const struct mg_str *str2, const char *str1); */ 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); + int mg_strcmp(const struct mg_str str1, const struct mg_str str2); int mg_strncmp(const struct mg_str str1, const struct mg_str str2, size_t n); +const char *mg_strstr(const struct mg_str haystack, const struct mg_str needle); + #ifdef __cplusplus } -#endif /* __cplusplus */ +#endif #endif /* CS_COMMON_MG_STR_H_ */ #ifdef MG_MODULE_LINES @@ -1788,94 +1976,6 @@ void mbuf_trim(struct mbuf *); #endif /* CS_COMMON_MBUF_H_ */ #ifdef MG_MODULE_LINES -#line 1 "common/sha1.h" -#endif -/* - * Copyright (c) 2014 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_COMMON_SHA1_H_ -#define CS_COMMON_SHA1_H_ - -#ifndef DISABLE_SHA1 -#define DISABLE_SHA1 0 -#endif - -#if !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 /* DISABLE_SHA1 */ - -#endif /* CS_COMMON_SHA1_H_ */ -#ifdef MG_MODULE_LINES -#line 1 "common/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 DISABLE_MD5 -#define DISABLE_MD5 0 -#endif - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -typedef struct MD5Context { - uint32_t buf[4]; - uint32_t bits[2]; - unsigned char in[64]; -} MD5_CTX; - -void MD5_Init(MD5_CTX *c); -void MD5_Update(MD5_CTX *c, const unsigned char *data, size_t len); -void MD5_Final(unsigned char *md, MD5_CTX *c); - -/* - * Return stringified MD5 hash for NULL terminated list of pointer/length pairs. - * A length should be specified as size_t variable. - * Example: - * - * char buf[33]; - * cs_md5(buf, "foo", (size_t) 3, "bar", (size_t) 3, NULL); - */ -char *cs_md5(char buf[33], ...); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* CS_COMMON_MD5_H_ */ -#ifdef MG_MODULE_LINES #line 1 "common/base64.h" #endif /* @@ -1939,6 +2039,7 @@ int cs_base64_decode(const unsigned char *s, int len, char *dst, int *dec_len); #include <stdlib.h> /* Amalgamated: #include "common/platform.h" */ +/* Amalgamated: #include "common/mg_str.h" */ #ifndef CS_ENABLE_STRDUP #define CS_ENABLE_STRDUP 0 @@ -2033,6 +2134,34 @@ 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); +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. + * + * Match is case-insensitive. Returns number of bytes matched, or -1 if no + * match. + */ +int mg_match_prefix(const char *pattern, int pattern_len, const char *str); +int mg_match_prefix_n(const struct mg_str pattern, const struct mg_str str); + #ifdef __cplusplus } #endif @@ -2896,10 +3025,6 @@ struct { \ #define MG_ENABLE_IPV6 0 #endif -#ifndef MG_ENABLE_JAVASCRIPT -#define MG_ENABLE_JAVASCRIPT 0 -#endif - #ifndef MG_ENABLE_MQTT #define MG_ENABLE_MQTT 1 #endif @@ -2963,6 +3088,18 @@ struct { \ #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/net_if.h" @@ -3002,7 +3139,7 @@ struct mg_iface_vtable; struct mg_iface { struct mg_mgr *mgr; void *data; /* Implementation-specific data */ - struct mg_iface_vtable *vtable; + const struct mg_iface_vtable *vtable; }; struct mg_iface_vtable { @@ -3041,11 +3178,11 @@ struct mg_iface_vtable { union socket_address *sa); }; -extern struct mg_iface_vtable *mg_ifaces[]; +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(struct mg_iface_vtable *vtable, +struct mg_iface *mg_if_create_iface(const struct mg_iface_vtable *vtable, struct mg_mgr *mgr); /* @@ -3053,7 +3190,7 @@ struct mg_iface *mg_if_create_iface(struct mg_iface_vtable *vtable, * interface `from`, exclusive. Returns NULL if none is found. */ struct mg_iface *mg_find_iface(struct mg_mgr *mgr, - struct mg_iface_vtable *vtable, + const struct mg_iface_vtable *vtable, struct mg_iface *from); /* * Deliver a new TCP connection. Returns NULL in case on error (unable to @@ -3141,6 +3278,7 @@ enum mg_ssl_if_result mg_ssl_if_conn_init( 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); @@ -3187,11 +3325,6 @@ int mg_ssl_if_write(struct mg_connection *nc, const void *data, size_t len); #ifndef CS_MONGOOSE_SRC_NET_H_ #define CS_MONGOOSE_SRC_NET_H_ -#if MG_ENABLE_JAVASCRIPT -#define EXCLUDE_COMMON -#include <v7.h> -#endif - /* Amalgamated: #include "mongoose/src/common.h" */ /* Amalgamated: #include "mongoose/src/net_if.h" */ /* Amalgamated: #include "common/mbuf.h" */ @@ -3229,13 +3362,13 @@ struct mg_connection; * 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); + 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 benn received. int *num_bytes */ +#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 * */ @@ -3254,9 +3387,7 @@ struct mg_mgr { void *user_data; /* User data */ int num_ifaces; struct mg_iface **ifaces; /* network interfaces */ -#if MG_ENABLE_JAVASCRIPT - struct v7 *v7; -#endif + const char *nameserver; /* DNS server to use */ }; /* @@ -3348,9 +3479,10 @@ void mg_mgr_init(struct mg_mgr *mgr, void *user_data); * `num_ifaces` pointers it contains will be reclaimed by `mg_mgr_free`. */ struct mg_mgr_init_opts { - struct mg_iface_vtable *main_iface; + const struct mg_iface_vtable *main_iface; int num_ifaces; - struct mg_iface_vtable **ifaces; + const struct mg_iface_vtable **ifaces; + const char *nameserver; }; /* @@ -3392,7 +3524,8 @@ time_t mg_mgr_poll(struct mg_mgr *, int milli); * 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 *, mg_event_handler_t func, void *, size_t); +void mg_broadcast(struct mg_mgr *mgr, mg_event_handler_t cb, void *data, + size_t len); #endif /* @@ -3408,7 +3541,7 @@ void mg_broadcast(struct mg_mgr *, mg_event_handler_t func, void *, size_t); * } * ``` */ -struct mg_connection *mg_next(struct mg_mgr *, struct mg_connection *); +struct mg_connection *mg_next(struct mg_mgr *mgr, struct mg_connection *c); /* * Optional parameters to `mg_add_sock_opt()`. @@ -3429,7 +3562,9 @@ struct mg_add_sock_opts { * * For more options see the `mg_add_sock_opt` variant. */ -struct mg_connection *mg_add_sock(struct mg_mgr *, sock_t, mg_event_handler_t); +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 @@ -3437,9 +3572,10 @@ struct mg_connection *mg_add_sock(struct mg_mgr *, sock_t, mg_event_handler_t); * * See the `mg_add_sock_opts` structure for a description of the options. */ -struct mg_connection *mg_add_sock_opt(struct mg_mgr *, sock_t, - mg_event_handler_t, - struct mg_add_sock_opts); +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()`. @@ -3485,8 +3621,9 @@ struct mg_bind_opts { * * See `mg_bind_opt` for full documentation. */ -struct mg_connection *mg_bind(struct mg_mgr *, const char *, - mg_event_handler_t); +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. * @@ -3495,7 +3632,7 @@ struct mg_connection *mg_bind(struct mg_mgr *, const char *, * `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` paramer has following + * prefix, e.g. `udp://:8000`. To summarize, `address` parameter has following * format: `[PROTO://][IP_ADDRESS]:PORT`, where `PROTO` could be `tcp` or * `udp`. * @@ -3506,7 +3643,8 @@ struct mg_connection *mg_bind(struct mg_mgr *, const char *, * 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_event_handler_t handler, + MG_CB(mg_event_handler_t handler, + void *user_data), struct mg_bind_opts opts); /* Optional parameters to `mg_connect_opt()` */ @@ -3515,6 +3653,7 @@ struct mg_connect_opts { 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. @@ -3569,7 +3708,8 @@ struct mg_connect_opts { * See `mg_connect_opt()` for full documentation. */ struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *address, - mg_event_handler_t handler); + MG_CB(mg_event_handler_t handler, + void *user_data)); /* * Connects to a remote host. @@ -3620,7 +3760,8 @@ struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *address, * ``` */ struct mg_connection *mg_connect_opt(struct mg_mgr *mgr, const char *address, - mg_event_handler_t handler, + 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 @@ -3701,7 +3842,7 @@ int mg_resolve(const char *domain_name, char *ip_addr_buf, size_t buf_len); * 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 acccesses, only allow 192.168/16 subnet + * `-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]. @@ -3710,17 +3851,6 @@ int mg_resolve(const char *domain_name, char *ip_addr_buf, size_t buf_len); */ int mg_check_ip_acl(const char *acl, uint32_t remote_ip); -#if MG_ENABLE_JAVASCRIPT -/* - * Enables server-side JavaScript scripting. - * Requires a `-DMG_ENABLE_JAVASCRIPT` compilation flag and V7 engine sources. - * V7 instance must not be destroyed during manager's lifetime. - * Returns a V7 error. - */ -enum v7_err mg_enable_javascript(struct mg_mgr *m, struct v7 *v7, - const char *init_js_file_name); -#endif - /* * Schedules an MG_EV_TIMER event to be delivered at `timestamp` time. * `timestamp` is UNIX time (the number of seconds since Epoch). It is @@ -3801,11 +3931,26 @@ extern "C" { * * Returns 0 on success, -1 on error. */ -int mg_parse_uri(struct mg_str uri, struct mg_str *scheme, +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 @@ -3836,8 +3981,12 @@ int mg_normalize_uri_path(const struct mg_str *in, struct mg_str *out); extern "C" { #endif /* __cplusplus */ -#ifndef MAX_PATH_SIZE -#define MAX_PATH_SIZE 500 +#ifndef MG_MAX_PATH +#ifdef PATH_MAX +#define MG_MAX_PATH PATH_MAX +#else +#define MG_MAX_PATH 256 +#endif #endif /* @@ -3898,6 +4047,21 @@ FILE *mg_fopen(const char *path, const char *mode); * 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 @@ -3927,9 +4091,10 @@ void mg_set_close_on_exec(sock_t); * * 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. */ -void mg_conn_addr_to_str(struct mg_connection *nc, char *buf, size_t len, - int flags); +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); @@ -3940,8 +4105,8 @@ void mg_sock_to_str(sock_t sock, char *buf, size_t len, int flags); * * `flags` is MG_SOCK_STRINGIFY_IP and/or MG_SOCK_STRINGIFY_PORT. */ -void mg_sock_addr_to_str(const union socket_address *sa, char *buf, size_t len, - int flags); +int mg_sock_addr_to_str(const union socket_address *sa, char *buf, size_t len, + int flags); #if MG_ENABLE_HEXDUMP /* @@ -3973,32 +4138,6 @@ void mg_hexdump_connection(struct mg_connection *nc, const char *path, */ int mg_is_big_endian(void); -/* - * 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); - -/* - * Matches 0-terminated string (mg_match_prefix) or string with given length - * mg_match_prefix_n against a glob pattern. - * - * Match is case-insensitive. Returns number of bytes matched, or -1 if no - * match. - */ -int mg_match_prefix(const char *pattern, int pattern_len, const char *str); -int mg_match_prefix_n(const struct mg_str pattern, const struct mg_str str); - /* * Use with cs_base64_init/update/finish in order to write out base64 in chunks. */ @@ -4014,7 +4153,16 @@ void mg_mbuf_append_base64(struct mbuf *mbuf, const void *data, size_t len); * If pass is NULL, then user is expected to contain the credentials pair * already encoded as `user:pass`. */ -void mg_basic_auth_header(const char *user, const char *pass, struct mbuf *buf); +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 } @@ -4052,14 +4200,6 @@ extern "C" { #define MG_MAX_HTTP_REQUEST_SIZE 1024 #endif -#ifndef MG_MAX_PATH -#ifdef PATH_MAX -#define MG_MAX_PATH PATH_MAX -#else -#define MG_MAX_PATH 256 -#endif -#endif - #ifndef MG_MAX_HTTP_SEND_MBUF #define MG_MAX_HTTP_SEND_MBUF 1024 #endif @@ -4071,6 +4211,7 @@ extern "C" { /* 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" */ @@ -4094,9 +4235,6 @@ struct http_message { /* Headers */ struct mg_str header_names[MG_MAX_HTTP_HEADERS]; struct mg_str header_values[MG_MAX_HTTP_HEADERS]; - - /* Message body */ - struct mg_str body; /* Zero-length for requests with no body */ }; #if MG_ENABLE_HTTP_WEBSOCKET @@ -4238,6 +4376,17 @@ 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. * @@ -4259,7 +4408,8 @@ void mg_send_websocket_handshake3(struct mg_connection *nc, const char *path, * ``` */ struct mg_connection *mg_connect_ws(struct mg_mgr *mgr, - mg_event_handler_t event_handler, + MG_CB(mg_event_handler_t event_handler, + void *user_data), const char *url, const char *protocol, const char *extra_headers); @@ -4269,11 +4419,10 @@ struct mg_connection *mg_connect_ws(struct mg_mgr *mgr, * 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_event_handler_t ev_handler, - struct mg_connect_opts opts, - const char *url, const char *protocol, - const char *extra_headers); +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. @@ -4299,7 +4448,8 @@ void mg_send_websocket_frame(struct mg_connection *nc, int op_and_flags, /* * Sends multiple websocket frames. * - * Like `mg_send_websocket_frame()`, but composes a frame from multiple buffers. + * Like `mg_send_websocket_frame()`, but composes a 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); @@ -4344,12 +4494,18 @@ void mg_printf_websocket_frame(struct mg_connection *nc, int op_and_flags, * (`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, + * 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); + #ifdef __cplusplus } #endif /* __cplusplus */ @@ -4473,7 +4629,8 @@ size_t mg_parse_multipart(const char *buf, size_t buf_len, char *var_name, * 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, `-1` is returned. + * 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); @@ -4724,7 +4881,8 @@ typedef struct mg_str (*mg_fu_fname_fn)(struct mg_connection *nc, * ``` */ void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data, - mg_fu_fname_fn local_name_fn); + mg_fu_fname_fn local_name_fn + MG_UD_ARG(void *user_data)); #endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */ #endif /* MG_ENABLE_FILESYSTEM */ @@ -4742,7 +4900,7 @@ void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data, * nc->flags |= MG_F_SEND_AND_CLOSE; * } * - * static void handle_hello1(struct mg_connection *nc, int ev, void *ev_data) { + * 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; @@ -4756,7 +4914,20 @@ void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data, * ``` */ void mg_register_http_endpoint(struct mg_connection *nc, const char *uri_path, - mg_event_handler_t handler); + 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. @@ -4765,11 +4936,23 @@ void mg_register_http_endpoint(struct mg_connection *nc, const char *uri_path, 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)` whill append the `3\r\nfoo\r\n` string + * `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 @@ -4794,7 +4977,7 @@ 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 reponse line. `extra_headers` must NOT end end with new line. + * after the response line. `extra_headers` must NOT end end with new line. * Example: * * mg_send_response_line(nc, 200, "Access-Control-Allow-Origin: *"); @@ -4818,7 +5001,7 @@ void mg_http_send_error(struct mg_connection *nc, int code, const char *reason); * `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 reponse line. `extra_headers` must NOT end end with new line. + * after the response line. `extra_headers` must NOT end end with new line. * * Example: * @@ -4911,11 +5094,10 @@ extern "C" { * "var_1=value_1&var_2=value_2"); * ``` */ -struct mg_connection *mg_connect_http(struct mg_mgr *mgr, - mg_event_handler_t event_handler, - const char *url, - const char *extra_headers, - const char *post_data); +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. @@ -4924,12 +5106,10 @@ struct mg_connection *mg_connect_http(struct mg_mgr *mgr, *parameters * (for example, SSL parameters) */ -struct mg_connection *mg_connect_http_opt(struct mg_mgr *mgr, - mg_event_handler_t ev_handler, - struct mg_connect_opts opts, - const char *url, - const char *extra_headers, - const char *post_data); +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, @@ -4973,6 +5153,7 @@ int mg_http_create_digest_auth_header(char *buf, size_t buf_len, struct mg_mqtt_message { int cmd; int qos; + int len; /* message length in the IO buffer */ struct mg_str topic; struct mg_str payload; @@ -5008,6 +5189,7 @@ struct mg_send_mqtt_handshake_opts { /* 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 */ @@ -5152,6 +5334,19 @@ void mg_mqtt_pong(struct mg_connection *nc); 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 */ @@ -5193,7 +5388,9 @@ int mg_mqtt_next_subscribe_topic(struct mg_mqtt_message *msg, extern "C" { #endif /* __cplusplus */ -#define MG_MQTT_MAX_SESSION_SUBSCRIPTIONS 512; +#ifndef MG_MQTT_MAX_SESSION_SUBSCRIPTIONS +#define MG_MQTT_MAX_SESSION_SUBSCRIPTIONS 512 +#endif struct mg_mqtt_broker; @@ -5290,6 +5487,8 @@ extern "C" { #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 @@ -5379,7 +5578,7 @@ int mg_dns_copy_questions(struct mbuf *io, struct mg_dns_message *msg); * struct because they might be invalidated as soon as the IO buffer grows * again. * - * Returns the number of bytes appened or -1 in case of error. + * 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, @@ -5557,7 +5756,7 @@ typedef void (*mg_resolve_callback_t)(struct mg_dns_message *dns_message, /* Options for `mg_resolve_async_opt`. */ struct mg_resolve_async_opts { - const char *nameserver_url; + 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 */ @@ -5569,6 +5768,9 @@ struct mg_resolve_async_opts { 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. * @@ -5717,7 +5919,7 @@ struct mg_coap_option *mg_coap_add_option(struct mg_coap_message *cm, /* * Frees the memory allocated for options. - * If the cm paramater doesn't contain any option it does nothing. + * If the cm parameter doesn't contain any option it does nothing. */ void mg_coap_free_options(struct mg_coap_message *cm); @@ -5809,7 +6011,8 @@ struct mg_sntp_message { /* Establishes connection to given sntp server */ struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, - mg_event_handler_t event_handler, + MG_CB(mg_event_handler_t event_handler, + void *user_data), const char *sntp_server_name); /* Sends time request to given connection */