Compare commits

...
19 Commits
Author SHA1 Message Date
Deomid Ryabkov afcb4d9df7 Do not post MG_SIG_CLOSE_CONN if the conn is already closing 2022-10-09 16:36:51 +01:00
Deomid Ryabkov 44b15b76f7 Fix leaking non-active (e.g., bound) LWIP TCP PCBs
tcp_close_tcpip() only closed active TCP PCBs, leaking other types of TCP PCBs
(notably bound only, not yet connected/used).  With time, this led to DoS
situations.

Fix by giving up on the hacky way of searching the LWIP innards for the TCP PCB
needing closing.  The searching was non-comprehensive, hence the problem in
principle, and inherently racy: could close a meanwhile freed & reused PCB.

Pass the pointer to the struct mg_lwip_conn_state of the struct mg_connection
being destroyed to tcp_close_tcpip() instead of the bare PCB pointer, so the PCB
pointer stored within can be used.  Delay unassigning that pointer from the
struct mg_connection until tcp_close_tcpip() finishes, so that
a mg_lwip_tcp_error_cb() meanwhile can clear the PCB pointer stored within.
That ensures no double-closing of the TCP PCB when a concurrent TCP error makes
LWIP close the TCP PCB from the LWIP side.

NB!  At the moment, PCBs can still leak if tcp_close() fails, which is unlikely
but possible as per the API.  Retrying tcp_close() should somehow be
implemented.  For now, at least add logging of tcp_close() failures.

Also: remove the extraneous though benign double-calling of tcp_arg(…, NULL).

h/t @QRPp
2022-09-27 09:20:15 +01:00
Deomid Ryabkov a1fcbb0718 Fix WS connections to URLs that don't end in /
Same as in mg_connect_http_opt().
2022-08-03 14:39:55 +01:00
Deomid Ryabkov 0c4d364cee LWIP: Fix races between TCP errors and queued callbacks
A TCP connection can get closed while there are TCPIP thread callbacks
queued. Add checks to close and write functions to avoid acting on
tcp_pcbs that have already been disposed of.
2022-06-14 21:27:24 +01:00
Deomid Ryabkov def2f39724 lwip: Check for accepted connection being NULL
This happens when lwip runs out of pcbs and just communicates an error.
2021-11-24 02:43:42 +00:00
Deomid Ryabkov 311fdb8bac lwip: Optimize UDP RX path and introduce a limit
Store adddress in the memory immediately preceding the packet data.
It is guaranteed to have enough space (IP header is 20 bytes).

Introduce a limit on the length of the RX chain,
MG_LWIP_MAX_UDP_RX_CHAIN_LEN. Default is 20.
2021-09-01 22:25:36 +01:00
Deomid Ryabkov 38f694d071 Update README 2021-06-12 02:00:49 +01:00
Deomid "rojer" Ryabkov da1e5438bd LWIP: (Ab)use mg_conection.priv_2 to pass input interface address
This is required to distinguish input interface for multicast traffic
and select correct output interface for outgoing traffic (advertisements).

http://savannah.nongnu.org/bugs/?49662 removed ability to receive
multicast traffic on on interface-bound sockets (making the previous
change useless, hence the revert), so for LWIP 2.0.1 and newer
multicast packets can only be received on INADDR_ANY listeners.
User is then required to distinguish input interface in the receive path.
This information is only available in the udp_recv callback, so we must
pass this down to dns-sd library somehow, and priv_2 is the how.
It is normally used for DNS resolver state, and is never used on
listening sockets. We thus avoid growing the mg_connection structure.
2021-06-12 01:58:08 +01:00
Deomid "rojer" Ryabkov e3a140c9a7 Add ability to enumerate endpoints 2021-06-12 01:56:50 +01:00
Deomid "rojer" Ryabkov a350208f35 Add SHA256 support for digest auth
https://tools.ietf.org/html/rfc7616
2021-06-12 01:56:29 +01:00
Deomid "rojer" Ryabkov 6fa305e36d Remove ESP8266 pbuf chain length check
It's been pushed up to LWIP: https://github.com/mongoose-os-libs/lwip/commit/f92662566e71238bd6559b23fee37b0728ecb8e7
2021-06-12 01:56:10 +01:00
Deomid "rojer" Ryabkov edd38f6ccd mongoose: Read file data directly into send_mbuf
Reduces stack usage and avoids an extra copy
2021-06-12 01:55:44 +01:00
Deomid "rojer" Ryabkov abced77c24 Remove NUL-temrination from cs_base64_{encode,decode} functions
Preserve for the corresponding `mg_*` variants for compatibility.
2021-06-12 01:54:51 +01:00
Deomid "rojer" Ryabkov 4699e90e99 Ensure struct mg_connect_opts size remains the same, SSL or not
It is passed by value so libraries compiled with and without SSL cannot interoperate if struct size is different.
2021-06-12 01:54:32 +01:00
Deomid "rojer" Ryabkov e9853be8f7 Reduce the size of struct mg_dns_message
From 2K to 1K. Still too big for most cases but manageable.
2021-06-12 01:54:21 +01:00
Deomid "rojer" Ryabkov fd062b530c Fix a memory leak in mg_lwip_if
When a UDP listener is closed, pending pbufs must be freed.
This was causing a leak on flappy wifi connections and dns-sd enabled.
2021-06-12 01:46:48 +01:00
Deomid "rojer" Ryabkov 2fbfe61c37 Fix connection management bugs in mg_lwip_if
* Do not set MG_F_SSL ona connection until it's been fully accepted (MG_SIG_ACCEPT is handled).
   This fixes a crash when SSL connection is polled before it's fully initialized.
 * Fix mg_lwip_tcp_error_cb to NULL out tcp_pcb pointer even if mg_connection is already shutting down.
   After mg_lwip_tcp_error_cb, tcp_pcb pointer can no longer be used and doesn't need to be freed.
   This fixes https://github.com/cesanta/mongoose-os/issues/545
2021-06-12 01:45:30 +01:00
Deomid "rojer" Ryabkov 70034b5873 Fix stack overflow when serving http files on ESP8266
ESP8266 non-OS has very limited stack - only 4K. Undetected stack
overflows cause nasty bugs and it turns out serving files over http
routinely causes stack overflow.

This was exposed by https://github.com/cesanta/mongoose-os/commit/a5f59123edcfef10c351b3e679f28720b5de9611

As a short-term workaround, push http_message into bss, this is enough
as an immediate workaround. Longer term, we need to think how to bring
8266 to parity with other platforms where we have at least 8K stack.

At the same time, get rid of the compiler bug workaround - modern
toolchain doesn't need this.
2021-06-12 00:30:51 +01:00
Deomid Ryabkov d6c9d3c181 Fix amalgam/unamalgam diffs 2021-06-12 00:22:26 +01:00
34 changed files with 793 additions and 492 deletions

No files matched your search

+6
View File
@@ -0,0 +1,6 @@
BasedOnStyle: Google
AllowShortFunctionsOnASingleLine: false
SpaceAfterCStyleCast: true
PointerBindsToType: false
DerivePointerBinding: false
IncludeBlocks: Preserve
+2 -66
View File
@@ -2,75 +2,11 @@
![](https://img.shields.io/badge/license-GPL_2-green.svg "License")
Mongoose is ideal for embedded environments. It has been designed
for connecting devices and bringing them online. On the market since 2004,
used by vast number of open source and
commercial products - it even runs on the International Space station!
Mongoose makes embedded network programming fast, robust, and easy.
- [Download Mongoose Source Code here](https://www.cesanta.com/download.html)
Connectivity is vital for software and embedded devices, but there are many pitfalls to consider when embedding a web server.
This white paper breaks down on the top 9 things to avoid when embedding a web server.
- [Download “9 Things NOT to do when embedding a web server” white paper here](https://www.cesanta.com/whitepaper.html)
Looking for a complete IoT firmware solution?
Check out [Mongoose OS](https://mongoose-os.com) - open source embedded operating system for low-power connected microcontrollers. Secure, designed for Internet of Things, complete environment for prototyping, development and managing.
# Support
- [Study mongoose example code](https://github.com/cesanta/mongoose/tree/master/examples)
- [Read User Guide and API reference](https://cesanta.com/docs/overview/intro.html)
- [Support Forum - ask your technical questions here](https://community.mongoose-os.com/)
- [Commercial licensing and support available](https://www.cesanta.com/licensing.html)
- [Check our latest releases](https://github.com/cesanta/mongoose/releases)
# Features
* Cross-platform: works on Linux/UNIX, MacOS, QNX, eCos, Windows, Android,
iPhone, FreeRTOS (TI CC3200, ESP8266), etc
* Supported hardware platforms: TI CC3200, TI MSP432, NRF52, STM32, PIC32, ESP8266, ESP32 and more
* Builtin protocols:
- plain TCP, plain UDP, SSL/TLS (over TCP, one-way or two-way)
- HTTP client, HTTP server
- WebSocket client, WebSocket server
- MQTT client, MQTT broker
- CoAP client, CoAP server
- DNS client, DNS server, async DNS resolver
* Single-threaded, asynchronous, non-blocking core with simple event-based API
* Native support for [PicoTCP embedded TCP/IP stack](http://www.picotcp.com),
[LWIP embedded TCP/IP stack](https://en.wikipedia.org/wiki/LwIP)
* Tiny static and run-time footprint
* Source code is both ISO C and ISO C++ compliant
* Very easy to integrate: just copy
[mongoose.c](https://raw.githubusercontent.com/cesanta/mongoose/master/mongoose.c) and
[mongoose.h](https://raw.githubusercontent.com/cesanta/mongoose/master/mongoose.h)
files to your build tree
This branch is only for use by [Mongoose OS](https://github.com/cesanta/mongoose-os),
for other projects please use the master branch.
# Licensing
Mongoose is released under Commercial and [GNU GPL v.2](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html) open source licenses.
Commercial Projects: [Contact us for commercial license.](https://www.cesanta.com/contact.html)
# Dashboard Example
Mongoose is often used to implement device dashboards and real-time
data exchange over Websocket. Here is a dashboard example that illustrates
the functionality:
![](http://www.cesanta.com/images/dashboard.png)
[Developing a new product? Contact us today to discuss how Mongoose can help.](https://www.cesanta.com/contact.html)
# Contributions
Contributions are welcome! Please follow the guidelines below:
- Sign [Cesanta CLA](https://cesanta.com/cla.html) and send GitHub pull request
- When making pull requests, please make sure that it has only one commit,
and imlements/fixes only one piece of functionality
# Looking for a pre-compiled Mongoose web server Windows or Mac binary?
- [Download pre-compiled Mongoose web server binary.](https://www.cesanta.com/binary.html)
+304 -204
View File
@@ -314,24 +314,18 @@ void cs_base64_finish(struct cs_base64_ctx *ctx) {
while (j % 4 != 0) { \
BASE64_OUT('='); \
} \
BASE64_FLUSH()
#define BASE64_OUT(ch) \
do { \
dst[j++] = (ch); \
} while (0)
#define BASE64_FLUSH() \
do { \
dst[j++] = '\0'; \
} while (0)
void cs_base64_encode(const unsigned char *src, int src_len, char *dst) {
void cs_base64_encode(const unsigned char *src, int src_len, char *dst, int *enc_len) {
BASE64_ENCODE_BODY;
if (enc_len != NULL) *enc_len = j;
}
#undef BASE64_OUT
#undef BASE64_FLUSH
#if CS_ENABLE_STDIO
#define BASE64_OUT(ch) \
@@ -340,14 +334,11 @@ void cs_base64_encode(const unsigned char *src, int src_len, char *dst) {
j++; \
} while (0)
#define BASE64_FLUSH()
void cs_fprint_base64(FILE *f, const unsigned char *src, int src_len) {
BASE64_ENCODE_BODY;
}
#undef BASE64_OUT
#undef BASE64_FLUSH
#endif /* CS_ENABLE_STDIO */
/* Convert one byte of encoded base64 input stream to 6-bit chunk */
@@ -406,7 +397,6 @@ int cs_base64_decode(const unsigned char *s, int len, char *dst, int *dec_len) {
if (d == 200) break;
*dst++ = c << 6 | d;
}
*dst = 0;
if (dec_len != NULL) *dec_len = (dst - orig_dst);
return orig_len - len;
}
@@ -1564,6 +1554,7 @@ struct mg_str mg_mk_str_n(const char *s, size_t len) {
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;
if (n1 == 0) return (n2 > 0 ? -1 : 0);
int r = strncmp(str1->p, str2, (n1 < n2) ? n1 : n2);
if (r == 0) {
return n1 - n2;
@@ -2757,7 +2748,6 @@ struct mg_connection *mg_if_accept_new_conn(struct mg_connection *lc) {
nc->user_data = lc->user_data;
nc->recv_mbuf_limit = lc->recv_mbuf_limit;
nc->iface = lc->iface;
if (lc->flags & MG_F_SSL) nc->flags |= MG_F_SSL;
mg_add_conn(nc->mgr, nc);
LOG(LL_DEBUG, ("%p %p %d %#x", lc, nc, (int) nc->sock, (int) nc->flags));
return nc;
@@ -2917,10 +2907,13 @@ static int mg_recv_udp(struct mg_connection *nc, char *buf, size_t len) {
mg_call(nc, NULL, nc->user_data, MG_EV_ACCEPT, &nc->sa);
}
}
/* Copy input interface address. */
if (nc != NULL) nc->priv_2 = lc->priv_2;
}
if (nc != NULL) {
DBG(("%p <- %d bytes from %s:%d", nc, n, inet_ntoa(nc->sa.sin.sin_addr),
ntohs(nc->sa.sin.sin_port)));
DBG(("%p <- %d bytes from %s:%d ia %#x", nc, n,
inet_ntoa(nc->sa.sin.sin_addr), ntohs(nc->sa.sin.sin_port),
(int) (intptr_t) nc->priv_2));
if (nc == lc) {
nc->recv_mbuf.len += n;
} else {
@@ -5762,18 +5755,6 @@ struct mg_http_proto_data_chuncked {
int64_t body_len; /* How many bytes of chunked body was reassembled. */
};
struct mg_http_endpoint {
struct mg_http_endpoint *next;
struct mg_str uri_pattern; /* owned */
char *auth_domain; /* owned */
char *auth_file; /* owned */
mg_event_handler_t handler;
#if MG_ENABLE_CALLBACK_USERDATA
void *user_data;
#endif
};
enum mg_http_multipart_stream_state {
MPS_BEGIN,
MPS_WAITING_FOR_BOUNDARY,
@@ -5878,6 +5859,13 @@ static void mg_http_free_proto_data_file(struct mg_http_proto_data_file *d) {
}
#endif
struct mg_http_endpoint *mg_get_http_endpoints(struct mg_connection *nc) {
if (nc == NULL) return NULL;
struct mg_http_proto_data *pd = (struct mg_http_proto_data *) nc->proto_data;
if (pd == NULL) return NULL;
return pd->endpoints;
}
static void mg_http_free_proto_data_endpoints(struct mg_http_endpoint **ep) {
struct mg_http_endpoint *current = *ep;
@@ -6176,8 +6164,7 @@ struct mg_str *mg_get_http_header(struct http_message *hm, const char *name) {
#if MG_ENABLE_FILESYSTEM
static void mg_http_transfer_file_data(struct mg_connection *nc) {
struct mg_http_proto_data *pd = mg_http_get_proto_data(nc);
char buf[MG_MAX_HTTP_SEND_MBUF];
size_t n = 0, to_read = 0, left = (size_t)(pd->file.cl - pd->file.sent);
size_t to_read = 0, left = (size_t)(pd->file.cl - pd->file.sent);
if (pd->file.type == DATA_FILE) {
struct mbuf *io = &nc->send_mbuf;
@@ -6190,11 +6177,16 @@ static void mg_http_transfer_file_data(struct mg_connection *nc) {
to_read = left;
}
if (to_read > 0) {
n = mg_fread(buf, 1, to_read, pd->file.fp);
if (n > 0) {
mg_send(nc, buf, n);
pd->file.sent += n;
DBG(("%p sent %d (total %d)", nc, (int) n, (int) pd->file.sent));
size_t new_size = io->len + to_read;
mbuf_resize(io, new_size);
if (io->size == new_size) {
char *buf = io->buf + io->len;
size_t n = mg_fread(buf, 1, to_read, pd->file.fp);
if (n > 0) {
io->len += n;
pd->file.sent += n;
DBG(("%p sent %d (total %d)", nc, (int) n, (int) pd->file.sent));
}
}
} else {
/* Rate-limited */
@@ -6395,30 +6387,17 @@ static void deliver_chunk(struct mg_connection *c, struct http_message *hm,
}
}
/*
* lx106 compiler has a bug (TODO(mkm) report and insert tracking bug here)
* If a big structure is declared in a big function, lx106 gcc will make it
* even bigger (round up to 4k, from 700 bytes of actual size).
*/
#ifdef __xtensa__
static void mg_http_handler2(struct mg_connection *nc, int ev,
void *ev_data MG_UD_ARG(void *user_data),
struct http_message *hm) __attribute__((noinline));
void mg_http_handler(struct mg_connection *nc, int ev,
void *ev_data MG_UD_ARG(void *user_data)) {
struct http_message hm;
mg_http_handler2(nc, ev, ev_data MG_UD_ARG(user_data), &hm);
}
static void mg_http_handler2(struct mg_connection *nc, int ev,
void *ev_data MG_UD_ARG(void *user_data),
struct http_message *hm) {
#else /* !__XTENSA__ */
void mg_http_handler(struct mg_connection *nc, int ev,
void *ev_data MG_UD_ARG(void *user_data)) {
/* On ESP8266 stack is especially limited (4K).
* Since Mongoose is single-threaded, we can use the same struct
* and save ~400 bytes. This may not seem like much but in fact it's the
* difference between overflowing and not (when serving files). */
#if CS_PLATFORM == CS_P_ESP8266
static struct http_message shm, *hm = &shm;
#else
struct http_message shm, *hm = &shm;
#endif /* __XTENSA__ */
#endif
struct mg_http_proto_data *pd = mg_http_get_proto_data(nc);
struct mbuf *io = &nc->recv_mbuf;
int req_len;
@@ -7514,10 +7493,10 @@ 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];
static void cs_digest(enum mg_auth_algo algo, char buf[65], ...) {
unsigned char hash[32];
const uint8_t *msgs[16], *p;
size_t msg_lens[16];
size_t num_msgs = 0;
va_list ap;
@@ -7529,44 +7508,61 @@ void cs_md5(char buf[33], ...) {
}
va_end(ap);
mg_hash_md5_v(num_msgs, msgs, msg_lens, hash);
cs_to_hex(buf, hash, sizeof(hash));
if (algo == MG_AUTH_ALGO_MD5) {
mg_hash_md5_v(num_msgs, msgs, msg_lens, hash);
cs_to_hex(buf, hash, 16);
} else {
mg_hash_sha256_v(num_msgs, msgs, msg_lens, hash);
cs_to_hex(buf, hash, 32);
}
}
static void mg_mkmd5resp(const char *method, size_t method_len, const char *uri,
size_t uri_len, const char *ha1, size_t ha1_len,
const char *nonce, size_t nonce_len, const char *nc,
size_t nc_len, const char *cnonce, size_t cnonce_len,
const char *qop, size_t qop_len, char *resp) {
static const char colon[] = ":";
static const size_t one = 1;
char ha2[33];
cs_md5(ha2, method, method_len, colon, one, uri, uri_len, NULL);
cs_md5(resp, ha1, ha1_len, colon, one, nonce, nonce_len, colon, one, nc,
nc_len, colon, one, cnonce, cnonce_len, colon, one, qop, qop_len,
colon, one, ha2, sizeof(ha2) - 1, NULL);
static void mg_mk_digest_resp(const char *method, size_t method_len,
const char *uri, size_t uri_len, const char *ha1,
size_t ha1_len, const char *nonce,
size_t nonce_len, const char *nc, size_t nc_len,
const char *cnonce, size_t cnonce_len,
const char *qop, size_t qop_len,
enum mg_auth_algo algo, char *resp) {
size_t one = 1;
char ha2[65];
size_t ha2_size = (algo == MG_AUTH_ALGO_MD5 ? 32 : 64);
cs_digest(algo, ha2, method, method_len, ":", one, uri, uri_len, NULL);
cs_digest(algo, resp, ha1, ha1_len, ":", one, nonce, nonce_len, ":", one, nc,
nc_len, ":", one, cnonce, cnonce_len, ":", one, qop, qop_len,
":", one, ha2, ha2_size, NULL);
}
int mg_http_create_digest_auth_header_algo(char *buf, size_t buf_len,
const char *method, const char *uri,
const char *auth_domain, const char *user,
const char *passwd, const char *nonce,
enum mg_auth_algo algo) {
const char qop[] = "auth";
const size_t one = 1;
char ha1[65], resp[65], cnonce[40];
size_t ha1_size = (algo == MG_AUTH_ALGO_MD5 ? 32 : 64);
snprintf(cnonce, sizeof(cnonce), "%lx", (unsigned long) mg_time());
cs_digest(algo, ha1, user, (size_t) strlen(user), ":", one, auth_domain,
(size_t) strlen(auth_domain), ":", one, passwd,
(size_t) strlen(passwd), NULL);
mg_mk_digest_resp(method, strlen(method), uri, strlen(uri), ha1, ha1_size,
nonce, strlen(nonce), "1", one, cnonce, strlen(cnonce), qop,
sizeof(qop) - 1, algo, resp);
return snprintf(buf, buf_len,
"Authorization: Digest username=\"%s\","
"realm=\"%s\",uri=\"%s\",qop=%s,nc=1,cnonce=%s,"
"nonce=%s,response=%s,algorithm=%s\r\n",
user, auth_domain, uri, qop, cnonce, nonce, resp,
(algo == MG_AUTH_ALGO_MD5 ? "MD5" : "SHA-256"));
}
int mg_http_create_digest_auth_header(char *buf, size_t buf_len,
const char *method, const char *uri,
const char *auth_domain, const char *user,
const char *passwd, const char *nonce) {
static const char colon[] = ":", qop[] = "auth";
static const size_t one = 1;
char ha1[33], resp[33], cnonce[40];
snprintf(cnonce, sizeof(cnonce), "%lx", (unsigned long) mg_time());
cs_md5(ha1, user, (size_t) strlen(user), colon, one, auth_domain,
(size_t) strlen(auth_domain), colon, one, passwd,
(size_t) strlen(passwd), NULL);
mg_mkmd5resp(method, strlen(method), uri, strlen(uri), ha1, sizeof(ha1) - 1,
nonce, strlen(nonce), "1", one, cnonce, strlen(cnonce), qop,
sizeof(qop) - 1, resp);
return snprintf(buf, buf_len,
"Authorization: Digest username=\"%s\","
"realm=\"%s\",uri=\"%s\",qop=%s,nc=1,cnonce=%s,"
"nonce=%s,response=%s\r\n",
user, auth_domain, uri, qop, cnonce, nonce, resp);
return mg_http_create_digest_auth_header_algo(buf, buf_len, method, uri, auth_domain, user, passwd, nonce, MG_AUTH_ALGO_MD5);
}
/*
@@ -7581,15 +7577,17 @@ static int mg_check_nonce(const char *nonce) {
return (now >= val) && (now - val < 60 * 60);
}
int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
FILE *fp) {
int mg_http_check_digest_auth_algo(struct http_message *hm, const char *auth_domain,
enum mg_auth_algo fp_algo, FILE *fp) {
int ret = 0;
struct mg_str *hdr;
char username_buf[50], cnonce_buf[64], response_buf[40], uri_buf[200],
qop_buf[20], nc_buf[20], nonce_buf[16];
char username_buf[50], cnonce_buf[64], response_buf[64], uri_buf[200],
qop_buf[20], nc_buf[20], nonce_buf[16], algo_buf[16];
char *username = username_buf, *cnonce = cnonce_buf, *response = response_buf,
*uri = uri_buf, *qop = qop_buf, *nc = nc_buf, *nonce = nonce_buf;
*uri = uri_buf, *qop = qop_buf, *nc = nc_buf, *nonce = nonce_buf, *algo = algo_buf;
enum mg_auth_algo hm_algo = MG_AUTH_ALGO_MD5;
/* Parse "Authorization:" header, fail fast on parse error */
if (hm == NULL || fp == NULL ||
@@ -7608,16 +7606,30 @@ int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
goto clean;
}
if (mg_http_parse_header2(hdr, "algorithm", &algo, sizeof(algo_buf)) != 0) {
if (strcmp(algo, "MD5") == 0) {
hm_algo = MG_AUTH_ALGO_MD5;
} else if (strcmp(algo, "SHA-256") == 0) {
hm_algo = MG_AUTH_ALGO_SHA256;
} else {
goto clean;
}
}
if (hm_algo != fp_algo) {
goto clean;
}
/* NOTE(lsm): due to a bug in MSIE, we do not compare URIs */
ret = mg_check_digest_auth(
ret = mg_check_digest_auth_algo(
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);
fp_algo, fp);
clean:
if (username != username_buf) MG_FREE(username);
@@ -7627,17 +7639,23 @@ clean:
if (qop != qop_buf) MG_FREE(qop);
if (nc != nc_buf) MG_FREE(nc);
if (nonce != nonce_buf) MG_FREE(nonce);
if (algo != algo_buf) MG_FREE(algo);
return ret;
}
int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
FILE *fp) {
return mg_http_check_digest_auth_algo(hm, auth_domain, MG_AUTH_ALGO_MD5, fp);
}
int mg_check_digest_auth_algo(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) {
struct mg_str auth_domain, enum mg_auth_algo algo, FILE *fp) {
char buf[128], f_user[sizeof(buf)], f_ha1[sizeof(buf)], f_domain[sizeof(buf)];
char exp_resp[33];
char exp_resp[65] = {0};
/*
* Read passwords file line by line. If should have htdigest format,
@@ -7649,9 +7667,9 @@ int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
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, exp_resp);
mg_mk_digest_resp(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, algo, exp_resp);
LOG(LL_DEBUG, ("%.*s %s %.*s %s", (int) username.len, username.p,
f_domain, (int) response.len, response.p, exp_resp));
return mg_ncasecmp(response.p, exp_resp, strlen(exp_resp)) == 0;
@@ -7662,6 +7680,16 @@ int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
return 0;
}
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) {
return mg_check_digest_auth_algo(
method, uri, username, cnonce, response, qop, nc, nonce, auth_domain,
MG_AUTH_ALGO_MD5, fp);
}
int mg_http_is_authorized(struct http_message *hm, struct mg_str path,
const char *domain, const char *passwords_file,
int flags) {
@@ -7670,27 +7698,30 @@ int mg_http_is_authorized(struct http_message *hm, struct mg_str path,
FILE *fp;
int authorized = 1;
if (domain != NULL && passwords_file != NULL) {
if (flags & MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE) {
fp = mg_fopen(passwords_file, "r");
} else if (flags & MG_AUTH_FLAG_IS_DIRECTORY) {
snprintf(buf, sizeof(buf), "%.*s%c%s", (int) path.len, path.p, DIRSEP,
passwords_file);
fp = mg_fopen(buf, "r");
} else {
p = strrchr(path.p, DIRSEP);
if (p == NULL) p = path.p;
snprintf(buf, sizeof(buf), "%.*s%c%s", (int) (p - path.p), path.p, DIRSEP,
passwords_file);
fp = mg_fopen(buf, "r");
}
if (domain == NULL || passwords_file == NULL) {
return 1;
}
if (fp != NULL) {
authorized = mg_http_check_digest_auth(hm, domain, fp);
fclose(fp);
} else if (!(flags & MG_AUTH_FLAG_ALLOW_MISSING_FILE)) {
authorized = 0;
}
if (flags & MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE) {
fp = mg_fopen(passwords_file, "r");
} else if (flags & MG_AUTH_FLAG_IS_DIRECTORY) {
snprintf(buf, sizeof(buf), "%.*s%c%s", (int) path.len, path.p, DIRSEP,
passwords_file);
fp = mg_fopen(buf, "r");
} else {
p = strrchr(path.p, DIRSEP);
if (p == NULL) p = path.p;
snprintf(buf, sizeof(buf), "%.*s%c%s", (int) (p - path.p), path.p, DIRSEP,
passwords_file);
fp = mg_fopen(buf, "r");
}
if (fp != NULL) {
enum mg_auth_algo fp_algo = (enum mg_auth_algo) ((flags >> 8) & 3);
authorized = mg_http_check_digest_auth_algo(hm, domain, fp_algo, fp);
fclose(fp);
} else if (!(flags & MG_AUTH_FLAG_ALLOW_MISSING_FILE)) {
authorized = 0;
}
LOG(LL_DEBUG, ("%.*s %s %x %d", (int) path.len, path.p,
@@ -8240,14 +8271,21 @@ MG_INTERNAL int mg_is_not_modified(struct http_message *hm, cs_stat_t *st) {
}
}
void mg_http_send_digest_auth_request(struct mg_connection *c,
const char *domain) {
void mg_http_send_digest_auth_request_algo(struct mg_connection *c,
const char *domain,
enum mg_auth_algo algo) {
mg_printf(c,
"HTTP/1.1 401 Unauthorized\r\n"
"WWW-Authenticate: Digest qop=\"auth\", "
"realm=\"%s\", nonce=\"%lx\"\r\n"
"realm=\"%s\", nonce=\"%lx\", algorithm=%s\r\n"
"Content-Length: 0\r\n\r\n",
domain, (unsigned long) mg_time());
domain, (unsigned long) mg_time(),
(algo == MG_AUTH_ALGO_MD5 ? "MD5" : "SHA-256"));
}
void mg_http_send_digest_auth_request(struct mg_connection *c,
const char *domain) {
mg_http_send_digest_auth_request_algo(c, domain, MG_AUTH_ALGO_MD5);
}
static void mg_http_send_options(struct mg_connection *nc,
@@ -8316,13 +8354,15 @@ MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path,
hm, mg_mk_str(path), opts->auth_domain, opts->global_auth_file,
((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) |
MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE |
MG_AUTH_FLAG_ALLOW_MISSING_FILE)) ||
MG_AUTH_FLAG_ALLOW_MISSING_FILE |
MG_AUTH_FLAG_ALGO(opts->auth_algo))) ||
!mg_http_is_authorized(
hm, mg_mk_str(path), opts->auth_domain,
opts->per_directory_auth_file,
((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) |
MG_AUTH_FLAG_ALLOW_MISSING_FILE))) {
mg_http_send_digest_auth_request(nc, opts->auth_domain);
MG_AUTH_FLAG_ALLOW_MISSING_FILE |
MG_AUTH_FLAG_ALGO(opts->auth_algo)))) {
mg_http_send_digest_auth_request_algo(nc, opts->auth_domain, opts->auth_algo);
} else if (is_cgi) {
#if MG_ENABLE_HTTP_CGI
mg_handle_cgi(nc, index_file ? index_file : path, path_info, hm, opts);
@@ -8345,7 +8385,7 @@ MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path,
((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) |
MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE |
MG_AUTH_FLAG_ALLOW_MISSING_FILE))))) {
mg_http_send_digest_auth_request(nc, opts->auth_domain);
mg_http_send_digest_auth_request_algo(nc, opts->auth_domain, opts->aith_algo);
#endif
} else if (!mg_vcmp(&hm->method, "MKCOL")) {
mg_handle_mkcol(nc, path, hm);
@@ -8408,9 +8448,6 @@ void mg_serve_http(struct mg_connection *nc, struct http_message *hm,
if (opts.document_root == NULL) {
opts.document_root = ".";
}
if (opts.per_directory_auth_file == NULL) {
opts.per_directory_auth_file = ".htpasswd";
}
if (opts.enable_directory_listing == NULL) {
opts.enable_directory_listing = "yes";
}
@@ -8760,6 +8797,7 @@ void mg_register_http_endpoint_opt(struct mg_connection *nc,
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->auth_algo = opts.auth_algo;
}
new_ep->handler = handler;
#if MG_ENABLE_CALLBACK_USERDATA
@@ -8784,8 +8822,9 @@ static void mg_http_call_endpoint_handler(struct mg_connection *nc, int ev,
if (ep != NULL) {
#if MG_ENABLE_FILESYSTEM && !MG_DISABLE_HTTP_DIGEST_AUTH
if (!mg_http_is_authorized(hm, hm->uri, ep->auth_domain, ep->auth_file,
MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE)) {
mg_http_send_digest_auth_request(nc, ep->auth_domain);
(MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE |
MG_AUTH_FLAG_ALGO(ep->auth_algo)))) {
mg_http_send_digest_auth_request_algo(nc, ep->auth_domain, ep->auth_algo);
return;
}
#endif
@@ -10218,7 +10257,7 @@ void mg_send_websocket_handshake3(struct mg_connection *nc, const char *path,
}
void mg_send_websocket_handshake3v(struct mg_connection *nc,
const struct mg_str path,
const struct mg_str path_in,
const struct mg_str host,
const struct mg_str protocol,
const struct mg_str extra_headers,
@@ -10237,6 +10276,7 @@ void mg_send_websocket_handshake3v(struct mg_connection *nc,
if (user.len > 0) {
mg_basic_auth_header(user, pass, &auth);
}
const struct mg_str path = (path_in.len > 0 ? path_in : mg_mk_str("/"));
/*
* NOTE: the (auth.buf == NULL ? "" : auth.buf) is because cc3200 libc is
@@ -10371,11 +10411,16 @@ size_t mg_fwrite(const void *ptr, size_t size, size_t count, FILE *f) {
#endif
void mg_base64_encode(const unsigned char *src, int src_len, char *dst) {
cs_base64_encode(src, src_len, dst);
int enc_len = 0;
cs_base64_encode(src, src_len, dst, &enc_len);
dst[enc_len] = '\0';
}
int mg_base64_decode(const unsigned char *s, int len, char *dst) {
return cs_base64_decode(s, len, dst, NULL);
int dec_len = 0;
int res = cs_base64_decode(s, len, dst, &dec_len);
dst[dec_len] = '\0';
return res;
}
#if MG_ENABLE_THREADS
@@ -14853,11 +14898,12 @@ struct mg_lwip_conn_state {
struct pbuf *rx_chain; /* Chain of incoming data segments. */
size_t rx_offset; /* Offset within the first pbuf (if partially consumed) */
/* Last SSL write size, for retries. */
int last_ssl_write_size;
unsigned int last_ssl_write_size : 16;
unsigned int rx_chain_len : 8;
/* Whether MG_SIG_RECV is already pending for this connection */
int recv_pending;
unsigned int recv_pending : 1;
/* Whether the connection is about to close, just `rx_chain` needs to drain */
int draining_rx_chain;
unsigned int draining_rx_chain : 1;
};
enum mg_sig_type {
@@ -14897,6 +14943,10 @@ void mg_lwip_mgr_schedule_poll(struct mg_mgr *mgr);
/* Amalgamated: #include "common/cs_dbg.h" */
#ifndef MG_LWIP_MAX_UDP_RX_CHAIN_LEN
#define MG_LWIP_MAX_UDP_RX_CHAIN_LEN 20
#endif
/*
* Newest versions of LWIP have ip_2_ip4, older have ipX_2_ip,
* even older have nothing.
@@ -14917,7 +14967,7 @@ void mg_lwip_mgr_schedule_poll(struct mg_mgr *mgr);
#define TCP_NEW tcp_new_ip6
#define TCP_BIND tcp_bind_ip6
#define UDP_BIND udp_bind_ip6
#define IPADDR_NTOA(x) ip6addr_ntoa((const ip6_addr_t *)(x))
#define IPADDR_NTOA(x) ip6addr_ntoa((const ip6_addr_t *) (x))
#define SET_ADDR(dst, src) \
memcpy((dst)->sin6.sin6_addr.s6_addr, (src)->ip6.addr, \
sizeof((dst)->sin6.sin6_addr.s6_addr))
@@ -15018,9 +15068,10 @@ static err_t mg_lwip_tcp_conn_cb(void *arg, struct tcp_pcb *tpcb, err_t err) {
static void mg_lwip_tcp_error_cb(void *arg, err_t err) {
struct mg_connection *nc = (struct mg_connection *) arg;
DBG(("%p conn error %d", nc, (int) err));
if (nc == NULL || (nc->flags & MG_F_CLOSE_IMMEDIATELY)) return;
if (nc == NULL) return;
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
cs->pcb.tcp = NULL; /* Has already been deallocated */
if (nc->flags & MG_F_CLOSE_IMMEDIATELY) return;
if (nc->flags & MG_F_CONNECTING) {
cs->err = err;
mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc);
@@ -15066,16 +15117,6 @@ static err_t mg_lwip_tcp_recv_cb(void *arg, struct tcp_pcb *tpcb,
mgos_lock();
if (cs->rx_chain == NULL) {
cs->rx_offset = 0;
} else if (pbuf_clen(cs->rx_chain) >= 4) {
/* ESP SDK has a limited pool of 5 pbufs. We must not hog them all or RX
* will be completely blocked. We already have at least 4 in the chain,
* this one is the last, so we have to make a copy and release this one. */
struct pbuf *np = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
if (np != NULL) {
pbuf_copy(np, p);
pbuf_free(p);
p = np;
}
}
mg_lwip_recv_common(nc, p);
mgos_unlock();
@@ -15088,8 +15129,10 @@ static err_t mg_lwip_tcp_sent_cb(void *arg, struct tcp_pcb *tpcb,
struct mg_connection *nc = (struct mg_connection *) arg;
DBG(("%p %p %u %p %p", nc, tpcb, num_sent, tpcb->unsent, tpcb->unacked));
if (nc == NULL) return ERR_OK;
if ((nc->flags & MG_F_SEND_AND_CLOSE) && !(nc->flags & MG_F_WANT_WRITE) &&
nc->send_mbuf.len == 0 && tpcb->unsent == NULL && tpcb->unacked == NULL) {
if ((nc->flags & (MG_F_SEND_AND_CLOSE | MG_F_CLOSE_IMMEDIATELY)) ==
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);
}
if (nc->send_mbuf.len > 0 || (nc->flags & MG_F_WANT_WRITE)) {
@@ -15139,6 +15182,11 @@ void mg_lwip_if_connect_tcp(struct mg_connection *nc,
mg_lwip_netif_run_on_tcpip(mg_lwip_if_connect_tcp_tcpip, &ctx);
}
struct mg_lwip_udp_info {
union socket_address remote_addr;
uint32_t local_ip4;
};
/*
* Lwip included in the SDKs for nRF5x chips has different type for the
* callback of `udp_recv()`
@@ -15152,26 +15200,26 @@ static void mg_lwip_udp_recv_cb(void *arg, struct udp_pcb *pcb, struct pbuf *p,
#endif
{
struct mg_connection *nc = (struct mg_connection *) arg;
/*
* Scribble address on the margins. There's enough room there,
* between pbuf header and payload - at a minimum, IP and UDP headers.
*/
struct mg_lwip_udp_info *ui =
(struct mg_lwip_udp_info *) ((((uintptr_t) p->payload) &
(~((uintptr_t) 3))) -
sizeof(*ui));
DBG(("%p %s:%u %p %u %u", nc, IPADDR_NTOA(addr), port, p, p->ref, p->len));
/* Put address in a separate pbuf and tack it onto the packet. */
struct pbuf *sap =
pbuf_alloc(PBUF_RAW, sizeof(union socket_address), PBUF_RAM);
if (sap == NULL) {
pbuf_free(p);
return;
}
union socket_address *sa = (union socket_address *) sap->payload;
struct netif *nif = ip_current_netif();
#if ((LWIP_VERSION_MAJOR << 8) | LWIP_VERSION_MINOR) >= 0x0105
sa->sin.sin_addr.s_addr = ip_2_ip4(addr)->addr;
ui->remote_addr.sin.sin_addr.s_addr = ip_2_ip4(addr)->addr;
ui->local_ip4 = ip_2_ip4(&nif->ip_addr)->addr;
#else
sa->sin.sin_addr.s_addr = addr->addr;
ui->remote_addr.sin.sin_addr.s_addr = addr->addr;
ui->local_ip4 = nif->ip_addr.addr;
#endif
sa->sin.sin_port = htons(port);
/* Logic in the recv handler requires that there be exactly one data pbuf. */
p = pbuf_coalesce(p, PBUF_RAW);
pbuf_chain(sap, p);
ui->remote_addr.sin.sin_port = htons(port);
mgos_lock();
mg_lwip_recv_common(nc, sap);
mg_lwip_recv_common(nc, p);
mgos_unlock();
(void) pcb;
}
@@ -15180,8 +15228,14 @@ static void mg_lwip_recv_common(struct mg_connection *nc, struct pbuf *p) {
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
if (cs->rx_chain == NULL) {
cs->rx_chain = p;
} else {
cs->rx_chain_len = 1;
} else if (!(nc->flags & MG_F_UDP) ||
cs->rx_chain_len < MG_LWIP_MAX_UDP_RX_CHAIN_LEN) {
pbuf_chain(cs->rx_chain, p);
cs->rx_chain_len++;
} else {
/* Drop it on the floor. */
pbuf_free(p);
}
if (!cs->recv_pending) {
cs->recv_pending = 1;
@@ -15191,25 +15245,32 @@ static void mg_lwip_recv_common(struct mg_connection *nc, struct pbuf *p) {
static int mg_lwip_if_udp_recv(struct mg_connection *nc, void *buf, size_t len,
union socket_address *sa, size_t *sa_len) {
/*
* For UDP, RX chain consists of interleaved address and packet bufs:
* Address pbuf followed by exactly one data pbuf (recv_cb took care of that).
*/
int res = 0;
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
if (nc->sock == INVALID_SOCKET) return -1;
mgos_lock();
if (cs->rx_chain != NULL) {
struct pbuf *ap = cs->rx_chain;
struct pbuf *dp = ap->next;
cs->rx_chain = pbuf_dechain(dp);
res = MIN(dp->len, len);
pbuf_copy_partial(dp, buf, res, 0);
pbuf_free(dp);
pbuf_copy_partial(ap, sa, MIN(*sa_len, ap->len), 0);
pbuf_free(ap);
struct pbuf *p = cs->rx_chain;
cs->rx_chain = pbuf_dechain(p);
cs->rx_chain_len--;
mgos_unlock();
struct mg_lwip_udp_info *ui =
(struct mg_lwip_udp_info *) ((((uintptr_t) p->payload) &
(~((uintptr_t) 3))) -
sizeof(*ui));
*sa = ui->remote_addr;
/* Store local interface address for incoming packets.
* Only for listener because outgoing connections may use priv_2 for DNS. */
if (nc->listener == NULL) {
nc->priv_2 = (void *) (uintptr_t) ui->local_ip4;
}
res = MIN(p->len, len);
pbuf_copy_partial(p, buf, res, 0);
pbuf_free(p);
} else {
mgos_unlock();
}
mgos_unlock();
(void) sa_len;
return res;
}
@@ -15232,10 +15293,6 @@ void mg_lwip_if_connect_udp(struct mg_connection *nc) {
mg_lwip_netif_run_on_tcpip(mg_lwip_if_connect_udp_tcpip, nc);
}
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;
@@ -15250,6 +15307,10 @@ static err_t mg_lwip_accept_cb(void *arg, struct tcp_pcb *newtpcb, err_t err) {
struct mg_connection *lc = (struct mg_connection *) arg, *nc;
struct mg_lwip_conn_state *lcs, *cs;
struct tcp_pcb_listen *lpcb;
if (newtpcb == NULL) {
LOG(LL_ERROR, ("%p accept error %d", lc, (int) err));
return ERR_ABRT;
}
LOG(LL_DEBUG,
("%p conn %p from %s:%u", lc, newtpcb,
IPADDR_NTOA(ipX_2_ip(&newtpcb->remote_ip)), newtpcb->remote_port));
@@ -15361,6 +15422,10 @@ static void mg_lwip_tcp_write_tcpip(void *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;
if (tpcb == NULL) {
/* Possible if tcp_err cb was invoked while this was queued. */
return;
}
size_t len = MIN(tpcb->mss, MIN(ctx->len, tpcb->snd_buf));
size_t unsent, unacked;
if (len == 0) {
@@ -15388,8 +15453,8 @@ static void mg_lwip_tcp_write_tcpip(void *arg) {
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, (int) cs->err, unsent, unacked));
DBG(("%p tcp_write %u = %d, %u %u", tpcb, len, (int) cs->err, unsent,
unacked));
if (cs->err != ERR_OK) {
/*
* We ignore ERR_MEM because memory will be freed up when the data is sent
@@ -15419,11 +15484,19 @@ struct udp_sendto_ctx {
struct pbuf *p;
ip_addr_t *ip;
uint16_t port;
uint32_t mcast_ip4;
int ret;
};
static void udp_sendto_tcpip(void *arg) {
struct udp_sendto_ctx *ctx = (struct udp_sendto_ctx *) arg;
if (ctx->mcast_ip4 != 0) {
#if LWIP_VERSION_MAJOR > 1 && LWIP_MULTICAST_TX_OPTIONS
ctx->upcb->mcast_ip4.addr = ctx->mcast_ip4;
#elif LWIP_VERSION_MAJOR == 1 && LWIP_IGMP
ctx->upcb->multicast_ip.addr = ctx->mcast_ip4;
#endif
}
ctx->ret = udp_sendto(ctx->upcb, ctx->p, ctx->ip, ctx->port);
}
@@ -15442,8 +15515,13 @@ static int mg_lwip_if_udp_send(struct mg_connection *nc, const void *data,
if (p == NULL) return 0;
memcpy(p->payload, data, len);
struct udp_sendto_ctx ctx = {.upcb = upcb, .p = p, .ip = &ip, .port = port};
if (ip_addr_ismulticast(&ip)) {
ctx.mcast_ip4 = (uint32_t) (uintptr_t) nc->priv_2;
}
mg_lwip_netif_run_on_tcpip(udp_sendto_tcpip, &ctx);
cs->err = ctx.ret;
DBG(("%p udp_sendto %x mc4 %x res %d", nc, (int) ip_2_ip4(ctx.ip)->addr,
(int) ctx.mcast_ip4, (int) cs->err));
pbuf_free(p);
return (cs->err == ERR_OK ? (int) len : -2);
}
@@ -15496,6 +15574,7 @@ static int mg_lwip_if_tcp_recv(struct mg_connection *nc, void *buf,
cs->rx_offset += copy_len;
if (cs->rx_offset == cs->rx_chain->len) {
cs->rx_chain = pbuf_dechain(cs->rx_chain);
cs->rx_chain_len--;
pbuf_free(seg);
cs->rx_offset = 0;
}
@@ -15517,6 +15596,27 @@ int mg_lwip_if_create_conn(struct mg_connection *nc) {
return 1;
}
static void tcp_close_tcpip(void *arg) {
/*
* A race with tcp error is possible:
* - mg_lwip_if_destroy_conn is called, schedules this function.
* - RST arrives, mg_lwip_tcp_error_cb is invoked and pcb is destroyed.
* - this function runs.
* That will have NULLed pcb.tcp.
*/
struct mg_lwip_conn_state *cs = arg;
struct tcp_pcb *pcb = cs->pcb.tcp;
if (pcb == NULL) return;
tcp_arg(pcb, NULL);
err_t err = tcp_close(pcb);
if (err == ERR_OK) {
cs->pcb.tcp = NULL;
} else {
LOG(LL_ERROR, ("tcp_close(%p) err %d, PCB leak!", pcb, (int) err));
}
}
static void udp_remove_tcpip(void *arg) {
udp_remove((struct udp_pcb *) arg);
}
@@ -15524,32 +15624,32 @@ static void udp_remove_tcpip(void *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;
/* Do not close "ephemeral" UDP conns */
if ((nc->flags & MG_F_UDP) && nc->listener != NULL) {
nc->sock = INVALID_SOCKET;
return;
}
if (!(nc->flags & MG_F_UDP)) {
struct tcp_pcb *tpcb = cs->pcb.tcp;
if (tpcb != NULL) {
tcp_arg(tpcb, NULL);
DBG(("%p tcp_close %p", nc, tpcb));
tcp_arg(tpcb, NULL);
mg_lwip_netif_run_on_tcpip(tcp_close_tcpip, tpcb);
mg_lwip_netif_run_on_tcpip(tcp_close_tcpip, cs);
}
while (cs->rx_chain != NULL) {
struct pbuf *seg = cs->rx_chain;
cs->rx_chain = pbuf_dechain(cs->rx_chain);
pbuf_free(seg);
}
memset(cs, 0, sizeof(*cs));
MG_FREE(cs);
} else if (nc->listener == NULL) {
/* Only close outgoing UDP pcb or listeners. */
} else {
struct udp_pcb *upcb = cs->pcb.udp;
if (upcb != NULL) {
DBG(("%p udp_remove %p", nc, upcb));
mg_lwip_netif_run_on_tcpip(udp_remove_tcpip, upcb);
}
memset(cs, 0, sizeof(*cs));
MG_FREE(cs);
}
nc->sock = INVALID_SOCKET;
while (cs->rx_chain != NULL) {
struct pbuf *seg = cs->rx_chain;
cs->rx_chain = pbuf_dechain(cs->rx_chain);
pbuf_free(seg);
}
memset(cs, 0, sizeof(*cs));
MG_FREE(cs);
}
void mg_lwip_if_get_conn_addr(struct mg_connection *nc, int remote,
+75 -7
View File
@@ -2152,7 +2152,7 @@ void cs_base64_init(struct cs_base64_ctx *ctx, cs_base64_putc_t putc,
void cs_base64_update(struct cs_base64_ctx *ctx, const char *str, size_t len);
void cs_base64_finish(struct cs_base64_ctx *ctx);
void cs_base64_encode(const unsigned char *src, int src_len, char *dst);
void cs_base64_encode(const unsigned char *src, int src_len, char *dst, int *enc_len);
void cs_fprint_base64(FILE *f, const unsigned char *src, int src_len);
/*
@@ -3871,6 +3871,15 @@ struct mg_connect_opts {
*/
const char *ssl_psk_identity;
const char *ssl_psk_key;
#else
/* Ensure struct size remains the same. */
const char *unused_ssl_cert;
const char *unused_ssl_key;
const char *unused_ssl_ca_cert;
const char *unused_ssl_cipher_suites;
const char *unused_ssl_server_name;
const char *unused_ssl_psk_identity;
const char *unused_ssl_psk_key;
#endif
};
@@ -4695,6 +4704,8 @@ 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);
extern void mg_hash_sha256_v(size_t num_msgs, const uint8_t *msgs[],
const size_t *msg_lens, uint8_t *digest);
/*
* Flags for `mg_http_is_authorized()`.
@@ -4702,6 +4713,7 @@ extern void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[],
#define MG_AUTH_FLAG_IS_DIRECTORY (1 << 0)
#define MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE (1 << 1)
#define MG_AUTH_FLAG_ALLOW_MISSING_FILE (1 << 2)
#define MG_AUTH_FLAG_ALGO(algo) ((((int) algo) & 3) << 8)
/*
* Checks whether an http request is authorized. `domain` is the authentication
@@ -4714,12 +4726,6 @@ int mg_http_is_authorized(struct http_message *hm, struct mg_str path,
const char *domain, const char *passwords_file,
int flags);
/*
* Sends 401 Unauthorized response.
*/
void mg_http_send_digest_auth_request(struct mg_connection *c,
const char *domain);
#ifdef __cplusplus
}
#endif /* __cplusplus */
@@ -4870,6 +4876,14 @@ size_t mg_parse_multipart(const char *buf, size_t buf_len, char *var_name,
int mg_get_http_var(const struct mg_str *buf, const char *name, char *dst,
size_t dst_len);
/*
* Supported digest auth algorithms.
*/
enum mg_auth_algo {
MG_AUTH_ALGO_MD5 = 0,
MG_AUTH_ALGO_SHA256 = 1,
};
#if MG_ENABLE_FILESYSTEM
/*
* This structure defines how `mg_serve_http()` works.
@@ -4906,6 +4920,11 @@ struct mg_serve_http_opts {
*/
const char *global_auth_file;
/*
* Password hashing algorithm used by the password files.
*/
enum mg_auth_algo auth_algo;
/* Set to "no" to disable directory listing. Enabled by default. */
const char *enable_directory_listing;
@@ -5157,6 +5176,7 @@ struct mg_http_endpoint_opts {
/* Authorization domain (realm) */
const char *auth_domain;
const char *auth_file;
enum mg_auth_algo auth_algo;
};
void mg_register_http_endpoint_opt(struct mg_connection *nc,
@@ -5164,12 +5184,44 @@ void mg_register_http_endpoint_opt(struct mg_connection *nc,
mg_event_handler_t handler,
struct mg_http_endpoint_opts opts);
/*
* HTTP endpoint descriptor.
*/
struct mg_http_endpoint {
struct mg_http_endpoint *next;
struct mg_str uri_pattern; /* owned */
char *auth_domain; /* owned */
char *auth_file; /* owned */
enum mg_auth_algo auth_algo;
mg_event_handler_t handler;
#if MG_ENABLE_CALLBACK_USERDATA
void *user_data;
#endif
};
/*
* Returns first registered endpoint.
*/
struct mg_http_endpoint *mg_get_http_endpoints(struct mg_connection *nc);
/*
* Sends 401 Unauthorized response.
*/
void mg_http_send_digest_auth_request(struct mg_connection *c,
const char *domain);
void mg_http_send_digest_auth_request_algo(struct mg_connection *c,
const char *domain,
enum mg_auth_algo algo);
/*
* Authenticates a HTTP request against an opened password file.
* Returns 1 if authenticated, 0 otherwise.
*/
int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
FILE *fp);
int mg_http_check_digest_auth_algo(struct http_message *hm, const char *auth_domain,
enum mg_auth_algo fp_algo, FILE *fp);
/*
* Authenticates given response params against an opened password file.
@@ -5182,6 +5234,12 @@ int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
struct mg_str response, struct mg_str qop,
struct mg_str nc, struct mg_str nonce,
struct mg_str auth_domain, FILE *fp);
int mg_check_digest_auth_algo(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, enum mg_auth_algo algo,
FILE *fp);
/*
* Sends buffer `buf` of size `len` to the client using chunked HTTP encoding.
@@ -5351,6 +5409,12 @@ int mg_http_create_digest_auth_header(char *buf, size_t buf_len,
const char *method, const char *uri,
const char *auth_domain, const char *user,
const char *passwd, const char *nonce);
int mg_http_create_digest_auth_header_algo(char *buf, size_t buf_len,
const char *method, const char *uri,
const char *auth_domain,
const char *user, const char *passwd,
const char *nonce,
enum mg_auth_algo algo);
#ifdef __cplusplus
}
@@ -5686,8 +5750,12 @@ extern "C" {
#define MG_DNS_ANY_RECORD 0xff
#define MG_DNS_NSEC_RECORD 0x2f
#ifndef MG_MAX_DNS_QUESTIONS
#define MG_MAX_DNS_QUESTIONS 32
#endif
#ifndef MG_MAX_DNS_ANSWERS
#define MG_MAX_DNS_ANSWERS 32
#endif
#define MG_DNS_MESSAGE 100 /* High-level DNS message event */
+2 -12
View File
@@ -101,24 +101,18 @@ void cs_base64_finish(struct cs_base64_ctx *ctx) {
while (j % 4 != 0) { \
BASE64_OUT('='); \
} \
BASE64_FLUSH()
#define BASE64_OUT(ch) \
do { \
dst[j++] = (ch); \
} while (0)
#define BASE64_FLUSH() \
do { \
dst[j++] = '\0'; \
} while (0)
void cs_base64_encode(const unsigned char *src, int src_len, char *dst) {
void cs_base64_encode(const unsigned char *src, int src_len, char *dst, int *enc_len) {
BASE64_ENCODE_BODY;
if (enc_len != NULL) *enc_len = j;
}
#undef BASE64_OUT
#undef BASE64_FLUSH
#if CS_ENABLE_STDIO
#define BASE64_OUT(ch) \
@@ -127,14 +121,11 @@ void cs_base64_encode(const unsigned char *src, int src_len, char *dst) {
j++; \
} while (0)
#define BASE64_FLUSH()
void cs_fprint_base64(FILE *f, const unsigned char *src, int src_len) {
BASE64_ENCODE_BODY;
}
#undef BASE64_OUT
#undef BASE64_FLUSH
#endif /* CS_ENABLE_STDIO */
/* Convert one byte of encoded base64 input stream to 6-bit chunk */
@@ -193,7 +184,6 @@ int cs_base64_decode(const unsigned char *s, int len, char *dst, int *dec_len) {
if (d == 200) break;
*dst++ = c << 6 | d;
}
*dst = 0;
if (dec_len != NULL) *dec_len = (dst - orig_dst);
return orig_len - len;
}
+1 -1
View File
@@ -29,7 +29,7 @@ void cs_base64_init(struct cs_base64_ctx *ctx, cs_base64_putc_t putc,
void cs_base64_update(struct cs_base64_ctx *ctx, const char *str, size_t len);
void cs_base64_finish(struct cs_base64_ctx *ctx);
void cs_base64_encode(const unsigned char *src, int src_len, char *dst);
void cs_base64_encode(const unsigned char *src, int src_len, char *dst, int *enc_len);
void cs_fprint_base64(FILE *f, const unsigned char *src, int src_len);
/*
+1
View File
@@ -1,3 +1,4 @@
/*
* This code implements the MD5 message-digest algorithm.
* The algorithm is due to Ron Rivest. This code was
+1
View File
@@ -1,3 +1,4 @@
/* Copyright(c) By Steve Reid <steve@edmweb.com> */
/* 100% Public Domain */
+1
View File
@@ -25,6 +25,7 @@ struct mg_str mg_mk_str_n(const char *s, size_t len) {
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;
if (n1 == 0) return (n2 > 0 ? -1 : 0);
int r = strncmp(str1->p, str2, (n1 < n2) ? n1 : n2);
if (r == 0) {
return n1 - n2;
+1
View File
@@ -1,3 +1,4 @@
#ifndef CS_COMMON_PLATFORM_H_
#define CS_COMMON_PLATFORM_H_
+117 -63
View File
@@ -17,6 +17,10 @@
#include "common/cs_dbg.h"
#ifndef MG_LWIP_MAX_UDP_RX_CHAIN_LEN
#define MG_LWIP_MAX_UDP_RX_CHAIN_LEN 20
#endif
/*
* Newest versions of LWIP have ip_2_ip4, older have ipX_2_ip,
* even older have nothing.
@@ -37,7 +41,7 @@
#define TCP_NEW tcp_new_ip6
#define TCP_BIND tcp_bind_ip6
#define UDP_BIND udp_bind_ip6
#define IPADDR_NTOA(x) ip6addr_ntoa((const ip6_addr_t *)(x))
#define IPADDR_NTOA(x) ip6addr_ntoa((const ip6_addr_t *) (x))
#define SET_ADDR(dst, src) \
memcpy((dst)->sin6.sin6_addr.s6_addr, (src)->ip6.addr, \
sizeof((dst)->sin6.sin6_addr.s6_addr))
@@ -138,9 +142,10 @@ static err_t mg_lwip_tcp_conn_cb(void *arg, struct tcp_pcb *tpcb, err_t err) {
static void mg_lwip_tcp_error_cb(void *arg, err_t err) {
struct mg_connection *nc = (struct mg_connection *) arg;
DBG(("%p conn error %d", nc, (int) err));
if (nc == NULL || (nc->flags & MG_F_CLOSE_IMMEDIATELY)) return;
if (nc == NULL) return;
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
cs->pcb.tcp = NULL; /* Has already been deallocated */
if (nc->flags & MG_F_CLOSE_IMMEDIATELY) return;
if (nc->flags & MG_F_CONNECTING) {
cs->err = err;
mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc);
@@ -186,16 +191,6 @@ static err_t mg_lwip_tcp_recv_cb(void *arg, struct tcp_pcb *tpcb,
mgos_lock();
if (cs->rx_chain == NULL) {
cs->rx_offset = 0;
} else if (pbuf_clen(cs->rx_chain) >= 4) {
/* ESP SDK has a limited pool of 5 pbufs. We must not hog them all or RX
* will be completely blocked. We already have at least 4 in the chain,
* this one is the last, so we have to make a copy and release this one. */
struct pbuf *np = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
if (np != NULL) {
pbuf_copy(np, p);
pbuf_free(p);
p = np;
}
}
mg_lwip_recv_common(nc, p);
mgos_unlock();
@@ -208,8 +203,10 @@ static err_t mg_lwip_tcp_sent_cb(void *arg, struct tcp_pcb *tpcb,
struct mg_connection *nc = (struct mg_connection *) arg;
DBG(("%p %p %u %p %p", nc, tpcb, num_sent, tpcb->unsent, tpcb->unacked));
if (nc == NULL) return ERR_OK;
if ((nc->flags & MG_F_SEND_AND_CLOSE) && !(nc->flags & MG_F_WANT_WRITE) &&
nc->send_mbuf.len == 0 && tpcb->unsent == NULL && tpcb->unacked == NULL) {
if ((nc->flags & (MG_F_SEND_AND_CLOSE | MG_F_CLOSE_IMMEDIATELY)) ==
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);
}
if (nc->send_mbuf.len > 0 || (nc->flags & MG_F_WANT_WRITE)) {
@@ -259,6 +256,11 @@ void mg_lwip_if_connect_tcp(struct mg_connection *nc,
mg_lwip_netif_run_on_tcpip(mg_lwip_if_connect_tcp_tcpip, &ctx);
}
struct mg_lwip_udp_info {
union socket_address remote_addr;
uint32_t local_ip4;
};
/*
* Lwip included in the SDKs for nRF5x chips has different type for the
* callback of `udp_recv()`
@@ -272,26 +274,26 @@ static void mg_lwip_udp_recv_cb(void *arg, struct udp_pcb *pcb, struct pbuf *p,
#endif
{
struct mg_connection *nc = (struct mg_connection *) arg;
/*
* Scribble address on the margins. There's enough room there,
* between pbuf header and payload - at a minimum, IP and UDP headers.
*/
struct mg_lwip_udp_info *ui =
(struct mg_lwip_udp_info *) ((((uintptr_t) p->payload) &
(~((uintptr_t) 3))) -
sizeof(*ui));
DBG(("%p %s:%u %p %u %u", nc, IPADDR_NTOA(addr), port, p, p->ref, p->len));
/* Put address in a separate pbuf and tack it onto the packet. */
struct pbuf *sap =
pbuf_alloc(PBUF_RAW, sizeof(union socket_address), PBUF_RAM);
if (sap == NULL) {
pbuf_free(p);
return;
}
union socket_address *sa = (union socket_address *) sap->payload;
struct netif *nif = ip_current_netif();
#if ((LWIP_VERSION_MAJOR << 8) | LWIP_VERSION_MINOR) >= 0x0105
sa->sin.sin_addr.s_addr = ip_2_ip4(addr)->addr;
ui->remote_addr.sin.sin_addr.s_addr = ip_2_ip4(addr)->addr;
ui->local_ip4 = ip_2_ip4(&nif->ip_addr)->addr;
#else
sa->sin.sin_addr.s_addr = addr->addr;
ui->remote_addr.sin.sin_addr.s_addr = addr->addr;
ui->local_ip4 = nif->ip_addr.addr;
#endif
sa->sin.sin_port = htons(port);
/* Logic in the recv handler requires that there be exactly one data pbuf. */
p = pbuf_coalesce(p, PBUF_RAW);
pbuf_chain(sap, p);
ui->remote_addr.sin.sin_port = htons(port);
mgos_lock();
mg_lwip_recv_common(nc, sap);
mg_lwip_recv_common(nc, p);
mgos_unlock();
(void) pcb;
}
@@ -300,8 +302,14 @@ static void mg_lwip_recv_common(struct mg_connection *nc, struct pbuf *p) {
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
if (cs->rx_chain == NULL) {
cs->rx_chain = p;
} else {
cs->rx_chain_len = 1;
} else if (!(nc->flags & MG_F_UDP) ||
cs->rx_chain_len < MG_LWIP_MAX_UDP_RX_CHAIN_LEN) {
pbuf_chain(cs->rx_chain, p);
cs->rx_chain_len++;
} else {
/* Drop it on the floor. */
pbuf_free(p);
}
if (!cs->recv_pending) {
cs->recv_pending = 1;
@@ -311,25 +319,32 @@ static void mg_lwip_recv_common(struct mg_connection *nc, struct pbuf *p) {
static int mg_lwip_if_udp_recv(struct mg_connection *nc, void *buf, size_t len,
union socket_address *sa, size_t *sa_len) {
/*
* For UDP, RX chain consists of interleaved address and packet bufs:
* Address pbuf followed by exactly one data pbuf (recv_cb took care of that).
*/
int res = 0;
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
if (nc->sock == INVALID_SOCKET) return -1;
mgos_lock();
if (cs->rx_chain != NULL) {
struct pbuf *ap = cs->rx_chain;
struct pbuf *dp = ap->next;
cs->rx_chain = pbuf_dechain(dp);
res = MIN(dp->len, len);
pbuf_copy_partial(dp, buf, res, 0);
pbuf_free(dp);
pbuf_copy_partial(ap, sa, MIN(*sa_len, ap->len), 0);
pbuf_free(ap);
struct pbuf *p = cs->rx_chain;
cs->rx_chain = pbuf_dechain(p);
cs->rx_chain_len--;
mgos_unlock();
struct mg_lwip_udp_info *ui =
(struct mg_lwip_udp_info *) ((((uintptr_t) p->payload) &
(~((uintptr_t) 3))) -
sizeof(*ui));
*sa = ui->remote_addr;
/* Store local interface address for incoming packets.
* Only for listener because outgoing connections may use priv_2 for DNS. */
if (nc->listener == NULL) {
nc->priv_2 = (void *) (uintptr_t) ui->local_ip4;
}
res = MIN(p->len, len);
pbuf_copy_partial(p, buf, res, 0);
pbuf_free(p);
} else {
mgos_unlock();
}
mgos_unlock();
(void) sa_len;
return res;
}
@@ -352,10 +367,6 @@ void mg_lwip_if_connect_udp(struct mg_connection *nc) {
mg_lwip_netif_run_on_tcpip(mg_lwip_if_connect_udp_tcpip, nc);
}
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;
@@ -370,6 +381,10 @@ static err_t mg_lwip_accept_cb(void *arg, struct tcp_pcb *newtpcb, err_t err) {
struct mg_connection *lc = (struct mg_connection *) arg, *nc;
struct mg_lwip_conn_state *lcs, *cs;
struct tcp_pcb_listen *lpcb;
if (newtpcb == NULL) {
LOG(LL_ERROR, ("%p accept error %d", lc, (int) err));
return ERR_ABRT;
}
LOG(LL_DEBUG,
("%p conn %p from %s:%u", lc, newtpcb,
IPADDR_NTOA(ipX_2_ip(&newtpcb->remote_ip)), newtpcb->remote_port));
@@ -481,6 +496,10 @@ static void mg_lwip_tcp_write_tcpip(void *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;
if (tpcb == NULL) {
/* Possible if tcp_err cb was invoked while this was queued. */
return;
}
size_t len = MIN(tpcb->mss, MIN(ctx->len, tpcb->snd_buf));
size_t unsent, unacked;
if (len == 0) {
@@ -508,8 +527,8 @@ static void mg_lwip_tcp_write_tcpip(void *arg) {
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, (int) cs->err, unsent, unacked));
DBG(("%p tcp_write %u = %d, %u %u", tpcb, len, (int) cs->err, unsent,
unacked));
if (cs->err != ERR_OK) {
/*
* We ignore ERR_MEM because memory will be freed up when the data is sent
@@ -539,11 +558,19 @@ struct udp_sendto_ctx {
struct pbuf *p;
ip_addr_t *ip;
uint16_t port;
uint32_t mcast_ip4;
int ret;
};
static void udp_sendto_tcpip(void *arg) {
struct udp_sendto_ctx *ctx = (struct udp_sendto_ctx *) arg;
if (ctx->mcast_ip4 != 0) {
#if LWIP_VERSION_MAJOR > 1 && LWIP_MULTICAST_TX_OPTIONS
ctx->upcb->mcast_ip4.addr = ctx->mcast_ip4;
#elif LWIP_VERSION_MAJOR == 1 && LWIP_IGMP
ctx->upcb->multicast_ip.addr = ctx->mcast_ip4;
#endif
}
ctx->ret = udp_sendto(ctx->upcb, ctx->p, ctx->ip, ctx->port);
}
@@ -562,8 +589,13 @@ static int mg_lwip_if_udp_send(struct mg_connection *nc, const void *data,
if (p == NULL) return 0;
memcpy(p->payload, data, len);
struct udp_sendto_ctx ctx = {.upcb = upcb, .p = p, .ip = &ip, .port = port};
if (ip_addr_ismulticast(&ip)) {
ctx.mcast_ip4 = (uint32_t) (uintptr_t) nc->priv_2;
}
mg_lwip_netif_run_on_tcpip(udp_sendto_tcpip, &ctx);
cs->err = ctx.ret;
DBG(("%p udp_sendto %x mc4 %x res %d", nc, (int) ip_2_ip4(ctx.ip)->addr,
(int) ctx.mcast_ip4, (int) cs->err));
pbuf_free(p);
return (cs->err == ERR_OK ? (int) len : -2);
}
@@ -616,6 +648,7 @@ static int mg_lwip_if_tcp_recv(struct mg_connection *nc, void *buf,
cs->rx_offset += copy_len;
if (cs->rx_offset == cs->rx_chain->len) {
cs->rx_chain = pbuf_dechain(cs->rx_chain);
cs->rx_chain_len--;
pbuf_free(seg);
cs->rx_offset = 0;
}
@@ -637,6 +670,27 @@ int mg_lwip_if_create_conn(struct mg_connection *nc) {
return 1;
}
static void tcp_close_tcpip(void *arg) {
/*
* A race with tcp error is possible:
* - mg_lwip_if_destroy_conn is called, schedules this function.
* - RST arrives, mg_lwip_tcp_error_cb is invoked and pcb is destroyed.
* - this function runs.
* That will have NULLed pcb.tcp.
*/
struct mg_lwip_conn_state *cs = arg;
struct tcp_pcb *pcb = cs->pcb.tcp;
if (pcb == NULL) return;
tcp_arg(pcb, NULL);
err_t err = tcp_close(pcb);
if (err == ERR_OK) {
cs->pcb.tcp = NULL;
} else {
LOG(LL_ERROR, ("tcp_close(%p) err %d, PCB leak!", pcb, (int) err));
}
}
static void udp_remove_tcpip(void *arg) {
udp_remove((struct udp_pcb *) arg);
}
@@ -644,32 +698,32 @@ static void udp_remove_tcpip(void *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;
/* Do not close "ephemeral" UDP conns */
if ((nc->flags & MG_F_UDP) && nc->listener != NULL) {
nc->sock = INVALID_SOCKET;
return;
}
if (!(nc->flags & MG_F_UDP)) {
struct tcp_pcb *tpcb = cs->pcb.tcp;
if (tpcb != NULL) {
tcp_arg(tpcb, NULL);
DBG(("%p tcp_close %p", nc, tpcb));
tcp_arg(tpcb, NULL);
mg_lwip_netif_run_on_tcpip(tcp_close_tcpip, tpcb);
mg_lwip_netif_run_on_tcpip(tcp_close_tcpip, cs);
}
while (cs->rx_chain != NULL) {
struct pbuf *seg = cs->rx_chain;
cs->rx_chain = pbuf_dechain(cs->rx_chain);
pbuf_free(seg);
}
memset(cs, 0, sizeof(*cs));
MG_FREE(cs);
} else if (nc->listener == NULL) {
/* Only close outgoing UDP pcb or listeners. */
} else {
struct udp_pcb *upcb = cs->pcb.udp;
if (upcb != NULL) {
DBG(("%p udp_remove %p", nc, upcb));
mg_lwip_netif_run_on_tcpip(udp_remove_tcpip, upcb);
}
memset(cs, 0, sizeof(*cs));
MG_FREE(cs);
}
nc->sock = INVALID_SOCKET;
while (cs->rx_chain != NULL) {
struct pbuf *seg = cs->rx_chain;
cs->rx_chain = pbuf_dechain(cs->rx_chain);
pbuf_free(seg);
}
memset(cs, 0, sizeof(*cs));
MG_FREE(cs);
}
void mg_lwip_if_get_conn_addr(struct mg_connection *nc, int remote,
+4 -3
View File
@@ -24,11 +24,12 @@ struct mg_lwip_conn_state {
struct pbuf *rx_chain; /* Chain of incoming data segments. */
size_t rx_offset; /* Offset within the first pbuf (if partially consumed) */
/* Last SSL write size, for retries. */
int last_ssl_write_size;
unsigned int last_ssl_write_size : 16;
unsigned int rx_chain_len : 8;
/* Whether MG_SIG_RECV is already pending for this connection */
int recv_pending;
unsigned int recv_pending : 1;
/* Whether the connection is about to close, just `rx_chain` needs to drain */
int draining_rx_chain;
unsigned int draining_rx_chain : 1;
};
enum mg_sig_type {
+1
View File
@@ -1,3 +1,4 @@
#ifndef CS_COMMON_PLATFORMS_PLATFORM_NRF51_H_
#define CS_COMMON_PLATFORMS_PLATFORM_NRF51_H_
#if CS_PLATFORM == CS_P_NRF51
+1
View File
@@ -1,3 +1,4 @@
#ifndef CS_COMMON_PLATFORMS_PLATFORM_NRF52_H_
#define CS_COMMON_PLATFORMS_PLATFORM_NRF52_H_
#if CS_PLATFORM == CS_P_NRF52
+1
View File
@@ -1,3 +1,4 @@
#ifndef CS_COMMON_PLATFORMS_PLATFORM_UNIX_H_
#define CS_COMMON_PLATFORMS_PLATFORM_UNIX_H_
#if CS_PLATFORM == CS_P_UNIX
+1
View File
@@ -1,3 +1,4 @@
#ifndef CS_COMMON_PLATFORMS_PLATFORM_WINCE_H_
#define CS_COMMON_PLATFORMS_PLATFORM_WINCE_H_
+1
View File
@@ -1,3 +1,4 @@
#ifndef CS_COMMON_PLATFORMS_PLATFORM_WINDOWS_H_
#define CS_COMMON_PLATFORMS_PLATFORM_WINDOWS_H_
#if CS_PLATFORM == CS_P_WINDOWS
@@ -1,3 +1,4 @@
#if MG_NET_IF == MG_NET_IF_SIMPLELINK && !defined(MG_SIMPLELINK_NO_OSI)
#include "mg_task.h"
+1
View File
@@ -1,3 +1,4 @@
/* clang-format off */
/*-
* Copyright (c) 1991, 1993
+1
View File
@@ -1,3 +1,4 @@
#ifndef CS_MONGOOSE_SRC_COMMON_H_
#define CS_MONGOOSE_SRC_COMMON_H_
+4
View File
@@ -22,8 +22,12 @@ extern "C" {
#define MG_DNS_ANY_RECORD 0xff
#define MG_DNS_NSEC_RECORD 0x2f
#ifndef MG_MAX_DNS_QUESTIONS
#define MG_MAX_DNS_QUESTIONS 32
#endif
#ifndef MG_MAX_DNS_ANSWERS
#define MG_MAX_DNS_ANSWERS 32
#endif
#define MG_DNS_MESSAGE 100 /* High-level DNS message event */
+166 -120
View File
@@ -107,18 +107,6 @@ struct mg_http_proto_data_chuncked {
int64_t body_len; /* How many bytes of chunked body was reassembled. */
};
struct mg_http_endpoint {
struct mg_http_endpoint *next;
struct mg_str uri_pattern; /* owned */
char *auth_domain; /* owned */
char *auth_file; /* owned */
mg_event_handler_t handler;
#if MG_ENABLE_CALLBACK_USERDATA
void *user_data;
#endif
};
enum mg_http_multipart_stream_state {
MPS_BEGIN,
MPS_WAITING_FOR_BOUNDARY,
@@ -223,6 +211,13 @@ static void mg_http_free_proto_data_file(struct mg_http_proto_data_file *d) {
}
#endif
struct mg_http_endpoint *mg_get_http_endpoints(struct mg_connection *nc) {
if (nc == NULL) return NULL;
struct mg_http_proto_data *pd = (struct mg_http_proto_data *) nc->proto_data;
if (pd == NULL) return NULL;
return pd->endpoints;
}
static void mg_http_free_proto_data_endpoints(struct mg_http_endpoint **ep) {
struct mg_http_endpoint *current = *ep;
@@ -521,8 +516,7 @@ struct mg_str *mg_get_http_header(struct http_message *hm, const char *name) {
#if MG_ENABLE_FILESYSTEM
static void mg_http_transfer_file_data(struct mg_connection *nc) {
struct mg_http_proto_data *pd = mg_http_get_proto_data(nc);
char buf[MG_MAX_HTTP_SEND_MBUF];
size_t n = 0, to_read = 0, left = (size_t)(pd->file.cl - pd->file.sent);
size_t to_read = 0, left = (size_t)(pd->file.cl - pd->file.sent);
if (pd->file.type == DATA_FILE) {
struct mbuf *io = &nc->send_mbuf;
@@ -535,11 +529,16 @@ static void mg_http_transfer_file_data(struct mg_connection *nc) {
to_read = left;
}
if (to_read > 0) {
n = mg_fread(buf, 1, to_read, pd->file.fp);
if (n > 0) {
mg_send(nc, buf, n);
pd->file.sent += n;
DBG(("%p sent %d (total %d)", nc, (int) n, (int) pd->file.sent));
size_t new_size = io->len + to_read;
mbuf_resize(io, new_size);
if (io->size == new_size) {
char *buf = io->buf + io->len;
size_t n = mg_fread(buf, 1, to_read, pd->file.fp);
if (n > 0) {
io->len += n;
pd->file.sent += n;
DBG(("%p sent %d (total %d)", nc, (int) n, (int) pd->file.sent));
}
}
} else {
/* Rate-limited */
@@ -740,30 +739,17 @@ static void deliver_chunk(struct mg_connection *c, struct http_message *hm,
}
}
/*
* lx106 compiler has a bug (TODO(mkm) report and insert tracking bug here)
* If a big structure is declared in a big function, lx106 gcc will make it
* even bigger (round up to 4k, from 700 bytes of actual size).
*/
#ifdef __xtensa__
static void mg_http_handler2(struct mg_connection *nc, int ev,
void *ev_data MG_UD_ARG(void *user_data),
struct http_message *hm) __attribute__((noinline));
void mg_http_handler(struct mg_connection *nc, int ev,
void *ev_data MG_UD_ARG(void *user_data)) {
struct http_message hm;
mg_http_handler2(nc, ev, ev_data MG_UD_ARG(user_data), &hm);
}
static void mg_http_handler2(struct mg_connection *nc, int ev,
void *ev_data MG_UD_ARG(void *user_data),
struct http_message *hm) {
#else /* !__XTENSA__ */
void mg_http_handler(struct mg_connection *nc, int ev,
void *ev_data MG_UD_ARG(void *user_data)) {
/* On ESP8266 stack is especially limited (4K).
* Since Mongoose is single-threaded, we can use the same struct
* and save ~400 bytes. This may not seem like much but in fact it's the
* difference between overflowing and not (when serving files). */
#if CS_PLATFORM == CS_P_ESP8266
static struct http_message shm, *hm = &shm;
#else
struct http_message shm, *hm = &shm;
#endif /* __XTENSA__ */
#endif
struct mg_http_proto_data *pd = mg_http_get_proto_data(nc);
struct mbuf *io = &nc->recv_mbuf;
int req_len;
@@ -1859,10 +1845,10 @@ 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];
static void cs_digest(enum mg_auth_algo algo, char buf[65], ...) {
unsigned char hash[32];
const uint8_t *msgs[16], *p;
size_t msg_lens[16];
size_t num_msgs = 0;
va_list ap;
@@ -1874,44 +1860,61 @@ void cs_md5(char buf[33], ...) {
}
va_end(ap);
mg_hash_md5_v(num_msgs, msgs, msg_lens, hash);
cs_to_hex(buf, hash, sizeof(hash));
if (algo == MG_AUTH_ALGO_MD5) {
mg_hash_md5_v(num_msgs, msgs, msg_lens, hash);
cs_to_hex(buf, hash, 16);
} else {
mg_hash_sha256_v(num_msgs, msgs, msg_lens, hash);
cs_to_hex(buf, hash, 32);
}
}
static void mg_mkmd5resp(const char *method, size_t method_len, const char *uri,
size_t uri_len, const char *ha1, size_t ha1_len,
const char *nonce, size_t nonce_len, const char *nc,
size_t nc_len, const char *cnonce, size_t cnonce_len,
const char *qop, size_t qop_len, char *resp) {
static const char colon[] = ":";
static const size_t one = 1;
char ha2[33];
cs_md5(ha2, method, method_len, colon, one, uri, uri_len, NULL);
cs_md5(resp, ha1, ha1_len, colon, one, nonce, nonce_len, colon, one, nc,
nc_len, colon, one, cnonce, cnonce_len, colon, one, qop, qop_len,
colon, one, ha2, sizeof(ha2) - 1, NULL);
static void mg_mk_digest_resp(const char *method, size_t method_len,
const char *uri, size_t uri_len, const char *ha1,
size_t ha1_len, const char *nonce,
size_t nonce_len, const char *nc, size_t nc_len,
const char *cnonce, size_t cnonce_len,
const char *qop, size_t qop_len,
enum mg_auth_algo algo, char *resp) {
size_t one = 1;
char ha2[65];
size_t ha2_size = (algo == MG_AUTH_ALGO_MD5 ? 32 : 64);
cs_digest(algo, ha2, method, method_len, ":", one, uri, uri_len, NULL);
cs_digest(algo, resp, ha1, ha1_len, ":", one, nonce, nonce_len, ":", one, nc,
nc_len, ":", one, cnonce, cnonce_len, ":", one, qop, qop_len,
":", one, ha2, ha2_size, NULL);
}
int mg_http_create_digest_auth_header_algo(char *buf, size_t buf_len,
const char *method, const char *uri,
const char *auth_domain, const char *user,
const char *passwd, const char *nonce,
enum mg_auth_algo algo) {
const char qop[] = "auth";
const size_t one = 1;
char ha1[65], resp[65], cnonce[40];
size_t ha1_size = (algo == MG_AUTH_ALGO_MD5 ? 32 : 64);
snprintf(cnonce, sizeof(cnonce), "%lx", (unsigned long) mg_time());
cs_digest(algo, ha1, user, (size_t) strlen(user), ":", one, auth_domain,
(size_t) strlen(auth_domain), ":", one, passwd,
(size_t) strlen(passwd), NULL);
mg_mk_digest_resp(method, strlen(method), uri, strlen(uri), ha1, ha1_size,
nonce, strlen(nonce), "1", one, cnonce, strlen(cnonce), qop,
sizeof(qop) - 1, algo, resp);
return snprintf(buf, buf_len,
"Authorization: Digest username=\"%s\","
"realm=\"%s\",uri=\"%s\",qop=%s,nc=1,cnonce=%s,"
"nonce=%s,response=%s,algorithm=%s\r\n",
user, auth_domain, uri, qop, cnonce, nonce, resp,
(algo == MG_AUTH_ALGO_MD5 ? "MD5" : "SHA-256"));
}
int mg_http_create_digest_auth_header(char *buf, size_t buf_len,
const char *method, const char *uri,
const char *auth_domain, const char *user,
const char *passwd, const char *nonce) {
static const char colon[] = ":", qop[] = "auth";
static const size_t one = 1;
char ha1[33], resp[33], cnonce[40];
snprintf(cnonce, sizeof(cnonce), "%lx", (unsigned long) mg_time());
cs_md5(ha1, user, (size_t) strlen(user), colon, one, auth_domain,
(size_t) strlen(auth_domain), colon, one, passwd,
(size_t) strlen(passwd), NULL);
mg_mkmd5resp(method, strlen(method), uri, strlen(uri), ha1, sizeof(ha1) - 1,
nonce, strlen(nonce), "1", one, cnonce, strlen(cnonce), qop,
sizeof(qop) - 1, resp);
return snprintf(buf, buf_len,
"Authorization: Digest username=\"%s\","
"realm=\"%s\",uri=\"%s\",qop=%s,nc=1,cnonce=%s,"
"nonce=%s,response=%s\r\n",
user, auth_domain, uri, qop, cnonce, nonce, resp);
return mg_http_create_digest_auth_header_algo(buf, buf_len, method, uri, auth_domain, user, passwd, nonce, MG_AUTH_ALGO_MD5);
}
/*
@@ -1926,15 +1929,17 @@ static int mg_check_nonce(const char *nonce) {
return (now >= val) && (now - val < 60 * 60);
}
int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
FILE *fp) {
int mg_http_check_digest_auth_algo(struct http_message *hm, const char *auth_domain,
enum mg_auth_algo fp_algo, FILE *fp) {
int ret = 0;
struct mg_str *hdr;
char username_buf[50], cnonce_buf[64], response_buf[40], uri_buf[200],
qop_buf[20], nc_buf[20], nonce_buf[16];
char username_buf[50], cnonce_buf[64], response_buf[64], uri_buf[200],
qop_buf[20], nc_buf[20], nonce_buf[16], algo_buf[16];
char *username = username_buf, *cnonce = cnonce_buf, *response = response_buf,
*uri = uri_buf, *qop = qop_buf, *nc = nc_buf, *nonce = nonce_buf;
*uri = uri_buf, *qop = qop_buf, *nc = nc_buf, *nonce = nonce_buf, *algo = algo_buf;
enum mg_auth_algo hm_algo = MG_AUTH_ALGO_MD5;
/* Parse "Authorization:" header, fail fast on parse error */
if (hm == NULL || fp == NULL ||
@@ -1953,16 +1958,30 @@ int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
goto clean;
}
if (mg_http_parse_header2(hdr, "algorithm", &algo, sizeof(algo_buf)) != 0) {
if (strcmp(algo, "MD5") == 0) {
hm_algo = MG_AUTH_ALGO_MD5;
} else if (strcmp(algo, "SHA-256") == 0) {
hm_algo = MG_AUTH_ALGO_SHA256;
} else {
goto clean;
}
}
if (hm_algo != fp_algo) {
goto clean;
}
/* NOTE(lsm): due to a bug in MSIE, we do not compare URIs */
ret = mg_check_digest_auth(
ret = mg_check_digest_auth_algo(
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);
fp_algo, fp);
clean:
if (username != username_buf) MG_FREE(username);
@@ -1972,17 +1991,23 @@ clean:
if (qop != qop_buf) MG_FREE(qop);
if (nc != nc_buf) MG_FREE(nc);
if (nonce != nonce_buf) MG_FREE(nonce);
if (algo != algo_buf) MG_FREE(algo);
return ret;
}
int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
FILE *fp) {
return mg_http_check_digest_auth_algo(hm, auth_domain, MG_AUTH_ALGO_MD5, fp);
}
int mg_check_digest_auth_algo(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) {
struct mg_str auth_domain, enum mg_auth_algo algo, FILE *fp) {
char buf[128], f_user[sizeof(buf)], f_ha1[sizeof(buf)], f_domain[sizeof(buf)];
char exp_resp[33];
char exp_resp[65] = {0};
/*
* Read passwords file line by line. If should have htdigest format,
@@ -1994,9 +2019,9 @@ int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
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, exp_resp);
mg_mk_digest_resp(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, algo, exp_resp);
LOG(LL_DEBUG, ("%.*s %s %.*s %s", (int) username.len, username.p,
f_domain, (int) response.len, response.p, exp_resp));
return mg_ncasecmp(response.p, exp_resp, strlen(exp_resp)) == 0;
@@ -2007,6 +2032,16 @@ int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
return 0;
}
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) {
return mg_check_digest_auth_algo(
method, uri, username, cnonce, response, qop, nc, nonce, auth_domain,
MG_AUTH_ALGO_MD5, fp);
}
int mg_http_is_authorized(struct http_message *hm, struct mg_str path,
const char *domain, const char *passwords_file,
int flags) {
@@ -2015,27 +2050,30 @@ int mg_http_is_authorized(struct http_message *hm, struct mg_str path,
FILE *fp;
int authorized = 1;
if (domain != NULL && passwords_file != NULL) {
if (flags & MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE) {
fp = mg_fopen(passwords_file, "r");
} else if (flags & MG_AUTH_FLAG_IS_DIRECTORY) {
snprintf(buf, sizeof(buf), "%.*s%c%s", (int) path.len, path.p, DIRSEP,
passwords_file);
fp = mg_fopen(buf, "r");
} else {
p = strrchr(path.p, DIRSEP);
if (p == NULL) p = path.p;
snprintf(buf, sizeof(buf), "%.*s%c%s", (int) (p - path.p), path.p, DIRSEP,
passwords_file);
fp = mg_fopen(buf, "r");
}
if (domain == NULL || passwords_file == NULL) {
return 1;
}
if (fp != NULL) {
authorized = mg_http_check_digest_auth(hm, domain, fp);
fclose(fp);
} else if (!(flags & MG_AUTH_FLAG_ALLOW_MISSING_FILE)) {
authorized = 0;
}
if (flags & MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE) {
fp = mg_fopen(passwords_file, "r");
} else if (flags & MG_AUTH_FLAG_IS_DIRECTORY) {
snprintf(buf, sizeof(buf), "%.*s%c%s", (int) path.len, path.p, DIRSEP,
passwords_file);
fp = mg_fopen(buf, "r");
} else {
p = strrchr(path.p, DIRSEP);
if (p == NULL) p = path.p;
snprintf(buf, sizeof(buf), "%.*s%c%s", (int) (p - path.p), path.p, DIRSEP,
passwords_file);
fp = mg_fopen(buf, "r");
}
if (fp != NULL) {
enum mg_auth_algo fp_algo = (enum mg_auth_algo) ((flags >> 8) & 3);
authorized = mg_http_check_digest_auth_algo(hm, domain, fp_algo, fp);
fclose(fp);
} else if (!(flags & MG_AUTH_FLAG_ALLOW_MISSING_FILE)) {
authorized = 0;
}
LOG(LL_DEBUG, ("%.*s %s %x %d", (int) path.len, path.p,
@@ -2585,14 +2623,21 @@ MG_INTERNAL int mg_is_not_modified(struct http_message *hm, cs_stat_t *st) {
}
}
void mg_http_send_digest_auth_request(struct mg_connection *c,
const char *domain) {
void mg_http_send_digest_auth_request_algo(struct mg_connection *c,
const char *domain,
enum mg_auth_algo algo) {
mg_printf(c,
"HTTP/1.1 401 Unauthorized\r\n"
"WWW-Authenticate: Digest qop=\"auth\", "
"realm=\"%s\", nonce=\"%lx\"\r\n"
"realm=\"%s\", nonce=\"%lx\", algorithm=%s\r\n"
"Content-Length: 0\r\n\r\n",
domain, (unsigned long) mg_time());
domain, (unsigned long) mg_time(),
(algo == MG_AUTH_ALGO_MD5 ? "MD5" : "SHA-256"));
}
void mg_http_send_digest_auth_request(struct mg_connection *c,
const char *domain) {
mg_http_send_digest_auth_request_algo(c, domain, MG_AUTH_ALGO_MD5);
}
static void mg_http_send_options(struct mg_connection *nc,
@@ -2661,13 +2706,15 @@ MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path,
hm, mg_mk_str(path), opts->auth_domain, opts->global_auth_file,
((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) |
MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE |
MG_AUTH_FLAG_ALLOW_MISSING_FILE)) ||
MG_AUTH_FLAG_ALLOW_MISSING_FILE |
MG_AUTH_FLAG_ALGO(opts->auth_algo))) ||
!mg_http_is_authorized(
hm, mg_mk_str(path), opts->auth_domain,
opts->per_directory_auth_file,
((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) |
MG_AUTH_FLAG_ALLOW_MISSING_FILE))) {
mg_http_send_digest_auth_request(nc, opts->auth_domain);
MG_AUTH_FLAG_ALLOW_MISSING_FILE |
MG_AUTH_FLAG_ALGO(opts->auth_algo)))) {
mg_http_send_digest_auth_request_algo(nc, opts->auth_domain, opts->auth_algo);
} else if (is_cgi) {
#if MG_ENABLE_HTTP_CGI
mg_handle_cgi(nc, index_file ? index_file : path, path_info, hm, opts);
@@ -2690,7 +2737,7 @@ MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path,
((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) |
MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE |
MG_AUTH_FLAG_ALLOW_MISSING_FILE))))) {
mg_http_send_digest_auth_request(nc, opts->auth_domain);
mg_http_send_digest_auth_request_algo(nc, opts->auth_domain, opts->aith_algo);
#endif
} else if (!mg_vcmp(&hm->method, "MKCOL")) {
mg_handle_mkcol(nc, path, hm);
@@ -2753,9 +2800,6 @@ void mg_serve_http(struct mg_connection *nc, struct http_message *hm,
if (opts.document_root == NULL) {
opts.document_root = ".";
}
if (opts.per_directory_auth_file == NULL) {
opts.per_directory_auth_file = ".htpasswd";
}
if (opts.enable_directory_listing == NULL) {
opts.enable_directory_listing = "yes";
}
@@ -3105,6 +3149,7 @@ void mg_register_http_endpoint_opt(struct mg_connection *nc,
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->auth_algo = opts.auth_algo;
}
new_ep->handler = handler;
#if MG_ENABLE_CALLBACK_USERDATA
@@ -3129,8 +3174,9 @@ static void mg_http_call_endpoint_handler(struct mg_connection *nc, int ev,
if (ep != NULL) {
#if MG_ENABLE_FILESYSTEM && !MG_DISABLE_HTTP_DIGEST_AUTH
if (!mg_http_is_authorized(hm, hm->uri, ep->auth_domain, ep->auth_file,
MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE)) {
mg_http_send_digest_auth_request(nc, ep->auth_domain);
(MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE |
MG_AUTH_FLAG_ALGO(ep->auth_algo)))) {
mg_http_send_digest_auth_request_algo(nc, ep->auth_domain, ep->auth_algo);
return;
}
#endif
+3 -6
View File
@@ -333,6 +333,8 @@ 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);
extern void mg_hash_sha256_v(size_t num_msgs, const uint8_t *msgs[],
const size_t *msg_lens, uint8_t *digest);
/*
* Flags for `mg_http_is_authorized()`.
@@ -340,6 +342,7 @@ extern void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[],
#define MG_AUTH_FLAG_IS_DIRECTORY (1 << 0)
#define MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE (1 << 1)
#define MG_AUTH_FLAG_ALLOW_MISSING_FILE (1 << 2)
#define MG_AUTH_FLAG_ALGO(algo) ((((int) algo) & 3) << 8)
/*
* Checks whether an http request is authorized. `domain` is the authentication
@@ -352,12 +355,6 @@ int mg_http_is_authorized(struct http_message *hm, struct mg_str path,
const char *domain, const char *passwords_file,
int flags);
/*
* Sends 401 Unauthorized response.
*/
void mg_http_send_digest_auth_request(struct mg_connection *c,
const char *domain);
#ifdef __cplusplus
}
#endif /* __cplusplus */
+7
View File
@@ -1,3 +1,4 @@
/*
* === Client API reference
*/
@@ -55,6 +56,12 @@ int mg_http_create_digest_auth_header(char *buf, size_t buf_len,
const char *method, const char *uri,
const char *auth_domain, const char *user,
const char *passwd, const char *nonce);
int mg_http_create_digest_auth_header_algo(char *buf, size_t buf_len,
const char *method, const char *uri,
const char *auth_domain,
const char *user, const char *passwd,
const char *nonce,
enum mg_auth_algo algo);
#ifdef __cplusplus
}
+53
View File
@@ -1,3 +1,4 @@
/*
* === Server API reference
*/
@@ -138,6 +139,14 @@ size_t mg_parse_multipart(const char *buf, size_t buf_len, char *var_name,
int mg_get_http_var(const struct mg_str *buf, const char *name, char *dst,
size_t dst_len);
/*
* Supported digest auth algorithms.
*/
enum mg_auth_algo {
MG_AUTH_ALGO_MD5 = 0,
MG_AUTH_ALGO_SHA256 = 1,
};
#if MG_ENABLE_FILESYSTEM
/*
* This structure defines how `mg_serve_http()` works.
@@ -174,6 +183,11 @@ struct mg_serve_http_opts {
*/
const char *global_auth_file;
/*
* Password hashing algorithm used by the password files.
*/
enum mg_auth_algo auth_algo;
/* Set to "no" to disable directory listing. Enabled by default. */
const char *enable_directory_listing;
@@ -425,6 +439,7 @@ struct mg_http_endpoint_opts {
/* Authorization domain (realm) */
const char *auth_domain;
const char *auth_file;
enum mg_auth_algo auth_algo;
};
void mg_register_http_endpoint_opt(struct mg_connection *nc,
@@ -432,12 +447,44 @@ void mg_register_http_endpoint_opt(struct mg_connection *nc,
mg_event_handler_t handler,
struct mg_http_endpoint_opts opts);
/*
* HTTP endpoint descriptor.
*/
struct mg_http_endpoint {
struct mg_http_endpoint *next;
struct mg_str uri_pattern; /* owned */
char *auth_domain; /* owned */
char *auth_file; /* owned */
enum mg_auth_algo auth_algo;
mg_event_handler_t handler;
#if MG_ENABLE_CALLBACK_USERDATA
void *user_data;
#endif
};
/*
* Returns first registered endpoint.
*/
struct mg_http_endpoint *mg_get_http_endpoints(struct mg_connection *nc);
/*
* Sends 401 Unauthorized response.
*/
void mg_http_send_digest_auth_request(struct mg_connection *c,
const char *domain);
void mg_http_send_digest_auth_request_algo(struct mg_connection *c,
const char *domain,
enum mg_auth_algo algo);
/*
* Authenticates a HTTP request against an opened password file.
* Returns 1 if authenticated, 0 otherwise.
*/
int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
FILE *fp);
int mg_http_check_digest_auth_algo(struct http_message *hm, const char *auth_domain,
enum mg_auth_algo fp_algo, FILE *fp);
/*
* Authenticates given response params against an opened password file.
@@ -450,6 +497,12 @@ int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
struct mg_str response, struct mg_str qop,
struct mg_str nc, struct mg_str nonce,
struct mg_str auth_domain, FILE *fp);
int mg_check_digest_auth_algo(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, enum mg_auth_algo algo,
FILE *fp);
/*
* Sends buffer `buf` of size `len` to the client using chunked HTTP encoding.
+2 -1
View File
@@ -424,7 +424,7 @@ void mg_send_websocket_handshake3(struct mg_connection *nc, const char *path,
}
void mg_send_websocket_handshake3v(struct mg_connection *nc,
const struct mg_str path,
const struct mg_str path_in,
const struct mg_str host,
const struct mg_str protocol,
const struct mg_str extra_headers,
@@ -443,6 +443,7 @@ void mg_send_websocket_handshake3v(struct mg_connection *nc,
if (user.len > 0) {
mg_basic_auth_header(user, pass, &auth);
}
const struct mg_str path = (path_in.len > 0 ? path_in : mg_mk_str("/"));
/*
* NOTE: the (auth.buf == NULL ? "" : auth.buf) is because cc3200 libc is
+5 -3
View File
@@ -521,7 +521,6 @@ struct mg_connection *mg_if_accept_new_conn(struct mg_connection *lc) {
nc->user_data = lc->user_data;
nc->recv_mbuf_limit = lc->recv_mbuf_limit;
nc->iface = lc->iface;
if (lc->flags & MG_F_SSL) nc->flags |= MG_F_SSL;
mg_add_conn(nc->mgr, nc);
LOG(LL_DEBUG, ("%p %p %d %#x", lc, nc, (int) nc->sock, (int) nc->flags));
return nc;
@@ -681,10 +680,13 @@ static int mg_recv_udp(struct mg_connection *nc, char *buf, size_t len) {
mg_call(nc, NULL, nc->user_data, MG_EV_ACCEPT, &nc->sa);
}
}
/* Copy input interface address. */
if (nc != NULL) nc->priv_2 = lc->priv_2;
}
if (nc != NULL) {
DBG(("%p <- %d bytes from %s:%d", nc, n, inet_ntoa(nc->sa.sin.sin_addr),
ntohs(nc->sa.sin.sin_port)));
DBG(("%p <- %d bytes from %s:%d ia %#x", nc, n,
inet_ntoa(nc->sa.sin.sin_addr), ntohs(nc->sa.sin.sin_port),
(int) (intptr_t) nc->priv_2));
if (nc == lc) {
nc->recv_mbuf.len += n;
} else {
+9
View File
@@ -393,6 +393,15 @@ struct mg_connect_opts {
*/
const char *ssl_psk_identity;
const char *ssl_psk_key;
#else
/* Ensure struct size remains the same. */
const char *unused_ssl_cert;
const char *unused_ssl_key;
const char *unused_ssl_ca_cert;
const char *unused_ssl_cipher_suites;
const char *unused_ssl_server_name;
const char *unused_ssl_psk_identity;
const char *unused_ssl_psk_key;
#endif
};
+1
View File
@@ -1,3 +1,4 @@
#include "mg_net_if.h"
#include "mg_internal.h"
#include "mg_net_if_socket.h"
+7 -2
View File
@@ -60,11 +60,16 @@ size_t mg_fwrite(const void *ptr, size_t size, size_t count, FILE *f) {
#endif
void mg_base64_encode(const unsigned char *src, int src_len, char *dst) {
cs_base64_encode(src, src_len, dst);
int enc_len = 0;
cs_base64_encode(src, src_len, dst, &enc_len);
dst[enc_len] = '\0';
}
int mg_base64_decode(const unsigned char *s, int len, char *dst) {
return cs_base64_decode(s, len, dst, NULL);
int dec_len = 0;
int res = cs_base64_decode(s, len, dst, &dec_len);
dst[dec_len] = '\0';
return res;
}
#if MG_ENABLE_THREADS
+5 -3
View File
@@ -16,9 +16,11 @@ $ tools/unamalgam.py mongoose.c
This produces directories and files under `mongoose/` and `common/` that are easeier to work with.
It also produces `mongoose.c.manifest` which can later be used to reconstruct the file back:
```
$ tools/amalgam.py --prefix=MG --public-header=mongoose.h $(cat mongoose.c.manifest) > mongoose.c
$ tools/amalgam.py --prefix=MG --license=LICENSE --public-header=mongoose.h $(cat src/mongoose.c.manifest) > mongoose.c
```
The same applies to `mongoose.h`, except `--public-header` should be omitted during amalgamation.
The same applies to `mongoose.h`, except `--public-header` should be omitted during amalgamation:
`tools/amalgam.py` can be used to assemble `mongoose.c` and `mongoose.h`.
```
$ tools/amalgam.py --prefix=MG --license=LICENSE $(cat src/mongoose.h.manifest) > mongoose.h
```
+1 -1
View File
@@ -3,4 +3,4 @@
set -x -e
tools/amalgam.py --prefix=MG --license=LICENSE $(cat src/mongoose.h.manifest) > mongoose.h
tools/amalgam.py --prefix=MG --public-header=mongoose.h --license=LICENSE $(cat src/mongoose.c.manifest) > mongoose.c
tools/amalgam.py --prefix=MG --license=LICENSE --public-header=mongoose.h $(cat src/mongoose.c.manifest) > mongoose.c
+1
View File
@@ -33,6 +33,7 @@ with open(fname) as f:
# open next file for writing
ofile = open(cur_src, "w")
ofile.write('\n')
print('=> %s' % cur_src, file=sys.stderr)
manifest.append(cur_src)
next(f)
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
set -e -x
tools/unamalgam.py mongoose.h
tools/unamalgam.py mongoose.c