mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-26 12:14:56 +00:00
Compare commits
19
Commits
hcd
...
mongoose-os
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
afcb4d9df7 | ||
|
|
44b15b76f7 | ||
|
|
a1fcbb0718 | ||
|
|
0c4d364cee | ||
|
|
def2f39724 | ||
|
|
311fdb8bac | ||
|
|
38f694d071 | ||
|
|
da1e5438bd | ||
|
|
e3a140c9a7 | ||
|
|
a350208f35 | ||
|
|
6fa305e36d | ||
|
|
edd38f6ccd | ||
|
|
abced77c24 | ||
|
|
4699e90e99 | ||
|
|
e9853be8f7 | ||
|
|
fd062b530c | ||
|
|
2fbfe61c37 | ||
|
|
70034b5873 | ||
|
|
d6c9d3c181 |
No files matched your search
@@ -0,0 +1,6 @@
|
|||||||
|
BasedOnStyle: Google
|
||||||
|
AllowShortFunctionsOnASingleLine: false
|
||||||
|
SpaceAfterCStyleCast: true
|
||||||
|
PointerBindsToType: false
|
||||||
|
DerivePointerBinding: false
|
||||||
|
IncludeBlocks: Preserve
|
||||||
@@ -2,75 +2,11 @@
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
Mongoose is ideal for embedded environments. It has been designed
|
This branch is only for use by [Mongoose OS](https://github.com/cesanta/mongoose-os),
|
||||||
for connecting devices and bringing them online. On the market since 2004,
|
for other projects please use the master branch.
|
||||||
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
|
|
||||||
|
|
||||||
# Licensing
|
# 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.
|
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)
|
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:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
[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
@@ -314,24 +314,18 @@ void cs_base64_finish(struct cs_base64_ctx *ctx) {
|
|||||||
while (j % 4 != 0) { \
|
while (j % 4 != 0) { \
|
||||||
BASE64_OUT('='); \
|
BASE64_OUT('='); \
|
||||||
} \
|
} \
|
||||||
BASE64_FLUSH()
|
|
||||||
|
|
||||||
#define BASE64_OUT(ch) \
|
#define BASE64_OUT(ch) \
|
||||||
do { \
|
do { \
|
||||||
dst[j++] = (ch); \
|
dst[j++] = (ch); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define BASE64_FLUSH() \
|
void cs_base64_encode(const unsigned char *src, int src_len, char *dst, int *enc_len) {
|
||||||
do { \
|
|
||||||
dst[j++] = '\0'; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
void cs_base64_encode(const unsigned char *src, int src_len, char *dst) {
|
|
||||||
BASE64_ENCODE_BODY;
|
BASE64_ENCODE_BODY;
|
||||||
|
if (enc_len != NULL) *enc_len = j;
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef BASE64_OUT
|
#undef BASE64_OUT
|
||||||
#undef BASE64_FLUSH
|
|
||||||
|
|
||||||
#if CS_ENABLE_STDIO
|
#if CS_ENABLE_STDIO
|
||||||
#define BASE64_OUT(ch) \
|
#define BASE64_OUT(ch) \
|
||||||
@@ -340,14 +334,11 @@ void cs_base64_encode(const unsigned char *src, int src_len, char *dst) {
|
|||||||
j++; \
|
j++; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define BASE64_FLUSH()
|
|
||||||
|
|
||||||
void cs_fprint_base64(FILE *f, const unsigned char *src, int src_len) {
|
void cs_fprint_base64(FILE *f, const unsigned char *src, int src_len) {
|
||||||
BASE64_ENCODE_BODY;
|
BASE64_ENCODE_BODY;
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef BASE64_OUT
|
#undef BASE64_OUT
|
||||||
#undef BASE64_FLUSH
|
|
||||||
#endif /* CS_ENABLE_STDIO */
|
#endif /* CS_ENABLE_STDIO */
|
||||||
|
|
||||||
/* Convert one byte of encoded base64 input stream to 6-bit chunk */
|
/* 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;
|
if (d == 200) break;
|
||||||
*dst++ = c << 6 | d;
|
*dst++ = c << 6 | d;
|
||||||
}
|
}
|
||||||
*dst = 0;
|
|
||||||
if (dec_len != NULL) *dec_len = (dst - orig_dst);
|
if (dec_len != NULL) *dec_len = (dst - orig_dst);
|
||||||
return orig_len - len;
|
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) WEAK;
|
||||||
int mg_vcmp(const struct mg_str *str1, const char *str2) {
|
int mg_vcmp(const struct mg_str *str1, const char *str2) {
|
||||||
size_t n2 = strlen(str2), n1 = str1->len;
|
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);
|
int r = strncmp(str1->p, str2, (n1 < n2) ? n1 : n2);
|
||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
return n1 - n2;
|
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->user_data = lc->user_data;
|
||||||
nc->recv_mbuf_limit = lc->recv_mbuf_limit;
|
nc->recv_mbuf_limit = lc->recv_mbuf_limit;
|
||||||
nc->iface = lc->iface;
|
nc->iface = lc->iface;
|
||||||
if (lc->flags & MG_F_SSL) nc->flags |= MG_F_SSL;
|
|
||||||
mg_add_conn(nc->mgr, nc);
|
mg_add_conn(nc->mgr, nc);
|
||||||
LOG(LL_DEBUG, ("%p %p %d %#x", lc, nc, (int) nc->sock, (int) nc->flags));
|
LOG(LL_DEBUG, ("%p %p %d %#x", lc, nc, (int) nc->sock, (int) nc->flags));
|
||||||
return nc;
|
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);
|
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) {
|
if (nc != NULL) {
|
||||||
DBG(("%p <- %d bytes from %s:%d", nc, n, inet_ntoa(nc->sa.sin.sin_addr),
|
DBG(("%p <- %d bytes from %s:%d ia %#x", nc, n,
|
||||||
ntohs(nc->sa.sin.sin_port)));
|
inet_ntoa(nc->sa.sin.sin_addr), ntohs(nc->sa.sin.sin_port),
|
||||||
|
(int) (intptr_t) nc->priv_2));
|
||||||
if (nc == lc) {
|
if (nc == lc) {
|
||||||
nc->recv_mbuf.len += n;
|
nc->recv_mbuf.len += n;
|
||||||
} else {
|
} else {
|
||||||
@@ -5762,18 +5755,6 @@ struct mg_http_proto_data_chuncked {
|
|||||||
int64_t body_len; /* How many bytes of chunked body was reassembled. */
|
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 {
|
enum mg_http_multipart_stream_state {
|
||||||
MPS_BEGIN,
|
MPS_BEGIN,
|
||||||
MPS_WAITING_FOR_BOUNDARY,
|
MPS_WAITING_FOR_BOUNDARY,
|
||||||
@@ -5878,6 +5859,13 @@ static void mg_http_free_proto_data_file(struct mg_http_proto_data_file *d) {
|
|||||||
}
|
}
|
||||||
#endif
|
#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) {
|
static void mg_http_free_proto_data_endpoints(struct mg_http_endpoint **ep) {
|
||||||
struct mg_http_endpoint *current = *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
|
#if MG_ENABLE_FILESYSTEM
|
||||||
static void mg_http_transfer_file_data(struct mg_connection *nc) {
|
static void mg_http_transfer_file_data(struct mg_connection *nc) {
|
||||||
struct mg_http_proto_data *pd = mg_http_get_proto_data(nc);
|
struct mg_http_proto_data *pd = mg_http_get_proto_data(nc);
|
||||||
char buf[MG_MAX_HTTP_SEND_MBUF];
|
size_t to_read = 0, left = (size_t)(pd->file.cl - pd->file.sent);
|
||||||
size_t n = 0, to_read = 0, left = (size_t)(pd->file.cl - pd->file.sent);
|
|
||||||
|
|
||||||
if (pd->file.type == DATA_FILE) {
|
if (pd->file.type == DATA_FILE) {
|
||||||
struct mbuf *io = &nc->send_mbuf;
|
struct mbuf *io = &nc->send_mbuf;
|
||||||
@@ -6190,11 +6177,16 @@ static void mg_http_transfer_file_data(struct mg_connection *nc) {
|
|||||||
to_read = left;
|
to_read = left;
|
||||||
}
|
}
|
||||||
if (to_read > 0) {
|
if (to_read > 0) {
|
||||||
n = mg_fread(buf, 1, to_read, pd->file.fp);
|
size_t new_size = io->len + to_read;
|
||||||
if (n > 0) {
|
mbuf_resize(io, new_size);
|
||||||
mg_send(nc, buf, n);
|
if (io->size == new_size) {
|
||||||
pd->file.sent += n;
|
char *buf = io->buf + io->len;
|
||||||
DBG(("%p sent %d (total %d)", nc, (int) n, (int) pd->file.sent));
|
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 {
|
} else {
|
||||||
/* Rate-limited */
|
/* 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 mg_http_handler(struct mg_connection *nc, int ev,
|
||||||
void *ev_data MG_UD_ARG(void *user_data)) {
|
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;
|
struct http_message shm, *hm = &shm;
|
||||||
#endif /* __XTENSA__ */
|
#endif
|
||||||
struct mg_http_proto_data *pd = mg_http_get_proto_data(nc);
|
struct mg_http_proto_data *pd = mg_http_get_proto_data(nc);
|
||||||
struct mbuf *io = &nc->recv_mbuf;
|
struct mbuf *io = &nc->recv_mbuf;
|
||||||
int req_len;
|
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);
|
const size_t *msg_lens, uint8_t *digest);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void cs_md5(char buf[33], ...) {
|
static void cs_digest(enum mg_auth_algo algo, char buf[65], ...) {
|
||||||
unsigned char hash[16];
|
unsigned char hash[32];
|
||||||
const uint8_t *msgs[20], *p;
|
const uint8_t *msgs[16], *p;
|
||||||
size_t msg_lens[20];
|
size_t msg_lens[16];
|
||||||
size_t num_msgs = 0;
|
size_t num_msgs = 0;
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
@@ -7529,44 +7508,61 @@ void cs_md5(char buf[33], ...) {
|
|||||||
}
|
}
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
mg_hash_md5_v(num_msgs, msgs, msg_lens, hash);
|
if (algo == MG_AUTH_ALGO_MD5) {
|
||||||
cs_to_hex(buf, hash, sizeof(hash));
|
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,
|
static void mg_mk_digest_resp(const char *method, size_t method_len,
|
||||||
size_t uri_len, const char *ha1, size_t ha1_len,
|
const char *uri, size_t uri_len, const char *ha1,
|
||||||
const char *nonce, size_t nonce_len, const char *nc,
|
size_t ha1_len, const char *nonce,
|
||||||
size_t nc_len, const char *cnonce, size_t cnonce_len,
|
size_t nonce_len, const char *nc, size_t nc_len,
|
||||||
const char *qop, size_t qop_len, char *resp) {
|
const char *cnonce, size_t cnonce_len,
|
||||||
static const char colon[] = ":";
|
const char *qop, size_t qop_len,
|
||||||
static const size_t one = 1;
|
enum mg_auth_algo algo, char *resp) {
|
||||||
char ha2[33];
|
size_t one = 1;
|
||||||
cs_md5(ha2, method, method_len, colon, one, uri, uri_len, NULL);
|
char ha2[65];
|
||||||
cs_md5(resp, ha1, ha1_len, colon, one, nonce, nonce_len, colon, one, nc,
|
size_t ha2_size = (algo == MG_AUTH_ALGO_MD5 ? 32 : 64);
|
||||||
nc_len, colon, one, cnonce, cnonce_len, colon, one, qop, qop_len,
|
cs_digest(algo, ha2, method, method_len, ":", one, uri, uri_len, NULL);
|
||||||
colon, one, ha2, sizeof(ha2) - 1, 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,
|
int mg_http_create_digest_auth_header(char *buf, size_t buf_len,
|
||||||
const char *method, const char *uri,
|
const char *method, const char *uri,
|
||||||
const char *auth_domain, const char *user,
|
const char *auth_domain, const char *user,
|
||||||
const char *passwd, const char *nonce) {
|
const char *passwd, const char *nonce) {
|
||||||
static const char colon[] = ":", qop[] = "auth";
|
return mg_http_create_digest_auth_header_algo(buf, buf_len, method, uri, auth_domain, user, passwd, nonce, MG_AUTH_ALGO_MD5);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -7581,15 +7577,17 @@ static int mg_check_nonce(const char *nonce) {
|
|||||||
return (now >= val) && (now - val < 60 * 60);
|
return (now >= val) && (now - val < 60 * 60);
|
||||||
}
|
}
|
||||||
|
|
||||||
int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
|
int mg_http_check_digest_auth_algo(struct http_message *hm, const char *auth_domain,
|
||||||
FILE *fp) {
|
enum mg_auth_algo fp_algo, FILE *fp) {
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
struct mg_str *hdr;
|
struct mg_str *hdr;
|
||||||
char username_buf[50], cnonce_buf[64], response_buf[40], uri_buf[200],
|
char username_buf[50], cnonce_buf[64], response_buf[64], uri_buf[200],
|
||||||
qop_buf[20], nc_buf[20], nonce_buf[16];
|
qop_buf[20], nc_buf[20], nonce_buf[16], algo_buf[16];
|
||||||
|
|
||||||
char *username = username_buf, *cnonce = cnonce_buf, *response = response_buf,
|
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 */
|
/* Parse "Authorization:" header, fail fast on parse error */
|
||||||
if (hm == NULL || fp == NULL ||
|
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;
|
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 */
|
/* 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,
|
hm->method,
|
||||||
mg_mk_str_n(
|
mg_mk_str_n(
|
||||||
hm->uri.p,
|
hm->uri.p,
|
||||||
hm->uri.len + (hm->query_string.len ? hm->query_string.len + 1 : 0)),
|
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(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),
|
mg_mk_str(qop), mg_mk_str(nc), mg_mk_str(nonce), mg_mk_str(auth_domain),
|
||||||
fp);
|
fp_algo, fp);
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
if (username != username_buf) MG_FREE(username);
|
if (username != username_buf) MG_FREE(username);
|
||||||
@@ -7627,17 +7639,23 @@ clean:
|
|||||||
if (qop != qop_buf) MG_FREE(qop);
|
if (qop != qop_buf) MG_FREE(qop);
|
||||||
if (nc != nc_buf) MG_FREE(nc);
|
if (nc != nc_buf) MG_FREE(nc);
|
||||||
if (nonce != nonce_buf) MG_FREE(nonce);
|
if (nonce != nonce_buf) MG_FREE(nonce);
|
||||||
|
if (algo != algo_buf) MG_FREE(algo);
|
||||||
|
|
||||||
return ret;
|
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 username, struct mg_str cnonce,
|
||||||
struct mg_str response, struct mg_str qop,
|
struct mg_str response, struct mg_str qop,
|
||||||
struct mg_str nc, struct mg_str nonce,
|
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 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,
|
* 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(&username, f_user) == 0 &&
|
||||||
mg_vcmp(&auth_domain, f_domain) == 0) {
|
mg_vcmp(&auth_domain, f_domain) == 0) {
|
||||||
/* Username and domain matched, check the password */
|
/* Username and domain matched, check the password */
|
||||||
mg_mkmd5resp(method.p, method.len, uri.p, uri.len, f_ha1, strlen(f_ha1),
|
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,
|
nonce.p, nonce.len, nc.p, nc.len, cnonce.p, cnonce.len,
|
||||||
qop.p, qop.len, exp_resp);
|
qop.p, qop.len, algo, exp_resp);
|
||||||
LOG(LL_DEBUG, ("%.*s %s %.*s %s", (int) username.len, username.p,
|
LOG(LL_DEBUG, ("%.*s %s %.*s %s", (int) username.len, username.p,
|
||||||
f_domain, (int) response.len, response.p, exp_resp));
|
f_domain, (int) response.len, response.p, exp_resp));
|
||||||
return mg_ncasecmp(response.p, exp_resp, strlen(exp_resp)) == 0;
|
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;
|
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,
|
int mg_http_is_authorized(struct http_message *hm, struct mg_str path,
|
||||||
const char *domain, const char *passwords_file,
|
const char *domain, const char *passwords_file,
|
||||||
int flags) {
|
int flags) {
|
||||||
@@ -7670,27 +7698,30 @@ int mg_http_is_authorized(struct http_message *hm, struct mg_str path,
|
|||||||
FILE *fp;
|
FILE *fp;
|
||||||
int authorized = 1;
|
int authorized = 1;
|
||||||
|
|
||||||
if (domain != NULL && passwords_file != NULL) {
|
if (domain == NULL || passwords_file == NULL) {
|
||||||
if (flags & MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE) {
|
return 1;
|
||||||
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) {
|
if (flags & MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE) {
|
||||||
authorized = mg_http_check_digest_auth(hm, domain, fp);
|
fp = mg_fopen(passwords_file, "r");
|
||||||
fclose(fp);
|
} else if (flags & MG_AUTH_FLAG_IS_DIRECTORY) {
|
||||||
} else if (!(flags & MG_AUTH_FLAG_ALLOW_MISSING_FILE)) {
|
snprintf(buf, sizeof(buf), "%.*s%c%s", (int) path.len, path.p, DIRSEP,
|
||||||
authorized = 0;
|
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,
|
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,
|
void mg_http_send_digest_auth_request_algo(struct mg_connection *c,
|
||||||
const char *domain) {
|
const char *domain,
|
||||||
|
enum mg_auth_algo algo) {
|
||||||
mg_printf(c,
|
mg_printf(c,
|
||||||
"HTTP/1.1 401 Unauthorized\r\n"
|
"HTTP/1.1 401 Unauthorized\r\n"
|
||||||
"WWW-Authenticate: Digest qop=\"auth\", "
|
"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",
|
"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,
|
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,
|
hm, mg_mk_str(path), opts->auth_domain, opts->global_auth_file,
|
||||||
((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) |
|
((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) |
|
||||||
MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE |
|
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(
|
!mg_http_is_authorized(
|
||||||
hm, mg_mk_str(path), opts->auth_domain,
|
hm, mg_mk_str(path), opts->auth_domain,
|
||||||
opts->per_directory_auth_file,
|
opts->per_directory_auth_file,
|
||||||
((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) |
|
((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) |
|
||||||
MG_AUTH_FLAG_ALLOW_MISSING_FILE))) {
|
MG_AUTH_FLAG_ALLOW_MISSING_FILE |
|
||||||
mg_http_send_digest_auth_request(nc, opts->auth_domain);
|
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) {
|
} else if (is_cgi) {
|
||||||
#if MG_ENABLE_HTTP_CGI
|
#if MG_ENABLE_HTTP_CGI
|
||||||
mg_handle_cgi(nc, index_file ? index_file : path, path_info, hm, opts);
|
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) |
|
((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) |
|
||||||
MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE |
|
MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE |
|
||||||
MG_AUTH_FLAG_ALLOW_MISSING_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
|
#endif
|
||||||
} else if (!mg_vcmp(&hm->method, "MKCOL")) {
|
} else if (!mg_vcmp(&hm->method, "MKCOL")) {
|
||||||
mg_handle_mkcol(nc, path, hm);
|
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) {
|
if (opts.document_root == NULL) {
|
||||||
opts.document_root = ".";
|
opts.document_root = ".";
|
||||||
}
|
}
|
||||||
if (opts.per_directory_auth_file == NULL) {
|
|
||||||
opts.per_directory_auth_file = ".htpasswd";
|
|
||||||
}
|
|
||||||
if (opts.enable_directory_listing == NULL) {
|
if (opts.enable_directory_listing == NULL) {
|
||||||
opts.enable_directory_listing = "yes";
|
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) {
|
if (opts.auth_domain != NULL && opts.auth_file != NULL) {
|
||||||
new_ep->auth_domain = strdup(opts.auth_domain);
|
new_ep->auth_domain = strdup(opts.auth_domain);
|
||||||
new_ep->auth_file = strdup(opts.auth_file);
|
new_ep->auth_file = strdup(opts.auth_file);
|
||||||
|
new_ep->auth_algo = opts.auth_algo;
|
||||||
}
|
}
|
||||||
new_ep->handler = handler;
|
new_ep->handler = handler;
|
||||||
#if MG_ENABLE_CALLBACK_USERDATA
|
#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 (ep != NULL) {
|
||||||
#if MG_ENABLE_FILESYSTEM && !MG_DISABLE_HTTP_DIGEST_AUTH
|
#if MG_ENABLE_FILESYSTEM && !MG_DISABLE_HTTP_DIGEST_AUTH
|
||||||
if (!mg_http_is_authorized(hm, hm->uri, ep->auth_domain, ep->auth_file,
|
if (!mg_http_is_authorized(hm, hm->uri, ep->auth_domain, ep->auth_file,
|
||||||
MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE)) {
|
(MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE |
|
||||||
mg_http_send_digest_auth_request(nc, ep->auth_domain);
|
MG_AUTH_FLAG_ALGO(ep->auth_algo)))) {
|
||||||
|
mg_http_send_digest_auth_request_algo(nc, ep->auth_domain, ep->auth_algo);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#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,
|
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 host,
|
||||||
const struct mg_str protocol,
|
const struct mg_str protocol,
|
||||||
const struct mg_str extra_headers,
|
const struct mg_str extra_headers,
|
||||||
@@ -10237,6 +10276,7 @@ void mg_send_websocket_handshake3v(struct mg_connection *nc,
|
|||||||
if (user.len > 0) {
|
if (user.len > 0) {
|
||||||
mg_basic_auth_header(user, pass, &auth);
|
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
|
* 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
|
#endif
|
||||||
|
|
||||||
void mg_base64_encode(const unsigned char *src, int src_len, char *dst) {
|
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) {
|
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
|
#if MG_ENABLE_THREADS
|
||||||
@@ -14853,11 +14898,12 @@ struct mg_lwip_conn_state {
|
|||||||
struct pbuf *rx_chain; /* Chain of incoming data segments. */
|
struct pbuf *rx_chain; /* Chain of incoming data segments. */
|
||||||
size_t rx_offset; /* Offset within the first pbuf (if partially consumed) */
|
size_t rx_offset; /* Offset within the first pbuf (if partially consumed) */
|
||||||
/* Last SSL write size, for retries. */
|
/* 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 */
|
/* 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 */
|
/* 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 {
|
enum mg_sig_type {
|
||||||
@@ -14897,6 +14943,10 @@ void mg_lwip_mgr_schedule_poll(struct mg_mgr *mgr);
|
|||||||
|
|
||||||
/* Amalgamated: #include "common/cs_dbg.h" */
|
/* 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,
|
* Newest versions of LWIP have ip_2_ip4, older have ipX_2_ip,
|
||||||
* even older have nothing.
|
* 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_NEW tcp_new_ip6
|
||||||
#define TCP_BIND tcp_bind_ip6
|
#define TCP_BIND tcp_bind_ip6
|
||||||
#define UDP_BIND udp_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) \
|
#define SET_ADDR(dst, src) \
|
||||||
memcpy((dst)->sin6.sin6_addr.s6_addr, (src)->ip6.addr, \
|
memcpy((dst)->sin6.sin6_addr.s6_addr, (src)->ip6.addr, \
|
||||||
sizeof((dst)->sin6.sin6_addr.s6_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) {
|
static void mg_lwip_tcp_error_cb(void *arg, err_t err) {
|
||||||
struct mg_connection *nc = (struct mg_connection *) arg;
|
struct mg_connection *nc = (struct mg_connection *) arg;
|
||||||
DBG(("%p conn error %d", nc, (int) err));
|
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;
|
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||||
cs->pcb.tcp = NULL; /* Has already been deallocated */
|
cs->pcb.tcp = NULL; /* Has already been deallocated */
|
||||||
|
if (nc->flags & MG_F_CLOSE_IMMEDIATELY) return;
|
||||||
if (nc->flags & MG_F_CONNECTING) {
|
if (nc->flags & MG_F_CONNECTING) {
|
||||||
cs->err = err;
|
cs->err = err;
|
||||||
mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc);
|
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();
|
mgos_lock();
|
||||||
if (cs->rx_chain == NULL) {
|
if (cs->rx_chain == NULL) {
|
||||||
cs->rx_offset = 0;
|
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);
|
mg_lwip_recv_common(nc, p);
|
||||||
mgos_unlock();
|
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;
|
struct mg_connection *nc = (struct mg_connection *) arg;
|
||||||
DBG(("%p %p %u %p %p", nc, tpcb, num_sent, tpcb->unsent, tpcb->unacked));
|
DBG(("%p %p %u %p %p", nc, tpcb, num_sent, tpcb->unsent, tpcb->unacked));
|
||||||
if (nc == NULL) return ERR_OK;
|
if (nc == NULL) return ERR_OK;
|
||||||
if ((nc->flags & MG_F_SEND_AND_CLOSE) && !(nc->flags & MG_F_WANT_WRITE) &&
|
if ((nc->flags & (MG_F_SEND_AND_CLOSE | MG_F_CLOSE_IMMEDIATELY)) ==
|
||||||
nc->send_mbuf.len == 0 && tpcb->unsent == NULL && tpcb->unacked == NULL) {
|
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);
|
mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc);
|
||||||
}
|
}
|
||||||
if (nc->send_mbuf.len > 0 || (nc->flags & MG_F_WANT_WRITE)) {
|
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);
|
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
|
* Lwip included in the SDKs for nRF5x chips has different type for the
|
||||||
* callback of `udp_recv()`
|
* 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
|
#endif
|
||||||
{
|
{
|
||||||
struct mg_connection *nc = (struct mg_connection *) arg;
|
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));
|
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 netif *nif = ip_current_netif();
|
||||||
struct pbuf *sap =
|
|
||||||
pbuf_alloc(PBUF_RAW, sizeof(union socket_address), PBUF_RAM);
|
|
||||||
if (sap == NULL) {
|
|
||||||
pbuf_free(p);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
union socket_address *sa = (union socket_address *) sap->payload;
|
|
||||||
#if ((LWIP_VERSION_MAJOR << 8) | LWIP_VERSION_MINOR) >= 0x0105
|
#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
|
#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
|
#endif
|
||||||
sa->sin.sin_port = htons(port);
|
ui->remote_addr.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);
|
|
||||||
mgos_lock();
|
mgos_lock();
|
||||||
mg_lwip_recv_common(nc, sap);
|
mg_lwip_recv_common(nc, p);
|
||||||
mgos_unlock();
|
mgos_unlock();
|
||||||
(void) pcb;
|
(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;
|
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||||
if (cs->rx_chain == NULL) {
|
if (cs->rx_chain == NULL) {
|
||||||
cs->rx_chain = p;
|
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);
|
pbuf_chain(cs->rx_chain, p);
|
||||||
|
cs->rx_chain_len++;
|
||||||
|
} else {
|
||||||
|
/* Drop it on the floor. */
|
||||||
|
pbuf_free(p);
|
||||||
}
|
}
|
||||||
if (!cs->recv_pending) {
|
if (!cs->recv_pending) {
|
||||||
cs->recv_pending = 1;
|
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,
|
static int mg_lwip_if_udp_recv(struct mg_connection *nc, void *buf, size_t len,
|
||||||
union socket_address *sa, size_t *sa_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;
|
int res = 0;
|
||||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||||
if (nc->sock == INVALID_SOCKET) return -1;
|
if (nc->sock == INVALID_SOCKET) return -1;
|
||||||
mgos_lock();
|
mgos_lock();
|
||||||
if (cs->rx_chain != NULL) {
|
if (cs->rx_chain != NULL) {
|
||||||
struct pbuf *ap = cs->rx_chain;
|
struct pbuf *p = cs->rx_chain;
|
||||||
struct pbuf *dp = ap->next;
|
cs->rx_chain = pbuf_dechain(p);
|
||||||
cs->rx_chain = pbuf_dechain(dp);
|
cs->rx_chain_len--;
|
||||||
res = MIN(dp->len, len);
|
mgos_unlock();
|
||||||
pbuf_copy_partial(dp, buf, res, 0);
|
struct mg_lwip_udp_info *ui =
|
||||||
pbuf_free(dp);
|
(struct mg_lwip_udp_info *) ((((uintptr_t) p->payload) &
|
||||||
pbuf_copy_partial(ap, sa, MIN(*sa_len, ap->len), 0);
|
(~((uintptr_t) 3))) -
|
||||||
pbuf_free(ap);
|
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;
|
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);
|
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) {
|
void mg_lwip_handle_accept(struct mg_connection *nc) {
|
||||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||||
if (cs->pcb.tcp == NULL) return;
|
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_connection *lc = (struct mg_connection *) arg, *nc;
|
||||||
struct mg_lwip_conn_state *lcs, *cs;
|
struct mg_lwip_conn_state *lcs, *cs;
|
||||||
struct tcp_pcb_listen *lpcb;
|
struct tcp_pcb_listen *lpcb;
|
||||||
|
if (newtpcb == NULL) {
|
||||||
|
LOG(LL_ERROR, ("%p accept error %d", lc, (int) err));
|
||||||
|
return ERR_ABRT;
|
||||||
|
}
|
||||||
LOG(LL_DEBUG,
|
LOG(LL_DEBUG,
|
||||||
("%p conn %p from %s:%u", lc, newtpcb,
|
("%p conn %p from %s:%u", lc, newtpcb,
|
||||||
IPADDR_NTOA(ipX_2_ip(&newtpcb->remote_ip)), newtpcb->remote_port));
|
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_connection *nc = ctx->nc;
|
||||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||||
struct tcp_pcb *tpcb = cs->pcb.tcp;
|
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 len = MIN(tpcb->mss, MIN(ctx->len, tpcb->snd_buf));
|
||||||
size_t unsent, unacked;
|
size_t unsent, unacked;
|
||||||
if (len == 0) {
|
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);
|
cs->err = tcp_write(tpcb, ctx->data, len, TCP_WRITE_FLAG_COPY);
|
||||||
unsent = (tpcb->unsent != NULL ? tpcb->unsent->len : 0);
|
unsent = (tpcb->unsent != NULL ? tpcb->unsent->len : 0);
|
||||||
unacked = (tpcb->unacked != NULL ? tpcb->unacked->len : 0);
|
unacked = (tpcb->unacked != NULL ? tpcb->unacked->len : 0);
|
||||||
DBG(("%p tcp_write %u = %d, %u %u",
|
DBG(("%p tcp_write %u = %d, %u %u", tpcb, len, (int) cs->err, unsent,
|
||||||
tpcb, len, (int) cs->err, unsent, unacked));
|
unacked));
|
||||||
if (cs->err != ERR_OK) {
|
if (cs->err != ERR_OK) {
|
||||||
/*
|
/*
|
||||||
* We ignore ERR_MEM because memory will be freed up when the data is sent
|
* 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;
|
struct pbuf *p;
|
||||||
ip_addr_t *ip;
|
ip_addr_t *ip;
|
||||||
uint16_t port;
|
uint16_t port;
|
||||||
|
uint32_t mcast_ip4;
|
||||||
int ret;
|
int ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void udp_sendto_tcpip(void *arg) {
|
static void udp_sendto_tcpip(void *arg) {
|
||||||
struct udp_sendto_ctx *ctx = (struct udp_sendto_ctx *) 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);
|
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;
|
if (p == NULL) return 0;
|
||||||
memcpy(p->payload, data, len);
|
memcpy(p->payload, data, len);
|
||||||
struct udp_sendto_ctx ctx = {.upcb = upcb, .p = p, .ip = &ip, .port = port};
|
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);
|
mg_lwip_netif_run_on_tcpip(udp_sendto_tcpip, &ctx);
|
||||||
cs->err = ctx.ret;
|
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);
|
pbuf_free(p);
|
||||||
return (cs->err == ERR_OK ? (int) len : -2);
|
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;
|
cs->rx_offset += copy_len;
|
||||||
if (cs->rx_offset == cs->rx_chain->len) {
|
if (cs->rx_offset == cs->rx_chain->len) {
|
||||||
cs->rx_chain = pbuf_dechain(cs->rx_chain);
|
cs->rx_chain = pbuf_dechain(cs->rx_chain);
|
||||||
|
cs->rx_chain_len--;
|
||||||
pbuf_free(seg);
|
pbuf_free(seg);
|
||||||
cs->rx_offset = 0;
|
cs->rx_offset = 0;
|
||||||
}
|
}
|
||||||
@@ -15517,6 +15596,27 @@ int mg_lwip_if_create_conn(struct mg_connection *nc) {
|
|||||||
return 1;
|
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) {
|
static void udp_remove_tcpip(void *arg) {
|
||||||
udp_remove((struct udp_pcb *) 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) {
|
void mg_lwip_if_destroy_conn(struct mg_connection *nc) {
|
||||||
if (nc->sock == INVALID_SOCKET) return;
|
if (nc->sock == INVALID_SOCKET) return;
|
||||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
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)) {
|
if (!(nc->flags & MG_F_UDP)) {
|
||||||
struct tcp_pcb *tpcb = cs->pcb.tcp;
|
struct tcp_pcb *tpcb = cs->pcb.tcp;
|
||||||
if (tpcb != NULL) {
|
if (tpcb != NULL) {
|
||||||
tcp_arg(tpcb, NULL);
|
|
||||||
DBG(("%p tcp_close %p", nc, tpcb));
|
DBG(("%p tcp_close %p", nc, tpcb));
|
||||||
tcp_arg(tpcb, NULL);
|
mg_lwip_netif_run_on_tcpip(tcp_close_tcpip, cs);
|
||||||
mg_lwip_netif_run_on_tcpip(tcp_close_tcpip, tpcb);
|
|
||||||
}
|
}
|
||||||
while (cs->rx_chain != NULL) {
|
} else {
|
||||||
struct pbuf *seg = cs->rx_chain;
|
|
||||||
cs->rx_chain = pbuf_dechain(cs->rx_chain);
|
|
||||||
pbuf_free(seg);
|
|
||||||
}
|
|
||||||
memset(cs, 0, sizeof(*cs));
|
|
||||||
MG_FREE(cs);
|
|
||||||
} else if (nc->listener == NULL) {
|
|
||||||
/* Only close outgoing UDP pcb or listeners. */
|
|
||||||
struct udp_pcb *upcb = cs->pcb.udp;
|
struct udp_pcb *upcb = cs->pcb.udp;
|
||||||
if (upcb != NULL) {
|
if (upcb != NULL) {
|
||||||
DBG(("%p udp_remove %p", nc, upcb));
|
DBG(("%p udp_remove %p", nc, upcb));
|
||||||
mg_lwip_netif_run_on_tcpip(udp_remove_tcpip, upcb);
|
mg_lwip_netif_run_on_tcpip(udp_remove_tcpip, upcb);
|
||||||
}
|
}
|
||||||
memset(cs, 0, sizeof(*cs));
|
|
||||||
MG_FREE(cs);
|
|
||||||
}
|
}
|
||||||
nc->sock = INVALID_SOCKET;
|
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,
|
void mg_lwip_if_get_conn_addr(struct mg_connection *nc, int remote,
|
||||||
|
|||||||
+75
-7
@@ -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_update(struct cs_base64_ctx *ctx, const char *str, size_t len);
|
||||||
void cs_base64_finish(struct cs_base64_ctx *ctx);
|
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);
|
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_identity;
|
||||||
const char *ssl_psk_key;
|
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
|
#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);
|
const size_t *msg_lens, uint8_t *digest);
|
||||||
extern void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[],
|
extern void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[],
|
||||||
const size_t *msg_lens, uint8_t *digest);
|
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()`.
|
* 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_DIRECTORY (1 << 0)
|
||||||
#define MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE (1 << 1)
|
#define MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE (1 << 1)
|
||||||
#define MG_AUTH_FLAG_ALLOW_MISSING_FILE (1 << 2)
|
#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
|
* 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,
|
const char *domain, const char *passwords_file,
|
||||||
int flags);
|
int flags);
|
||||||
|
|
||||||
/*
|
|
||||||
* Sends 401 Unauthorized response.
|
|
||||||
*/
|
|
||||||
void mg_http_send_digest_auth_request(struct mg_connection *c,
|
|
||||||
const char *domain);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif /* __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,
|
int mg_get_http_var(const struct mg_str *buf, const char *name, char *dst,
|
||||||
size_t dst_len);
|
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
|
#if MG_ENABLE_FILESYSTEM
|
||||||
/*
|
/*
|
||||||
* This structure defines how `mg_serve_http()` works.
|
* This structure defines how `mg_serve_http()` works.
|
||||||
@@ -4906,6 +4920,11 @@ struct mg_serve_http_opts {
|
|||||||
*/
|
*/
|
||||||
const char *global_auth_file;
|
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. */
|
/* Set to "no" to disable directory listing. Enabled by default. */
|
||||||
const char *enable_directory_listing;
|
const char *enable_directory_listing;
|
||||||
|
|
||||||
@@ -5157,6 +5176,7 @@ struct mg_http_endpoint_opts {
|
|||||||
/* Authorization domain (realm) */
|
/* Authorization domain (realm) */
|
||||||
const char *auth_domain;
|
const char *auth_domain;
|
||||||
const char *auth_file;
|
const char *auth_file;
|
||||||
|
enum mg_auth_algo auth_algo;
|
||||||
};
|
};
|
||||||
|
|
||||||
void mg_register_http_endpoint_opt(struct mg_connection *nc,
|
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,
|
mg_event_handler_t handler,
|
||||||
struct mg_http_endpoint_opts opts);
|
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.
|
* Authenticates a HTTP request against an opened password file.
|
||||||
* Returns 1 if authenticated, 0 otherwise.
|
* Returns 1 if authenticated, 0 otherwise.
|
||||||
*/
|
*/
|
||||||
int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
|
int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
|
||||||
FILE *fp);
|
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.
|
* 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 response, struct mg_str qop,
|
||||||
struct mg_str nc, struct mg_str nonce,
|
struct mg_str nc, struct mg_str nonce,
|
||||||
struct mg_str auth_domain, FILE *fp);
|
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.
|
* 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 *method, const char *uri,
|
||||||
const char *auth_domain, const char *user,
|
const char *auth_domain, const char *user,
|
||||||
const char *passwd, const char *nonce);
|
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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
@@ -5686,8 +5750,12 @@ extern "C" {
|
|||||||
#define MG_DNS_ANY_RECORD 0xff
|
#define MG_DNS_ANY_RECORD 0xff
|
||||||
#define MG_DNS_NSEC_RECORD 0x2f
|
#define MG_DNS_NSEC_RECORD 0x2f
|
||||||
|
|
||||||
|
#ifndef MG_MAX_DNS_QUESTIONS
|
||||||
#define MG_MAX_DNS_QUESTIONS 32
|
#define MG_MAX_DNS_QUESTIONS 32
|
||||||
|
#endif
|
||||||
|
#ifndef MG_MAX_DNS_ANSWERS
|
||||||
#define MG_MAX_DNS_ANSWERS 32
|
#define MG_MAX_DNS_ANSWERS 32
|
||||||
|
#endif
|
||||||
|
|
||||||
#define MG_DNS_MESSAGE 100 /* High-level DNS message event */
|
#define MG_DNS_MESSAGE 100 /* High-level DNS message event */
|
||||||
|
|
||||||
|
|||||||
+2
-12
@@ -101,24 +101,18 @@ void cs_base64_finish(struct cs_base64_ctx *ctx) {
|
|||||||
while (j % 4 != 0) { \
|
while (j % 4 != 0) { \
|
||||||
BASE64_OUT('='); \
|
BASE64_OUT('='); \
|
||||||
} \
|
} \
|
||||||
BASE64_FLUSH()
|
|
||||||
|
|
||||||
#define BASE64_OUT(ch) \
|
#define BASE64_OUT(ch) \
|
||||||
do { \
|
do { \
|
||||||
dst[j++] = (ch); \
|
dst[j++] = (ch); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define BASE64_FLUSH() \
|
void cs_base64_encode(const unsigned char *src, int src_len, char *dst, int *enc_len) {
|
||||||
do { \
|
|
||||||
dst[j++] = '\0'; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
void cs_base64_encode(const unsigned char *src, int src_len, char *dst) {
|
|
||||||
BASE64_ENCODE_BODY;
|
BASE64_ENCODE_BODY;
|
||||||
|
if (enc_len != NULL) *enc_len = j;
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef BASE64_OUT
|
#undef BASE64_OUT
|
||||||
#undef BASE64_FLUSH
|
|
||||||
|
|
||||||
#if CS_ENABLE_STDIO
|
#if CS_ENABLE_STDIO
|
||||||
#define BASE64_OUT(ch) \
|
#define BASE64_OUT(ch) \
|
||||||
@@ -127,14 +121,11 @@ void cs_base64_encode(const unsigned char *src, int src_len, char *dst) {
|
|||||||
j++; \
|
j++; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define BASE64_FLUSH()
|
|
||||||
|
|
||||||
void cs_fprint_base64(FILE *f, const unsigned char *src, int src_len) {
|
void cs_fprint_base64(FILE *f, const unsigned char *src, int src_len) {
|
||||||
BASE64_ENCODE_BODY;
|
BASE64_ENCODE_BODY;
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef BASE64_OUT
|
#undef BASE64_OUT
|
||||||
#undef BASE64_FLUSH
|
|
||||||
#endif /* CS_ENABLE_STDIO */
|
#endif /* CS_ENABLE_STDIO */
|
||||||
|
|
||||||
/* Convert one byte of encoded base64 input stream to 6-bit chunk */
|
/* 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;
|
if (d == 200) break;
|
||||||
*dst++ = c << 6 | d;
|
*dst++ = c << 6 | d;
|
||||||
}
|
}
|
||||||
*dst = 0;
|
|
||||||
if (dec_len != NULL) *dec_len = (dst - orig_dst);
|
if (dec_len != NULL) *dec_len = (dst - orig_dst);
|
||||||
return orig_len - len;
|
return orig_len - len;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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_update(struct cs_base64_ctx *ctx, const char *str, size_t len);
|
||||||
void cs_base64_finish(struct cs_base64_ctx *ctx);
|
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);
|
void cs_fprint_base64(FILE *f, const unsigned char *src, int src_len);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* This code implements the MD5 message-digest algorithm.
|
* This code implements the MD5 message-digest algorithm.
|
||||||
* The algorithm is due to Ron Rivest. This code was
|
* The algorithm is due to Ron Rivest. This code was
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
/* Copyright(c) By Steve Reid <steve@edmweb.com> */
|
/* Copyright(c) By Steve Reid <steve@edmweb.com> */
|
||||||
/* 100% Public Domain */
|
/* 100% Public Domain */
|
||||||
|
|
||||||
|
|||||||
@@ -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) WEAK;
|
||||||
int mg_vcmp(const struct mg_str *str1, const char *str2) {
|
int mg_vcmp(const struct mg_str *str1, const char *str2) {
|
||||||
size_t n2 = strlen(str2), n1 = str1->len;
|
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);
|
int r = strncmp(str1->p, str2, (n1 < n2) ? n1 : n2);
|
||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
return n1 - n2;
|
return n1 - n2;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
#ifndef CS_COMMON_PLATFORM_H_
|
#ifndef CS_COMMON_PLATFORM_H_
|
||||||
#define CS_COMMON_PLATFORM_H_
|
#define CS_COMMON_PLATFORM_H_
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,10 @@
|
|||||||
|
|
||||||
#include "common/cs_dbg.h"
|
#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,
|
* Newest versions of LWIP have ip_2_ip4, older have ipX_2_ip,
|
||||||
* even older have nothing.
|
* even older have nothing.
|
||||||
@@ -37,7 +41,7 @@
|
|||||||
#define TCP_NEW tcp_new_ip6
|
#define TCP_NEW tcp_new_ip6
|
||||||
#define TCP_BIND tcp_bind_ip6
|
#define TCP_BIND tcp_bind_ip6
|
||||||
#define UDP_BIND udp_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) \
|
#define SET_ADDR(dst, src) \
|
||||||
memcpy((dst)->sin6.sin6_addr.s6_addr, (src)->ip6.addr, \
|
memcpy((dst)->sin6.sin6_addr.s6_addr, (src)->ip6.addr, \
|
||||||
sizeof((dst)->sin6.sin6_addr.s6_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) {
|
static void mg_lwip_tcp_error_cb(void *arg, err_t err) {
|
||||||
struct mg_connection *nc = (struct mg_connection *) arg;
|
struct mg_connection *nc = (struct mg_connection *) arg;
|
||||||
DBG(("%p conn error %d", nc, (int) err));
|
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;
|
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||||
cs->pcb.tcp = NULL; /* Has already been deallocated */
|
cs->pcb.tcp = NULL; /* Has already been deallocated */
|
||||||
|
if (nc->flags & MG_F_CLOSE_IMMEDIATELY) return;
|
||||||
if (nc->flags & MG_F_CONNECTING) {
|
if (nc->flags & MG_F_CONNECTING) {
|
||||||
cs->err = err;
|
cs->err = err;
|
||||||
mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc);
|
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();
|
mgos_lock();
|
||||||
if (cs->rx_chain == NULL) {
|
if (cs->rx_chain == NULL) {
|
||||||
cs->rx_offset = 0;
|
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);
|
mg_lwip_recv_common(nc, p);
|
||||||
mgos_unlock();
|
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;
|
struct mg_connection *nc = (struct mg_connection *) arg;
|
||||||
DBG(("%p %p %u %p %p", nc, tpcb, num_sent, tpcb->unsent, tpcb->unacked));
|
DBG(("%p %p %u %p %p", nc, tpcb, num_sent, tpcb->unsent, tpcb->unacked));
|
||||||
if (nc == NULL) return ERR_OK;
|
if (nc == NULL) return ERR_OK;
|
||||||
if ((nc->flags & MG_F_SEND_AND_CLOSE) && !(nc->flags & MG_F_WANT_WRITE) &&
|
if ((nc->flags & (MG_F_SEND_AND_CLOSE | MG_F_CLOSE_IMMEDIATELY)) ==
|
||||||
nc->send_mbuf.len == 0 && tpcb->unsent == NULL && tpcb->unacked == NULL) {
|
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);
|
mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc);
|
||||||
}
|
}
|
||||||
if (nc->send_mbuf.len > 0 || (nc->flags & MG_F_WANT_WRITE)) {
|
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);
|
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
|
* Lwip included in the SDKs for nRF5x chips has different type for the
|
||||||
* callback of `udp_recv()`
|
* 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
|
#endif
|
||||||
{
|
{
|
||||||
struct mg_connection *nc = (struct mg_connection *) arg;
|
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));
|
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 netif *nif = ip_current_netif();
|
||||||
struct pbuf *sap =
|
|
||||||
pbuf_alloc(PBUF_RAW, sizeof(union socket_address), PBUF_RAM);
|
|
||||||
if (sap == NULL) {
|
|
||||||
pbuf_free(p);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
union socket_address *sa = (union socket_address *) sap->payload;
|
|
||||||
#if ((LWIP_VERSION_MAJOR << 8) | LWIP_VERSION_MINOR) >= 0x0105
|
#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
|
#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
|
#endif
|
||||||
sa->sin.sin_port = htons(port);
|
ui->remote_addr.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);
|
|
||||||
mgos_lock();
|
mgos_lock();
|
||||||
mg_lwip_recv_common(nc, sap);
|
mg_lwip_recv_common(nc, p);
|
||||||
mgos_unlock();
|
mgos_unlock();
|
||||||
(void) pcb;
|
(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;
|
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||||
if (cs->rx_chain == NULL) {
|
if (cs->rx_chain == NULL) {
|
||||||
cs->rx_chain = p;
|
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);
|
pbuf_chain(cs->rx_chain, p);
|
||||||
|
cs->rx_chain_len++;
|
||||||
|
} else {
|
||||||
|
/* Drop it on the floor. */
|
||||||
|
pbuf_free(p);
|
||||||
}
|
}
|
||||||
if (!cs->recv_pending) {
|
if (!cs->recv_pending) {
|
||||||
cs->recv_pending = 1;
|
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,
|
static int mg_lwip_if_udp_recv(struct mg_connection *nc, void *buf, size_t len,
|
||||||
union socket_address *sa, size_t *sa_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;
|
int res = 0;
|
||||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||||
if (nc->sock == INVALID_SOCKET) return -1;
|
if (nc->sock == INVALID_SOCKET) return -1;
|
||||||
mgos_lock();
|
mgos_lock();
|
||||||
if (cs->rx_chain != NULL) {
|
if (cs->rx_chain != NULL) {
|
||||||
struct pbuf *ap = cs->rx_chain;
|
struct pbuf *p = cs->rx_chain;
|
||||||
struct pbuf *dp = ap->next;
|
cs->rx_chain = pbuf_dechain(p);
|
||||||
cs->rx_chain = pbuf_dechain(dp);
|
cs->rx_chain_len--;
|
||||||
res = MIN(dp->len, len);
|
mgos_unlock();
|
||||||
pbuf_copy_partial(dp, buf, res, 0);
|
struct mg_lwip_udp_info *ui =
|
||||||
pbuf_free(dp);
|
(struct mg_lwip_udp_info *) ((((uintptr_t) p->payload) &
|
||||||
pbuf_copy_partial(ap, sa, MIN(*sa_len, ap->len), 0);
|
(~((uintptr_t) 3))) -
|
||||||
pbuf_free(ap);
|
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;
|
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);
|
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) {
|
void mg_lwip_handle_accept(struct mg_connection *nc) {
|
||||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||||
if (cs->pcb.tcp == NULL) return;
|
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_connection *lc = (struct mg_connection *) arg, *nc;
|
||||||
struct mg_lwip_conn_state *lcs, *cs;
|
struct mg_lwip_conn_state *lcs, *cs;
|
||||||
struct tcp_pcb_listen *lpcb;
|
struct tcp_pcb_listen *lpcb;
|
||||||
|
if (newtpcb == NULL) {
|
||||||
|
LOG(LL_ERROR, ("%p accept error %d", lc, (int) err));
|
||||||
|
return ERR_ABRT;
|
||||||
|
}
|
||||||
LOG(LL_DEBUG,
|
LOG(LL_DEBUG,
|
||||||
("%p conn %p from %s:%u", lc, newtpcb,
|
("%p conn %p from %s:%u", lc, newtpcb,
|
||||||
IPADDR_NTOA(ipX_2_ip(&newtpcb->remote_ip)), newtpcb->remote_port));
|
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_connection *nc = ctx->nc;
|
||||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||||
struct tcp_pcb *tpcb = cs->pcb.tcp;
|
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 len = MIN(tpcb->mss, MIN(ctx->len, tpcb->snd_buf));
|
||||||
size_t unsent, unacked;
|
size_t unsent, unacked;
|
||||||
if (len == 0) {
|
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);
|
cs->err = tcp_write(tpcb, ctx->data, len, TCP_WRITE_FLAG_COPY);
|
||||||
unsent = (tpcb->unsent != NULL ? tpcb->unsent->len : 0);
|
unsent = (tpcb->unsent != NULL ? tpcb->unsent->len : 0);
|
||||||
unacked = (tpcb->unacked != NULL ? tpcb->unacked->len : 0);
|
unacked = (tpcb->unacked != NULL ? tpcb->unacked->len : 0);
|
||||||
DBG(("%p tcp_write %u = %d, %u %u",
|
DBG(("%p tcp_write %u = %d, %u %u", tpcb, len, (int) cs->err, unsent,
|
||||||
tpcb, len, (int) cs->err, unsent, unacked));
|
unacked));
|
||||||
if (cs->err != ERR_OK) {
|
if (cs->err != ERR_OK) {
|
||||||
/*
|
/*
|
||||||
* We ignore ERR_MEM because memory will be freed up when the data is sent
|
* 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;
|
struct pbuf *p;
|
||||||
ip_addr_t *ip;
|
ip_addr_t *ip;
|
||||||
uint16_t port;
|
uint16_t port;
|
||||||
|
uint32_t mcast_ip4;
|
||||||
int ret;
|
int ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void udp_sendto_tcpip(void *arg) {
|
static void udp_sendto_tcpip(void *arg) {
|
||||||
struct udp_sendto_ctx *ctx = (struct udp_sendto_ctx *) 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);
|
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;
|
if (p == NULL) return 0;
|
||||||
memcpy(p->payload, data, len);
|
memcpy(p->payload, data, len);
|
||||||
struct udp_sendto_ctx ctx = {.upcb = upcb, .p = p, .ip = &ip, .port = port};
|
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);
|
mg_lwip_netif_run_on_tcpip(udp_sendto_tcpip, &ctx);
|
||||||
cs->err = ctx.ret;
|
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);
|
pbuf_free(p);
|
||||||
return (cs->err == ERR_OK ? (int) len : -2);
|
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;
|
cs->rx_offset += copy_len;
|
||||||
if (cs->rx_offset == cs->rx_chain->len) {
|
if (cs->rx_offset == cs->rx_chain->len) {
|
||||||
cs->rx_chain = pbuf_dechain(cs->rx_chain);
|
cs->rx_chain = pbuf_dechain(cs->rx_chain);
|
||||||
|
cs->rx_chain_len--;
|
||||||
pbuf_free(seg);
|
pbuf_free(seg);
|
||||||
cs->rx_offset = 0;
|
cs->rx_offset = 0;
|
||||||
}
|
}
|
||||||
@@ -637,6 +670,27 @@ int mg_lwip_if_create_conn(struct mg_connection *nc) {
|
|||||||
return 1;
|
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) {
|
static void udp_remove_tcpip(void *arg) {
|
||||||
udp_remove((struct udp_pcb *) 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) {
|
void mg_lwip_if_destroy_conn(struct mg_connection *nc) {
|
||||||
if (nc->sock == INVALID_SOCKET) return;
|
if (nc->sock == INVALID_SOCKET) return;
|
||||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
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)) {
|
if (!(nc->flags & MG_F_UDP)) {
|
||||||
struct tcp_pcb *tpcb = cs->pcb.tcp;
|
struct tcp_pcb *tpcb = cs->pcb.tcp;
|
||||||
if (tpcb != NULL) {
|
if (tpcb != NULL) {
|
||||||
tcp_arg(tpcb, NULL);
|
|
||||||
DBG(("%p tcp_close %p", nc, tpcb));
|
DBG(("%p tcp_close %p", nc, tpcb));
|
||||||
tcp_arg(tpcb, NULL);
|
mg_lwip_netif_run_on_tcpip(tcp_close_tcpip, cs);
|
||||||
mg_lwip_netif_run_on_tcpip(tcp_close_tcpip, tpcb);
|
|
||||||
}
|
}
|
||||||
while (cs->rx_chain != NULL) {
|
} else {
|
||||||
struct pbuf *seg = cs->rx_chain;
|
|
||||||
cs->rx_chain = pbuf_dechain(cs->rx_chain);
|
|
||||||
pbuf_free(seg);
|
|
||||||
}
|
|
||||||
memset(cs, 0, sizeof(*cs));
|
|
||||||
MG_FREE(cs);
|
|
||||||
} else if (nc->listener == NULL) {
|
|
||||||
/* Only close outgoing UDP pcb or listeners. */
|
|
||||||
struct udp_pcb *upcb = cs->pcb.udp;
|
struct udp_pcb *upcb = cs->pcb.udp;
|
||||||
if (upcb != NULL) {
|
if (upcb != NULL) {
|
||||||
DBG(("%p udp_remove %p", nc, upcb));
|
DBG(("%p udp_remove %p", nc, upcb));
|
||||||
mg_lwip_netif_run_on_tcpip(udp_remove_tcpip, upcb);
|
mg_lwip_netif_run_on_tcpip(udp_remove_tcpip, upcb);
|
||||||
}
|
}
|
||||||
memset(cs, 0, sizeof(*cs));
|
|
||||||
MG_FREE(cs);
|
|
||||||
}
|
}
|
||||||
nc->sock = INVALID_SOCKET;
|
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,
|
void mg_lwip_if_get_conn_addr(struct mg_connection *nc, int remote,
|
||||||
|
|||||||
@@ -24,11 +24,12 @@ struct mg_lwip_conn_state {
|
|||||||
struct pbuf *rx_chain; /* Chain of incoming data segments. */
|
struct pbuf *rx_chain; /* Chain of incoming data segments. */
|
||||||
size_t rx_offset; /* Offset within the first pbuf (if partially consumed) */
|
size_t rx_offset; /* Offset within the first pbuf (if partially consumed) */
|
||||||
/* Last SSL write size, for retries. */
|
/* 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 */
|
/* 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 */
|
/* 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 {
|
enum mg_sig_type {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
#ifndef CS_COMMON_PLATFORMS_PLATFORM_NRF51_H_
|
#ifndef CS_COMMON_PLATFORMS_PLATFORM_NRF51_H_
|
||||||
#define CS_COMMON_PLATFORMS_PLATFORM_NRF51_H_
|
#define CS_COMMON_PLATFORMS_PLATFORM_NRF51_H_
|
||||||
#if CS_PLATFORM == CS_P_NRF51
|
#if CS_PLATFORM == CS_P_NRF51
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
#ifndef CS_COMMON_PLATFORMS_PLATFORM_NRF52_H_
|
#ifndef CS_COMMON_PLATFORMS_PLATFORM_NRF52_H_
|
||||||
#define CS_COMMON_PLATFORMS_PLATFORM_NRF52_H_
|
#define CS_COMMON_PLATFORMS_PLATFORM_NRF52_H_
|
||||||
#if CS_PLATFORM == CS_P_NRF52
|
#if CS_PLATFORM == CS_P_NRF52
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
#ifndef CS_COMMON_PLATFORMS_PLATFORM_UNIX_H_
|
#ifndef CS_COMMON_PLATFORMS_PLATFORM_UNIX_H_
|
||||||
#define CS_COMMON_PLATFORMS_PLATFORM_UNIX_H_
|
#define CS_COMMON_PLATFORMS_PLATFORM_UNIX_H_
|
||||||
#if CS_PLATFORM == CS_P_UNIX
|
#if CS_PLATFORM == CS_P_UNIX
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
#ifndef CS_COMMON_PLATFORMS_PLATFORM_WINCE_H_
|
#ifndef CS_COMMON_PLATFORMS_PLATFORM_WINCE_H_
|
||||||
#define CS_COMMON_PLATFORMS_PLATFORM_WINCE_H_
|
#define CS_COMMON_PLATFORMS_PLATFORM_WINCE_H_
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
#ifndef CS_COMMON_PLATFORMS_PLATFORM_WINDOWS_H_
|
#ifndef CS_COMMON_PLATFORMS_PLATFORM_WINDOWS_H_
|
||||||
#define CS_COMMON_PLATFORMS_PLATFORM_WINDOWS_H_
|
#define CS_COMMON_PLATFORMS_PLATFORM_WINDOWS_H_
|
||||||
#if CS_PLATFORM == CS_P_WINDOWS
|
#if CS_PLATFORM == CS_P_WINDOWS
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
#if MG_NET_IF == MG_NET_IF_SIMPLELINK && !defined(MG_SIMPLELINK_NO_OSI)
|
#if MG_NET_IF == MG_NET_IF_SIMPLELINK && !defined(MG_SIMPLELINK_NO_OSI)
|
||||||
|
|
||||||
#include "mg_task.h"
|
#include "mg_task.h"
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
/* clang-format off */
|
/* clang-format off */
|
||||||
/*-
|
/*-
|
||||||
* Copyright (c) 1991, 1993
|
* Copyright (c) 1991, 1993
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
#ifndef CS_MONGOOSE_SRC_COMMON_H_
|
#ifndef CS_MONGOOSE_SRC_COMMON_H_
|
||||||
#define CS_MONGOOSE_SRC_COMMON_H_
|
#define CS_MONGOOSE_SRC_COMMON_H_
|
||||||
|
|
||||||
|
|||||||
@@ -22,8 +22,12 @@ extern "C" {
|
|||||||
#define MG_DNS_ANY_RECORD 0xff
|
#define MG_DNS_ANY_RECORD 0xff
|
||||||
#define MG_DNS_NSEC_RECORD 0x2f
|
#define MG_DNS_NSEC_RECORD 0x2f
|
||||||
|
|
||||||
|
#ifndef MG_MAX_DNS_QUESTIONS
|
||||||
#define MG_MAX_DNS_QUESTIONS 32
|
#define MG_MAX_DNS_QUESTIONS 32
|
||||||
|
#endif
|
||||||
|
#ifndef MG_MAX_DNS_ANSWERS
|
||||||
#define MG_MAX_DNS_ANSWERS 32
|
#define MG_MAX_DNS_ANSWERS 32
|
||||||
|
#endif
|
||||||
|
|
||||||
#define MG_DNS_MESSAGE 100 /* High-level DNS message event */
|
#define MG_DNS_MESSAGE 100 /* High-level DNS message event */
|
||||||
|
|
||||||
|
|||||||
+166
-120
@@ -107,18 +107,6 @@ struct mg_http_proto_data_chuncked {
|
|||||||
int64_t body_len; /* How many bytes of chunked body was reassembled. */
|
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 {
|
enum mg_http_multipart_stream_state {
|
||||||
MPS_BEGIN,
|
MPS_BEGIN,
|
||||||
MPS_WAITING_FOR_BOUNDARY,
|
MPS_WAITING_FOR_BOUNDARY,
|
||||||
@@ -223,6 +211,13 @@ static void mg_http_free_proto_data_file(struct mg_http_proto_data_file *d) {
|
|||||||
}
|
}
|
||||||
#endif
|
#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) {
|
static void mg_http_free_proto_data_endpoints(struct mg_http_endpoint **ep) {
|
||||||
struct mg_http_endpoint *current = *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
|
#if MG_ENABLE_FILESYSTEM
|
||||||
static void mg_http_transfer_file_data(struct mg_connection *nc) {
|
static void mg_http_transfer_file_data(struct mg_connection *nc) {
|
||||||
struct mg_http_proto_data *pd = mg_http_get_proto_data(nc);
|
struct mg_http_proto_data *pd = mg_http_get_proto_data(nc);
|
||||||
char buf[MG_MAX_HTTP_SEND_MBUF];
|
size_t to_read = 0, left = (size_t)(pd->file.cl - pd->file.sent);
|
||||||
size_t n = 0, to_read = 0, left = (size_t)(pd->file.cl - pd->file.sent);
|
|
||||||
|
|
||||||
if (pd->file.type == DATA_FILE) {
|
if (pd->file.type == DATA_FILE) {
|
||||||
struct mbuf *io = &nc->send_mbuf;
|
struct mbuf *io = &nc->send_mbuf;
|
||||||
@@ -535,11 +529,16 @@ static void mg_http_transfer_file_data(struct mg_connection *nc) {
|
|||||||
to_read = left;
|
to_read = left;
|
||||||
}
|
}
|
||||||
if (to_read > 0) {
|
if (to_read > 0) {
|
||||||
n = mg_fread(buf, 1, to_read, pd->file.fp);
|
size_t new_size = io->len + to_read;
|
||||||
if (n > 0) {
|
mbuf_resize(io, new_size);
|
||||||
mg_send(nc, buf, n);
|
if (io->size == new_size) {
|
||||||
pd->file.sent += n;
|
char *buf = io->buf + io->len;
|
||||||
DBG(("%p sent %d (total %d)", nc, (int) n, (int) pd->file.sent));
|
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 {
|
} else {
|
||||||
/* Rate-limited */
|
/* 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 mg_http_handler(struct mg_connection *nc, int ev,
|
||||||
void *ev_data MG_UD_ARG(void *user_data)) {
|
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;
|
struct http_message shm, *hm = &shm;
|
||||||
#endif /* __XTENSA__ */
|
#endif
|
||||||
struct mg_http_proto_data *pd = mg_http_get_proto_data(nc);
|
struct mg_http_proto_data *pd = mg_http_get_proto_data(nc);
|
||||||
struct mbuf *io = &nc->recv_mbuf;
|
struct mbuf *io = &nc->recv_mbuf;
|
||||||
int req_len;
|
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);
|
const size_t *msg_lens, uint8_t *digest);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void cs_md5(char buf[33], ...) {
|
static void cs_digest(enum mg_auth_algo algo, char buf[65], ...) {
|
||||||
unsigned char hash[16];
|
unsigned char hash[32];
|
||||||
const uint8_t *msgs[20], *p;
|
const uint8_t *msgs[16], *p;
|
||||||
size_t msg_lens[20];
|
size_t msg_lens[16];
|
||||||
size_t num_msgs = 0;
|
size_t num_msgs = 0;
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
@@ -1874,44 +1860,61 @@ void cs_md5(char buf[33], ...) {
|
|||||||
}
|
}
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
mg_hash_md5_v(num_msgs, msgs, msg_lens, hash);
|
if (algo == MG_AUTH_ALGO_MD5) {
|
||||||
cs_to_hex(buf, hash, sizeof(hash));
|
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,
|
static void mg_mk_digest_resp(const char *method, size_t method_len,
|
||||||
size_t uri_len, const char *ha1, size_t ha1_len,
|
const char *uri, size_t uri_len, const char *ha1,
|
||||||
const char *nonce, size_t nonce_len, const char *nc,
|
size_t ha1_len, const char *nonce,
|
||||||
size_t nc_len, const char *cnonce, size_t cnonce_len,
|
size_t nonce_len, const char *nc, size_t nc_len,
|
||||||
const char *qop, size_t qop_len, char *resp) {
|
const char *cnonce, size_t cnonce_len,
|
||||||
static const char colon[] = ":";
|
const char *qop, size_t qop_len,
|
||||||
static const size_t one = 1;
|
enum mg_auth_algo algo, char *resp) {
|
||||||
char ha2[33];
|
size_t one = 1;
|
||||||
cs_md5(ha2, method, method_len, colon, one, uri, uri_len, NULL);
|
char ha2[65];
|
||||||
cs_md5(resp, ha1, ha1_len, colon, one, nonce, nonce_len, colon, one, nc,
|
size_t ha2_size = (algo == MG_AUTH_ALGO_MD5 ? 32 : 64);
|
||||||
nc_len, colon, one, cnonce, cnonce_len, colon, one, qop, qop_len,
|
cs_digest(algo, ha2, method, method_len, ":", one, uri, uri_len, NULL);
|
||||||
colon, one, ha2, sizeof(ha2) - 1, 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,
|
int mg_http_create_digest_auth_header(char *buf, size_t buf_len,
|
||||||
const char *method, const char *uri,
|
const char *method, const char *uri,
|
||||||
const char *auth_domain, const char *user,
|
const char *auth_domain, const char *user,
|
||||||
const char *passwd, const char *nonce) {
|
const char *passwd, const char *nonce) {
|
||||||
static const char colon[] = ":", qop[] = "auth";
|
return mg_http_create_digest_auth_header_algo(buf, buf_len, method, uri, auth_domain, user, passwd, nonce, MG_AUTH_ALGO_MD5);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1926,15 +1929,17 @@ static int mg_check_nonce(const char *nonce) {
|
|||||||
return (now >= val) && (now - val < 60 * 60);
|
return (now >= val) && (now - val < 60 * 60);
|
||||||
}
|
}
|
||||||
|
|
||||||
int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
|
int mg_http_check_digest_auth_algo(struct http_message *hm, const char *auth_domain,
|
||||||
FILE *fp) {
|
enum mg_auth_algo fp_algo, FILE *fp) {
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
struct mg_str *hdr;
|
struct mg_str *hdr;
|
||||||
char username_buf[50], cnonce_buf[64], response_buf[40], uri_buf[200],
|
char username_buf[50], cnonce_buf[64], response_buf[64], uri_buf[200],
|
||||||
qop_buf[20], nc_buf[20], nonce_buf[16];
|
qop_buf[20], nc_buf[20], nonce_buf[16], algo_buf[16];
|
||||||
|
|
||||||
char *username = username_buf, *cnonce = cnonce_buf, *response = response_buf,
|
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 */
|
/* Parse "Authorization:" header, fail fast on parse error */
|
||||||
if (hm == NULL || fp == NULL ||
|
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;
|
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 */
|
/* 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,
|
hm->method,
|
||||||
mg_mk_str_n(
|
mg_mk_str_n(
|
||||||
hm->uri.p,
|
hm->uri.p,
|
||||||
hm->uri.len + (hm->query_string.len ? hm->query_string.len + 1 : 0)),
|
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(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),
|
mg_mk_str(qop), mg_mk_str(nc), mg_mk_str(nonce), mg_mk_str(auth_domain),
|
||||||
fp);
|
fp_algo, fp);
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
if (username != username_buf) MG_FREE(username);
|
if (username != username_buf) MG_FREE(username);
|
||||||
@@ -1972,17 +1991,23 @@ clean:
|
|||||||
if (qop != qop_buf) MG_FREE(qop);
|
if (qop != qop_buf) MG_FREE(qop);
|
||||||
if (nc != nc_buf) MG_FREE(nc);
|
if (nc != nc_buf) MG_FREE(nc);
|
||||||
if (nonce != nonce_buf) MG_FREE(nonce);
|
if (nonce != nonce_buf) MG_FREE(nonce);
|
||||||
|
if (algo != algo_buf) MG_FREE(algo);
|
||||||
|
|
||||||
return ret;
|
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 username, struct mg_str cnonce,
|
||||||
struct mg_str response, struct mg_str qop,
|
struct mg_str response, struct mg_str qop,
|
||||||
struct mg_str nc, struct mg_str nonce,
|
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 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,
|
* 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(&username, f_user) == 0 &&
|
||||||
mg_vcmp(&auth_domain, f_domain) == 0) {
|
mg_vcmp(&auth_domain, f_domain) == 0) {
|
||||||
/* Username and domain matched, check the password */
|
/* Username and domain matched, check the password */
|
||||||
mg_mkmd5resp(method.p, method.len, uri.p, uri.len, f_ha1, strlen(f_ha1),
|
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,
|
nonce.p, nonce.len, nc.p, nc.len, cnonce.p, cnonce.len,
|
||||||
qop.p, qop.len, exp_resp);
|
qop.p, qop.len, algo, exp_resp);
|
||||||
LOG(LL_DEBUG, ("%.*s %s %.*s %s", (int) username.len, username.p,
|
LOG(LL_DEBUG, ("%.*s %s %.*s %s", (int) username.len, username.p,
|
||||||
f_domain, (int) response.len, response.p, exp_resp));
|
f_domain, (int) response.len, response.p, exp_resp));
|
||||||
return mg_ncasecmp(response.p, exp_resp, strlen(exp_resp)) == 0;
|
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;
|
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,
|
int mg_http_is_authorized(struct http_message *hm, struct mg_str path,
|
||||||
const char *domain, const char *passwords_file,
|
const char *domain, const char *passwords_file,
|
||||||
int flags) {
|
int flags) {
|
||||||
@@ -2015,27 +2050,30 @@ int mg_http_is_authorized(struct http_message *hm, struct mg_str path,
|
|||||||
FILE *fp;
|
FILE *fp;
|
||||||
int authorized = 1;
|
int authorized = 1;
|
||||||
|
|
||||||
if (domain != NULL && passwords_file != NULL) {
|
if (domain == NULL || passwords_file == NULL) {
|
||||||
if (flags & MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE) {
|
return 1;
|
||||||
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) {
|
if (flags & MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE) {
|
||||||
authorized = mg_http_check_digest_auth(hm, domain, fp);
|
fp = mg_fopen(passwords_file, "r");
|
||||||
fclose(fp);
|
} else if (flags & MG_AUTH_FLAG_IS_DIRECTORY) {
|
||||||
} else if (!(flags & MG_AUTH_FLAG_ALLOW_MISSING_FILE)) {
|
snprintf(buf, sizeof(buf), "%.*s%c%s", (int) path.len, path.p, DIRSEP,
|
||||||
authorized = 0;
|
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,
|
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,
|
void mg_http_send_digest_auth_request_algo(struct mg_connection *c,
|
||||||
const char *domain) {
|
const char *domain,
|
||||||
|
enum mg_auth_algo algo) {
|
||||||
mg_printf(c,
|
mg_printf(c,
|
||||||
"HTTP/1.1 401 Unauthorized\r\n"
|
"HTTP/1.1 401 Unauthorized\r\n"
|
||||||
"WWW-Authenticate: Digest qop=\"auth\", "
|
"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",
|
"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,
|
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,
|
hm, mg_mk_str(path), opts->auth_domain, opts->global_auth_file,
|
||||||
((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) |
|
((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) |
|
||||||
MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE |
|
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(
|
!mg_http_is_authorized(
|
||||||
hm, mg_mk_str(path), opts->auth_domain,
|
hm, mg_mk_str(path), opts->auth_domain,
|
||||||
opts->per_directory_auth_file,
|
opts->per_directory_auth_file,
|
||||||
((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) |
|
((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) |
|
||||||
MG_AUTH_FLAG_ALLOW_MISSING_FILE))) {
|
MG_AUTH_FLAG_ALLOW_MISSING_FILE |
|
||||||
mg_http_send_digest_auth_request(nc, opts->auth_domain);
|
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) {
|
} else if (is_cgi) {
|
||||||
#if MG_ENABLE_HTTP_CGI
|
#if MG_ENABLE_HTTP_CGI
|
||||||
mg_handle_cgi(nc, index_file ? index_file : path, path_info, hm, opts);
|
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) |
|
((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) |
|
||||||
MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE |
|
MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE |
|
||||||
MG_AUTH_FLAG_ALLOW_MISSING_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
|
#endif
|
||||||
} else if (!mg_vcmp(&hm->method, "MKCOL")) {
|
} else if (!mg_vcmp(&hm->method, "MKCOL")) {
|
||||||
mg_handle_mkcol(nc, path, hm);
|
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) {
|
if (opts.document_root == NULL) {
|
||||||
opts.document_root = ".";
|
opts.document_root = ".";
|
||||||
}
|
}
|
||||||
if (opts.per_directory_auth_file == NULL) {
|
|
||||||
opts.per_directory_auth_file = ".htpasswd";
|
|
||||||
}
|
|
||||||
if (opts.enable_directory_listing == NULL) {
|
if (opts.enable_directory_listing == NULL) {
|
||||||
opts.enable_directory_listing = "yes";
|
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) {
|
if (opts.auth_domain != NULL && opts.auth_file != NULL) {
|
||||||
new_ep->auth_domain = strdup(opts.auth_domain);
|
new_ep->auth_domain = strdup(opts.auth_domain);
|
||||||
new_ep->auth_file = strdup(opts.auth_file);
|
new_ep->auth_file = strdup(opts.auth_file);
|
||||||
|
new_ep->auth_algo = opts.auth_algo;
|
||||||
}
|
}
|
||||||
new_ep->handler = handler;
|
new_ep->handler = handler;
|
||||||
#if MG_ENABLE_CALLBACK_USERDATA
|
#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 (ep != NULL) {
|
||||||
#if MG_ENABLE_FILESYSTEM && !MG_DISABLE_HTTP_DIGEST_AUTH
|
#if MG_ENABLE_FILESYSTEM && !MG_DISABLE_HTTP_DIGEST_AUTH
|
||||||
if (!mg_http_is_authorized(hm, hm->uri, ep->auth_domain, ep->auth_file,
|
if (!mg_http_is_authorized(hm, hm->uri, ep->auth_domain, ep->auth_file,
|
||||||
MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE)) {
|
(MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE |
|
||||||
mg_http_send_digest_auth_request(nc, ep->auth_domain);
|
MG_AUTH_FLAG_ALGO(ep->auth_algo)))) {
|
||||||
|
mg_http_send_digest_auth_request_algo(nc, ep->auth_domain, ep->auth_algo);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+3
-6
@@ -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);
|
const size_t *msg_lens, uint8_t *digest);
|
||||||
extern void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[],
|
extern void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[],
|
||||||
const size_t *msg_lens, uint8_t *digest);
|
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()`.
|
* 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_DIRECTORY (1 << 0)
|
||||||
#define MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE (1 << 1)
|
#define MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE (1 << 1)
|
||||||
#define MG_AUTH_FLAG_ALLOW_MISSING_FILE (1 << 2)
|
#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
|
* 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,
|
const char *domain, const char *passwords_file,
|
||||||
int flags);
|
int flags);
|
||||||
|
|
||||||
/*
|
|
||||||
* Sends 401 Unauthorized response.
|
|
||||||
*/
|
|
||||||
void mg_http_send_digest_auth_request(struct mg_connection *c,
|
|
||||||
const char *domain);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif /* __cplusplus */
|
#endif /* __cplusplus */
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* === Client API reference
|
* === 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 *method, const char *uri,
|
||||||
const char *auth_domain, const char *user,
|
const char *auth_domain, const char *user,
|
||||||
const char *passwd, const char *nonce);
|
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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* === Server API reference
|
* === 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,
|
int mg_get_http_var(const struct mg_str *buf, const char *name, char *dst,
|
||||||
size_t dst_len);
|
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
|
#if MG_ENABLE_FILESYSTEM
|
||||||
/*
|
/*
|
||||||
* This structure defines how `mg_serve_http()` works.
|
* This structure defines how `mg_serve_http()` works.
|
||||||
@@ -174,6 +183,11 @@ struct mg_serve_http_opts {
|
|||||||
*/
|
*/
|
||||||
const char *global_auth_file;
|
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. */
|
/* Set to "no" to disable directory listing. Enabled by default. */
|
||||||
const char *enable_directory_listing;
|
const char *enable_directory_listing;
|
||||||
|
|
||||||
@@ -425,6 +439,7 @@ struct mg_http_endpoint_opts {
|
|||||||
/* Authorization domain (realm) */
|
/* Authorization domain (realm) */
|
||||||
const char *auth_domain;
|
const char *auth_domain;
|
||||||
const char *auth_file;
|
const char *auth_file;
|
||||||
|
enum mg_auth_algo auth_algo;
|
||||||
};
|
};
|
||||||
|
|
||||||
void mg_register_http_endpoint_opt(struct mg_connection *nc,
|
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,
|
mg_event_handler_t handler,
|
||||||
struct mg_http_endpoint_opts opts);
|
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.
|
* Authenticates a HTTP request against an opened password file.
|
||||||
* Returns 1 if authenticated, 0 otherwise.
|
* Returns 1 if authenticated, 0 otherwise.
|
||||||
*/
|
*/
|
||||||
int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
|
int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
|
||||||
FILE *fp);
|
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.
|
* 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 response, struct mg_str qop,
|
||||||
struct mg_str nc, struct mg_str nonce,
|
struct mg_str nc, struct mg_str nonce,
|
||||||
struct mg_str auth_domain, FILE *fp);
|
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.
|
* Sends buffer `buf` of size `len` to the client using chunked HTTP encoding.
|
||||||
|
|||||||
@@ -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,
|
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 host,
|
||||||
const struct mg_str protocol,
|
const struct mg_str protocol,
|
||||||
const struct mg_str extra_headers,
|
const struct mg_str extra_headers,
|
||||||
@@ -443,6 +443,7 @@ void mg_send_websocket_handshake3v(struct mg_connection *nc,
|
|||||||
if (user.len > 0) {
|
if (user.len > 0) {
|
||||||
mg_basic_auth_header(user, pass, &auth);
|
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
|
* NOTE: the (auth.buf == NULL ? "" : auth.buf) is because cc3200 libc is
|
||||||
|
|||||||
+5
-3
@@ -521,7 +521,6 @@ struct mg_connection *mg_if_accept_new_conn(struct mg_connection *lc) {
|
|||||||
nc->user_data = lc->user_data;
|
nc->user_data = lc->user_data;
|
||||||
nc->recv_mbuf_limit = lc->recv_mbuf_limit;
|
nc->recv_mbuf_limit = lc->recv_mbuf_limit;
|
||||||
nc->iface = lc->iface;
|
nc->iface = lc->iface;
|
||||||
if (lc->flags & MG_F_SSL) nc->flags |= MG_F_SSL;
|
|
||||||
mg_add_conn(nc->mgr, nc);
|
mg_add_conn(nc->mgr, nc);
|
||||||
LOG(LL_DEBUG, ("%p %p %d %#x", lc, nc, (int) nc->sock, (int) nc->flags));
|
LOG(LL_DEBUG, ("%p %p %d %#x", lc, nc, (int) nc->sock, (int) nc->flags));
|
||||||
return nc;
|
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);
|
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) {
|
if (nc != NULL) {
|
||||||
DBG(("%p <- %d bytes from %s:%d", nc, n, inet_ntoa(nc->sa.sin.sin_addr),
|
DBG(("%p <- %d bytes from %s:%d ia %#x", nc, n,
|
||||||
ntohs(nc->sa.sin.sin_port)));
|
inet_ntoa(nc->sa.sin.sin_addr), ntohs(nc->sa.sin.sin_port),
|
||||||
|
(int) (intptr_t) nc->priv_2));
|
||||||
if (nc == lc) {
|
if (nc == lc) {
|
||||||
nc->recv_mbuf.len += n;
|
nc->recv_mbuf.len += n;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -393,6 +393,15 @@ struct mg_connect_opts {
|
|||||||
*/
|
*/
|
||||||
const char *ssl_psk_identity;
|
const char *ssl_psk_identity;
|
||||||
const char *ssl_psk_key;
|
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
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
#include "mg_net_if.h"
|
#include "mg_net_if.h"
|
||||||
#include "mg_internal.h"
|
#include "mg_internal.h"
|
||||||
#include "mg_net_if_socket.h"
|
#include "mg_net_if_socket.h"
|
||||||
|
|||||||
+7
-2
@@ -60,11 +60,16 @@ size_t mg_fwrite(const void *ptr, size_t size, size_t count, FILE *f) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
void mg_base64_encode(const unsigned char *src, int src_len, char *dst) {
|
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) {
|
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
|
#if MG_ENABLE_THREADS
|
||||||
|
|||||||
+5
-3
@@ -16,9 +16,11 @@ $ tools/unamalgam.py mongoose.c
|
|||||||
This produces directories and files under `mongoose/` and `common/` that are easeier to work with.
|
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:
|
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
@@ -3,4 +3,4 @@
|
|||||||
set -x -e
|
set -x -e
|
||||||
|
|
||||||
tools/amalgam.py --prefix=MG --license=LICENSE $(cat src/mongoose.h.manifest) > mongoose.h
|
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
|
||||||
@@ -33,6 +33,7 @@ with open(fname) as f:
|
|||||||
|
|
||||||
# open next file for writing
|
# open next file for writing
|
||||||
ofile = open(cur_src, "w")
|
ofile = open(cur_src, "w")
|
||||||
|
ofile.write('\n')
|
||||||
print('=> %s' % cur_src, file=sys.stderr)
|
print('=> %s' % cur_src, file=sys.stderr)
|
||||||
manifest.append(cur_src)
|
manifest.append(cur_src)
|
||||||
next(f)
|
next(f)
|
||||||
|
|||||||
Executable
+6
@@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e -x
|
||||||
|
|
||||||
|
tools/unamalgam.py mongoose.h
|
||||||
|
tools/unamalgam.py mongoose.c
|
||||||
Reference in new issue
Block a user