diff --git a/APP_Framework/Applications/app_test/test_socket.c b/APP_Framework/Applications/app_test/test_socket.c index 2d4d10be9..d717ba251 100644 --- a/APP_Framework/Applications/app_test/test_socket.c +++ b/APP_Framework/Applications/app_test/test_socket.c @@ -76,10 +76,12 @@ struct IperfParam { static void* TestIperfServer(void* param) { struct IperfParam* iperf_param = (struct IperfParam*)param; - int sock = socket(AF_INET, SOCK_STREAM, 0); + int sock = socket(AF_INET, SOCK_STREAM, 6); if (sock < 0) { printf("[%s] Err: Can't create socker.\n", __func__); return NULL; + } else { + printf("[%s] Info Create server socket %d\n", __func__, sock); } uint8_t* recv_data = (uint8_t*)malloc(IPERF_BUFSZ); @@ -121,8 +123,9 @@ static void* TestIperfServer(void* param) socklen_t sin_size = sizeof(struct sockaddr_in); struct sockaddr_in client_addr; int connection = accept(sock, (struct sockaddr*)&client_addr, &sin_size); - printf("[%s] Info: New client connected from (%s, %d)\n", __func__, - inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); + printf("[%s] Info: New client connected from (%s, %d), connect: %d\n", __func__, + inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), + connection); int flag = 1; setsockopt(connection, @@ -141,8 +144,8 @@ static void* TestIperfServer(void* param) inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); break; } else if (bytes_received < 0) { - KPrintf("recv error, client: (%s, %d)\n", - inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); + KPrintf("recv error: %d, client: (%s, %d)\n", + bytes_received, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); break; } @@ -258,8 +261,6 @@ enum IperfParamEnum { void TestSocket(int argc, char* argv[]) { - lwip_config_tcp(0, lwip_ipaddr, lwip_netmask, lwip_gwaddr); - static char usage_info[] = "Run either a iperf server or iperf client."; static char program_info[] = "Lwip socket test task, a simple iperf."; static const char* const usages[] = { diff --git a/APP_Framework/Applications/mongoose/Makefile b/APP_Framework/Applications/mongoose/Makefile index 12fd727dd..40f525b5e 100644 --- a/APP_Framework/Applications/mongoose/Makefile +++ b/APP_Framework/Applications/mongoose/Makefile @@ -1,3 +1,3 @@ -SRC_FILES += mongoose.c netsetting.c project.c +SRC_FILES += project.c include $(KERNEL_ROOT)/compiler.mk \ No newline at end of file diff --git a/APP_Framework/Applications/mongoose/lib/Makefile b/APP_Framework/Applications/mongoose/lib/Makefile new file mode 100644 index 000000000..0824ed779 --- /dev/null +++ b/APP_Framework/Applications/mongoose/lib/Makefile @@ -0,0 +1,3 @@ +SRC_FILES += mongoose.c + +include $(KERNEL_ROOT)/compiler.mk \ No newline at end of file diff --git a/APP_Framework/Applications/mongoose/mongoose.a b/APP_Framework/Applications/mongoose/mongoose.a new file mode 100644 index 000000000..fd5000247 Binary files /dev/null and b/APP_Framework/Applications/mongoose/mongoose.a differ diff --git a/APP_Framework/Applications/mongoose/mongoose.c b/APP_Framework/Applications/mongoose/mongoose.c deleted file mode 100644 index b4323b0f1..000000000 --- a/APP_Framework/Applications/mongoose/mongoose.c +++ /dev/null @@ -1,10610 +0,0 @@ -// Copyright (c) 2004-2013 Sergey Lyubka -// Copyright (c) 2013-2022 Cesanta Software Limited -// All rights reserved -// -// This software is dual-licensed: you can redistribute it and/or modify -// it under the terms of the GNU General Public License version 2 as -// published by the Free Software Foundation. For the terms of this -// license, see http://www.gnu.org/licenses/ -// -// You are free to use this software under the terms of the GNU General -// Public License, but WITHOUT ANY WARRANTY; without even the implied -// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU General Public License for more details. -// -// Alternatively, you can license this software under a commercial -// license, as set out in https://www.mongoose.ws/licensing/ -// -// SPDX-License-Identifier: GPL-2.0-only or commercial - -#include "mongoose.h" - -#ifdef MG_ENABLE_LINES -#line 1 "src/base64.c" -#endif - -static int mg_base64_encode_single(int c) -{ - if (c < 26) { - return c + 'A'; - } else if (c < 52) { - return c - 26 + 'a'; - } else if (c < 62) { - return c - 52 + '0'; - } else { - return c == 62 ? '+' : '/'; - } -} - -static int mg_base64_decode_single(int c) -{ - if (c >= 'A' && c <= 'Z') { - return c - 'A'; - } else if (c >= 'a' && c <= 'z') { - return c + 26 - 'a'; - } else if (c >= '0' && c <= '9') { - return c + 52 - '0'; - } else if (c == '+') { - return 62; - } else if (c == '/') { - return 63; - } else if (c == '=') { - return 64; - } else { - return -1; - } -} - -size_t mg_base64_update(unsigned char ch, char* to, size_t n) -{ - unsigned long rem = (n & 3) % 3; - if (rem == 0) { - to[n] = (char)mg_base64_encode_single(ch >> 2); - to[++n] = (char)((ch & 3) << 4); - } else if (rem == 1) { - to[n] = (char)mg_base64_encode_single(to[n] | (ch >> 4)); - to[++n] = (char)((ch & 15) << 2); - } else { - to[n] = (char)mg_base64_encode_single(to[n] | (ch >> 6)); - to[++n] = (char)mg_base64_encode_single(ch & 63); - n++; - } - return n; -} - -size_t mg_base64_final(char* to, size_t n) -{ - size_t saved = n; - // printf("---[%.*s]\n", n, to); - if (n & 3) - n = mg_base64_update(0, to, n); - if ((saved & 3) == 2) - n--; - // printf(" %d[%.*s]\n", n, n, to); - while (n & 3) - to[n++] = '='; - to[n] = '\0'; - return n; -} - -size_t mg_base64_encode(const unsigned char* p, size_t n, char* to, size_t dl) -{ - size_t i, len = 0; - if (dl > 0) - to[0] = '\0'; - if (dl < ((n / 3) + (n % 3 ? 1 : 0)) * 4 + 1) - return 0; - for (i = 0; i < n; i++) - len = mg_base64_update(p[i], to, len); - len = mg_base64_final(to, len); - return len; -} - -size_t mg_base64_decode(const char* src, size_t n, char* dst, size_t dl) -{ - const char* end = src == NULL ? NULL : src + n; // Cannot add to NULL - size_t len = 0; - if (dl > 0) - dst[0] = '\0'; - if (dl < n / 4 * 3 + 1) - return 0; - while (src != NULL && src + 3 < end) { - int a = mg_base64_decode_single(src[0]), - b = mg_base64_decode_single(src[1]), - c = mg_base64_decode_single(src[2]), - d = mg_base64_decode_single(src[3]); - if (a == 64 || a < 0 || b == 64 || b < 0 || c < 0 || d < 0) - return 0; - dst[len++] = (char)((a << 2) | (b >> 4)); - if (src[2] != '=') { - dst[len++] = (char)((b << 4) | (c >> 2)); - if (src[3] != '=') - dst[len++] = (char)((c << 6) | d); - } - src += 4; - } - dst[len] = '\0'; - return len; -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/dns.c" -#endif - -struct dns_data { - struct dns_data* next; - struct mg_connection* c; - uint64_t expire; - uint16_t txnid; -}; - -static void mg_sendnsreq(struct mg_connection*, struct mg_str*, int, - struct mg_dns*, bool); - -static void mg_dns_free(struct dns_data** head, struct dns_data* d) -{ - LIST_DELETE(struct dns_data, head, d); - free(d); -} - -void mg_resolve_cancel(struct mg_connection* c) -{ - struct dns_data *tmp, *d; - struct dns_data** head = (struct dns_data**)&c->mgr->active_dns_requests; - for (d = *head; d != NULL; d = tmp) { - tmp = d->next; - if (d->c == c) - mg_dns_free(head, d); - } -} - -static size_t mg_dns_parse_name_depth(const uint8_t* s, size_t len, size_t ofs, - char* to, size_t tolen, size_t j, - int depth) -{ - size_t i = 0; - if (tolen > 0 && depth == 0) - to[0] = '\0'; - if (depth > 5) - return 0; - // MG_INFO(("ofs %lx %x %x", (unsigned long) ofs, s[ofs], s[ofs + 1])); - while (ofs + i + 1 < len) { - size_t n = s[ofs + i]; - if (n == 0) { - i++; - break; - } - if (n & 0xc0) { - size_t ptr = (((n & 0x3f) << 8) | s[ofs + i + 1]); // 12 is hdr len - // MG_INFO(("PTR %lx", (unsigned long) ptr)); - if (ptr + 1 < len && (s[ptr] & 0xc0) == 0 && mg_dns_parse_name_depth(s, len, ptr, to, tolen, j, depth + 1) == 0) - return 0; - i += 2; - break; - } - if (ofs + i + n + 1 >= len) - return 0; - if (j > 0) { - if (j < tolen) - to[j] = '.'; - j++; - } - if (j + n < tolen) - memcpy(&to[j], &s[ofs + i + 1], n); - j += n; - i += n + 1; - if (j < tolen) - to[j] = '\0'; // Zero-terminate this chunk - // MG_INFO(("--> [%s]", to)); - } - if (tolen > 0) - to[tolen - 1] = '\0'; // Make sure make sure it is nul-term - return i; -} - -static size_t mg_dns_parse_name(const uint8_t* s, size_t n, size_t ofs, - char* dst, size_t dstlen) -{ - return mg_dns_parse_name_depth(s, n, ofs, dst, dstlen, 0, 0); -} - -size_t mg_dns_parse_rr(const uint8_t* buf, size_t len, size_t ofs, - bool is_question, struct mg_dns_rr* rr) -{ - const uint8_t *s = buf + ofs, *e = &buf[len]; - - memset(rr, 0, sizeof(*rr)); - if (len < sizeof(struct mg_dns_header)) - return 0; // Too small - if (len > 512) - return 0; // Too large, we don't expect that - if (s >= e) - return 0; // Overflow - - if ((rr->nlen = (uint16_t)mg_dns_parse_name(buf, len, ofs, NULL, 0)) == 0) - return 0; - s += rr->nlen + 4; - if (s > e) - return 0; - rr->atype = (uint16_t)(((uint16_t)s[-4] << 8) | s[-3]); - rr->aclass = (uint16_t)(((uint16_t)s[-2] << 8) | s[-1]); - if (is_question) - return (size_t)(rr->nlen + 4); - - s += 6; - if (s > e) - return 0; - rr->alen = (uint16_t)(((uint16_t)s[-2] << 8) | s[-1]); - if (s + rr->alen > e) - return 0; - return (size_t)(rr->nlen + rr->alen + 10); -} - -bool mg_dns_parse(const uint8_t* buf, size_t len, struct mg_dns_message* dm) -{ - const struct mg_dns_header* h = (struct mg_dns_header*)buf; - struct mg_dns_rr rr; - size_t i, n, ofs = sizeof(*h); - memset(dm, 0, sizeof(*dm)); - - if (len < sizeof(*h)) - return 0; // Too small, headers dont fit - if (mg_ntohs(h->num_questions) > 1) - return 0; // Sanity - if (mg_ntohs(h->num_answers) > 10) - return 0; // Sanity - dm->txnid = mg_ntohs(h->txnid); - - for (i = 0; i < mg_ntohs(h->num_questions); i++) { - if ((n = mg_dns_parse_rr(buf, len, ofs, true, &rr)) == 0) - return false; - // MG_INFO(("Q %lu %lu %hu/%hu", ofs, n, rr.atype, rr.aclass)); - ofs += n; - } - for (i = 0; i < mg_ntohs(h->num_answers); i++) { - if ((n = mg_dns_parse_rr(buf, len, ofs, false, &rr)) == 0) - return false; - // MG_INFO(("A -- %lu %lu %hu/%hu %s", ofs, n, rr.atype, rr.aclass, - // dm->name)); - mg_dns_parse_name(buf, len, ofs, dm->name, sizeof(dm->name)); - ofs += n; - - if (rr.alen == 4 && rr.atype == 1 && rr.aclass == 1) { - dm->addr.is_ip6 = false; - memcpy(&dm->addr.ip, &buf[ofs - 4], 4); - dm->resolved = true; - break; // Return success - } else if (rr.alen == 16 && rr.atype == 28 && rr.aclass == 1) { - dm->addr.is_ip6 = true; - memcpy(&dm->addr.ip, &buf[ofs - 16], 16); - dm->resolved = true; - break; // Return success - } - } - return true; -} - -static void dns_cb(struct mg_connection* c, int ev, void* ev_data, - void* fn_data) -{ - struct dns_data *d, *tmp; - struct dns_data** head = (struct dns_data**)&c->mgr->active_dns_requests; - if (ev == MG_EV_POLL) { - uint64_t now = *(uint64_t*)ev_data; - for (d = *head; d != NULL; d = tmp) { - tmp = d->next; - // MG_DEBUG ("%lu %lu dns poll", d->expire, now)); - if (now > d->expire) - mg_error(d->c, "DNS timeout"); - } - } else if (ev == MG_EV_READ) { - struct mg_dns_message dm; - int resolved = 0; - if (mg_dns_parse(c->recv.buf, c->recv.len, &dm) == false) { - MG_ERROR(("Unexpected DNS response:")); - mg_hexdump(c->recv.buf, c->recv.len); - } else { - // MG_VERBOSE(("%s %d", dm.name, dm.resolved)); - for (d = *head; d != NULL; d = tmp) { - tmp = d->next; - // MG_INFO(("d %p %hu %hu", d, d->txnid, dm.txnid)); - if (dm.txnid != d->txnid) - continue; - if (d->c->is_resolving) { - if (dm.resolved) { - dm.addr.port = d->c->rem.port; // Save port - d->c->rem = dm.addr; // Copy resolved address - MG_DEBUG( - ("%lu %s is %M", d->c->id, dm.name, mg_print_ip, &d->c->rem)); - mg_connect_resolved(d->c); -#if MG_ENABLE_IPV6 - } else if (dm.addr.is_ip6 == false && dm.name[0] != '\0' && c->mgr->use_dns6 == false) { - struct mg_str x = mg_str(dm.name); - mg_sendnsreq(d->c, &x, c->mgr->dnstimeout, &c->mgr->dns6, true); -#endif - } else { - mg_error(d->c, "%s DNS lookup failed", dm.name); - } - } else { - MG_ERROR(("%lu already resolved", d->c->id)); - } - mg_dns_free(head, d); - resolved = 1; - } - } - if (!resolved) - MG_ERROR(("stray DNS reply")); - c->recv.len = 0; - } else if (ev == MG_EV_CLOSE) { - for (d = *head; d != NULL; d = tmp) { - tmp = d->next; - mg_error(d->c, "DNS error"); - mg_dns_free(head, d); - } - } - (void)fn_data; -} - -static bool mg_dns_send(struct mg_connection* c, const struct mg_str* name, - uint16_t txnid, bool ipv6) -{ - struct { - struct mg_dns_header header; - uint8_t data[256]; - } pkt; - size_t i, n; - memset(&pkt, 0, sizeof(pkt)); - pkt.header.txnid = mg_htons(txnid); - pkt.header.flags = mg_htons(0x100); - pkt.header.num_questions = mg_htons(1); - for (i = n = 0; i < sizeof(pkt.data) - 5; i++) { - if (name->ptr[i] == '.' || i >= name->len) { - pkt.data[n] = (uint8_t)(i - n); - memcpy(&pkt.data[n + 1], name->ptr + n, i - n); - n = i + 1; - } - if (i >= name->len) - break; - } - memcpy(&pkt.data[n], "\x00\x00\x01\x00\x01", 5); // A query - n += 5; - if (ipv6) - pkt.data[n - 3] = 0x1c; // AAAA query - // memcpy(&pkt.data[n], "\xc0\x0c\x00\x1c\x00\x01", 6); // AAAA query - // n += 6; - return mg_send(c, &pkt, sizeof(pkt.header) + n); -} - -static void mg_sendnsreq(struct mg_connection* c, struct mg_str* name, int ms, - struct mg_dns* dnsc, bool ipv6) -{ - struct dns_data* d = NULL; - if (dnsc->url == NULL) { - mg_error(c, "DNS server URL is NULL. Call mg_mgr_init()"); - } else if (dnsc->c == NULL) { - dnsc->c = mg_connect(c->mgr, dnsc->url, NULL, NULL); - if (dnsc->c != NULL) { - dnsc->c->pfn = dns_cb; - // dnsc->c->is_hexdumping = 1; - } - } - if (dnsc->c == NULL) { - mg_error(c, "resolver"); - } else if ((d = (struct dns_data*)calloc(1, sizeof(*d))) == NULL) { - mg_error(c, "resolve OOM"); - } else { - struct dns_data* reqs = (struct dns_data*)c->mgr->active_dns_requests; - d->txnid = reqs ? (uint16_t)(reqs->txnid + 1) : 1; - d->next = (struct dns_data*)c->mgr->active_dns_requests; - c->mgr->active_dns_requests = d; - d->expire = mg_millis() + (uint64_t)ms; - d->c = c; - c->is_resolving = 1; - MG_VERBOSE(("%lu resolving %.*s @ %s, txnid %hu", c->id, (int)name->len, - name->ptr, dnsc->url, d->txnid)); - if (!mg_dns_send(dnsc->c, name, d->txnid, ipv6)) { - mg_error(dnsc->c, "DNS send"); - } - } -} - -void mg_resolve(struct mg_connection* c, const char* url) -{ - struct mg_str host = mg_url_host(url); - c->rem.port = mg_htons(mg_url_port(url)); - if (mg_aton(host, &c->rem)) { - // host is an IP address, do not fire name resolution - mg_connect_resolved(c); - } else { - // host is not an IP, send DNS resolution request - struct mg_dns* dns = c->mgr->use_dns6 ? &c->mgr->dns6 : &c->mgr->dns4; - mg_sendnsreq(c, &host, c->mgr->dnstimeout, dns, c->mgr->use_dns6); - } -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/event.c" -#endif - -void mg_call(struct mg_connection* c, int ev, void* ev_data) -{ - // Run user-defined handler first, in order to give it an ability - // to intercept processing (e.g. clean input buffer) before the - // protocol handler kicks in - if (c->fn != NULL) - c->fn(c, ev, ev_data, c->fn_data); - if (c->pfn != NULL) - c->pfn(c, ev, ev_data, c->pfn_data); -} - -void mg_error(struct mg_connection* c, const char* fmt, ...) -{ - char buf[64]; - va_list ap; - va_start(ap, fmt); - mg_vsnprintf(buf, sizeof(buf), fmt, &ap); - va_end(ap); - MG_ERROR(("%lu %ld %s", c->id, c->fd, buf)); - c->is_closing = 1; // Set is_closing before sending MG_EV_CALL - mg_call(c, MG_EV_ERROR, buf); // Let user handler to override it -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/fmt.c" -#endif - -static bool is_digit(int c) -{ - return c >= '0' && c <= '9'; -} - -static int addexp(char* buf, int e, int sign) -{ - int n = 0; - buf[n++] = 'e'; - buf[n++] = (char)sign; - if (e > 400) - return 0; - if (e < 10) - buf[n++] = '0'; - if (e >= 100) - buf[n++] = (char)(e / 100 + '0'), e -= 100 * (e / 100); - if (e >= 10) - buf[n++] = (char)(e / 10 + '0'), e -= 10 * (e / 10); - buf[n++] = (char)(e + '0'); - return n; -} - -static int xisinf(double x) -{ - union { - double f; - uint64_t u; - } ieee754 = { x }; - return ((unsigned)(ieee754.u >> 32) & 0x7fffffff) == 0x7ff00000 && ((unsigned)ieee754.u == 0); -} - -static int xisnan(double x) -{ - union { - double f; - uint64_t u; - } ieee754 = { x }; - return ((unsigned)(ieee754.u >> 32) & 0x7fffffff) + ((unsigned)ieee754.u != 0) > 0x7ff00000; -} - -static size_t mg_dtoa(char* dst, size_t dstlen, double d, int width, bool tz) -{ - char buf[40]; - int i, s = 0, n = 0, e = 0; - double t, mul, saved; - if (d == 0.0) - return mg_snprintf(dst, dstlen, "%s", "0"); - if (xisinf(d)) - return mg_snprintf(dst, dstlen, "%s", d > 0 ? "inf" : "-inf"); - if (xisnan(d)) - return mg_snprintf(dst, dstlen, "%s", "nan"); - if (d < 0.0) - d = -d, buf[s++] = '-'; - - // Round - saved = d; - mul = 1.0; - while (d >= 10.0 && d / mul >= 10.0) - mul *= 10.0; - while (d <= 1.0 && d / mul <= 1.0) - mul /= 10.0; - for (i = 0, t = mul * 5; i < width; i++) - t /= 10.0; - d += t; - // Calculate exponent, and 'mul' for scientific representation - mul = 1.0; - while (d >= 10.0 && d / mul >= 10.0) - mul *= 10.0, e++; - while (d < 1.0 && d / mul < 1.0) - mul /= 10.0, e--; - // printf(" --> %g %d %g %g\n", saved, e, t, mul); - - if (e >= width && width > 1) { - n = (int)mg_dtoa(buf, sizeof(buf), saved / mul, width, tz); - // printf(" --> %.*g %d [%.*s]\n", 10, d / t, e, n, buf); - n += addexp(buf + s + n, e, '+'); - return mg_snprintf(dst, dstlen, "%.*s", n, buf); - } else if (e <= -width && width > 1) { - n = (int)mg_dtoa(buf, sizeof(buf), saved / mul, width, tz); - // printf(" --> %.*g %d [%.*s]\n", 10, d / mul, e, n, buf); - n += addexp(buf + s + n, -e, '-'); - return mg_snprintf(dst, dstlen, "%.*s", n, buf); - } else { - for (i = 0, t = mul; t >= 1.0 && s + n < (int)sizeof(buf); i++) { - int ch = (int)(d / t); - if (n > 0 || ch > 0) - buf[s + n++] = (char)(ch + '0'); - d -= ch * t; - t /= 10.0; - } - // printf(" --> [%g] -> %g %g (%d) [%.*s]\n", saved, d, t, n, s + n, buf); - if (n == 0) - buf[s++] = '0'; - while (t >= 1.0 && n + s < (int)sizeof(buf)) - buf[n++] = '0', t /= 10.0; - if (s + n < (int)sizeof(buf)) - buf[n + s++] = '.'; - // printf(" 1--> [%g] -> [%.*s]\n", saved, s + n, buf); - for (i = 0, t = 0.1; s + n < (int)sizeof(buf) && n < width; i++) { - int ch = (int)(d / t); - buf[s + n++] = (char)(ch + '0'); - d -= ch * t; - t /= 10.0; - } - } - while (tz && n > 0 && buf[s + n - 1] == '0') - n--; // Trim trailing zeroes - if (n > 0 && buf[s + n - 1] == '.') - n--; // Trim trailing dot - n += s; - if (n >= (int)sizeof(buf)) - n = (int)sizeof(buf) - 1; - buf[n] = '\0'; - return mg_snprintf(dst, dstlen, "%s", buf); -} - -static size_t mg_lld(char* buf, int64_t val, bool is_signed, bool is_hex) -{ - const char* letters = "0123456789abcdef"; - uint64_t v = (uint64_t)val; - size_t s = 0, n, i; - if (is_signed && val < 0) - buf[s++] = '-', v = (uint64_t)(-val); - // This loop prints a number in reverse order. I guess this is because we - // write numbers from right to left: least significant digit comes last. - // Maybe because we use Arabic numbers, and Arabs write RTL? - if (is_hex) { - for (n = 0; v; v >>= 4) - buf[s + n++] = letters[v & 15]; - } else { - for (n = 0; v; v /= 10) - buf[s + n++] = letters[v % 10]; - } - // Reverse a string - for (i = 0; i < n / 2; i++) { - char t = buf[s + i]; - buf[s + i] = buf[s + n - i - 1], buf[s + n - i - 1] = t; - } - if (val == 0) - buf[n++] = '0'; // Handle special case - return n + s; -} - -static size_t scpy(void (*out)(char, void*), void* ptr, char* buf, - size_t len) -{ - size_t i = 0; - while (i < len && buf[i] != '\0') - out(buf[i++], ptr); - return i; -} - -size_t mg_xprintf(void (*out)(char, void*), void* ptr, const char* fmt, ...) -{ - size_t len = 0; - va_list ap; - va_start(ap, fmt); - len = mg_vxprintf(out, ptr, fmt, &ap); - va_end(ap); - return len; -} - -size_t mg_vxprintf(void (*out)(char, void*), void* param, const char* fmt, - va_list* ap) -{ - size_t i = 0, n = 0; - while (fmt[i] != '\0') { - if (fmt[i] == '%') { - size_t j, k, x = 0, is_long = 0, w = 0 /* width */, pr = ~0U /* prec */; - char pad = ' ', minus = 0, c = fmt[++i]; - if (c == '#') - x++, c = fmt[++i]; - if (c == '-') - minus++, c = fmt[++i]; - if (c == '0') - pad = '0', c = fmt[++i]; - while (is_digit(c)) - w *= 10, w += (size_t)(c - '0'), c = fmt[++i]; - if (c == '.') { - c = fmt[++i]; - if (c == '*') { - pr = (size_t)va_arg(*ap, int); - c = fmt[++i]; - } else { - pr = 0; - while (is_digit(c)) - pr *= 10, pr += (size_t)(c - '0'), c = fmt[++i]; - } - } - while (c == 'h') - c = fmt[++i]; // Treat h and hh as int - if (c == 'l') { - is_long++, c = fmt[++i]; - if (c == 'l') - is_long++, c = fmt[++i]; - } - if (c == 'p') - x = 1, is_long = 1; - if (c == 'd' || c == 'u' || c == 'x' || c == 'X' || c == 'p' || c == 'g' || c == 'f') { - bool s = (c == 'd'), h = (c == 'x' || c == 'X' || c == 'p'); - char tmp[40]; - size_t xl = x ? 2 : 0; - if (c == 'g' || c == 'f') { - double v = va_arg(*ap, double); - if (pr == ~0U) - pr = 6; - k = mg_dtoa(tmp, sizeof(tmp), v, (int)pr, c == 'g'); - } else if (is_long == 2) { - int64_t v = va_arg(*ap, int64_t); - k = mg_lld(tmp, v, s, h); - } else if (is_long == 1) { - long v = va_arg(*ap, long); - k = mg_lld(tmp, s ? (int64_t)v : (int64_t)(unsigned long)v, s, h); - } else { - int v = va_arg(*ap, int); - k = mg_lld(tmp, s ? (int64_t)v : (int64_t)(unsigned)v, s, h); - } - for (j = 0; j < xl && w > 0; j++) - w--; - for (j = 0; pad == ' ' && !minus && k < w && j + k < w; j++) - n += scpy(out, param, &pad, 1); - n += scpy(out, param, (char*)"0x", xl); - for (j = 0; pad == '0' && k < w && j + k < w; j++) - n += scpy(out, param, &pad, 1); - n += scpy(out, param, tmp, k); - for (j = 0; pad == ' ' && minus && k < w && j + k < w; j++) - n += scpy(out, param, &pad, 1); - } else if (c == 'm' || c == 'M') { - mg_pm_t f = va_arg(*ap, mg_pm_t); - if (c == 'm') - out('"', param); - n += f(out, param, ap); - if (c == 'm') - n += 2, out('"', param); - } else if (c == 'c') { - int ch = va_arg(*ap, int); - out((char)ch, param); - n++; - } else if (c == 's') { - char* p = va_arg(*ap, char*); - if (pr == ~0U) - pr = p == NULL ? 0 : strlen(p); - for (j = 0; !minus && pr < w && j + pr < w; j++) - n += scpy(out, param, &pad, 1); - n += scpy(out, param, p, pr); - for (j = 0; minus && pr < w && j + pr < w; j++) - n += scpy(out, param, &pad, 1); - } else if (c == '%') { - out('%', param); - n++; - } else { - out('%', param); - out(c, param); - n += 2; - } - i++; - } else { - out(fmt[i], param), n++, i++; - } - } - return n; -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/fs.c" -#endif - -struct mg_fd* mg_fs_open(struct mg_fs* fs, const char* path, int flags) -{ - struct mg_fd* fd = (struct mg_fd*)calloc(1, sizeof(*fd)); - if (fd != NULL) { - fd->fd = calloc(1, sizeof(int)); - *(int*)fd->fd = PrivOpen(path, 0); - MG_DEBUG(("open path: %s, fd: %d", path, *(int*)fd->fd)); - fd->fs = fs; - if (fd->fd == NULL) { - free(fd); - fd = NULL; - } - } - return fd; -} - -void mg_fs_close(struct mg_fd* fd) -{ - if (fd != NULL) { - fd->fs->cl(fd->fd); - free(fd); - } -} - -char* mg_file_read(struct mg_fs* fs, const char* path, size_t* sizep) -{ - struct mg_fd* fd; - char* data = NULL; - size_t size = 0; - fs->st(path, &size, NULL); - if ((fd = mg_fs_open(fs, path, MG_FS_READ)) != NULL) { - data = (char*)calloc(1, size + 1); - if (data != NULL) { - if (fs->rd(fd->fd, data, size) != size) { - free(data); - data = NULL; - } else { - data[size] = '\0'; - if (sizep != NULL) - *sizep = size; - } - } - mg_fs_close(fd); - } - return data; -} - -bool mg_file_write(struct mg_fs* fs, const char* path, const void* buf, - size_t len) -{ - bool result = false; - struct mg_fd* fd; - char tmp[MG_PATH_MAX]; - mg_snprintf(tmp, sizeof(tmp), "%s..%d", path, rand()); - if ((fd = mg_fs_open(fs, tmp, MG_FS_WRITE)) != NULL) { - result = fs->wr(fd->fd, buf, len) == len; - mg_fs_close(fd); - if (result) { - fs->rm(path); - fs->mv(tmp, path); - } else { - fs->rm(tmp); - } - } - return result; -} - -bool mg_file_printf(struct mg_fs* fs, const char* path, const char* fmt, ...) -{ - va_list ap; - char* data; - bool result = false; - va_start(ap, fmt); - data = mg_vmprintf(fmt, &ap); - va_end(ap); - result = mg_file_write(fs, path, data, strlen(data)); - free(data); - return result; -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/fs_fat.c" -#endif - -#if MG_ENABLE_FATFS -#include - -static int mg_days_from_epoch(int y, int m, int d) -{ - y -= m <= 2; - int era = y / 400; - int yoe = y - era * 400; - int doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1; - int doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; - return era * 146097 + doe - 719468; -} - -static time_t mg_timegm(const struct tm* t) -{ - int year = t->tm_year + 1900; - int month = t->tm_mon; // 0-11 - if (month > 11) { - year += month / 12; - month %= 12; - } else if (month < 0) { - int years_diff = (11 - month) / 12; - year -= years_diff; - month += 12 * years_diff; - } - int x = mg_days_from_epoch(year, month + 1, t->tm_mday); - return 60 * (60 * (24L * x + t->tm_hour) + t->tm_min) + t->tm_sec; -} - -static time_t ff_time_to_epoch(uint16_t fdate, uint16_t ftime) -{ - struct tm tm; - memset(&tm, 0, sizeof(struct tm)); - tm.tm_sec = (ftime << 1) & 0x3e; - tm.tm_min = ((ftime >> 5) & 0x3f); - tm.tm_hour = ((ftime >> 11) & 0x1f); - tm.tm_mday = (fdate & 0x1f); - tm.tm_mon = ((fdate >> 5) & 0x0f) - 1; - tm.tm_year = ((fdate >> 9) & 0x7f) + 80; - return mg_timegm(&tm); -} - -static int ff_stat(const char* path, size_t* size, time_t* mtime) -{ - MG_DEBUG(("%s fatstat for %s", __func__, path)); - FILINFO fi; - if (path[0] == '\0') { - if (size) - *size = 0; - if (mtime) - *mtime = 0; - return MG_FS_DIR; - } else if (f_stat(path, &fi) == 0) { - if (size) - *size = (size_t)fi.fsize; - if (mtime) - *mtime = ff_time_to_epoch(fi.fdate, fi.ftime); - MG_DEBUG(("%s successful fatstat, file size: %u", __func__, *size)); - return MG_FS_READ | MG_FS_WRITE | ((fi.fattrib & AM_DIR) ? MG_FS_DIR : 0); - } else { - return 0; - } -} - -static void ff_list(const char* dir, void (*fn)(const char*, void*), - void* userdata) -{ - DIR d; - FILINFO fi; - if (f_opendir(&d, dir) == FR_OK) { - while (f_readdir(&d, &fi) == FR_OK && fi.fname[0] != '\0') { - if (!strcmp(fi.fname, ".") || !strcmp(fi.fname, "..")) - continue; - fn(fi.fname, userdata); - } - f_closedir(&d); - } -} - -static void* ff_open(const char* path, int flags) -{ - FIL f; - unsigned char mode = FA_READ; - if (flags & MG_FS_WRITE) - mode |= FA_WRITE | FA_OPEN_ALWAYS | FA_OPEN_APPEND; - if (f_open(&f, path, mode) == 0) { - FIL* fp; - if ((fp = calloc(1, sizeof(*fp))) != NULL) { - memcpy(fp, &f, sizeof(*fp)); - return fp; - } - } - return NULL; -} - -static void ff_close(void* fp) -{ - if (fp != NULL) { - f_close((FIL*)fp); - free(fp); - } -} - -static size_t ff_read(void* fp, void* buf, size_t len) -{ - UINT n = 0, misalign = ((size_t)buf) & 3; - if (misalign) { - char aligned[4]; - f_read((FIL*)fp, aligned, len > misalign ? misalign : len, &n); - memcpy(buf, aligned, n); - } else { - f_read((FIL*)fp, buf, len, &n); - } - return n; -} - -static size_t ff_write(void* fp, const void* buf, size_t len) -{ - UINT n = 0; - return f_write((FIL*)fp, (char*)buf, len, &n) == FR_OK ? n : 0; -} - -static size_t ff_seek(void* fp, size_t offset) -{ - f_lseek((FIL*)fp, offset); - return offset; -} - -static bool ff_rename(const char* from, const char* to) -{ - return f_rename(from, to) == FR_OK; -} - -static bool ff_remove(const char* path) -{ - return f_unlink(path) == FR_OK; -} - -static bool ff_mkdir(const char* path) -{ - return f_mkdir(path) == FR_OK; -} - -struct mg_fs mg_fs_fat = { ff_stat, ff_list, ff_open, ff_close, ff_read, - ff_write, ff_seek, ff_rename, ff_remove, ff_mkdir }; -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/fs_packed.c" -#endif - -struct packed_file { - const char* data; - size_t size; - size_t pos; -}; - -#if MG_ENABLE_PACKED_FS -#else -const char* mg_unpack(const char* path, size_t* size, time_t* mtime) -{ - *size = 0, *mtime = 0; - (void)path; - return NULL; -} -const char* mg_unlist(size_t no) -{ - (void)no; - return NULL; -} -#endif - -struct mg_str mg_unpacked(const char* path) -{ - size_t len = 0; - const char* buf = mg_unpack(path, &len, NULL); - return mg_str_n(buf, len); -} - -static int is_dir_prefix(const char* prefix, size_t n, const char* path) -{ - // MG_INFO(("[%.*s] [%s] %c", (int) n, prefix, path, path[n])); - return n < strlen(path) && strncmp(prefix, path, n) == 0 && (n == 0 || path[n] == '/' || path[n - 1] == '/'); -} - -static int packed_stat(const char* path, size_t* size, time_t* mtime) -{ - const char* p; - size_t i, n = strlen(path); - if (mg_unpack(path, size, mtime)) - return MG_FS_READ; // Regular file - // Scan all files. If `path` is a dir prefix for any of them, it's a dir - for (i = 0; (p = mg_unlist(i)) != NULL; i++) { - if (is_dir_prefix(path, n, p)) - return MG_FS_DIR; - } - return 0; -} - -static void packed_list(const char* dir, void (*fn)(const char*, void*), - void* userdata) -{ - char buf[MG_PATH_MAX], tmp[sizeof(buf)]; - const char *path, *begin, *end; - size_t i, n = strlen(dir); - tmp[0] = '\0'; // Previously listed entry - for (i = 0; (path = mg_unlist(i)) != NULL; i++) { - if (!is_dir_prefix(dir, n, path)) - continue; - begin = &path[n + 1]; - end = strchr(begin, '/'); - if (end == NULL) - end = begin + strlen(begin); - mg_snprintf(buf, sizeof(buf), "%.*s", (int)(end - begin), begin); - buf[sizeof(buf) - 1] = '\0'; - // If this entry has been already listed, skip - // NOTE: we're assuming that file list is sorted alphabetically - if (strcmp(buf, tmp) == 0) - continue; - fn(buf, userdata); // Not yet listed, call user function - strcpy(tmp, buf); // And save this entry as listed - } -} - -static void* packed_open(const char* path, int flags) -{ - size_t size = 0; - const char* data = mg_unpack(path, &size, NULL); - struct packed_file* fp = NULL; - if (data == NULL) - return NULL; - if (flags & MG_FS_WRITE) - return NULL; - if ((fp = (struct packed_file*)calloc(1, sizeof(*fp))) != NULL) { - fp->size = size; - fp->data = data; - } - return (void*)fp; -} - -static void packed_close(void* fp) -{ - if (fp != NULL) - free(fp); -} - -static size_t packed_read(void* fd, void* buf, size_t len) -{ - struct packed_file* fp = (struct packed_file*)fd; - if (fp->pos + len > fp->size) - len = fp->size - fp->pos; - memcpy(buf, &fp->data[fp->pos], len); - fp->pos += len; - return len; -} - -static size_t packed_write(void* fd, const void* buf, size_t len) -{ - (void)fd, (void)buf, (void)len; - return 0; -} - -static size_t packed_seek(void* fd, size_t offset) -{ - struct packed_file* fp = (struct packed_file*)fd; - fp->pos = offset; - if (fp->pos > fp->size) - fp->pos = fp->size; - return fp->pos; -} - -static bool packed_rename(const char* from, const char* to) -{ - (void)from, (void)to; - return false; -} - -static bool packed_remove(const char* path) -{ - (void)path; - return false; -} - -static bool packed_mkdir(const char* path) -{ - (void)path; - return false; -} - -struct mg_fs mg_fs_packed = { - packed_stat, packed_list, packed_open, packed_close, packed_read, - packed_write, packed_seek, packed_rename, packed_remove, packed_mkdir -}; - -#ifdef MG_ENABLE_LINES -#line 1 "src/fs_posix.c" -#endif - -#if MG_ENABLE_FILE - -#ifndef MG_STAT_STRUCT -#define MG_STAT_STRUCT stat -#endif - -#ifndef MG_STAT_FUNC -#define MG_STAT_FUNC stat -#endif - -static int p_stat(const char* path, size_t* size, time_t* mtime) -{ -#if !defined(S_ISDIR) - MG_ERROR(("stat() API is not supported. %p %p %p", path, size, mtime)); - return 0; -#else -#if MG_ARCH == MG_ARCH_WIN32 - struct _stati64 st; - wchar_t tmp[MG_PATH_MAX]; - MultiByteToWideChar(CP_UTF8, 0, path, -1, tmp, sizeof(tmp) / sizeof(tmp[0])); - if (_wstati64(tmp, &st) != 0) - return 0; - // If path is a symlink, windows reports 0 in st.st_size. - // Get a real file size by opening it and jumping to the end - if (st.st_size == 0 && (st.st_mode & _S_IFREG)) { - FILE* fp = _wfopen(tmp, L"rb"); - if (fp != NULL) { - fseek(fp, 0, SEEK_END); - if (ftell(fp) > 0) - st.st_size = ftell(fp); // Use _ftelli64 on win10+ - fclose(fp); - } - } -#else - struct MG_STAT_STRUCT st; - if (MG_STAT_FUNC(path, &st) != 0) - return 0; -#endif - if (size) - *size = (size_t)st.st_size; - if (mtime) - *mtime = st.st_mtime; - return MG_FS_READ | MG_FS_WRITE | (S_ISDIR(st.st_mode) ? MG_FS_DIR : 0); -#endif -} - -#if MG_ARCH == MG_ARCH_WIN32 -struct dirent { - char d_name[MAX_PATH]; -}; - -typedef struct win32_dir { - HANDLE handle; - WIN32_FIND_DATAW info; - struct dirent result; -} DIR; - -int gettimeofday(struct timeval* tv, void* tz) -{ - FILETIME ft; - unsigned __int64 tmpres = 0; - - if (tv != NULL) { - GetSystemTimeAsFileTime(&ft); - tmpres |= ft.dwHighDateTime; - tmpres <<= 32; - tmpres |= ft.dwLowDateTime; - tmpres /= 10; // convert into microseconds - tmpres -= (int64_t)11644473600000000; - tv->tv_sec = (long)(tmpres / 1000000UL); - tv->tv_usec = (long)(tmpres % 1000000UL); - } - (void)tz; - return 0; -} - -static int to_wchar(const char* path, wchar_t* wbuf, size_t wbuf_len) -{ - int ret; - char buf[MAX_PATH * 2], buf2[MAX_PATH * 2], *p; - strncpy(buf, path, sizeof(buf)); - buf[sizeof(buf) - 1] = '\0'; - // Trim trailing slashes. Leave backslash for paths like "X:\" - p = buf + strlen(buf) - 1; - while (p > buf && p[-1] != ':' && (p[0] == '\\' || p[0] == '/')) - *p-- = '\0'; - memset(wbuf, 0, wbuf_len * sizeof(wchar_t)); - ret = MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int)wbuf_len); - // Convert back to Unicode. If doubly-converted string does not match the - // original, something is fishy, reject. - WideCharToMultiByte(CP_UTF8, 0, wbuf, (int)wbuf_len, buf2, sizeof(buf2), - NULL, NULL); - if (strcmp(buf, buf2) != 0) { - wbuf[0] = L'\0'; - ret = 0; - } - return ret; -} - -DIR* opendir(const char* name) -{ - DIR* d = NULL; - wchar_t wpath[MAX_PATH]; - DWORD attrs; - - if (name == NULL) { - SetLastError(ERROR_BAD_ARGUMENTS); - } else if ((d = (DIR*)calloc(1, sizeof(*d))) == NULL) { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - } else { - to_wchar(name, wpath, sizeof(wpath) / sizeof(wpath[0])); - attrs = GetFileAttributesW(wpath); - if (attrs != 0Xffffffff && (attrs & FILE_ATTRIBUTE_DIRECTORY)) { - (void)wcscat(wpath, L"\\*"); - d->handle = FindFirstFileW(wpath, &d->info); - d->result.d_name[0] = '\0'; - } else { - free(d); - d = NULL; - } - } - return d; -} - -int closedir(DIR* d) -{ - int result = 0; - if (d != NULL) { - if (d->handle != INVALID_HANDLE_VALUE) - result = FindClose(d->handle) ? 0 : -1; - free(d); - } else { - result = -1; - SetLastError(ERROR_BAD_ARGUMENTS); - } - return result; -} - -struct dirent* readdir(DIR* d) -{ - struct dirent* result = NULL; - if (d != NULL) { - memset(&d->result, 0, sizeof(d->result)); - if (d->handle != INVALID_HANDLE_VALUE) { - result = &d->result; - WideCharToMultiByte(CP_UTF8, 0, d->info.cFileName, -1, result->d_name, - sizeof(result->d_name), NULL, NULL); - if (!FindNextFileW(d->handle, &d->info)) { - FindClose(d->handle); - d->handle = INVALID_HANDLE_VALUE; - } - } else { - SetLastError(ERROR_FILE_NOT_FOUND); - } - } else { - SetLastError(ERROR_BAD_ARGUMENTS); - } - return result; -} -#endif - -static void p_list(const char* dir, void (*fn)(const char*, void*), - void* userdata) -{ -#if MG_ENABLE_DIRLIST - struct dirent* dp; - DIR* dirp; - if ((dirp = (opendir(dir))) == NULL) - return; - while ((dp = readdir(dirp)) != NULL) { - if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) - continue; - fn(dp->d_name, userdata); - } - closedir(dirp); -#else - (void)dir, (void)fn, (void)userdata; -#endif -} - -static void* p_open(const char* path, int flags) -{ -#if MG_ARCH == MG_ARCH_WIN32 - const char* mode = flags == MG_FS_READ ? "rb" : "a+b"; - wchar_t b1[MG_PATH_MAX], b2[10]; - MultiByteToWideChar(CP_UTF8, 0, path, -1, b1, sizeof(b1) / sizeof(b1[0])); - MultiByteToWideChar(CP_UTF8, 0, mode, -1, b2, sizeof(b2) / sizeof(b2[0])); - return (void*)_wfopen(b1, b2); -#else - const char* mode = flags == MG_FS_READ ? "rbe" : "a+be"; // e for CLOEXEC - return (void*)fopen(path, mode); -#endif -} - -static void p_close(void* fp) -{ - // fclose((FILE*)fp); - PrivClose(*(int*)fp); -} - -static size_t p_read(void* fp, void* buf, size_t len) -{ - // return fread(buf, 1, len, (FILE*)fp); - int read_len = PrivRead(*(int*)fp, buf, len); - MG_DEBUG(("read len: %d", read_len)); - return read_len; -} - -static size_t p_write(void* fp, const void* buf, size_t len) -{ - int write_len = PrivWrite(*(int*)fp, buf, len); - MG_DEBUG(("write len: %d", write_len)); - return write_len; -} - -static size_t p_seek(void* fp, size_t offset) -{ - MG_DEBUG(("lseek offset: %d", offset)); - if (PrivLseek(*(int*)fp, (long)offset, SEEK_SET) != 0) - (void)0; - return PrivLseek(*(int*)fp, 0, SEEK_CUR); -} - -static bool p_rename(const char* from, const char* to) -{ - return rename(from, to) == 0; -} - -static bool p_remove(const char* path) -{ - return remove(path) == 0; -} - -static bool p_mkdir(const char* path) -{ - return mkdir(path, 0775) == 0; -} - -#else - -static int p_stat(const char* path, size_t* size, time_t* mtime) -{ - (void)path, (void)size, (void)mtime; - return 0; -} -static void p_list(const char* path, void (*fn)(const char*, void*), - void* userdata) -{ - (void)path, (void)fn, (void)userdata; -} -static void* p_open(const char* path, int flags) -{ - (void)path, (void)flags; - return NULL; -} -static void p_close(void* fp) -{ - (void)fp; -} -static size_t p_read(void* fd, void* buf, size_t len) -{ - (void)fd, (void)buf, (void)len; - return 0; -} -static size_t p_write(void* fd, const void* buf, size_t len) -{ - (void)fd, (void)buf, (void)len; - return 0; -} -static size_t p_seek(void* fd, size_t offset) -{ - (void)fd, (void)offset; - return (size_t)~0; -} -static bool p_rename(const char* from, const char* to) -{ - (void)from, (void)to; - return false; -} -static bool p_remove(const char* path) -{ - (void)path; - return false; -} -static bool p_mkdir(const char* path) -{ - (void)path; - return false; -} -#endif - -struct mg_fs mg_fs_posix = { p_stat, p_list, p_open, p_close, p_read, - p_write, p_seek, p_rename, p_remove, p_mkdir }; - -#ifdef MG_ENABLE_LINES -#line 1 "src/http.c" -#endif - -bool mg_to_size_t(struct mg_str str, size_t* val); -bool mg_to_size_t(struct mg_str str, size_t* val) -{ - size_t i = 0, max = (size_t)-1, max2 = max / 10, result = 0, ndigits = 0; - while (i < str.len && (str.ptr[i] == ' ' || str.ptr[i] == '\t')) - i++; - if (i < str.len && str.ptr[i] == '-') - return false; - while (i < str.len && str.ptr[i] >= '0' && str.ptr[i] <= '9') { - size_t digit = (size_t)(str.ptr[i] - '0'); - if (result > max2) - return false; // Overflow - result *= 10; - if (result > max - digit) - return false; // Overflow - result += digit; - i++, ndigits++; - } - while (i < str.len && (str.ptr[i] == ' ' || str.ptr[i] == '\t')) - i++; - if (ndigits == 0) - return false; // #2322: Content-Length = 1 * DIGIT - if (i != str.len) - return false; // Ditto - *val = (size_t)result; - return true; -} - -// Chunk deletion marker is the MSB in the "processed" counter -#define MG_DMARK ((size_t)1 << (sizeof(size_t) * 8 - 1)) - -// Multipart POST example: -// --xyz -// Content-Disposition: form-data; name="val" -// -// abcdef -// --xyz -// Content-Disposition: form-data; name="foo"; filename="a.txt" -// Content-Type: text/plain -// -// hello world -// -// --xyz-- -size_t mg_http_next_multipart(struct mg_str body, size_t ofs, - struct mg_http_part* part) -{ - struct mg_str cd = mg_str_n("Content-Disposition", 19); - const char* s = body.ptr; - size_t b = ofs, h1, h2, b1, b2, max = body.len; - - // Init part params - if (part != NULL) - part->name = part->filename = part->body = mg_str_n(0, 0); - - // Skip boundary - while (b + 2 < max && s[b] != '\r' && s[b + 1] != '\n') - b++; - if (b <= ofs || b + 2 >= max) - return 0; - // MG_INFO(("B: %zu %zu [%.*s]", ofs, b - ofs, (int) (b - ofs), s)); - - // Skip headers - h1 = h2 = b + 2; - for (;;) { - while (h2 + 2 < max && s[h2] != '\r' && s[h2 + 1] != '\n') - h2++; - if (h2 == h1) - break; - if (h2 + 2 >= max) - return 0; - // MG_INFO(("Header: [%.*s]", (int) (h2 - h1), &s[h1])); - if (part != NULL && h1 + cd.len + 2 < h2 && s[h1 + cd.len] == ':' && mg_ncasecmp(&s[h1], cd.ptr, cd.len) == 0) { - struct mg_str v = mg_str_n(&s[h1 + cd.len + 2], h2 - (h1 + cd.len + 2)); - part->name = mg_http_get_header_var(v, mg_str_n("name", 4)); - part->filename = mg_http_get_header_var(v, mg_str_n("filename", 8)); - } - h1 = h2 = h2 + 2; - } - b1 = b2 = h2 + 2; - while (b2 + 2 + (b - ofs) + 2 < max && !(s[b2] == '\r' && s[b2 + 1] == '\n' && memcmp(&s[b2 + 2], s, b - ofs) == 0)) - b2++; - - if (b2 + 2 >= max) - return 0; - if (part != NULL) - part->body = mg_str_n(&s[b1], b2 - b1); - // MG_INFO(("Body: [%.*s]", (int) (b2 - b1), &s[b1])); - return b2 + 2; -} - -void mg_http_bauth(struct mg_connection* c, const char* user, - const char* pass) -{ - struct mg_str u = mg_str(user), p = mg_str(pass); - size_t need = c->send.len + 36 + (u.len + p.len) * 2; - if (c->send.size < need) - mg_iobuf_resize(&c->send, need); - if (c->send.size >= need) { - size_t i, n = 0; - char* buf = (char*)&c->send.buf[c->send.len]; - memcpy(buf, "Authorization: Basic ", 21); // DON'T use mg_send! - for (i = 0; i < u.len; i++) { - n = mg_base64_update(((unsigned char*)u.ptr)[i], buf + 21, n); - } - if (p.len > 0) { - n = mg_base64_update(':', buf + 21, n); - for (i = 0; i < p.len; i++) { - n = mg_base64_update(((unsigned char*)p.ptr)[i], buf + 21, n); - } - } - n = mg_base64_final(buf + 21, n); - c->send.len += 21 + (size_t)n + 2; - memcpy(&c->send.buf[c->send.len - 2], "\r\n", 2); - } else { - MG_ERROR(("%lu oom %d->%d ", c->id, (int)c->send.size, (int)need)); - } -} - -struct mg_str mg_http_var(struct mg_str buf, struct mg_str name) -{ - struct mg_str k, v, result = mg_str_n(NULL, 0); - while (mg_split(&buf, &k, &v, '&')) { - if (name.len == k.len && mg_ncasecmp(name.ptr, k.ptr, k.len) == 0) { - result = v; - break; - } - } - return result; -} - -int mg_http_get_var(const struct mg_str* buf, const char* name, char* dst, - size_t dst_len) -{ - int len; - if (dst == NULL || dst_len == 0) { - len = -2; // Bad destination - } else if (buf->ptr == NULL || name == NULL || buf->len == 0) { - len = -1; // Bad source - dst[0] = '\0'; - } else { - struct mg_str v = mg_http_var(*buf, mg_str(name)); - if (v.ptr == NULL) { - len = -4; // Name does not exist - } else { - len = mg_url_decode(v.ptr, v.len, dst, dst_len, 1); - if (len < 0) - len = -3; // Failed to decode - } - } - return len; -} - -static bool isx(int c) -{ - return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); -} - -int mg_url_decode(const char* src, size_t src_len, char* dst, size_t dst_len, - int is_form_url_encoded) -{ - size_t i, j; - for (i = j = 0; i < src_len && j + 1 < dst_len; i++, j++) { - if (src[i] == '%') { - // Use `i + 2 < src_len`, not `i < src_len - 2`, note small src_len - if (i + 2 < src_len && isx(src[i + 1]) && isx(src[i + 2])) { - mg_unhex(src + i + 1, 2, (uint8_t*)&dst[j]); - i += 2; - } else { - return -1; - } - } else if (is_form_url_encoded && src[i] == '+') { - dst[j] = ' '; - } else { - dst[j] = src[i]; - } - } - if (j < dst_len) - dst[j] = '\0'; // Null-terminate the destination - return i >= src_len && j < dst_len ? (int)j : -1; -} - -static bool isok(uint8_t c) -{ - return c == '\n' || c == '\r' || c >= ' '; -} - -int mg_http_get_request_len(const unsigned char* buf, size_t buf_len) -{ - size_t i; - for (i = 0; i < buf_len; i++) { - if (!isok(buf[i])) - return -1; - if ((i > 0 && buf[i] == '\n' && buf[i - 1] == '\n') || (i > 3 && buf[i] == '\n' && buf[i - 1] == '\r' && buf[i - 2] == '\n')) - return (int)i + 1; - } - return 0; -} -struct mg_str* mg_http_get_header(struct mg_http_message* h, const char* name) -{ - size_t i, n = strlen(name), max = sizeof(h->headers) / sizeof(h->headers[0]); - for (i = 0; i < max && h->headers[i].name.len > 0; i++) { - struct mg_str *k = &h->headers[i].name, *v = &h->headers[i].value; - if (n == k->len && mg_ncasecmp(k->ptr, name, n) == 0) - return v; - } - return NULL; -} - -// Get character length. Used to parse method, URI, headers -static size_t clen(const char* s) -{ - uint8_t c = *(uint8_t*)s; - if (c > ' ' && c < '~') - return 1; // Usual ascii printed char - if ((c & 0xe0) == 0xc0) - return 2; // 2-byte UTF8 - if ((c & 0xf0) == 0xe0) - return 3; // 3-byte UTF8 - if ((c & 0xf8) == 0xf0) - return 4; // 4-byte UTF8 - return 0; -} - -// Skip until the newline. Return advanced `s`, or NULL on error -static const char* skiptorn(const char* s, const char* end, struct mg_str* v) -{ - v->ptr = s; - while (s < end && s[0] != '\n' && s[0] != '\r') - s++, v->len++; // To newline - if (s >= end || (s[0] == '\r' && s[1] != '\n')) - return NULL; // Stray \r - if (s < end && s[0] == '\r') - s++; // Skip \r - if (s >= end || *s++ != '\n') - return NULL; // Skip \n - return s; -} - -static bool mg_http_parse_headers(const char* s, const char* end, - struct mg_http_header* h, size_t max_hdrs) -{ - size_t i, n; - for (i = 0; i < max_hdrs; i++) { - struct mg_str k = { NULL, 0 }, v = { NULL, 0 }; - if (s >= end) - return false; - if (s[0] == '\n' || (s[0] == '\r' && s[1] == '\n')) - break; - k.ptr = s; - while (s < end && s[0] != ':' && (n = clen(s)) > 0) - s += n, k.len += n; - if (k.len == 0) - return false; // Empty name - if (s >= end || *s++ != ':') - return false; // Invalid, not followed by : - while (s < end && s[0] == ' ') - s++; // Skip spaces - if ((s = skiptorn(s, end, &v)) == NULL) - return false; - while (v.len > 0 && v.ptr[v.len - 1] == ' ') - v.len--; // Trim spaces - // MG_INFO(("--HH [%.*s] [%.*s]", (int) k.len, k.ptr, (int) v.len, v.ptr)); - h[i].name = k, h[i].value = v; // Success. Assign values - } - return true; -} - -int mg_http_parse(const char* s, size_t len, struct mg_http_message* hm) -{ - int is_response, req_len = mg_http_get_request_len((unsigned char*)s, len); - const char *end = s == NULL ? NULL : s + req_len, *qs; // Cannot add to NULL - struct mg_str* cl; - size_t n; - - memset(hm, 0, sizeof(*hm)); - if (req_len <= 0) - return req_len; - - hm->message.ptr = hm->head.ptr = s; - hm->body.ptr = end; - hm->head.len = (size_t)req_len; - hm->chunk.ptr = end; - hm->message.len = hm->body.len = (size_t)~0; // Set body length to infinite - - // Parse request line - hm->method.ptr = s; - while (s < end && (n = clen(s)) > 0) - s += n, hm->method.len += n; - while (s < end && s[0] == ' ') - s++; // Skip spaces - hm->uri.ptr = s; - while (s < end && (n = clen(s)) > 0) - s += n, hm->uri.len += n; - while (s < end && s[0] == ' ') - s++; // Skip spaces - if ((s = skiptorn(s, end, &hm->proto)) == NULL) - return false; - - // If URI contains '?' character, setup query string - if ((qs = (const char*)memchr(hm->uri.ptr, '?', hm->uri.len)) != NULL) { - hm->query.ptr = qs + 1; - hm->query.len = (size_t)(&hm->uri.ptr[hm->uri.len] - (qs + 1)); - hm->uri.len = (size_t)(qs - hm->uri.ptr); - } - - // Sanity check. Allow protocol/reason to be empty - // Do this check after hm->method.len and hm->uri.len are finalised - if (hm->method.len == 0 || hm->uri.len == 0) - return -1; - - if (!mg_http_parse_headers(s, end, hm->headers, - sizeof(hm->headers) / sizeof(hm->headers[0]))) - return -1; // error when parsing - if ((cl = mg_http_get_header(hm, "Content-Length")) != NULL) { - if (mg_to_size_t(*cl, &hm->body.len) == false) - return -1; - hm->message.len = (size_t)req_len + hm->body.len; - } - - // mg_http_parse() is used to parse both HTTP requests and HTTP - // responses. If HTTP response does not have Content-Length set, then - // body is read until socket is closed, i.e. body.len is infinite (~0). - // - // For HTTP requests though, according to - // http://tools.ietf.org/html/rfc7231#section-8.1.3, - // only POST and PUT methods have defined body semantics. - // Therefore, if Content-Length is not specified and methods are - // not one of PUT or POST, set body length to 0. - // - // So, if it is HTTP request, and Content-Length is not set, - // and method is not (PUT or POST) then reset body length to zero. - is_response = mg_ncasecmp(hm->method.ptr, "HTTP/", 5) == 0; - if (hm->body.len == (size_t)~0 && !is_response && mg_vcasecmp(&hm->method, "PUT") != 0 && mg_vcasecmp(&hm->method, "POST") != 0) { - hm->body.len = 0; - hm->message.len = (size_t)req_len; - } - - // The 204 (No content) responses also have 0 body length - if (hm->body.len == (size_t)~0 && is_response && mg_vcasecmp(&hm->uri, "204") == 0) { - hm->body.len = 0; - hm->message.len = (size_t)req_len; - } - if (hm->message.len < (size_t)req_len) - return -1; // Overflow protection - - return req_len; -} - -static void mg_http_vprintf_chunk(struct mg_connection* c, const char* fmt, - va_list* ap) -{ - size_t len = c->send.len; - mg_send(c, " \r\n", 10); - mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, ap); - if (c->send.len >= len + 10) { - mg_snprintf((char*)c->send.buf + len, 9, "%08lx", c->send.len - len - 10); - c->send.buf[len + 8] = '\r'; - if (c->send.len == len + 10) - c->is_resp = 0; // Last chunk, reset marker - } - mg_send(c, "\r\n", 2); -} - -void mg_http_printf_chunk(struct mg_connection* c, const char* fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - mg_http_vprintf_chunk(c, fmt, &ap); - va_end(ap); -} - -void mg_http_write_chunk(struct mg_connection* c, const char* buf, size_t len) -{ - mg_printf(c, "%lx\r\n", (unsigned long)len); - mg_send(c, buf, len); - mg_send(c, "\r\n", 2); - if (len == 0) - c->is_resp = 0; -} - -// clang-format off -static const char *mg_http_status_code_str(int status_code) { - switch (status_code) { - case 100: return "Continue"; - case 101: return "Switching Protocols"; - case 102: return "Processing"; - case 200: return "OK"; - case 201: return "Created"; - case 202: return "Accepted"; - case 203: return "Non-authoritative Information"; - case 204: return "No Content"; - case 205: return "Reset Content"; - case 206: return "Partial Content"; - case 207: return "Multi-Status"; - case 208: return "Already Reported"; - case 226: return "IM Used"; - case 300: return "Multiple Choices"; - case 301: return "Moved Permanently"; - case 302: return "Found"; - case 303: return "See Other"; - case 304: return "Not Modified"; - case 305: return "Use Proxy"; - case 307: return "Temporary Redirect"; - case 308: return "Permanent Redirect"; - case 400: return "Bad Request"; - case 401: return "Unauthorized"; - case 402: return "Payment Required"; - case 403: return "Forbidden"; - case 404: return "Not Found"; - case 405: return "Method Not Allowed"; - case 406: return "Not Acceptable"; - case 407: return "Proxy Authentication Required"; - case 408: return "Request Timeout"; - case 409: return "Conflict"; - case 410: return "Gone"; - case 411: return "Length Required"; - case 412: return "Precondition Failed"; - case 413: return "Payload Too Large"; - case 414: return "Request-URI Too Long"; - case 415: return "Unsupported Media Type"; - case 416: return "Requested Range Not Satisfiable"; - case 417: return "Expectation Failed"; - case 418: return "I'm a teapot"; - case 421: return "Misdirected Request"; - case 422: return "Unprocessable Entity"; - case 423: return "Locked"; - case 424: return "Failed Dependency"; - case 426: return "Upgrade Required"; - case 428: return "Precondition Required"; - case 429: return "Too Many Requests"; - case 431: return "Request Header Fields Too Large"; - case 444: return "Connection Closed Without Response"; - case 451: return "Unavailable For Legal Reasons"; - case 499: return "Client Closed Request"; - case 500: return "Internal Server Error"; - case 501: return "Not Implemented"; - case 502: return "Bad Gateway"; - case 503: return "Service Unavailable"; - case 504: return "Gateway Timeout"; - case 505: return "HTTP Version Not Supported"; - case 506: return "Variant Also Negotiates"; - case 507: return "Insufficient Storage"; - case 508: return "Loop Detected"; - case 510: return "Not Extended"; - case 511: return "Network Authentication Required"; - case 599: return "Network Connect Timeout Error"; - default: return ""; - } -} -// clang-format on - -void mg_http_reply(struct mg_connection* c, int code, const char* headers, - const char* fmt, ...) -{ - va_list ap; - size_t len; - mg_printf(c, "HTTP/1.1 %d %s\r\n%sContent-Length: \r\n\r\n", code, - mg_http_status_code_str(code), headers == NULL ? "" : headers); - len = c->send.len; - va_start(ap, fmt); - mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, &ap); - va_end(ap); - if (c->send.len > 16) { - size_t n = mg_snprintf((char*)&c->send.buf[len - 15], 11, "%-10lu", - (unsigned long)(c->send.len - len)); - c->send.buf[len - 15 + n] = ' '; // Change ending 0 to space - } - c->is_resp = 0; -} - -static void http_cb(struct mg_connection*, int, void*, void*); -static void restore_http_cb(struct mg_connection* c) -{ - mg_fs_close((struct mg_fd*)c->pfn_data); - c->pfn_data = NULL; - c->pfn = http_cb; - c->is_resp = 0; -} - -char* mg_http_etag(char* buf, size_t len, size_t size, time_t mtime); -char* mg_http_etag(char* buf, size_t len, size_t size, time_t mtime) -{ - mg_snprintf(buf, len, "\"%lld.%lld\"", (int64_t)mtime, (int64_t)size); - return buf; -} - -static void static_cb(struct mg_connection* c, int ev, void* ev_data, - void* fn_data) -{ - if (ev == MG_EV_WRITE || ev == MG_EV_POLL) { - struct mg_fd* fd = (struct mg_fd*)fn_data; - // Read to send IO buffer directly, avoid extra on-stack buffer - size_t n, max = MG_IO_SIZE, space; - size_t* cl = (size_t*)&c->data[(sizeof(c->data) - sizeof(size_t)) / sizeof(size_t) * sizeof(size_t)]; - if (c->send.size < max) - mg_iobuf_resize(&c->send, max); - if (c->send.len >= c->send.size) - return; // Rate limit - if ((space = c->send.size - c->send.len) > *cl) - space = *cl; - n = fd->fs->rd(fd->fd, c->send.buf + c->send.len, space); - c->send.len += n; - *cl -= n; - if (n == 0) - restore_http_cb(c); - } else if (ev == MG_EV_CLOSE) { - restore_http_cb(c); - } - (void)ev_data; -} - -// Known mime types. Keep it outside guess_content_type() function, since -// some environments don't like it defined there. -// clang-format off -static struct mg_str s_known_types[] = { - MG_C_STR("html"), MG_C_STR("text/html; charset=utf-8"), - MG_C_STR("htm"), MG_C_STR("text/html; charset=utf-8"), - MG_C_STR("css"), MG_C_STR("text/css; charset=utf-8"), - MG_C_STR("js"), MG_C_STR("text/javascript; charset=utf-8"), - MG_C_STR("gif"), MG_C_STR("image/gif"), - MG_C_STR("png"), MG_C_STR("image/png"), - MG_C_STR("jpg"), MG_C_STR("image/jpeg"), - MG_C_STR("jpeg"), MG_C_STR("image/jpeg"), - MG_C_STR("woff"), MG_C_STR("font/woff"), - MG_C_STR("ttf"), MG_C_STR("font/ttf"), - MG_C_STR("svg"), MG_C_STR("image/svg+xml"), - MG_C_STR("txt"), MG_C_STR("text/plain; charset=utf-8"), - MG_C_STR("avi"), MG_C_STR("video/x-msvideo"), - MG_C_STR("csv"), MG_C_STR("text/csv"), - MG_C_STR("doc"), MG_C_STR("application/msword"), - MG_C_STR("exe"), MG_C_STR("application/octet-stream"), - MG_C_STR("gz"), MG_C_STR("application/gzip"), - MG_C_STR("ico"), MG_C_STR("image/x-icon"), - MG_C_STR("json"), MG_C_STR("application/json"), - MG_C_STR("mov"), MG_C_STR("video/quicktime"), - MG_C_STR("mp3"), MG_C_STR("audio/mpeg"), - MG_C_STR("mp4"), MG_C_STR("video/mp4"), - MG_C_STR("mpeg"), MG_C_STR("video/mpeg"), - MG_C_STR("pdf"), MG_C_STR("application/pdf"), - MG_C_STR("shtml"), MG_C_STR("text/html; charset=utf-8"), - MG_C_STR("tgz"), MG_C_STR("application/tar-gz"), - MG_C_STR("wav"), MG_C_STR("audio/wav"), - MG_C_STR("webp"), MG_C_STR("image/webp"), - MG_C_STR("zip"), MG_C_STR("application/zip"), - MG_C_STR("3gp"), MG_C_STR("video/3gpp"), - {0, 0}, -}; -// clang-format on - -static struct mg_str guess_content_type(struct mg_str path, const char* extra) -{ - struct mg_str k, v, s = mg_str(extra); - size_t i = 0; - - // Shrink path to its extension only - while (i < path.len && path.ptr[path.len - i - 1] != '.') - i++; - path.ptr += path.len - i; - path.len = i; - - // Process user-provided mime type overrides, if any - while (mg_commalist(&s, &k, &v)) { - if (mg_strcmp(path, k) == 0) - return v; - } - - // Process built-in mime types - for (i = 0; s_known_types[i].ptr != NULL; i += 2) { - if (mg_strcmp(path, s_known_types[i]) == 0) - return s_known_types[i + 1]; - } - - return mg_str("text/plain; charset=utf-8"); -} - -static int getrange(struct mg_str* s, size_t* a, size_t* b) -{ - size_t i, numparsed = 0; - for (i = 0; i + 6 < s->len; i++) { - struct mg_str k, v = mg_str_n(s->ptr + i + 6, s->len - i - 6); - if (memcmp(&s->ptr[i], "bytes=", 6) != 0) - continue; - if (mg_split(&v, &k, NULL, '-')) { - if (mg_to_size_t(k, a)) - numparsed++; - if (v.len > 0 && mg_to_size_t(v, b)) - numparsed++; - } else { - if (mg_to_size_t(v, a)) - numparsed++; - } - break; - } - return (int)numparsed; -} - -void mg_http_serve_file(struct mg_connection* c, struct mg_http_message* hm, - const char* path, - const struct mg_http_serve_opts* opts) -{ - char etag[64], tmp[MG_PATH_MAX]; - struct mg_fs* fs = opts->fs == NULL ? &mg_fs_posix : opts->fs; - struct mg_fd* fd = NULL; - size_t size = 0; - time_t mtime = 0; - struct mg_str* inm = NULL; - struct mg_str mime = guess_content_type(mg_str(path), opts->mime_types); - bool gzip = false; - - if (path != NULL) { - // If a browser sends us "Accept-Encoding: gzip", try to open .gz first - // struct mg_str* ae = mg_http_get_header(hm, "Accept-Encoding"); - // if (ae != NULL && mg_strstr(*ae, mg_str("gzip")) != NULL) { - // mg_snprintf(tmp, sizeof(tmp), "%s.gz", path); - // fd = mg_fs_open(fs, tmp, MG_FS_READ); - // if (fd != NULL) - // gzip = true, path = tmp; - // } - // No luck opening .gz? Open what we've told to open - // if (fd == NULL) - fd = mg_fs_open(fs, path, MG_FS_READ); - } - - // Failed to open, and page404 is configured? Open it, then - if (fd == NULL && opts->page404 != NULL) { - fd = mg_fs_open(fs, opts->page404, MG_FS_READ); - mime = guess_content_type(mg_str(path), opts->mime_types); - path = opts->page404; - } - - if (fd == NULL || fs->st(path, &size, &mtime) == 0) { - mg_http_reply(c, 404, opts->extra_headers, "Not found\n"); - mg_fs_close(fd); - // NOTE: mg_http_etag() call should go first! - } else if (mg_http_etag(etag, sizeof(etag), size, mtime) != NULL && (inm = mg_http_get_header(hm, "If-None-Match")) != NULL && mg_vcasecmp(inm, etag) == 0) { - mg_fs_close(fd); - mg_http_reply(c, 304, opts->extra_headers, ""); - } else { - int n, status = 200; - char range[100]; - size_t r1 = 0, r2 = 0, cl = size; - - // Handle Range header - struct mg_str* rh = mg_http_get_header(hm, "Range"); - range[0] = '\0'; - if (rh != NULL && (n = getrange(rh, &r1, &r2)) > 0) { - // If range is specified like "400-", set second limit to content len - if (n == 1) - r2 = cl - 1; - if (r1 > r2 || r2 >= cl) { - status = 416; - cl = 0; - mg_snprintf(range, sizeof(range), "Content-Range: bytes */%lld\r\n", - (int64_t)size); - } else { - status = 206; - cl = r2 - r1 + 1; - mg_snprintf(range, sizeof(range), - "Content-Range: bytes %llu-%llu/%llu\r\n", (uint64_t)r1, - (uint64_t)(r1 + cl - 1), (uint64_t)size); - fs->sk(fd->fd, r1); - } - } - mg_printf(c, - "HTTP/1.1 %d %s\r\n" - "Content-Type: %.*s\r\n" - "Etag: %s\r\n" - "Content-Length: %llu\r\n" - "%s%s%s\r\n", - status, mg_http_status_code_str(status), (int)mime.len, mime.ptr, - etag, (uint64_t)cl, gzip ? "Content-Encoding: gzip\r\n" : "", - range, opts->extra_headers ? opts->extra_headers : ""); - if (mg_vcasecmp(&hm->method, "HEAD") == 0) { - c->is_draining = 1; - c->is_resp = 0; - mg_fs_close(fd); - } else { - // Track to-be-sent content length at the end of c->data, aligned - size_t* clp = (size_t*)&c->data[(sizeof(c->data) - sizeof(size_t)) / sizeof(size_t) * sizeof(size_t)]; - c->pfn = static_cb; - c->pfn_data = fd; - *clp = cl; - } - } -} - -struct printdirentrydata { - struct mg_connection* c; - struct mg_http_message* hm; - const struct mg_http_serve_opts* opts; - const char* dir; -}; - -#if MG_ENABLE_DIRLIST -static void printdirentry(const char* name, void* userdata) -{ - struct printdirentrydata* d = (struct printdirentrydata*)userdata; - struct mg_fs* fs = d->opts->fs == NULL ? &mg_fs_posix : d->opts->fs; - size_t size = 0; - time_t t = 0; - char path[MG_PATH_MAX], sz[40], mod[40]; - int flags, n = 0; - - // MG_DEBUG(("[%s] [%s]", d->dir, name)); - if (mg_snprintf(path, sizeof(path), "%s%c%s", d->dir, '/', name) > sizeof(path)) { - MG_ERROR(("%s truncated", name)); - } else if ((flags = fs->st(path, &size, &t)) == 0) { - MG_ERROR(("%lu stat(%s): %d", d->c->id, path, errno)); - } else { - const char* slash = flags & MG_FS_DIR ? "/" : ""; - if (flags & MG_FS_DIR) { - mg_snprintf(sz, sizeof(sz), "%s", "[DIR]"); - } else { - mg_snprintf(sz, sizeof(sz), "%lld", (uint64_t)size); - } -#if defined(MG_HTTP_DIRLIST_TIME_FMT) - { - char time_str[40]; - struct tm* time_info = localtime(&t); - strftime(time_str, sizeof time_str, "%Y/%m/%d %H:%M:%S", time_info); - mg_snprintf(mod, sizeof(mod), "%s", time_str); - } -#else - mg_snprintf(mod, sizeof(mod), "%lu", (unsigned long)t); -#endif - n = (int)mg_url_encode(name, strlen(name), path, sizeof(path)); - mg_printf(d->c, - " %s%s" - "%s%s\n", - n, path, slash, name, slash, (unsigned long)t, mod, - flags & MG_FS_DIR ? (int64_t)-1 : (int64_t)size, sz); - } -} - -static void listdir(struct mg_connection* c, struct mg_http_message* hm, - const struct mg_http_serve_opts* opts, char* dir) -{ - const char* sort_js_code = ""; - struct mg_fs* fs = opts->fs == NULL ? &mg_fs_posix : opts->fs; - struct printdirentrydata d = { c, hm, opts, dir }; - char tmp[10], buf[MG_PATH_MAX]; - size_t off, n; - int len = mg_url_decode(hm->uri.ptr, hm->uri.len, buf, sizeof(buf), 0); - struct mg_str uri = len > 0 ? mg_str_n(buf, (size_t)len) : hm->uri; - - mg_printf(c, - "HTTP/1.1 200 OK\r\n" - "Content-Type: text/html; charset=utf-8\r\n" - "%s" - "Content-Length: \r\n\r\n", - opts->extra_headers == NULL ? "" : opts->extra_headers); - off = c->send.len; // Start of body - mg_printf(c, - "Index of %.*s%s%s" - "" - "

Index of %.*s

" - "" - "" - "" - "" - "\n", - (int)uri.len, uri.ptr, sort_js_code, sort_js_code2, (int)uri.len, - uri.ptr); - mg_printf(c, "%s", - " " - "\n"); - - fs->ls(dir, printdirentry, &d); - mg_printf(c, - "" - "
Name" - "ModifiedSize

..[DIR]

Mongoose v.%s
\n", - MG_VERSION); - n = mg_snprintf(tmp, sizeof(tmp), "%lu", (unsigned long)(c->send.len - off)); - if (n > sizeof(tmp)) - n = 0; - memcpy(c->send.buf + off - 12, tmp, n); // Set content length - c->is_resp = 0; // Mark response end -} -#endif - -// Resolve requested file into `path` and return its fs->st() result -static int uri_to_path2(struct mg_connection* c, struct mg_http_message* hm, - struct mg_fs* fs, struct mg_str url, struct mg_str dir, - char* path, size_t path_size) -{ - int flags, tmp; - // Append URI to the root_dir, and sanitize it - size_t n = mg_snprintf(path, path_size, "%.*s", (int)dir.len, dir.ptr); - if (n + 2 >= path_size) { - mg_http_reply(c, 400, "", "Exceeded path size"); - return -1; - } - path[path_size - 1] = '\0'; - // Terminate root dir with slash - if (n > 0 && path[n - 1] != '/') - path[n++] = '/', path[n] = '\0'; - if (url.len < hm->uri.len) { - mg_url_decode(hm->uri.ptr + url.len, hm->uri.len - url.len, path + n, - path_size - n, 0); - } - path[path_size - 1] = '\0'; // Double-check - if (!mg_path_is_sane(path)) { - mg_http_reply(c, 400, "", "Invalid path"); - return -1; - } - n = strlen(path); - while (n > 1 && path[n - 1] == '/') - path[--n] = 0; // Trim trailing slashes - flags = mg_vcmp(&hm->uri, "/") == 0 ? MG_FS_DIR : fs->st(path, NULL, NULL); - MG_VERBOSE(("%lu %.*s -> %s %d", c->id, (int)hm->uri.len, hm->uri.ptr, path, - flags)); - if (flags == 0) { - // Do nothing - let's caller decide - } else if ((flags & MG_FS_DIR) && hm->uri.len > 0 && hm->uri.ptr[hm->uri.len - 1] != '/') { - mg_printf(c, - "HTTP/1.1 301 Moved\r\n" - "Location: %.*s/\r\n" - "Content-Length: 0\r\n" - "\r\n", - (int)hm->uri.len, hm->uri.ptr); - c->is_resp = 0; - flags = -1; - } else if (flags & MG_FS_DIR) { - if (((mg_snprintf(path + n, path_size - n, "/" MG_HTTP_INDEX) > 0 && (tmp = fs->st(path, NULL, NULL)) != 0) || (mg_snprintf(path + n, path_size - n, "/index.shtml") > 0 && (tmp = fs->st(path, NULL, NULL)) != 0))) { - flags = tmp; - } else if ((mg_snprintf(path + n, path_size - n, "/" MG_HTTP_INDEX ".gz") > 0 && (tmp = fs->st(path, NULL, NULL)) != 0)) { // check for gzipped index - flags = tmp; - path[n + 1 + strlen(MG_HTTP_INDEX)] = '\0'; // Remove appended .gz in index file name - } else { - path[n] = '\0'; // Remove appended index file name - } - } - return flags; -} - -static int uri_to_path(struct mg_connection* c, struct mg_http_message* hm, - const struct mg_http_serve_opts* opts, char* path, - size_t path_size) -{ - struct mg_fs* fs = opts->fs == NULL ? &mg_fs_posix : opts->fs; - struct mg_str k, v, s = mg_str(opts->root_dir), u = { 0, 0 }, p = { 0, 0 }; - while (mg_commalist(&s, &k, &v)) { - if (v.len == 0) - v = k, k = mg_str("/"), u = k, p = v; - if (hm->uri.len < k.len) - continue; - if (mg_strcmp(k, mg_str_n(hm->uri.ptr, k.len)) != 0) - continue; - u = k, p = v; - } - return uri_to_path2(c, hm, fs, u, p, path, path_size); -} - -void mg_http_serve_dir(struct mg_connection* c, struct mg_http_message* hm, - const struct mg_http_serve_opts* opts) -{ - char path[MG_PATH_MAX]; - const char* sp = opts->ssi_pattern; - int flags = uri_to_path(c, hm, opts, path, sizeof(path)); - MG_DEBUG(("path: %s", path)); - if (flags < 0) { - // Do nothing: the response has already been sent by uri_to_path() - } else if (flags & MG_FS_DIR) { -#if MG_ENABLE_DIRLIST - listdir(c, hm, opts, path); -#else - mg_http_reply(c, 403, "", "Forbidden\n"); -#endif - } else if (flags && sp != NULL && mg_globmatch(sp, strlen(sp), path, strlen(path))) { - mg_http_serve_ssi(c, opts->root_dir, path); - } else { - mg_http_serve_file(c, hm, path, opts); - } -} - -static bool mg_is_url_safe(int c) -{ - return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '.' || c == '_' || c == '-' || c == '~'; -} - -size_t mg_url_encode(const char* s, size_t sl, char* buf, size_t len) -{ - size_t i, n = 0; - for (i = 0; i < sl; i++) { - int c = *(unsigned char*)&s[i]; - if (n + 4 >= len) - return 0; - if (mg_is_url_safe(c)) { - buf[n++] = s[i]; - } else { - buf[n++] = '%'; - mg_hex(&s[i], 1, &buf[n]); - n += 2; - } - } - if (len > 0 && n < len - 1) - buf[n] = '\0'; // Null-terminate the destination - if (len > 0) - buf[len - 1] = '\0'; // Always. - return n; -} - -void mg_http_creds(struct mg_http_message* hm, char* user, size_t userlen, - char* pass, size_t passlen) -{ - struct mg_str* v = mg_http_get_header(hm, "Authorization"); - user[0] = pass[0] = '\0'; - if (v != NULL && v->len > 6 && memcmp(v->ptr, "Basic ", 6) == 0) { - char buf[256]; - size_t n = mg_base64_decode(v->ptr + 6, v->len - 6, buf, sizeof(buf)); - const char* p = (const char*)memchr(buf, ':', n > 0 ? n : 0); - if (p != NULL) { - mg_snprintf(user, userlen, "%.*s", p - buf, buf); - mg_snprintf(pass, passlen, "%.*s", n - (size_t)(p - buf) - 1, p + 1); - } - } else if (v != NULL && v->len > 7 && memcmp(v->ptr, "Bearer ", 7) == 0) { - mg_snprintf(pass, passlen, "%.*s", (int)v->len - 7, v->ptr + 7); - } else if ((v = mg_http_get_header(hm, "Cookie")) != NULL) { - struct mg_str t = mg_http_get_header_var(*v, mg_str_n("access_token", 12)); - if (t.len > 0) - mg_snprintf(pass, passlen, "%.*s", (int)t.len, t.ptr); - } else { - mg_http_get_var(&hm->query, "access_token", pass, passlen); - } -} - -static struct mg_str stripquotes(struct mg_str s) -{ - return s.len > 1 && s.ptr[0] == '"' && s.ptr[s.len - 1] == '"' - ? mg_str_n(s.ptr + 1, s.len - 2) - : s; -} - -struct mg_str mg_http_get_header_var(struct mg_str s, struct mg_str v) -{ - size_t i; - for (i = 0; v.len > 0 && i + v.len + 2 < s.len; i++) { - if (s.ptr[i + v.len] == '=' && memcmp(&s.ptr[i], v.ptr, v.len) == 0) { - const char *p = &s.ptr[i + v.len + 1], *b = p, *x = &s.ptr[s.len]; - int q = p < x && *p == '"' ? 1 : 0; - while (p < x && (q ? p == b || *p != '"' : *p != ';' && *p != ' ' && *p != ',')) - p++; - // MG_INFO(("[%.*s] [%.*s] [%.*s]", (int) s.len, s.ptr, (int) v.len, - // v.ptr, (int) (p - b), b)); - return stripquotes(mg_str_n(b, (size_t)(p - b + q))); - } - } - return mg_str_n(NULL, 0); -} - -bool mg_http_match_uri(const struct mg_http_message* hm, const char* glob) -{ - return mg_match(hm->uri, mg_str(glob), NULL); -} - -long mg_http_upload(struct mg_connection* c, struct mg_http_message* hm, - struct mg_fs* fs, const char* path, size_t max_size) -{ - char buf[20] = "0"; - long res = 0, offset; - mg_http_get_var(&hm->query, "offset", buf, sizeof(buf)); - offset = strtol(buf, NULL, 0); - if (hm->body.len == 0) { - mg_http_reply(c, 200, "", "%ld", res); // Nothing to write - } else { - struct mg_fd* fd; - size_t current_size = 0; - MG_DEBUG(("%s -> %d bytes @ %ld", path, (int)hm->body.len, offset)); - if (offset == 0) - fs->rm(path); // If offset if 0, truncate file - fs->st(path, ¤t_size, NULL); - if (offset < 0) { - mg_http_reply(c, 400, "", "offset required"); - res = -1; - } else if (offset > 0 && current_size != (size_t)offset) { - mg_http_reply(c, 400, "", "%s: offset mismatch", path); - res = -2; - } else if ((size_t)offset + hm->body.len > max_size) { - mg_http_reply(c, 400, "", "%s: over max size of %lu", path, - (unsigned long)max_size); - res = -3; - } else if ((fd = mg_fs_open(fs, path, MG_FS_WRITE)) == NULL) { - mg_http_reply(c, 400, "", "open(%s): %d", path, errno); - res = -4; - } else { - res = offset + (long)fs->wr(fd->fd, hm->body.ptr, hm->body.len); - mg_fs_close(fd); - mg_http_reply(c, 200, "", "%ld", res); - } - } - return res; -} - -int mg_http_status(const struct mg_http_message* hm) -{ - return atoi(hm->uri.ptr); -} - -static bool is_hex_digit(int c) -{ - return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); -} - -// If a server sends data to the client using chunked encoding, Mongoose strips -// off the chunking prefix (hex length and \r\n) and suffix (\r\n), appends the -// stripped data to the body, and fires the MG_EV_HTTP_CHUNK event. When zero -// chunk is received, we fire MG_EV_HTTP_MSG, and the body already has all -// chunking prefixes/suffixes stripped. -// -// If a server sends data without chunked encoding, we also fire a series of -// MG_EV_HTTP_CHUNK events for every received piece of data, and then we fire -// MG_EV_HTTP_MSG event in the end. -// -// We track total processed length in the c->pfn_data, which is a void * -// pointer: we store a size_t value there. -static bool getchunk(struct mg_str s, size_t* prefixlen, size_t* datalen) -{ - size_t i = 0, n; - while (i < s.len && is_hex_digit(s.ptr[i])) - i++; - n = mg_unhexn(s.ptr, i); - // MG_INFO(("%d %d", (int) (i + n + 4), (int) s.len)); - if (s.len < i + n + 4) - return false; // Chunk not yet fully buffered - if (s.ptr[i] != '\r' || s.ptr[i + 1] != '\n') - return false; - if (s.ptr[i + n + 2] != '\r' || s.ptr[i + n + 3] != '\n') - return false; - *prefixlen = i + 2; - *datalen = n; - return true; -} - -static bool mg_is_chunked(struct mg_http_message* hm) -{ - const char* needle = "chunked"; - struct mg_str* te = mg_http_get_header(hm, "Transfer-Encoding"); - return te != NULL && mg_vcasecmp(te, needle) == 0; -} - -void mg_http_delete_chunk(struct mg_connection* c, struct mg_http_message* hm) -{ - size_t ofs = (size_t)(hm->chunk.ptr - (char*)c->recv.buf); - mg_iobuf_del(&c->recv, ofs, hm->chunk.len); - c->pfn_data = (void*)((size_t)c->pfn_data | MG_DMARK); -} - -static void deliver_chunked_chunks(struct mg_connection* c, size_t hlen, - struct mg_http_message* hm, bool* next) -{ - // | ... headers ... | HEXNUM\r\n ..data.. \r\n | ...... - // +------------------+--------------------------+---- - // | hlen | chunk1 | ...... - char *buf = (char*)&c->recv.buf[hlen], *p = buf; - size_t len = c->recv.len - hlen; - size_t processed = ((size_t)c->pfn_data) & ~MG_DMARK; - size_t mark, pl, dl, del = 0, ofs = 0; - bool last = false; - if (processed <= len) - len -= processed, buf += processed; - while (!last && getchunk(mg_str_n(buf + ofs, len - ofs), &pl, &dl)) { - size_t saved = c->recv.len; - memmove(p + processed, buf + ofs + pl, dl); - // MG_INFO(("P2 [%.*s]", (int) (processed + dl), p)); - hm->chunk = mg_str_n(p + processed, dl); - mg_call(c, MG_EV_HTTP_CHUNK, hm); - ofs += pl + dl + 2, del += pl + 2; // 2 is for \r\n suffix - processed += dl; - if (c->recv.len != saved) - processed -= dl, buf -= dl; - // mg_hexdump(c->recv.buf, hlen + processed); - last = (dl == 0); - } - mg_iobuf_del(&c->recv, hlen + processed, del); - mark = ((size_t)c->pfn_data) & MG_DMARK; - c->pfn_data = (void*)(processed | mark); - if (last) { - hm->body.len = processed; - hm->message.len = hlen + processed; - c->pfn_data = NULL; - if (mark) - mg_iobuf_del(&c->recv, 0, hlen), *next = true; - // MG_INFO(("LAST, mark: %lx", mark)); - // mg_hexdump(c->recv.buf, c->recv.len); - } -} - -static void deliver_normal_chunks(struct mg_connection* c, size_t hlen, - struct mg_http_message* hm, bool* next) -{ - size_t left, processed = ((size_t)c->pfn_data) & ~MG_DMARK; - size_t deleted = ((size_t)c->pfn_data) & MG_DMARK; - hm->chunk = mg_str_n((char*)&c->recv.buf[hlen], c->recv.len - hlen); - if (processed <= hm->chunk.len && !deleted) { - hm->chunk.len -= processed; - hm->chunk.ptr += processed; - } - left = hm->body.len < processed ? 0 : hm->body.len - processed; - if (hm->chunk.len > left) - hm->chunk.len = left; - if (hm->chunk.len > 0) - mg_call(c, MG_EV_HTTP_CHUNK, hm); - processed += hm->chunk.len; - deleted = ((size_t)c->pfn_data) & MG_DMARK; // Re-evaluate after user call - if (processed >= hm->body.len) { // Last, 0-len chunk - hm->chunk.len = 0; // Reset length - mg_call(c, MG_EV_HTTP_CHUNK, hm); // Call user handler - c->pfn_data = NULL; // Reset processed counter - if (processed && deleted) - mg_iobuf_del(&c->recv, 0, hlen), *next = true; - } else { - c->pfn_data = (void*)(processed | deleted); // if it is set - } -} - -static void http_cb(struct mg_connection* c, int ev, void* evd, void* fnd) -{ - if (ev == MG_EV_READ || ev == MG_EV_CLOSE) { - struct mg_http_message hm; - while (c->recv.buf != NULL && c->recv.len > 0) { - bool next = false; - int hlen = mg_http_parse((char*)c->recv.buf, c->recv.len, &hm); - if (hlen < 0) { - mg_error(c, "HTTP parse:\n%.*s", (int)c->recv.len, c->recv.buf); - break; - } - if (c->is_resp) - break; // Response is still generated - if (hlen == 0) - break; // Request is not buffered yet - if (ev == MG_EV_CLOSE) { // If client did not set Content-Length - hm.message.len = c->recv.len; // and closes now, deliver a MSG - hm.body.len = hm.message.len - (size_t)(hm.body.ptr - hm.message.ptr); - } - if (mg_is_chunked(&hm)) { - deliver_chunked_chunks(c, (size_t)hlen, &hm, &next); - } else { - deliver_normal_chunks(c, (size_t)hlen, &hm, &next); - } - if (next) - continue; // Chunks & request were deleted - // Chunk events are delivered. If we have full body, deliver MSG - if (c->recv.len < hm.message.len) - break; - if (c->is_accepted) - c->is_resp = 1; // Start generating response - mg_call(c, MG_EV_HTTP_MSG, &hm); // User handler can clear is_resp - mg_iobuf_del(&c->recv, 0, hm.message.len); - } - } - (void)evd, (void)fnd; -} - -static void mg_hfn(struct mg_connection* c, int ev, void* ev_data, void* fnd) -{ - if (ev == MG_EV_HTTP_MSG) { - struct mg_http_message* hm = (struct mg_http_message*)ev_data; - if (mg_http_match_uri(hm, "/quit")) { - mg_http_reply(c, 200, "", "ok\n"); - c->is_draining = 1; - c->data[0] = 'X'; - } else if (mg_http_match_uri(hm, "/debug")) { - int level = (int)mg_json_get_long(hm->body, "$.level", MG_LL_DEBUG); - mg_log_set(level); - mg_http_reply(c, 200, "", "Debug level set to %d\n", level); - } else { - mg_http_reply(c, 200, "", "hi\n"); - } - } else if (ev == MG_EV_CLOSE) { - if (c->data[0] == 'X') - *(bool*)fnd = true; - } -} - -void mg_hello(const char* url) -{ - struct mg_mgr mgr; - bool done = false; - mg_mgr_init(&mgr); - if (mg_http_listen(&mgr, url, mg_hfn, &done) == NULL) - done = true; - while (done == false) - mg_mgr_poll(&mgr, 100); - mg_mgr_free(&mgr); -} - -struct mg_connection* mg_http_connect(struct mg_mgr* mgr, const char* url, - mg_event_handler_t fn, void* fn_data) -{ - struct mg_connection* c = mg_connect(mgr, url, fn, fn_data); - if (c != NULL) - c->pfn = http_cb; - return c; -} - -struct mg_connection* mg_http_listen(struct mg_mgr* mgr, const char* url, - mg_event_handler_t fn, void* fn_data) -{ - struct mg_connection* c = mg_listen(mgr, url, fn, fn_data); - if (c != NULL) - c->pfn = http_cb; - return c; -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/iobuf.c" -#endif - -static size_t roundup(size_t size, size_t align) -{ - return align == 0 ? size : (size + align - 1) / align * align; -} - -int mg_iobuf_resize(struct mg_iobuf* io, size_t new_size) -{ - int ok = 1; - new_size = roundup(new_size, io->align); - if (new_size == 0) { - mg_bzero(io->buf, io->size); - free(io->buf); - io->buf = NULL; - io->len = io->size = 0; - } else if (new_size != io->size) { - // NOTE(lsm): do not use realloc here. Use calloc/free only, to ease the - // porting to some obscure platforms like FreeRTOS - void* p = calloc(1, new_size); - if (p != NULL) { - size_t len = new_size < io->len ? new_size : io->len; - if (len > 0 && io->buf != NULL) - memmove(p, io->buf, len); - mg_bzero(io->buf, io->size); - free(io->buf); - io->buf = (unsigned char*)p; - io->size = new_size; - } else { - ok = 0; - MG_ERROR(("%lld->%lld", (uint64_t)io->size, (uint64_t)new_size)); - } - } - return ok; -} - -int mg_iobuf_init(struct mg_iobuf* io, size_t size, size_t align) -{ - io->buf = NULL; - io->align = align; - io->size = io->len = 0; - return mg_iobuf_resize(io, size); -} - -size_t mg_iobuf_add(struct mg_iobuf* io, size_t ofs, const void* buf, - size_t len) -{ - size_t new_size = roundup(io->len + len, io->align); - mg_iobuf_resize(io, new_size); // Attempt to resize - if (new_size != io->size) - len = 0; // Resize failure, append nothing - if (ofs < io->len) - memmove(io->buf + ofs + len, io->buf + ofs, io->len - ofs); - if (buf != NULL) - memmove(io->buf + ofs, buf, len); - if (ofs > io->len) - io->len += ofs - io->len; - io->len += len; - return len; -} - -size_t mg_iobuf_del(struct mg_iobuf* io, size_t ofs, size_t len) -{ - if (ofs > io->len) - ofs = io->len; - if (ofs + len > io->len) - len = io->len - ofs; - if (io->buf) - memmove(io->buf + ofs, io->buf + ofs + len, io->len - ofs - len); - if (io->buf) - mg_bzero(io->buf + io->len - len, len); - io->len -= len; - return len; -} - -void mg_iobuf_free(struct mg_iobuf* io) -{ - mg_iobuf_resize(io, 0); -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/json.c" -#endif - -static const char* escapeseq(int esc) -{ - return esc ? "\b\f\n\r\t\\\"" : "bfnrt\\\""; -} - -static char json_esc(int c, int esc) -{ - const char *p, *esc1 = escapeseq(esc), *esc2 = escapeseq(!esc); - for (p = esc1; *p != '\0'; p++) { - if (*p == c) - return esc2[p - esc1]; - } - return 0; -} - -static int mg_pass_string(const char* s, int len) -{ - int i; - for (i = 0; i < len; i++) { - if (s[i] == '\\' && i + 1 < len && json_esc(s[i + 1], 1)) { - i++; - } else if (s[i] == '\0') { - return MG_JSON_INVALID; - } else if (s[i] == '"') { - return i; - } - } - return MG_JSON_INVALID; -} - -static double mg_atod(const char* p, int len, int* numlen) -{ - double d = 0.0; - int i = 0, sign = 1; - - // Sign - if (i < len && *p == '-') { - sign = -1, i++; - } else if (i < len && *p == '+') { - i++; - } - - // Decimal - for (; i < len && p[i] >= '0' && p[i] <= '9'; i++) { - d *= 10.0; - d += p[i] - '0'; - } - d *= sign; - - // Fractional - if (i < len && p[i] == '.') { - double frac = 0.0, base = 0.1; - i++; - for (; i < len && p[i] >= '0' && p[i] <= '9'; i++) { - frac += base * (p[i] - '0'); - base /= 10.0; - } - d += frac * sign; - } - - // Exponential - if (i < len && (p[i] == 'e' || p[i] == 'E')) { - int j, exp = 0, minus = 0; - i++; - if (i < len && p[i] == '-') - minus = 1, i++; - if (i < len && p[i] == '+') - i++; - while (i < len && p[i] >= '0' && p[i] <= '9' && exp < 308) - exp = exp * 10 + (p[i++] - '0'); - if (minus) - exp = -exp; - for (j = 0; j < exp; j++) - d *= 10.0; - for (j = 0; j < -exp; j++) - d /= 10.0; - } - - if (numlen != NULL) - *numlen = i; - return d; -} - -int mg_json_get(struct mg_str json, const char* path, int* toklen) -{ - const char* s = json.ptr; - int len = (int)json.len; - enum { S_VALUE, - S_KEY, - S_COLON, - S_COMMA_OR_EOO } expecting - = S_VALUE; - unsigned char nesting[MG_JSON_MAX_DEPTH]; - int i = 0; // Current offset in `s` - int j = 0; // Offset in `s` we're looking for (return value) - int depth = 0; // Current depth (nesting level) - int ed = 0; // Expected depth - int pos = 1; // Current position in `path` - int ci = -1, ei = -1; // Current and expected index in array - - if (toklen) - *toklen = 0; - if (path[0] != '$') - return MG_JSON_INVALID; - -#define MG_CHECKRET(x) \ - do { \ - if (depth == ed && path[pos] == '\0' && ci == ei) { \ - if (toklen) \ - *toklen = i - j + 1; \ - return j; \ - } \ - } while (0) - -// In the ascii table, the distance between `[` and `]` is 2. -// Ditto for `{` and `}`. Hence +2 in the code below. -#define MG_EOO(x) \ - do { \ - if (depth == ed && ci != ei) \ - return MG_JSON_NOT_FOUND; \ - if (c != nesting[depth - 1] + 2) \ - return MG_JSON_INVALID; \ - depth--; \ - MG_CHECKRET(x); \ - } while (0) - - for (i = 0; i < len; i++) { - unsigned char c = ((unsigned char*)s)[i]; - if (c == ' ' || c == '\t' || c == '\n' || c == '\r') - continue; - switch (expecting) { - case S_VALUE: - // p("V %s [%.*s] %d %d %d %d\n", path, pos, path, depth, ed, ci, ei); - if (depth == ed) - j = i; - if (c == '{') { - if (depth >= (int)sizeof(nesting)) - return MG_JSON_TOO_DEEP; - if (depth == ed && path[pos] == '.' && ci == ei) { - // If we start the object, reset array indices - ed++, pos++, ci = ei = -1; - } - nesting[depth++] = c; - expecting = S_KEY; - break; - } else if (c == '[') { - if (depth >= (int)sizeof(nesting)) - return MG_JSON_TOO_DEEP; - if (depth == ed && path[pos] == '[' && ei == ci) { - ed++, pos++, ci = 0; - for (ei = 0; path[pos] != ']' && path[pos] != '\0'; pos++) { - ei *= 10; - ei += path[pos] - '0'; - } - if (path[pos] != 0) - pos++; - } - nesting[depth++] = c; - break; - } else if (c == ']' && depth > 0) { // Empty array - MG_EOO(']'); - } else if (c == 't' && i + 3 < len && memcmp(&s[i], "true", 4) == 0) { - i += 3; - } else if (c == 'n' && i + 3 < len && memcmp(&s[i], "null", 4) == 0) { - i += 3; - } else if (c == 'f' && i + 4 < len && memcmp(&s[i], "false", 5) == 0) { - i += 4; - } else if (c == '-' || ((c >= '0' && c <= '9'))) { - int numlen = 0; - mg_atod(&s[i], len - i, &numlen); - i += numlen - 1; - } else if (c == '"') { - int n = mg_pass_string(&s[i + 1], len - i - 1); - if (n < 0) - return n; - i += n + 1; - } else { - return MG_JSON_INVALID; - } - MG_CHECKRET('V'); - if (depth == ed && ei >= 0) - ci++; - expecting = S_COMMA_OR_EOO; - break; - - case S_KEY: - if (c == '"') { - int n = mg_pass_string(&s[i + 1], len - i - 1); - if (n < 0) - return n; - if (i + 1 + n >= len) - return MG_JSON_NOT_FOUND; - if (depth < ed) - return MG_JSON_NOT_FOUND; - if (depth == ed && path[pos - 1] != '.') - return MG_JSON_NOT_FOUND; - // printf("K %s [%.*s] [%.*s] %d %d %d %d %d\n", path, pos, path, n, - // &s[i + 1], n, depth, ed, ci, ei); - // NOTE(cpq): in the check sequence below is important. - // strncmp() must go first: it fails fast if the remaining length of - // the path is smaller than `n`. - if (depth == ed && path[pos - 1] == '.' && strncmp(&s[i + 1], &path[pos], (size_t)n) == 0 && (path[pos + n] == '\0' || path[pos + n] == '.' || path[pos + n] == '[')) { - pos += n; - } - i += n + 1; - expecting = S_COLON; - } else if (c == '}') { // Empty object - MG_EOO('}'); - expecting = S_COMMA_OR_EOO; - if (depth == ed && ei >= 0) - ci++; - } else { - return MG_JSON_INVALID; - } - break; - - case S_COLON: - if (c == ':') { - expecting = S_VALUE; - } else { - return MG_JSON_INVALID; - } - break; - - case S_COMMA_OR_EOO: - if (depth <= 0) { - return MG_JSON_INVALID; - } else if (c == ',') { - expecting = (nesting[depth - 1] == '{') ? S_KEY : S_VALUE; - } else if (c == ']' || c == '}') { - if (depth == ed && c == '}' && path[pos - 1] == '.') - return MG_JSON_NOT_FOUND; - if (depth == ed && c == ']' && path[pos - 1] == ',') - return MG_JSON_NOT_FOUND; - MG_EOO('O'); - if (depth == ed && ei >= 0) - ci++; - } else { - return MG_JSON_INVALID; - } - break; - } - } - return MG_JSON_NOT_FOUND; -} - -bool mg_json_get_num(struct mg_str json, const char* path, double* v) -{ - int n, toklen, found = 0; - if ((n = mg_json_get(json, path, &toklen)) >= 0 && (json.ptr[n] == '-' || (json.ptr[n] >= '0' && json.ptr[n] <= '9'))) { - if (v != NULL) - *v = mg_atod(json.ptr + n, toklen, NULL); - found = 1; - } - return found; -} - -bool mg_json_get_bool(struct mg_str json, const char* path, bool* v) -{ - int found = 0, off = mg_json_get(json, path, NULL); - if (off >= 0 && (json.ptr[off] == 't' || json.ptr[off] == 'f')) { - if (v != NULL) - *v = json.ptr[off] == 't'; - found = 1; - } - return found; -} - -bool mg_json_unescape(struct mg_str s, char* to, size_t n) -{ - size_t i, j; - for (i = 0, j = 0; i < s.len && j < n; i++, j++) { - if (s.ptr[i] == '\\' && i + 5 < s.len && s.ptr[i + 1] == 'u') { - // \uXXXX escape. We could process a simple one-byte chars - // \u00xx from the ASCII range. More complex chars would require - // dragging in a UTF8 library, which is too much for us - if (s.ptr[i + 2] != '0' || s.ptr[i + 3] != '0') - return false; // Give up - ((unsigned char*)to)[j] = (unsigned char)mg_unhexn(s.ptr + i + 4, 2); - - i += 5; - } else if (s.ptr[i] == '\\' && i + 1 < s.len) { - char c = json_esc(s.ptr[i + 1], 0); - if (c == 0) - return false; - to[j] = c; - i++; - } else { - to[j] = s.ptr[i]; - } - } - if (j >= n) - return false; - if (n > 0) - to[j] = '\0'; - return true; -} - -char* mg_json_get_str(struct mg_str json, const char* path) -{ - char* result = NULL; - int len = 0, off = mg_json_get(json, path, &len); - if (off >= 0 && len > 1 && json.ptr[off] == '"') { - if ((result = (char*)calloc(1, (size_t)len)) != NULL && !mg_json_unescape(mg_str_n(json.ptr + off + 1, (size_t)(len - 2)), result, (size_t)len)) { - free(result); - result = NULL; - } - } - return result; -} - -char* mg_json_get_b64(struct mg_str json, const char* path, int* slen) -{ - char* result = NULL; - int len = 0, off = mg_json_get(json, path, &len); - if (off >= 0 && json.ptr[off] == '"' && len > 1 && (result = (char*)calloc(1, (size_t)len)) != NULL) { - size_t k = mg_base64_decode(json.ptr + off + 1, (size_t)(len - 2), result, - (size_t)len); - if (slen != NULL) - *slen = (int)k; - } - return result; -} - -char* mg_json_get_hex(struct mg_str json, const char* path, int* slen) -{ - char* result = NULL; - int len = 0, off = mg_json_get(json, path, &len); - if (off >= 0 && json.ptr[off] == '"' && len > 1 && (result = (char*)calloc(1, (size_t)len / 2)) != NULL) { - mg_unhex(json.ptr + off + 1, (size_t)(len - 2), (uint8_t*)result); - result[len / 2 - 1] = '\0'; - if (slen != NULL) - *slen = len / 2 - 1; - } - return result; -} - -long mg_json_get_long(struct mg_str json, const char* path, long dflt) -{ - double dv; - long result = dflt; - if (mg_json_get_num(json, path, &dv)) - result = (long)dv; - return result; -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/log.c" -#endif - -static int s_level = MG_LL_INFO; -static mg_pfn_t s_log_func = mg_pfn_stdout; -static void* s_log_func_param = NULL; - -void mg_log_set_fn(mg_pfn_t fn, void* param) -{ - s_log_func = fn; - s_log_func_param = param; -} - -static void logc(unsigned char c) -{ - s_log_func((char)c, s_log_func_param); -} - -static void logs(const char* buf, size_t len) -{ - size_t i; - for (i = 0; i < len; i++) - logc(((unsigned char*)buf)[i]); -} - -void mg_log_set(int log_level) -{ - MG_DEBUG(("Setting log level to %d", log_level)); - s_level = log_level; -} - -bool mg_log_prefix(int level, const char* file, int line, const char* fname) -{ - if (level <= s_level) { - const char* p = strrchr(file, '/'); - char buf[51]; - size_t n; - if (p == NULL) - p = strrchr(file, '\\'); - n = mg_snprintf(buf, sizeof(buf), "%-6llx %d %s:%d:%s", mg_millis(), level, - p == NULL ? file : p + 1, line, fname); - if (n > sizeof(buf) - 2) - n = sizeof(buf) - 2; - while (n < sizeof(buf)) - buf[n++] = ' '; - logs(buf, n - 1); - return true; - } else { - return false; - } -} - -void mg_log(const char* fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - mg_vxprintf(s_log_func, s_log_func_param, fmt, &ap); - va_end(ap); - logs("\r\n", 2); -} - -static unsigned char nibble(unsigned c) -{ - return (unsigned char)(c < 10 ? c + '0' : c + 'W'); -} - -#define ISPRINT(x) ((x) >= ' ' && (x) <= '~') -void mg_hexdump(const void* buf, size_t len) -{ - const unsigned char* p = (const unsigned char*)buf; - unsigned char ascii[16], alen = 0; - size_t i; - for (i = 0; i < len; i++) { - if ((i % 16) == 0) { - // Print buffered ascii chars - if (i > 0) - logs(" ", 2), logs((char*)ascii, 16), logc('\n'), alen = 0; - // Print hex address, then \t - logc(nibble((i >> 12) & 15)), logc(nibble((i >> 8) & 15)), - logc(nibble((i >> 4) & 15)), logc('0'), logs(" ", 3); - } - logc(nibble(p[i] >> 4)), logc(nibble(p[i] & 15)); // Two nibbles, e.g. c5 - logc(' '); // Space after hex number - ascii[alen++] = ISPRINT(p[i]) ? p[i] : '.'; // Add to the ascii buf - } - while (alen < 16) - logs(" ", 3), ascii[alen++] = ' '; - logs(" ", 2), logs((char*)ascii, 16), logc('\n'); -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/md5.c" -#endif - -#if defined(MG_ENABLE_MD5) && MG_ENABLE_MD5 - -static void mg_byte_reverse(unsigned char* buf, unsigned longs) -{ - if (MG_BIG_ENDIAN) { - do { - uint32_t t = (uint32_t)((unsigned)buf[3] << 8 | buf[2]) << 16 | ((unsigned)buf[1] << 8 | buf[0]); - *(uint32_t*)buf = t; - buf += 4; - } while (--longs); - } else { - (void)buf, (void)longs; // Little endian. Do nothing - } -} - -#define F1(x, y, z) (z ^ (x & (y ^ z))) -#define F2(x, y, z) F1(z, x, y) -#define F3(x, y, z) (x ^ y ^ z) -#define F4(x, y, z) (y ^ (x | ~z)) - -#define MD5STEP(f, w, x, y, z, data, s) \ - (w += f(x, y, z) + data, w = w << s | w >> (32 - s), w += x) - -/* - * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious - * initialization constants. - */ -void mg_md5_init(mg_md5_ctx* ctx) -{ - ctx->buf[0] = 0x67452301; - ctx->buf[1] = 0xefcdab89; - ctx->buf[2] = 0x98badcfe; - ctx->buf[3] = 0x10325476; - - ctx->bits[0] = 0; - ctx->bits[1] = 0; -} - -static void mg_md5_transform(uint32_t buf[4], uint32_t const in[16]) -{ - uint32_t a, b, c, d; - - a = buf[0]; - b = buf[1]; - c = buf[2]; - d = buf[3]; - - MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); - MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); - MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); - MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); - MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); - MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); - MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); - MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); - MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); - MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); - MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); - MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); - MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); - MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); - MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); - MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); - - MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); - MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); - MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); - MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); - MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); - MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); - MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); - MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); - MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); - MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); - MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); - MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); - MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); - MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); - MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); - MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); - - MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); - MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); - MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); - MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); - MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); - MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); - MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); - MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); - MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); - MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); - MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); - MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); - MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); - MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); - MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); - MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); - - MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); - MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); - MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); - MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); - MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); - MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); - MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); - MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); - MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); - MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); - MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); - MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); - MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); - MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); - MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); - MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); - - buf[0] += a; - buf[1] += b; - buf[2] += c; - buf[3] += d; -} - -void mg_md5_update(mg_md5_ctx* ctx, const unsigned char* buf, size_t len) -{ - uint32_t t; - - t = ctx->bits[0]; - if ((ctx->bits[0] = t + ((uint32_t)len << 3)) < t) - ctx->bits[1]++; - ctx->bits[1] += (uint32_t)len >> 29; - - t = (t >> 3) & 0x3f; - - if (t) { - unsigned char* p = (unsigned char*)ctx->in + t; - - t = 64 - t; - if (len < t) { - memcpy(p, buf, len); - return; - } - memcpy(p, buf, t); - mg_byte_reverse(ctx->in, 16); - mg_md5_transform(ctx->buf, (uint32_t*)ctx->in); - buf += t; - len -= t; - } - - while (len >= 64) { - memcpy(ctx->in, buf, 64); - mg_byte_reverse(ctx->in, 16); - mg_md5_transform(ctx->buf, (uint32_t*)ctx->in); - buf += 64; - len -= 64; - } - - memcpy(ctx->in, buf, len); -} - -void mg_md5_final(mg_md5_ctx* ctx, unsigned char digest[16]) -{ - unsigned count; - unsigned char* p; - uint32_t* a; - - count = (ctx->bits[0] >> 3) & 0x3F; - - p = ctx->in + count; - *p++ = 0x80; - count = 64 - 1 - count; - if (count < 8) { - memset(p, 0, count); - mg_byte_reverse(ctx->in, 16); - mg_md5_transform(ctx->buf, (uint32_t*)ctx->in); - memset(ctx->in, 0, 56); - } else { - memset(p, 0, count - 8); - } - mg_byte_reverse(ctx->in, 14); - - a = (uint32_t*)ctx->in; - a[14] = ctx->bits[0]; - a[15] = ctx->bits[1]; - - mg_md5_transform(ctx->buf, (uint32_t*)ctx->in); - mg_byte_reverse((unsigned char*)ctx->buf, 4); - memcpy(digest, ctx->buf, 16); - memset((char*)ctx, 0, sizeof(*ctx)); -} -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/mqtt.c" -#endif - -#define MQTT_CLEAN_SESSION 0x02 -#define MQTT_HAS_WILL 0x04 -#define MQTT_WILL_RETAIN 0x20 -#define MQTT_HAS_PASSWORD 0x40 -#define MQTT_HAS_USER_NAME 0x80 - -struct mg_mqtt_pmap { - uint8_t id; - uint8_t type; -}; - -static const struct mg_mqtt_pmap s_prop_map[] = { - { MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, MQTT_PROP_TYPE_BYTE }, - { MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, MQTT_PROP_TYPE_INT }, - { MQTT_PROP_CONTENT_TYPE, MQTT_PROP_TYPE_STRING }, - { MQTT_PROP_RESPONSE_TOPIC, MQTT_PROP_TYPE_STRING }, - { MQTT_PROP_CORRELATION_DATA, MQTT_PROP_TYPE_BINARY_DATA }, - { MQTT_PROP_SUBSCRIPTION_IDENTIFIER, MQTT_PROP_TYPE_VARIABLE_INT }, - { MQTT_PROP_SESSION_EXPIRY_INTERVAL, MQTT_PROP_TYPE_INT }, - { MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, MQTT_PROP_TYPE_STRING }, - { MQTT_PROP_SERVER_KEEP_ALIVE, MQTT_PROP_TYPE_SHORT }, - { MQTT_PROP_AUTHENTICATION_METHOD, MQTT_PROP_TYPE_STRING }, - { MQTT_PROP_AUTHENTICATION_DATA, MQTT_PROP_TYPE_BINARY_DATA }, - { MQTT_PROP_REQUEST_PROBLEM_INFORMATION, MQTT_PROP_TYPE_BYTE }, - { MQTT_PROP_WILL_DELAY_INTERVAL, MQTT_PROP_TYPE_INT }, - { MQTT_PROP_REQUEST_RESPONSE_INFORMATION, MQTT_PROP_TYPE_BYTE }, - { MQTT_PROP_RESPONSE_INFORMATION, MQTT_PROP_TYPE_STRING }, - { MQTT_PROP_SERVER_REFERENCE, MQTT_PROP_TYPE_STRING }, - { MQTT_PROP_REASON_STRING, MQTT_PROP_TYPE_STRING }, - { MQTT_PROP_RECEIVE_MAXIMUM, MQTT_PROP_TYPE_SHORT }, - { MQTT_PROP_TOPIC_ALIAS_MAXIMUM, MQTT_PROP_TYPE_SHORT }, - { MQTT_PROP_TOPIC_ALIAS, MQTT_PROP_TYPE_SHORT }, - { MQTT_PROP_MAXIMUM_QOS, MQTT_PROP_TYPE_BYTE }, - { MQTT_PROP_RETAIN_AVAILABLE, MQTT_PROP_TYPE_BYTE }, - { MQTT_PROP_USER_PROPERTY, MQTT_PROP_TYPE_STRING_PAIR }, - { MQTT_PROP_MAXIMUM_PACKET_SIZE, MQTT_PROP_TYPE_INT }, - { MQTT_PROP_WILDCARD_SUBSCRIPTION_AVAILABLE, MQTT_PROP_TYPE_BYTE }, - { MQTT_PROP_SUBSCRIPTION_IDENTIFIER_AVAILABLE, MQTT_PROP_TYPE_BYTE }, - { MQTT_PROP_SHARED_SUBSCRIPTION_AVAILABLE, MQTT_PROP_TYPE_BYTE } -}; - -void mg_mqtt_send_header(struct mg_connection* c, uint8_t cmd, uint8_t flags, - uint32_t len) -{ - uint8_t buf[1 + sizeof(len)], *vlen = &buf[1]; - buf[0] = (uint8_t)((cmd << 4) | flags); - do { - *vlen = len % 0x80; - len /= 0x80; - if (len > 0) - *vlen |= 0x80; - vlen++; - } while (len > 0 && vlen < &buf[sizeof(buf)]); - mg_send(c, buf, (size_t)(vlen - buf)); -} - -static void mg_send_u16(struct mg_connection* c, uint16_t value) -{ - mg_send(c, &value, sizeof(value)); -} - -static void mg_send_u32(struct mg_connection* c, uint32_t value) -{ - mg_send(c, &value, sizeof(value)); -} - -static uint8_t varint_size(size_t length) -{ - uint8_t bytes_needed = 0; - do { - bytes_needed++; - length /= 0x80; - } while (length > 0); - return bytes_needed; -} - -static size_t encode_varint(uint8_t* buf, size_t value) -{ - size_t len = 0; - - do { - uint8_t byte = (uint8_t)(value % 128); - value /= 128; - if (value > 0) - byte |= 0x80; - buf[len++] = byte; - } while (value > 0); - - return len; -} - -static size_t decode_varint(const uint8_t* buf, size_t len, size_t* value) -{ - size_t multiplier = 1, offset; - *value = 0; - - for (offset = 0; offset < 4 && offset < len; offset++) { - uint8_t encoded_byte = buf[offset]; - *value += (encoded_byte & 0x7f) * multiplier; - multiplier *= 128; - - if ((encoded_byte & 0x80) == 0) - return offset + 1; - } - - return 0; -} - -static int mqtt_prop_type_by_id(uint8_t prop_id) -{ - size_t i, num_properties = sizeof(s_prop_map) / sizeof(s_prop_map[0]); - for (i = 0; i < num_properties; ++i) { - if (s_prop_map[i].id == prop_id) - return s_prop_map[i].type; - } - return -1; // Property ID not found -} - -// Returns the size of the properties section, without the -// size of the content's length -static size_t get_properties_length(struct mg_mqtt_prop* props, size_t count) -{ - size_t i, size = 0; - for (i = 0; i < count; i++) { - size++; // identifier - switch (mqtt_prop_type_by_id(props[i].id)) { - case MQTT_PROP_TYPE_STRING_PAIR: - size += (uint32_t)(props[i].val.len + props[i].key.len + 2 * sizeof(uint16_t)); - break; - case MQTT_PROP_TYPE_STRING: - size += (uint32_t)(props[i].val.len + sizeof(uint16_t)); - break; - case MQTT_PROP_TYPE_BINARY_DATA: - size += (uint32_t)(props[i].val.len + sizeof(uint16_t)); - break; - case MQTT_PROP_TYPE_VARIABLE_INT: - size += varint_size((uint32_t)props[i].iv); - break; - case MQTT_PROP_TYPE_INT: - size += (uint32_t)sizeof(uint32_t); - break; - case MQTT_PROP_TYPE_SHORT: - size += (uint32_t)sizeof(uint16_t); - break; - case MQTT_PROP_TYPE_BYTE: - size += (uint32_t)sizeof(uint8_t); - break; - default: - return size; // cannot parse further down - } - } - - return size; -} - -// returns the entire size of the properties section, including the -// size of the variable length of the content -static size_t get_props_size(struct mg_mqtt_prop* props, size_t count) -{ - size_t size = get_properties_length(props, count); - size += varint_size(size); - return size; -} - -static void mg_send_mqtt_properties(struct mg_connection* c, - struct mg_mqtt_prop* props, size_t nprops) -{ - size_t total_size = get_properties_length(props, nprops); - uint8_t buf_v[4] = { 0, 0, 0, 0 }; - uint8_t buf[4] = { 0, 0, 0, 0 }; - size_t i, len = encode_varint(buf, total_size); - - mg_send(c, buf, (size_t)len); - for (i = 0; i < nprops; i++) { - mg_send(c, &props[i].id, sizeof(props[i].id)); - switch (mqtt_prop_type_by_id(props[i].id)) { - case MQTT_PROP_TYPE_STRING_PAIR: - mg_send_u16(c, mg_htons((uint16_t)props[i].key.len)); - mg_send(c, props[i].key.ptr, props[i].key.len); - mg_send_u16(c, mg_htons((uint16_t)props[i].val.len)); - mg_send(c, props[i].val.ptr, props[i].val.len); - break; - case MQTT_PROP_TYPE_BYTE: - mg_send(c, &props[i].iv, sizeof(uint8_t)); - break; - case MQTT_PROP_TYPE_SHORT: - mg_send_u16(c, mg_htons((uint16_t)props[i].iv)); - break; - case MQTT_PROP_TYPE_INT: - mg_send_u32(c, mg_htonl((uint32_t)props[i].iv)); - break; - case MQTT_PROP_TYPE_STRING: - mg_send_u16(c, mg_htons((uint16_t)props[i].val.len)); - mg_send(c, props[i].val.ptr, props[i].val.len); - break; - case MQTT_PROP_TYPE_BINARY_DATA: - mg_send_u16(c, mg_htons((uint16_t)props[i].val.len)); - mg_send(c, props[i].val.ptr, props[i].val.len); - break; - case MQTT_PROP_TYPE_VARIABLE_INT: - len = encode_varint(buf_v, props[i].iv); - mg_send(c, buf_v, (size_t)len); - break; - } - } -} - -size_t mg_mqtt_next_prop(struct mg_mqtt_message* msg, struct mg_mqtt_prop* prop, - size_t ofs) -{ - uint8_t* i = (uint8_t*)msg->dgram.ptr + msg->props_start + ofs; - uint8_t* end = (uint8_t*)msg->dgram.ptr + msg->dgram.len; - size_t new_pos = ofs, len; - prop->id = i[0]; - - if (ofs >= msg->dgram.len || ofs >= msg->props_start + msg->props_size) - return 0; - i++, new_pos++; - - switch (mqtt_prop_type_by_id(prop->id)) { - case MQTT_PROP_TYPE_STRING_PAIR: - prop->key.len = (uint16_t)((((uint16_t)i[0]) << 8) | i[1]); - prop->key.ptr = (char*)i + 2; - i += 2 + prop->key.len; - prop->val.len = (uint16_t)((((uint16_t)i[0]) << 8) | i[1]); - prop->val.ptr = (char*)i + 2; - new_pos += 2 * sizeof(uint16_t) + prop->val.len + prop->key.len; - break; - case MQTT_PROP_TYPE_BYTE: - prop->iv = (uint8_t)i[0]; - new_pos++; - break; - case MQTT_PROP_TYPE_SHORT: - prop->iv = (uint16_t)((((uint16_t)i[0]) << 8) | i[1]); - new_pos += sizeof(uint16_t); - break; - case MQTT_PROP_TYPE_INT: - prop->iv = ((uint32_t)i[0] << 24) | ((uint32_t)i[1] << 16) | ((uint32_t)i[2] << 8) | i[3]; - new_pos += sizeof(uint32_t); - break; - case MQTT_PROP_TYPE_STRING: - prop->val.len = (uint16_t)((((uint16_t)i[0]) << 8) | i[1]); - prop->val.ptr = (char*)i + 2; - new_pos += 2 + prop->val.len; - break; - case MQTT_PROP_TYPE_BINARY_DATA: - prop->val.len = (uint16_t)((((uint16_t)i[0]) << 8) | i[1]); - prop->val.ptr = (char*)i + 2; - new_pos += 2 + prop->val.len; - break; - case MQTT_PROP_TYPE_VARIABLE_INT: - len = decode_varint(i, (size_t)(end - i), (size_t*)&prop->iv); - new_pos = (!len) ? 0 : new_pos + len; - break; - default: - new_pos = 0; - } - - return new_pos; -} - -void mg_mqtt_login(struct mg_connection* c, const struct mg_mqtt_opts* opts) -{ - char rnd[10], client_id[21]; - struct mg_str cid = opts->client_id; - size_t total_len = 7 + 1 + 2 + 2; - uint8_t hdr[8] = { 0, 4, 'M', 'Q', 'T', 'T', opts->version, 0 }; - - if (cid.len == 0) { - mg_random(rnd, sizeof(rnd)); - mg_hex(rnd, sizeof(rnd), client_id); - client_id[sizeof(client_id) - 1] = '\0'; - cid = mg_str(client_id); - } - - if (hdr[6] == 0) - hdr[6] = 4; // If version is not set, use 4 (3.1.1) - c->is_mqtt5 = hdr[6] == 5; // Set version 5 flag - hdr[7] = (uint8_t)((opts->qos & 3) << 3); // Connection flags - if (opts->user.len > 0) { - total_len += 2 + (uint32_t)opts->user.len; - hdr[7] |= MQTT_HAS_USER_NAME; - } - if (opts->pass.len > 0) { - total_len += 2 + (uint32_t)opts->pass.len; - hdr[7] |= MQTT_HAS_PASSWORD; - } - if (opts->topic.len > 0 && opts->message.len > 0) { - total_len += 4 + (uint32_t)opts->topic.len + (uint32_t)opts->message.len; - hdr[7] |= MQTT_HAS_WILL; - } - if (opts->clean || cid.len == 0) - hdr[7] |= MQTT_CLEAN_SESSION; - if (opts->retain) - hdr[7] |= MQTT_WILL_RETAIN; - total_len += (uint32_t)cid.len; - if (c->is_mqtt5) { - total_len += get_props_size(opts->props, opts->num_props); - if (hdr[7] & MQTT_HAS_WILL) - total_len += get_props_size(opts->will_props, opts->num_will_props); - } - - mg_mqtt_send_header(c, MQTT_CMD_CONNECT, 0, (uint32_t)total_len); - mg_send(c, hdr, sizeof(hdr)); - // keepalive == 0 means "do not disconnect us!" - mg_send_u16(c, mg_htons((uint16_t)opts->keepalive)); - - if (c->is_mqtt5) - mg_send_mqtt_properties(c, opts->props, opts->num_props); - - mg_send_u16(c, mg_htons((uint16_t)cid.len)); - mg_send(c, cid.ptr, cid.len); - - if (hdr[7] & MQTT_HAS_WILL) { - if (c->is_mqtt5) - mg_send_mqtt_properties(c, opts->will_props, opts->num_will_props); - - mg_send_u16(c, mg_htons((uint16_t)opts->topic.len)); - mg_send(c, opts->topic.ptr, opts->topic.len); - mg_send_u16(c, mg_htons((uint16_t)opts->message.len)); - mg_send(c, opts->message.ptr, opts->message.len); - } - if (opts->user.len > 0) { - mg_send_u16(c, mg_htons((uint16_t)opts->user.len)); - mg_send(c, opts->user.ptr, opts->user.len); - } - if (opts->pass.len > 0) { - mg_send_u16(c, mg_htons((uint16_t)opts->pass.len)); - mg_send(c, opts->pass.ptr, opts->pass.len); - } -} - -void mg_mqtt_pub(struct mg_connection* c, const struct mg_mqtt_opts* opts) -{ - uint8_t flags = (uint8_t)(((opts->qos & 3) << 1) | (opts->retain ? 1 : 0)); - size_t len = 2 + opts->topic.len + opts->message.len; - MG_DEBUG(("%lu [%.*s] -> [%.*s]", c->id, (int)opts->topic.len, - (char*)opts->topic.ptr, (int)opts->message.len, - (char*)opts->message.ptr)); - if (opts->qos > 0) - len += 2; - if (c->is_mqtt5) - len += get_props_size(opts->props, opts->num_props); - - mg_mqtt_send_header(c, MQTT_CMD_PUBLISH, flags, (uint32_t)len); - mg_send_u16(c, mg_htons((uint16_t)opts->topic.len)); - mg_send(c, opts->topic.ptr, opts->topic.len); - if (opts->qos > 0) { - if (++c->mgr->mqtt_id == 0) - ++c->mgr->mqtt_id; - mg_send_u16(c, mg_htons(c->mgr->mqtt_id)); - } - - if (c->is_mqtt5) - mg_send_mqtt_properties(c, opts->props, opts->num_props); - - mg_send(c, opts->message.ptr, opts->message.len); -} - -void mg_mqtt_sub(struct mg_connection* c, const struct mg_mqtt_opts* opts) -{ - uint8_t qos_ = opts->qos & 3; - size_t plen = c->is_mqtt5 ? get_props_size(opts->props, opts->num_props) : 0; - size_t len = 2 + opts->topic.len + 2 + 1 + plen; - - mg_mqtt_send_header(c, MQTT_CMD_SUBSCRIBE, 2, (uint32_t)len); - if (++c->mgr->mqtt_id == 0) - ++c->mgr->mqtt_id; - mg_send_u16(c, mg_htons(c->mgr->mqtt_id)); - if (c->is_mqtt5) - mg_send_mqtt_properties(c, opts->props, opts->num_props); - - mg_send_u16(c, mg_htons((uint16_t)opts->topic.len)); - mg_send(c, opts->topic.ptr, opts->topic.len); - mg_send(c, &qos_, sizeof(qos_)); -} - -int mg_mqtt_parse(const uint8_t* buf, size_t len, uint8_t version, - struct mg_mqtt_message* m) -{ - uint8_t lc = 0, *p, *end; - uint32_t n = 0, len_len = 0; - - memset(m, 0, sizeof(*m)); - m->dgram.ptr = (char*)buf; - if (len < 2) - return MQTT_INCOMPLETE; - m->cmd = (uint8_t)(buf[0] >> 4); - m->qos = (buf[0] >> 1) & 3; - - n = len_len = 0; - p = (uint8_t*)buf + 1; - while ((size_t)(p - buf) < len) { - lc = *((uint8_t*)p++); - n += (uint32_t)((lc & 0x7f) << 7 * len_len); - len_len++; - if (!(lc & 0x80)) - break; - if (len_len >= 4) - return MQTT_MALFORMED; - } - end = p + n; - if ((lc & 0x80) || (end > buf + len)) - return MQTT_INCOMPLETE; - m->dgram.len = (size_t)(end - buf); - - switch (m->cmd) { - case MQTT_CMD_CONNACK: - if (end - p < 2) - return MQTT_MALFORMED; - m->ack = p[1]; - break; - case MQTT_CMD_PUBACK: - case MQTT_CMD_PUBREC: - case MQTT_CMD_PUBREL: - case MQTT_CMD_PUBCOMP: - case MQTT_CMD_SUBSCRIBE: - case MQTT_CMD_SUBACK: - case MQTT_CMD_UNSUBSCRIBE: - case MQTT_CMD_UNSUBACK: - if (p + 2 > end) - return MQTT_MALFORMED; - m->id = (uint16_t)((((uint16_t)p[0]) << 8) | p[1]); - p += 2; - break; - case MQTT_CMD_PUBLISH: { - if (p + 2 > end) - return MQTT_MALFORMED; - m->topic.len = (uint16_t)((((uint16_t)p[0]) << 8) | p[1]); - m->topic.ptr = (char*)p + 2; - p += 2 + m->topic.len; - if (p > end) - return MQTT_MALFORMED; - if (m->qos > 0) { - if (p + 2 > end) - return MQTT_MALFORMED; - m->id = (uint16_t)((((uint16_t)p[0]) << 8) | p[1]); - p += 2; - } - if (p > end) - return MQTT_MALFORMED; - if (version == 5 && p + 2 < end) { - len_len = (uint32_t)decode_varint(p, (size_t)(end - p), &m->props_size); - if (!len_len) - return MQTT_MALFORMED; - m->props_start = (size_t)(p + len_len - buf); - p += len_len + m->props_size; - } - if (p > end) - return MQTT_MALFORMED; - m->data.ptr = (char*)p; - m->data.len = (size_t)(end - p); - break; - } - default: - break; - } - return MQTT_OK; -} - -static void mqtt_cb(struct mg_connection* c, int ev, void* ev_data, - void* fn_data) -{ - if (ev == MG_EV_READ) { - for (;;) { - uint8_t version = c->is_mqtt5 ? 5 : 4; - struct mg_mqtt_message mm; - int rc = mg_mqtt_parse(c->recv.buf, c->recv.len, version, &mm); - if (rc == MQTT_MALFORMED) { - MG_ERROR(("%lu MQTT malformed message", c->id)); - c->is_closing = 1; - break; - } else if (rc == MQTT_OK) { - MG_VERBOSE(("%lu MQTT CMD %d len %d [%.*s]", c->id, mm.cmd, - (int)mm.dgram.len, (int)mm.data.len, mm.data.ptr)); - switch (mm.cmd) { - case MQTT_CMD_CONNACK: - mg_call(c, MG_EV_MQTT_OPEN, &mm.ack); - if (mm.ack == 0) { - MG_DEBUG(("%lu Connected", c->id)); - } else { - MG_ERROR(("%lu MQTT auth failed, code %d", c->id, mm.ack)); - c->is_closing = 1; - } - break; - case MQTT_CMD_PUBLISH: { - MG_DEBUG(("%lu [%.*s] -> [%.*s]", c->id, (int)mm.topic.len, - mm.topic.ptr, (int)mm.data.len, mm.data.ptr)); - if (mm.qos > 0) { - uint16_t id = mg_ntohs(mm.id); - uint32_t remaining_len = sizeof(id); - if (c->is_mqtt5) - remaining_len += 2; // 3.4.2 - - mg_mqtt_send_header( - c, - (uint8_t)(mm.qos == 2 ? MQTT_CMD_PUBREC : MQTT_CMD_PUBACK), - 0, remaining_len); - mg_send(c, &id, sizeof(id)); - - if (c->is_mqtt5) { - uint16_t zero = 0; - mg_send(c, &zero, sizeof(zero)); - } - } - mg_call(c, MG_EV_MQTT_MSG, &mm); // let the app handle qos stuff - break; - } - case MQTT_CMD_PUBREC: { // MQTT5: 3.5.2-1 TODO(): variable header rc - uint16_t id = mg_ntohs(mm.id); - uint32_t remaining_len = sizeof(id); // MQTT5 3.6.2-1 - mg_mqtt_send_header(c, MQTT_CMD_PUBREL, 2, remaining_len); - mg_send(c, &id, sizeof(id)); // MQTT5 3.6.1-1, flags = 2 - break; - } - case MQTT_CMD_PUBREL: { // MQTT5: 3.6.2-1 TODO(): variable header rc - uint16_t id = mg_ntohs(mm.id); - uint32_t remaining_len = sizeof(id); // MQTT5 3.7.2-1 - mg_mqtt_send_header(c, MQTT_CMD_PUBCOMP, 0, remaining_len); - mg_send(c, &id, sizeof(id)); - break; - } - } - mg_call(c, MG_EV_MQTT_CMD, &mm); - mg_iobuf_del(&c->recv, 0, mm.dgram.len); - } else { - break; - } - } - } - (void)ev_data; - (void)fn_data; -} - -void mg_mqtt_ping(struct mg_connection* nc) -{ - mg_mqtt_send_header(nc, MQTT_CMD_PINGREQ, 0, 0); -} - -void mg_mqtt_pong(struct mg_connection* nc) -{ - mg_mqtt_send_header(nc, MQTT_CMD_PINGRESP, 0, 0); -} - -void mg_mqtt_disconnect(struct mg_connection* c, - const struct mg_mqtt_opts* opts) -{ - size_t len = 0; - if (c->is_mqtt5) - len = 1 + get_props_size(opts->props, opts->num_props); - mg_mqtt_send_header(c, MQTT_CMD_DISCONNECT, 0, (uint32_t)len); - - if (c->is_mqtt5) { - uint8_t zero = 0; - mg_send(c, &zero, sizeof(zero)); // reason code - mg_send_mqtt_properties(c, opts->props, opts->num_props); - } -} - -struct mg_connection* mg_mqtt_connect(struct mg_mgr* mgr, const char* url, - const struct mg_mqtt_opts* opts, - mg_event_handler_t fn, void* fn_data) -{ - struct mg_connection* c = mg_connect(mgr, url, fn, fn_data); - if (c != NULL) { - struct mg_mqtt_opts empty; - memset(&empty, 0, sizeof(empty)); - mg_mqtt_login(c, opts == NULL ? &empty : opts); - c->pfn = mqtt_cb; - } - return c; -} - -struct mg_connection* mg_mqtt_listen(struct mg_mgr* mgr, const char* url, - mg_event_handler_t fn, void* fn_data) -{ - struct mg_connection* c = mg_listen(mgr, url, fn, fn_data); - if (c != NULL) - c->pfn = mqtt_cb, c->pfn_data = mgr; - return c; -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/net.c" -#endif - -size_t mg_vprintf(struct mg_connection* c, const char* fmt, va_list* ap) -{ - size_t old = c->send.len; - mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, ap); - return c->send.len - old; -} - -size_t mg_printf(struct mg_connection* c, const char* fmt, ...) -{ - size_t len = 0; - va_list ap; - va_start(ap, fmt); - len = mg_vprintf(c, fmt, &ap); - va_end(ap); - return len; -} - -static bool mg_atonl(struct mg_str str, struct mg_addr* addr) -{ - uint32_t localhost = mg_htonl(0x7f000001); - if (mg_vcasecmp(&str, "localhost") != 0) - return false; - memcpy(addr->ip, &localhost, sizeof(uint32_t)); - addr->is_ip6 = false; - return true; -} - -static bool mg_atone(struct mg_str str, struct mg_addr* addr) -{ - if (str.len > 0) - return false; - memset(addr->ip, 0, sizeof(addr->ip)); - addr->is_ip6 = false; - return true; -} - -static bool mg_aton4(struct mg_str str, struct mg_addr* addr) -{ - uint8_t data[4] = { 0, 0, 0, 0 }; - size_t i, num_dots = 0; - for (i = 0; i < str.len; i++) { - if (str.ptr[i] >= '0' && str.ptr[i] <= '9') { - int octet = data[num_dots] * 10 + (str.ptr[i] - '0'); - if (octet > 255) - return false; - data[num_dots] = (uint8_t)octet; - } else if (str.ptr[i] == '.') { - if (num_dots >= 3 || i == 0 || str.ptr[i - 1] == '.') - return false; - num_dots++; - } else { - return false; - } - } - if (num_dots != 3 || str.ptr[i - 1] == '.') - return false; - memcpy(&addr->ip, data, sizeof(data)); - addr->is_ip6 = false; - return true; -} - -static bool mg_v4mapped(struct mg_str str, struct mg_addr* addr) -{ - int i; - uint32_t ipv4; - if (str.len < 14) - return false; - if (str.ptr[0] != ':' || str.ptr[1] != ':' || str.ptr[6] != ':') - return false; - for (i = 2; i < 6; i++) { - if (str.ptr[i] != 'f' && str.ptr[i] != 'F') - return false; - } - // struct mg_str s = mg_str_n(&str.ptr[7], str.len - 7); - if (!mg_aton4(mg_str_n(&str.ptr[7], str.len - 7), addr)) - return false; - memcpy(&ipv4, addr->ip, sizeof(ipv4)); - memset(addr->ip, 0, sizeof(addr->ip)); - addr->ip[10] = addr->ip[11] = 255; - memcpy(&addr->ip[12], &ipv4, 4); - addr->is_ip6 = true; - return true; -} - -static bool mg_aton6(struct mg_str str, struct mg_addr* addr) -{ - size_t i, j = 0, n = 0, dc = 42; - if (str.len > 2 && str.ptr[0] == '[') - str.ptr++, str.len -= 2; - if (mg_v4mapped(str, addr)) - return true; - for (i = 0; i < str.len; i++) { - if ((str.ptr[i] >= '0' && str.ptr[i] <= '9') || (str.ptr[i] >= 'a' && str.ptr[i] <= 'f') || (str.ptr[i] >= 'A' && str.ptr[i] <= 'F')) { - unsigned long val; - if (i > j + 3) - return false; - // MG_DEBUG(("%zu %zu [%.*s]", i, j, (int) (i - j + 1), &str.ptr[j])); - val = mg_unhexn(&str.ptr[j], i - j + 1); - addr->ip[n] = (uint8_t)((val >> 8) & 255); - addr->ip[n + 1] = (uint8_t)(val & 255); - } else if (str.ptr[i] == ':') { - j = i + 1; - if (i > 0 && str.ptr[i - 1] == ':') { - dc = n; // Double colon - if (i > 1 && str.ptr[i - 2] == ':') - return false; - } else if (i > 0) { - n += 2; - } - if (n > 14) - return false; - addr->ip[n] = addr->ip[n + 1] = 0; // For trailing :: - } else { - return false; - } - } - if (n < 14 && dc == 42) - return false; - if (n < 14) { - memmove(&addr->ip[dc + (14 - n)], &addr->ip[dc], n - dc + 2); - memset(&addr->ip[dc], 0, 14 - n); - } - - addr->is_ip6 = true; - return true; -} - -bool mg_aton(struct mg_str str, struct mg_addr* addr) -{ - // MG_INFO(("[%.*s]", (int) str.len, str.ptr)); - return mg_atone(str, addr) || mg_atonl(str, addr) || mg_aton4(str, addr) || mg_aton6(str, addr); -} - -struct mg_connection* mg_alloc_conn(struct mg_mgr* mgr) -{ - struct mg_connection* c = (struct mg_connection*)calloc(1, sizeof(*c) + mgr->extraconnsize); - if (c != NULL) { - c->mgr = mgr; - c->send.align = c->recv.align = MG_IO_SIZE; - c->id = ++mgr->nextid; - } - return c; -} - -void mg_close_conn(struct mg_connection* c) -{ - mg_resolve_cancel(c); // Close any pending DNS query - LIST_DELETE(struct mg_connection, &c->mgr->conns, c); - if (c == c->mgr->dns4.c) - c->mgr->dns4.c = NULL; - if (c == c->mgr->dns6.c) - c->mgr->dns6.c = NULL; - // Order of operations is important. `MG_EV_CLOSE` event must be fired - // before we deallocate received data, see #1331 - mg_call(c, MG_EV_CLOSE, NULL); - MG_DEBUG(("%lu %ld closed", c->id, c->fd)); - - mg_tls_free(c); - mg_iobuf_free(&c->recv); - mg_iobuf_free(&c->send); - mg_bzero((unsigned char*)c, sizeof(*c)); - free(c); -} - -struct mg_connection* mg_connect(struct mg_mgr* mgr, const char* url, - mg_event_handler_t fn, void* fn_data) -{ - struct mg_connection* c = NULL; - if (url == NULL || url[0] == '\0') { - MG_ERROR(("null url")); - } else if ((c = mg_alloc_conn(mgr)) == NULL) { - MG_ERROR(("OOM")); - } else { - LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c); - c->is_udp = (strncmp(url, "udp:", 4) == 0); - c->fd = (void*)(size_t)MG_INVALID_SOCKET; - c->fn = fn; - c->is_client = true; - c->fn_data = fn_data; - MG_DEBUG(("%lu %ld %s", c->id, c->fd, url)); - mg_call(c, MG_EV_OPEN, (void*)url); - mg_resolve(c, url); - } - return c; -} - -struct mg_connection* mg_listen(struct mg_mgr* mgr, const char* url, - mg_event_handler_t fn, void* fn_data) -{ - struct mg_connection* c = NULL; - if ((c = mg_alloc_conn(mgr)) == NULL) { - MG_ERROR(("OOM %s", url)); - } else if (!mg_open_listener(c, url)) { - MG_ERROR(("Failed: %s, errno %d", url, errno)); - free(c); - c = NULL; - } else { - c->is_listening = 1; - c->is_udp = strncmp(url, "udp:", 4) == 0; - LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c); - c->fn = fn; - c->fn_data = fn_data; - mg_call(c, MG_EV_OPEN, NULL); - if (mg_url_is_ssl(url)) - c->is_tls = 1; // Accepted connection must - MG_DEBUG(("%lu %ld %s", c->id, c->fd, url)); - } - return c; -} - -struct mg_connection* mg_wrapfd(struct mg_mgr* mgr, int fd, - mg_event_handler_t fn, void* fn_data) -{ - struct mg_connection* c = mg_alloc_conn(mgr); - if (c != NULL) { - c->fd = (void*)(size_t)fd; - c->fn = fn; - c->fn_data = fn_data; - MG_EPOLL_ADD(c); - mg_call(c, MG_EV_OPEN, NULL); - LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c); - } - return c; -} - -struct mg_timer* mg_timer_add(struct mg_mgr* mgr, uint64_t milliseconds, - unsigned flags, void (*fn)(void*), void* arg) -{ - struct mg_timer* t = (struct mg_timer*)calloc(1, sizeof(*t)); - if (t != NULL) { - mg_timer_init(&mgr->timers, t, milliseconds, flags, fn, arg); - t->id = mgr->timerid++; - } - return t; -} - -void mg_mgr_free(struct mg_mgr* mgr) -{ - struct mg_connection* c; - struct mg_timer *tmp, *t = mgr->timers; - while (t != NULL) - tmp = t->next, free(t), t = tmp; - mgr->timers = NULL; // Important. Next call to poll won't touch timers - for (c = mgr->conns; c != NULL; c = c->next) - c->is_closing = 1; - mg_mgr_poll(mgr, 0); -#if MG_ENABLE_FREERTOS_TCP - FreeRTOS_DeleteSocketSet(mgr->ss); -#endif - MG_DEBUG(("All connections closed")); -#if MG_ENABLE_EPOLL - if (mgr->epoll_fd >= 0) - close(mgr->epoll_fd), mgr->epoll_fd = -1; -#endif - mg_tls_ctx_free(mgr); -} - -void mg_mgr_init(struct mg_mgr* mgr) -{ - memset(mgr, 0, sizeof(*mgr)); -#if MG_ENABLE_EPOLL - if ((mgr->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) - MG_ERROR(("epoll_create1 errno %d", errno)); -#else - mgr->epoll_fd = -1; -#endif -#if MG_ARCH == MG_ARCH_WIN32 && MG_ENABLE_WINSOCK - // clang-format off - { WSADATA data; WSAStartup(MAKEWORD(2, 2), &data); } - // clang-format on -#elif MG_ENABLE_FREERTOS_TCP - mgr->ss = FreeRTOS_CreateSocketSet(); -#elif defined(__unix) || defined(__unix__) || defined(__APPLE__) - // Ignore SIGPIPE signal, so if client cancels the request, it - // won't kill the whole process. - signal(SIGPIPE, SIG_IGN); -#endif - mgr->dnstimeout = 3000; - mgr->dns4.url = "udp://8.8.8.8:53"; - mgr->dns6.url = "udp://[2001:4860:4860::8888]:53"; - mg_tls_ctx_init(mgr); -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/net_builtin.c" -#endif - -#if defined(MG_ENABLE_TCPIP) && MG_ENABLE_TCPIP -#define MG_EPHEMERAL_PORT_BASE 32768 -#define PDIFF(a, b) ((size_t)(((char*)(b)) - ((char*)(a)))) - -#ifndef MIP_TCP_KEEPALIVE_MS -#define MIP_TCP_KEEPALIVE_MS 45000 // TCP keep-alive period, ms -#endif - -#define MIP_TCP_ACK_MS 150 // Timeout for ACKing -#define MIP_TCP_ARP_MS 100 // Timeout for ARP response -#define MIP_TCP_SYN_MS 15000 // Timeout for connection establishment -#define MIP_TCP_FIN_MS 1000 // Timeout for closing connection - -struct connstate { - uint32_t seq, ack; // TCP seq/ack counters - uint64_t timer; // TCP keep-alive / ACK timer - uint8_t mac[6]; // Peer MAC address - uint8_t ttype; // Timer type. 0: ack, 1: keep-alive -#define MIP_TTYPE_KEEPALIVE 0 // Connection is idle for long, send keepalive -#define MIP_TTYPE_ACK 1 // Peer sent us data, we have to ack it soon -#define MIP_TTYPE_ARP 2 // ARP resolve sent, waiting for response -#define MIP_TTYPE_SYN 3 // SYN sent, waiting for response -#define MIP_TTYPE_FIN 4 // FIN sent, waiting until terminating the connection - uint8_t tmiss; // Number of keep-alive misses - struct mg_iobuf raw; // For TLS only. Incoming raw data -}; - -#pragma pack(push, 1) - -struct lcp { - uint8_t addr, ctrl, proto[2], code, id, len[2]; -}; - -struct eth { - uint8_t dst[6]; // Destination MAC address - uint8_t src[6]; // Source MAC address - uint16_t type; // Ethernet type -}; - -struct ip { - uint8_t ver; // Version - uint8_t tos; // Unused - uint16_t len; // Length - uint16_t id; // Unused - uint16_t frag; // Fragmentation -#define IP_FRAG_OFFSET_MSK 0xFF1F -#define IP_MORE_FRAGS_MSK 0x20 - uint8_t ttl; // Time to live - uint8_t proto; // Upper level protocol - uint16_t csum; // Checksum - uint32_t src; // Source IP - uint32_t dst; // Destination IP -}; - -struct ip6 { - uint8_t ver; // Version - uint8_t opts[3]; // Options - uint16_t len; // Length - uint8_t proto; // Upper level protocol - uint8_t ttl; // Time to live - uint8_t src[16]; // Source IP - uint8_t dst[16]; // Destination IP -}; - -struct icmp { - uint8_t type; - uint8_t code; - uint16_t csum; -}; - -struct arp { - uint16_t fmt; // Format of hardware address - uint16_t pro; // Format of protocol address - uint8_t hlen; // Length of hardware address - uint8_t plen; // Length of protocol address - uint16_t op; // Operation - uint8_t sha[6]; // Sender hardware address - uint32_t spa; // Sender protocol address - uint8_t tha[6]; // Target hardware address - uint32_t tpa; // Target protocol address -}; - -struct tcp { - uint16_t sport; // Source port - uint16_t dport; // Destination port - uint32_t seq; // Sequence number - uint32_t ack; // Acknowledgement number - uint8_t off; // Data offset - uint8_t flags; // TCP flags -#define TH_FIN 0x01 -#define TH_SYN 0x02 -#define TH_RST 0x04 -#define TH_PUSH 0x08 -#define TH_ACK 0x10 -#define TH_URG 0x20 -#define TH_ECE 0x40 -#define TH_CWR 0x80 - uint16_t win; // Window - uint16_t csum; // Checksum - uint16_t urp; // Urgent pointer -}; - -struct udp { - uint16_t sport; // Source port - uint16_t dport; // Destination port - uint16_t len; // UDP length - uint16_t csum; // UDP checksum -}; - -struct dhcp { - uint8_t op, htype, hlen, hops; - uint32_t xid; - uint16_t secs, flags; - uint32_t ciaddr, yiaddr, siaddr, giaddr; - uint8_t hwaddr[208]; - uint32_t magic; - uint8_t options[32]; -}; - -#pragma pack(pop) - -struct pkt { - struct mg_str raw; // Raw packet data - struct mg_str pay; // Payload data - struct eth* eth; - struct llc* llc; - struct arp* arp; - struct ip* ip; - struct ip6* ip6; - struct icmp* icmp; - struct tcp* tcp; - struct udp* udp; - struct dhcp* dhcp; -}; - -static void send_syn(struct mg_connection* c); - -static void mkpay(struct pkt* pkt, void* p) -{ - pkt->pay = mg_str_n((char*)p, (size_t)(&pkt->raw.ptr[pkt->raw.len] - (char*)p)); -} - -static uint32_t csumup(uint32_t sum, const void* buf, size_t len) -{ - const uint8_t* p = (const uint8_t*)buf; - for (size_t i = 0; i < len; i++) - sum += i & 1 ? p[i] : (uint32_t)(p[i] << 8); - return sum; -} - -static uint16_t csumfin(uint32_t sum) -{ - while (sum >> 16) - sum = (sum & 0xffff) + (sum >> 16); - return mg_htons(~sum & 0xffff); -} - -static uint16_t ipcsum(const void* buf, size_t len) -{ - uint32_t sum = csumup(0, buf, len); - return csumfin(sum); -} - -static void settmout(struct mg_connection* c, uint8_t type) -{ - struct mg_tcpip_if* ifp = (struct mg_tcpip_if*)c->mgr->priv; - struct connstate* s = (struct connstate*)(c + 1); - unsigned n = type == MIP_TTYPE_ACK ? MIP_TCP_ACK_MS - : type == MIP_TTYPE_ARP ? MIP_TCP_ARP_MS - : type == MIP_TTYPE_SYN ? MIP_TCP_SYN_MS - : type == MIP_TTYPE_FIN ? MIP_TCP_FIN_MS - : MIP_TCP_KEEPALIVE_MS; - s->timer = ifp->now + n; - s->ttype = type; - MG_VERBOSE(("%lu %d -> %llx", c->id, type, s->timer)); -} - -static size_t ether_output(struct mg_tcpip_if* ifp, size_t len) -{ - // size_t min = 64; // Pad short frames to 64 bytes (minimum Ethernet size) - // if (len < min) memset(ifp->tx.ptr + len, 0, min - len), len = min; - // mg_hexdump(ifp->tx.ptr, len); - size_t n = ifp->driver->tx(ifp->tx.ptr, len, ifp); - if (n == len) - ifp->nsent++; - return n; -} - -static void arp_ask(struct mg_tcpip_if* ifp, uint32_t ip) -{ - struct eth* eth = (struct eth*)ifp->tx.ptr; - struct arp* arp = (struct arp*)(eth + 1); - memset(eth->dst, 255, sizeof(eth->dst)); - memcpy(eth->src, ifp->mac, sizeof(eth->src)); - eth->type = mg_htons(0x806); - memset(arp, 0, sizeof(*arp)); - arp->fmt = mg_htons(1), arp->pro = mg_htons(0x800), arp->hlen = 6, - arp->plen = 4; - arp->op = mg_htons(1), arp->tpa = ip, arp->spa = ifp->ip; - memcpy(arp->sha, ifp->mac, sizeof(arp->sha)); - ether_output(ifp, PDIFF(eth, arp + 1)); -} - -static void onstatechange(struct mg_tcpip_if* ifp) -{ - if (ifp->state == MG_TCPIP_STATE_READY) { - MG_INFO(("READY, IP: %M", mg_print_ip4, &ifp->ip)); - MG_INFO((" GW: %M", mg_print_ip4, &ifp->gw)); - MG_INFO((" MAC: %M", mg_print_mac, &ifp->mac)); - arp_ask(ifp, ifp->gw); - } else if (ifp->state == MG_TCPIP_STATE_UP) { - MG_ERROR(("Link up")); - srand((unsigned int)mg_millis()); - } else if (ifp->state == MG_TCPIP_STATE_DOWN) { - MG_ERROR(("Link down")); - } -} - -static struct ip* tx_ip(struct mg_tcpip_if* ifp, uint8_t* mac_dst, - uint8_t proto, uint32_t ip_src, uint32_t ip_dst, - size_t plen) -{ - struct eth* eth = (struct eth*)ifp->tx.ptr; - struct ip* ip = (struct ip*)(eth + 1); - memcpy(eth->dst, mac_dst, sizeof(eth->dst)); - memcpy(eth->src, ifp->mac, sizeof(eth->src)); // Use our MAC - eth->type = mg_htons(0x800); - memset(ip, 0, sizeof(*ip)); - ip->ver = 0x45; // Version 4, header length 5 words - ip->frag = 0x40; // Don't fragment - ip->len = mg_htons((uint16_t)(sizeof(*ip) + plen)); - ip->ttl = 64; - ip->proto = proto; - ip->src = ip_src; - ip->dst = ip_dst; - ip->csum = ipcsum(ip, sizeof(*ip)); - return ip; -} - -static void tx_udp(struct mg_tcpip_if* ifp, uint8_t* mac_dst, uint32_t ip_src, - uint16_t sport, uint32_t ip_dst, uint16_t dport, - const void* buf, size_t len) -{ - struct ip* ip = tx_ip(ifp, mac_dst, 17, ip_src, ip_dst, len + sizeof(struct udp)); - struct udp* udp = (struct udp*)(ip + 1); - // MG_DEBUG(("UDP XX LEN %d %d", (int) len, (int) ifp->tx.len)); - udp->sport = sport; - udp->dport = dport; - udp->len = mg_htons((uint16_t)(sizeof(*udp) + len)); - udp->csum = 0; - uint32_t cs = csumup(0, udp, sizeof(*udp)); - cs = csumup(cs, buf, len); - cs = csumup(cs, &ip->src, sizeof(ip->src)); - cs = csumup(cs, &ip->dst, sizeof(ip->dst)); - cs += (uint32_t)(ip->proto + sizeof(*udp) + len); - udp->csum = csumfin(cs); - memmove(udp + 1, buf, len); - // MG_DEBUG(("UDP LEN %d %d", (int) len, (int) ifp->frame_len)); - ether_output(ifp, sizeof(struct eth) + sizeof(*ip) + sizeof(*udp) + len); -} - -static void tx_dhcp(struct mg_tcpip_if* ifp, uint8_t* mac_dst, uint32_t ip_src, - uint32_t ip_dst, uint8_t* opts, size_t optslen, - bool ciaddr) -{ - // https://datatracker.ietf.org/doc/html/rfc2132#section-9.6 - struct dhcp dhcp = { 1, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, { 0 }, 0, { 0 } }; - dhcp.magic = mg_htonl(0x63825363); - memcpy(&dhcp.hwaddr, ifp->mac, sizeof(ifp->mac)); - memcpy(&dhcp.xid, ifp->mac + 2, sizeof(dhcp.xid)); - memcpy(&dhcp.options, opts, optslen); - if (ciaddr) - dhcp.ciaddr = ip_src; - tx_udp(ifp, mac_dst, ip_src, mg_htons(68), ip_dst, mg_htons(67), &dhcp, - sizeof(dhcp)); -} - -static const uint8_t broadcast[] = { 255, 255, 255, 255, 255, 255 }; - -// RFC-2131 #4.3.6, #4.4.1 -static void tx_dhcp_request_sel(struct mg_tcpip_if* ifp, uint32_t ip_req, - uint32_t ip_srv) -{ - uint8_t opts[] = { - 53, 1, 3, // Type: DHCP request - 55, 2, 1, 3, // GW and mask - 12, 3, 'm', 'i', 'p', // Host name: "mip" - 54, 4, 0, 0, 0, 0, // DHCP server ID - 50, 4, 0, 0, 0, 0, // Requested IP - 255 // End of options - }; - memcpy(opts + 14, &ip_srv, sizeof(ip_srv)); - memcpy(opts + 20, &ip_req, sizeof(ip_req)); - tx_dhcp(ifp, (uint8_t*)broadcast, 0, 0xffffffff, opts, sizeof(opts), false); - MG_DEBUG(("DHCP req sent")); -} - -// RFC-2131 #4.3.6, #4.4.5 (renewing: unicast, rebinding: bcast) -static void tx_dhcp_request_re(struct mg_tcpip_if* ifp, uint8_t* mac_dst, - uint32_t ip_src, uint32_t ip_dst) -{ - uint8_t opts[] = { - 53, 1, 3, // Type: DHCP request - 255 // End of options - }; - tx_dhcp(ifp, mac_dst, ip_src, ip_dst, opts, sizeof(opts), true); - MG_DEBUG(("DHCP req sent")); -} - -static void tx_dhcp_discover(struct mg_tcpip_if* ifp) -{ - uint8_t opts[] = { - 53, 1, 1, // Type: DHCP discover - 55, 2, 1, 3, // Parameters: ip, mask - 255 // End of options - }; - tx_dhcp(ifp, (uint8_t*)broadcast, 0, 0xffffffff, opts, sizeof(opts), false); - MG_DEBUG(("DHCP discover sent. Our MAC: %M", mg_print_mac, ifp->mac)); -} - -static struct mg_connection* getpeer(struct mg_mgr* mgr, struct pkt* pkt, - bool lsn) -{ - struct mg_connection* c = NULL; - for (c = mgr->conns; c != NULL; c = c->next) { - if (c->is_arplooking && pkt->arp && memcmp(&pkt->arp->spa, c->rem.ip, sizeof(pkt->arp->spa)) == 0) - break; - if (c->is_udp && pkt->udp && c->loc.port == pkt->udp->dport) - break; - if (!c->is_udp && pkt->tcp && c->loc.port == pkt->tcp->dport && lsn == c->is_listening && (lsn || c->rem.port == pkt->tcp->sport)) - break; - } - return c; -} - -static void rx_arp(struct mg_tcpip_if* ifp, struct pkt* pkt) -{ - if (pkt->arp->op == mg_htons(1) && pkt->arp->tpa == ifp->ip) { - // ARP request. Make a response, then send - // MG_DEBUG(("ARP op %d %M: %M", mg_ntohs(pkt->arp->op), mg_print_ip4, - // &pkt->arp->spa, mg_print_ip4, &pkt->arp->tpa)); - struct eth* eth = (struct eth*)ifp->tx.ptr; - struct arp* arp = (struct arp*)(eth + 1); - memcpy(eth->dst, pkt->eth->src, sizeof(eth->dst)); - memcpy(eth->src, ifp->mac, sizeof(eth->src)); - eth->type = mg_htons(0x806); - *arp = *pkt->arp; - arp->op = mg_htons(2); - memcpy(arp->tha, pkt->arp->sha, sizeof(pkt->arp->tha)); - memcpy(arp->sha, ifp->mac, sizeof(pkt->arp->sha)); - arp->tpa = pkt->arp->spa; - arp->spa = ifp->ip; - MG_DEBUG(("ARP: tell %M we're %M", mg_print_ip4, &arp->tpa, mg_print_mac, - &ifp->mac)); - ether_output(ifp, PDIFF(eth, arp + 1)); - } else if (pkt->arp->op == mg_htons(2)) { - if (memcmp(pkt->arp->tha, ifp->mac, sizeof(pkt->arp->tha)) != 0) - return; - if (pkt->arp->spa == ifp->gw) { - // Got response for the GW ARP request. Set ifp->gwmac - memcpy(ifp->gwmac, pkt->arp->sha, sizeof(ifp->gwmac)); - } else { - struct mg_connection* c = getpeer(ifp->mgr, pkt, false); - if (c != NULL && c->is_arplooking) { - struct connstate* s = (struct connstate*)(c + 1); - memcpy(s->mac, pkt->arp->sha, sizeof(s->mac)); - MG_DEBUG(("%lu ARP resolved %M -> %M", c->id, mg_print_ip4, c->rem.ip, - mg_print_mac, s->mac)); - c->is_arplooking = 0; - send_syn(c); - settmout(c, MIP_TTYPE_SYN); - } - } - } -} - -static void rx_icmp(struct mg_tcpip_if* ifp, struct pkt* pkt) -{ - // MG_DEBUG(("ICMP %d", (int) len)); - if (pkt->icmp->type == 8 && pkt->ip != NULL && pkt->ip->dst == ifp->ip) { - size_t hlen = sizeof(struct eth) + sizeof(struct ip) + sizeof(struct icmp); - size_t space = ifp->tx.len - hlen, plen = pkt->pay.len; - if (plen > space) - plen = space; - struct ip* ip = tx_ip(ifp, pkt->eth->src, 1, ifp->ip, pkt->ip->src, - sizeof(struct icmp) + plen); - struct icmp* icmp = (struct icmp*)(ip + 1); - memset(icmp, 0, sizeof(*icmp)); // Set csum to 0 - memcpy(icmp + 1, pkt->pay.ptr, plen); // Copy RX payload to TX - icmp->csum = ipcsum(icmp, sizeof(*icmp) + plen); - ether_output(ifp, hlen + plen); - } -} - -static void rx_dhcp_client(struct mg_tcpip_if* ifp, struct pkt* pkt) -{ - uint32_t ip = 0, gw = 0, mask = 0, lease = 0; - uint8_t msgtype = 0, state = ifp->state; - // perform size check first, then access fields - uint8_t *p = pkt->dhcp->options, - *end = (uint8_t*)&pkt->raw.ptr[pkt->raw.len]; - if (end < (uint8_t*)(pkt->dhcp + 1)) - return; - if (memcmp(&pkt->dhcp->xid, ifp->mac + 2, sizeof(pkt->dhcp->xid))) - return; - while (p + 1 < end && p[0] != 255) { // Parse options RFC-1533 #9 - if (p[0] == 1 && p[1] == sizeof(ifp->mask) && p + 6 < end) { // Mask - memcpy(&mask, p + 2, sizeof(mask)); - } else if (p[0] == 3 && p[1] == sizeof(ifp->gw) && p + 6 < end) { // GW - memcpy(&gw, p + 2, sizeof(gw)); - ip = pkt->dhcp->yiaddr; - } else if (p[0] == 51 && p[1] == 4 && p + 6 < end) { // Lease - memcpy(&lease, p + 2, sizeof(lease)); - lease = mg_ntohl(lease); - } else if (p[0] == 53 && p[1] == 1 && p + 6 < end) { // Msg Type - msgtype = p[2]; - } - p += p[1] + 2; - } - // Process message type, RFC-1533 (9.4); RFC-2131 (3.1, 4) - if (msgtype == 6 && ifp->ip == ip) { // DHCPNACK, release IP - ifp->state = MG_TCPIP_STATE_UP, ifp->ip = 0; - } else if (msgtype == 2 && ifp->state == MG_TCPIP_STATE_UP && ip && gw && lease) { // DHCPOFFER - tx_dhcp_request_sel(ifp, ip, pkt->dhcp->siaddr); // select IP, (4.4.1) - ifp->state = MG_TCPIP_STATE_REQ; // REQUESTING state - } else if (msgtype == 5) { // DHCPACK - if (ifp->state == MG_TCPIP_STATE_REQ && ip && gw && lease) { // got an IP - ifp->lease_expire = ifp->now + lease * 1000; - MG_INFO(("Lease: %u sec (%lld)", lease, ifp->lease_expire / 1000)); - // assume DHCP server = router until ARP resolves - memcpy(ifp->gwmac, pkt->eth->src, sizeof(ifp->gwmac)); - ifp->ip = ip, ifp->gw = gw, ifp->mask = mask; - ifp->state = MG_TCPIP_STATE_READY; // BOUND state - uint64_t rand; - mg_random(&rand, sizeof(rand)); - srand((unsigned int)(rand + mg_millis())); - } else if (ifp->state == MG_TCPIP_STATE_READY && ifp->ip == ip) { // renew - ifp->lease_expire = ifp->now + lease * 1000; - MG_INFO(("Lease: %u sec (%lld)", lease, ifp->lease_expire / 1000)); - } // TODO(): accept provided T1/T2 and store server IP for renewal (4.4) - } - if (ifp->state != state) - onstatechange(ifp); -} - -// Simple DHCP server that assigns a next IP address: ifp->ip + 1 -static void rx_dhcp_server(struct mg_tcpip_if* ifp, struct pkt* pkt) -{ - uint8_t op = 0, *p = pkt->dhcp->options, - *end = (uint8_t*)&pkt->raw.ptr[pkt->raw.len]; - if (end < (uint8_t*)(pkt->dhcp + 1)) - return; - // struct dhcp *req = pkt->dhcp; - struct dhcp res = { 2, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, { 0 }, 0, { 0 } }; - res.yiaddr = ifp->ip; - ((uint8_t*)(&res.yiaddr))[3]++; // Offer our IP + 1 - while (p + 1 < end && p[0] != 255) { // Parse options - if (p[0] == 53 && p[1] == 1 && p + 2 < end) { // Message type - op = p[2]; - } - p += p[1] + 2; - } - if (op == 1 || op == 3) { // DHCP Discover or DHCP Request - uint8_t msg = op == 1 ? 2 : 5; // Message type: DHCP OFFER or DHCP ACK - uint8_t opts[] = { - 53, 1, msg, // Message type - 1, 4, 0, 0, 0, 0, // Subnet mask - 54, 4, 0, 0, 0, 0, // Server ID - 12, 3, 'm', 'i', 'p', // Host name: "mip" - 51, 4, 255, 255, 255, 255, // Lease time - 255 // End of options - }; - memcpy(&res.hwaddr, pkt->dhcp->hwaddr, 6); - memcpy(opts + 5, &ifp->mask, sizeof(ifp->mask)); - memcpy(opts + 11, &ifp->ip, sizeof(ifp->ip)); - memcpy(&res.options, opts, sizeof(opts)); - res.magic = pkt->dhcp->magic; - res.xid = pkt->dhcp->xid; - // memcpy(ifp->gwmac, pkt->eth->src, sizeof(ifp->gwmac)); - tx_udp(ifp, pkt->eth->src, ifp->ip, mg_htons(67), - op == 1 ? ~0U : res.yiaddr, mg_htons(68), &res, sizeof(res)); - } -} - -static void rx_udp(struct mg_tcpip_if* ifp, struct pkt* pkt) -{ - struct mg_connection* c = getpeer(ifp->mgr, pkt, true); - if (c == NULL) { - // No UDP listener on this port. Should send ICMP, but keep silent. - } else { - c->rem.port = pkt->udp->sport; - memcpy(c->rem.ip, &pkt->ip->src, sizeof(uint32_t)); - struct connstate* s = (struct connstate*)(c + 1); - memcpy(s->mac, pkt->eth->src, sizeof(s->mac)); - if (c->recv.len >= MG_MAX_RECV_SIZE) { - mg_error(c, "max_recv_buf_size reached"); - } else if (c->recv.size - c->recv.len < pkt->pay.len && !mg_iobuf_resize(&c->recv, c->recv.len + pkt->pay.len)) { - mg_error(c, "oom"); - } else { - memcpy(&c->recv.buf[c->recv.len], pkt->pay.ptr, pkt->pay.len); - c->recv.len += pkt->pay.len; - mg_call(c, MG_EV_READ, &pkt->pay.len); - } - } -} - -static size_t tx_tcp(struct mg_tcpip_if* ifp, uint8_t* dst_mac, uint32_t dst_ip, - uint8_t flags, uint16_t sport, uint16_t dport, - uint32_t seq, uint32_t ack, const void* buf, size_t len) -{ - struct ip* ip = tx_ip(ifp, dst_mac, 6, ifp->ip, dst_ip, sizeof(struct tcp) + len); - struct tcp* tcp = (struct tcp*)(ip + 1); - memset(tcp, 0, sizeof(*tcp)); - if (buf != NULL && len) - memmove(tcp + 1, buf, len); - tcp->sport = sport; - tcp->dport = dport; - tcp->seq = seq; - tcp->ack = ack; - tcp->flags = flags; - tcp->win = mg_htons(8192); - tcp->off = (uint8_t)(sizeof(*tcp) / 4 << 4); - uint32_t cs = 0; - uint16_t n = (uint16_t)(sizeof(*tcp) + len); - uint8_t pseudo[] = { 0, ip->proto, (uint8_t)(n >> 8), (uint8_t)(n & 255) }; - cs = csumup(cs, tcp, n); - cs = csumup(cs, &ip->src, sizeof(ip->src)); - cs = csumup(cs, &ip->dst, sizeof(ip->dst)); - cs = csumup(cs, pseudo, sizeof(pseudo)); - tcp->csum = csumfin(cs); - MG_VERBOSE(("TCP %M:%hu -> %M:%hu fl %x len %u", mg_print_ip4, &ip->src, - mg_ntohs(tcp->sport), mg_print_ip4, &ip->dst, - mg_ntohs(tcp->dport), tcp->flags, (int)len)); - // mg_hexdump(ifp->tx.ptr, PDIFF(ifp->tx.ptr, tcp + 1) + len); - return ether_output(ifp, PDIFF(ifp->tx.ptr, tcp + 1) + len); -} - -static size_t tx_tcp_pkt(struct mg_tcpip_if* ifp, struct pkt* pkt, - uint8_t flags, uint32_t seq, const void* buf, - size_t len) -{ - uint32_t delta = (pkt->tcp->flags & (TH_SYN | TH_FIN)) ? 1 : 0; - return tx_tcp(ifp, pkt->eth->src, pkt->ip->src, flags, pkt->tcp->dport, - pkt->tcp->sport, seq, mg_htonl(mg_ntohl(pkt->tcp->seq) + delta), - buf, len); -} - -static struct mg_connection* accept_conn(struct mg_connection* lsn, - struct pkt* pkt) -{ - struct mg_connection* c = mg_alloc_conn(lsn->mgr); - if (c == NULL) { - MG_ERROR(("OOM")); - return NULL; - } - struct connstate* s = (struct connstate*)(c + 1); - s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq); - memcpy(s->mac, pkt->eth->src, sizeof(s->mac)); - settmout(c, MIP_TTYPE_KEEPALIVE); - memcpy(c->rem.ip, &pkt->ip->src, sizeof(uint32_t)); - c->rem.port = pkt->tcp->sport; - MG_DEBUG(("%lu accepted %M", c->id, mg_print_ip_port, &c->rem)); - LIST_ADD_HEAD(struct mg_connection, &lsn->mgr->conns, c); - c->is_accepted = 1; - c->is_hexdumping = lsn->is_hexdumping; - c->pfn = lsn->pfn; - c->loc = lsn->loc; - c->pfn_data = lsn->pfn_data; - c->fn = lsn->fn; - c->fn_data = lsn->fn_data; - mg_call(c, MG_EV_OPEN, NULL); - mg_call(c, MG_EV_ACCEPT, NULL); - return c; -} - -static size_t trim_len(struct mg_connection* c, size_t len) -{ - struct mg_tcpip_if* ifp = (struct mg_tcpip_if*)c->mgr->priv; - size_t eth_h_len = 14, ip_max_h_len = 24, tcp_max_h_len = 60, udp_h_len = 8; - size_t max_headers_len = eth_h_len + ip_max_h_len + (c->is_udp ? udp_h_len : tcp_max_h_len); - size_t min_mtu = c->is_udp ? 68 /* RFC-791 */ : max_headers_len - eth_h_len; - - // If the frame exceeds the available buffer, trim the length - if (len + max_headers_len > ifp->tx.len) { - len = ifp->tx.len - max_headers_len; - } - // Ensure the MTU isn't lower than the minimum allowed value - if (ifp->mtu < min_mtu) { - MG_ERROR(("MTU is lower than minimum possible value. Setting it to %d.", - min_mtu)); - ifp->mtu = (uint16_t)min_mtu; - } - // If the total packet size exceeds the MTU, trim the length - if (len + max_headers_len - eth_h_len > ifp->mtu) { - len = ifp->mtu - max_headers_len + eth_h_len; - if (c->is_udp) { - MG_ERROR(("UDP datagram exceeds MTU. Truncating it.")); - } - } - - return len; -} - -long mg_io_send(struct mg_connection* c, const void* buf, size_t len) -{ - struct mg_tcpip_if* ifp = (struct mg_tcpip_if*)c->mgr->priv; - struct connstate* s = (struct connstate*)(c + 1); - uint32_t rem_ip; - memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t)); - len = trim_len(c, len); - if (c->is_udp) { - tx_udp(ifp, s->mac, ifp->ip, c->loc.port, rem_ip, c->rem.port, buf, len); - } else { - if (tx_tcp(ifp, s->mac, rem_ip, TH_PUSH | TH_ACK, c->loc.port, c->rem.port, - mg_htonl(s->seq), mg_htonl(s->ack), buf, len) - > 0) { - s->seq += (uint32_t)len; - if (s->ttype == MIP_TTYPE_ACK) - settmout(c, MIP_TTYPE_KEEPALIVE); - } else { - return MG_IO_ERR; - } - } - return (long)len; -} - -long mg_io_recv(struct mg_connection* c, void* buf, size_t len) -{ - struct connstate* s = (struct connstate*)(c + 1); - if (s->raw.len == 0) - return MG_IO_WAIT; - if (len > s->raw.len) - len = s->raw.len; - memcpy(buf, s->raw.buf, len); - mg_iobuf_del(&s->raw, 0, len); - return (long)len; -} - -static void read_conn(struct mg_connection* c, struct pkt* pkt) -{ - struct connstate* s = (struct connstate*)(c + 1); - struct mg_iobuf* io = c->is_tls ? &s->raw : &c->recv; - uint32_t seq = mg_ntohl(pkt->tcp->seq); - s->raw.align = c->recv.align; - uint32_t rem_ip; - memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t)); - if (pkt->tcp->flags & TH_FIN) { - // If we initiated the closure, we reply with ACK upon receiving FIN - // If we didn't initiate it, we reply with FIN as part of the normal TCP - // closure process - uint8_t flags = TH_ACK; - s->ack = (uint32_t)(mg_htonl(pkt->tcp->seq) + pkt->pay.len + 1); - if (c->is_draining && s->ttype == MIP_TTYPE_FIN) { - if (s->seq == mg_htonl(pkt->tcp->ack)) { // Simultaneous closure ? - s->seq++; // Yes. Increment our SEQ - } else { // Otherwise, - s->seq = mg_htonl(pkt->tcp->ack); // Set to peer's ACK - } - } else { - flags |= TH_FIN; - c->is_draining = 1; - settmout(c, MIP_TTYPE_FIN); - } - tx_tcp((struct mg_tcpip_if*)c->mgr->priv, s->mac, rem_ip, flags, - c->loc.port, c->rem.port, mg_htonl(s->seq), mg_htonl(s->ack), "", 0); - } else if (pkt->pay.len == 0) { - // TODO(cpq): handle this peer's ACK - } else if (seq != s->ack) { - uint32_t ack = (uint32_t)(mg_htonl(pkt->tcp->seq) + pkt->pay.len); - if (s->ack == ack) { - MG_VERBOSE(("ignoring duplicate pkt")); - } else { - MG_VERBOSE(("SEQ != ACK: %x %x %x", seq, s->ack, ack)); - tx_tcp((struct mg_tcpip_if*)c->mgr->priv, s->mac, rem_ip, TH_ACK, - c->loc.port, c->rem.port, mg_htonl(s->seq), mg_htonl(s->ack), "", - 0); - } - } else if (io->size - io->len < pkt->pay.len && !mg_iobuf_resize(io, io->len + pkt->pay.len)) { - mg_error(c, "oom"); - } else { - // Copy TCP payload into the IO buffer. If the connection is plain text, - // we copy to c->recv. If the connection is TLS, this data is encrypted, - // therefore we copy that encrypted data to the s->raw iobuffer instead, - // and then call mg_tls_recv() to decrypt it. NOTE: mg_tls_recv() will - // call back mg_io_recv() which grabs raw data from s->raw - memcpy(&io->buf[io->len], pkt->pay.ptr, pkt->pay.len); - io->len += pkt->pay.len; - - MG_VERBOSE(("%lu SEQ %x -> %x", c->id, mg_htonl(pkt->tcp->seq), s->ack)); - // Advance ACK counter - s->ack = (uint32_t)(mg_htonl(pkt->tcp->seq) + pkt->pay.len); -#if 0 - // Send ACK immediately - uint32_t rem_ip; - memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t)); - MG_DEBUG((" imm ACK", c->id, mg_htonl(pkt->tcp->seq), s->ack)); - tx_tcp((struct mg_tcpip_if *) c->mgr->priv, s->mac, rem_ip, TH_ACK, c->loc.port, - c->rem.port, mg_htonl(s->seq), mg_htonl(s->ack), "", 0); -#else - // if not already running, setup a timer to send an ACK later - if (s->ttype != MIP_TTYPE_ACK) - settmout(c, MIP_TTYPE_ACK); -#endif - - if (c->is_tls) { - // TLS connection. Make room for decrypted data in c->recv - io = &c->recv; - if (io->size - io->len < pkt->pay.len && !mg_iobuf_resize(io, io->len + pkt->pay.len)) { - mg_error(c, "oom"); - } else { - // Decrypt data directly into c->recv - long n = mg_tls_recv(c, &io->buf[io->len], io->size - io->len); - if (n == MG_IO_ERR) { - mg_error(c, "TLS recv error"); - } else if (n > 0) { - // Decrypted successfully - trigger MG_EV_READ - io->len += (size_t)n; - mg_call(c, MG_EV_READ, &n); - } - } - } else { - // Plain text connection, data is already in c->recv, trigger - // MG_EV_READ - mg_call(c, MG_EV_READ, &pkt->pay.len); - } - } -} - -static void rx_tcp(struct mg_tcpip_if* ifp, struct pkt* pkt) -{ - struct mg_connection* c = getpeer(ifp->mgr, pkt, false); - struct connstate* s = c == NULL ? NULL : (struct connstate*)(c + 1); -#if 0 - MG_INFO(("%lu %hhu %d", c ? c->id : 0, pkt->tcp->flags, (int) pkt->pay.len)); -#endif - if (c != NULL && c->is_connecting && pkt->tcp->flags == (TH_SYN | TH_ACK)) { - s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq) + 1; - tx_tcp_pkt(ifp, pkt, TH_ACK, pkt->tcp->ack, NULL, 0); - c->is_connecting = 0; // Client connected - settmout(c, MIP_TTYPE_KEEPALIVE); - mg_call(c, MG_EV_CONNECT, NULL); // Let user know - } else if (c != NULL && c->is_connecting && pkt->tcp->flags != TH_ACK) { - // mg_hexdump(pkt->raw.ptr, pkt->raw.len); - tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0); - } else if (c != NULL && pkt->tcp->flags & TH_RST) { - mg_error(c, "peer RST"); // RFC-1122 4.2.2.13 - } else if (c != NULL) { -#if 0 - MG_DEBUG(("%lu %d %M:%hu -> %M:%hu", c->id, (int) pkt->raw.len, - mg_print_ip4, &pkt->ip->src, mg_ntohs(pkt->tcp->sport), - mg_print_ip4, &pkt->ip->dst, mg_ntohs(pkt->tcp->dport))); - mg_hexdump(pkt->pay.ptr, pkt->pay.len); -#endif - s->tmiss = 0; // Reset missed keep-alive counter - if (s->ttype == MIP_TTYPE_KEEPALIVE) // Advance keep-alive timer - settmout(c, - MIP_TTYPE_KEEPALIVE); // unless a former ACK timeout is pending - read_conn(c, pkt); // Override timer with ACK timeout if needed - } else if ((c = getpeer(ifp->mgr, pkt, true)) == NULL) { - tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0); - } else if (pkt->tcp->flags & TH_RST) { - if (c->is_accepted) - mg_error(c, "peer RST"); // RFC-1122 4.2.2.13 - // ignore RST if not connected - } else if (pkt->tcp->flags & TH_SYN) { - // Use peer's source port as ISN, in order to recognise the handshake - uint32_t isn = mg_htonl((uint32_t)mg_ntohs(pkt->tcp->sport)); - tx_tcp_pkt(ifp, pkt, TH_SYN | TH_ACK, isn, NULL, 0); - } else if (pkt->tcp->flags & TH_FIN) { - tx_tcp_pkt(ifp, pkt, TH_FIN | TH_ACK, pkt->tcp->ack, NULL, 0); - } else if (mg_htonl(pkt->tcp->ack) == mg_htons(pkt->tcp->sport) + 1U) { - accept_conn(c, pkt); - } else if (!c->is_accepted) { // no peer - tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0); - } else { - // MG_VERBOSE(("dropped silently..")); - } -} - -static void rx_ip(struct mg_tcpip_if* ifp, struct pkt* pkt) -{ - if (pkt->ip->frag & IP_MORE_FRAGS_MSK || pkt->ip->frag & IP_FRAG_OFFSET_MSK) { - if (pkt->ip->proto == 17) - pkt->udp = (struct udp*)(pkt->ip + 1); - if (pkt->ip->proto == 6) - pkt->tcp = (struct tcp*)(pkt->ip + 1); - struct mg_connection* c = getpeer(ifp->mgr, pkt, false); - if (c) - mg_error(c, "Received fragmented packet"); - } else if (pkt->ip->proto == 1) { - pkt->icmp = (struct icmp*)(pkt->ip + 1); - if (pkt->pay.len < sizeof(*pkt->icmp)) - return; - mkpay(pkt, pkt->icmp + 1); - rx_icmp(ifp, pkt); - } else if (pkt->ip->proto == 17) { - pkt->udp = (struct udp*)(pkt->ip + 1); - if (pkt->pay.len < sizeof(*pkt->udp)) - return; - mkpay(pkt, pkt->udp + 1); - MG_VERBOSE(("UDP %M:%hu -> %M:%hu len %u", mg_print_ip4, &pkt->ip->src, - mg_ntohs(pkt->udp->sport), mg_print_ip4, &pkt->ip->dst, - mg_ntohs(pkt->udp->dport), (int)pkt->pay.len)); - if (ifp->enable_dhcp_client && pkt->udp->dport == mg_htons(68)) { - pkt->dhcp = (struct dhcp*)(pkt->udp + 1); - mkpay(pkt, pkt->dhcp + 1); - rx_dhcp_client(ifp, pkt); - } else if (ifp->enable_dhcp_server && pkt->udp->dport == mg_htons(67)) { - pkt->dhcp = (struct dhcp*)(pkt->udp + 1); - mkpay(pkt, pkt->dhcp + 1); - rx_dhcp_server(ifp, pkt); - } else { - rx_udp(ifp, pkt); - } - } else if (pkt->ip->proto == 6) { - pkt->tcp = (struct tcp*)(pkt->ip + 1); - if (pkt->pay.len < sizeof(*pkt->tcp)) - return; - mkpay(pkt, pkt->tcp + 1); - uint16_t iplen = mg_ntohs(pkt->ip->len); - uint16_t off = (uint16_t)(sizeof(*pkt->ip) + ((pkt->tcp->off >> 4) * 4U)); - if (iplen >= off) - pkt->pay.len = (size_t)(iplen - off); - MG_VERBOSE(("TCP %M:%hu -> %M:%hu len %u", mg_print_ip4, &pkt->ip->src, - mg_ntohs(pkt->tcp->sport), mg_print_ip4, &pkt->ip->dst, - mg_ntohs(pkt->tcp->dport), (int)pkt->pay.len)); - rx_tcp(ifp, pkt); - } -} - -static void rx_ip6(struct mg_tcpip_if* ifp, struct pkt* pkt) -{ - // MG_DEBUG(("IP %d", (int) len)); - if (pkt->ip6->proto == 1 || pkt->ip6->proto == 58) { - pkt->icmp = (struct icmp*)(pkt->ip6 + 1); - if (pkt->pay.len < sizeof(*pkt->icmp)) - return; - mkpay(pkt, pkt->icmp + 1); - rx_icmp(ifp, pkt); - } else if (pkt->ip6->proto == 17) { - pkt->udp = (struct udp*)(pkt->ip6 + 1); - if (pkt->pay.len < sizeof(*pkt->udp)) - return; - // MG_DEBUG((" UDP %u %u -> %u", len, mg_htons(udp->sport), - // mg_htons(udp->dport))); - mkpay(pkt, pkt->udp + 1); - } -} - -static void mg_tcpip_rx(struct mg_tcpip_if* ifp, void* buf, size_t len) -{ - struct pkt pkt; - memset(&pkt, 0, sizeof(pkt)); - pkt.raw.ptr = (char*)buf; - pkt.raw.len = len; - pkt.eth = (struct eth*)buf; - // mg_hexdump(buf, len > 16 ? 16: len); - if (pkt.raw.len < sizeof(*pkt.eth)) - return; // Truncated - runt? - if (ifp->enable_mac_check && memcmp(pkt.eth->dst, ifp->mac, sizeof(pkt.eth->dst)) != 0 && memcmp(pkt.eth->dst, broadcast, sizeof(pkt.eth->dst)) != 0) - return; - if (ifp->enable_crc32_check && len > 4) { - len -= 4; // TODO(scaprile): check on bigendian - uint32_t crc = mg_crc32(0, (const char*)buf, len); - if (memcmp((void*)((size_t)buf + len), &crc, sizeof(crc))) - return; - } - if (pkt.eth->type == mg_htons(0x806)) { - pkt.arp = (struct arp*)(pkt.eth + 1); - if (sizeof(*pkt.eth) + sizeof(*pkt.arp) > pkt.raw.len) - return; // Truncated - rx_arp(ifp, &pkt); - } else if (pkt.eth->type == mg_htons(0x86dd)) { - pkt.ip6 = (struct ip6*)(pkt.eth + 1); - if (pkt.raw.len < sizeof(*pkt.eth) + sizeof(*pkt.ip6)) - return; // Truncated - if ((pkt.ip6->ver >> 4) != 0x6) - return; // Not IP - mkpay(&pkt, pkt.ip6 + 1); - rx_ip6(ifp, &pkt); - } else if (pkt.eth->type == mg_htons(0x800)) { - pkt.ip = (struct ip*)(pkt.eth + 1); - if (pkt.raw.len < sizeof(*pkt.eth) + sizeof(*pkt.ip)) - return; // Truncated - // Truncate frame to what IP header tells us - if ((size_t)mg_ntohs(pkt.ip->len) + sizeof(struct eth) < pkt.raw.len) { - pkt.raw.len = (size_t)mg_ntohs(pkt.ip->len) + sizeof(struct eth); - } - if (pkt.raw.len < sizeof(*pkt.eth) + sizeof(*pkt.ip)) - return; // Truncated - if ((pkt.ip->ver >> 4) != 4) - return; // Not IP - mkpay(&pkt, pkt.ip + 1); - rx_ip(ifp, &pkt); - } else { - MG_DEBUG(("Unknown eth type %x", mg_htons(pkt.eth->type))); - mg_hexdump(buf, len >= 32 ? 32 : len); - } -} - -static void mg_tcpip_poll(struct mg_tcpip_if* ifp, uint64_t uptime_ms) -{ - if (ifp == NULL || ifp->driver == NULL) - return; - bool expired_1000ms = mg_timer_expired(&ifp->timer_1000ms, 1000, uptime_ms); - ifp->now = uptime_ms; - - // Handle physical interface up/down status - if (expired_1000ms && ifp->driver->up) { - bool up = ifp->driver->up(ifp); - bool current = ifp->state != MG_TCPIP_STATE_DOWN; - if (up != current) { - ifp->state = up == false ? MG_TCPIP_STATE_DOWN - : ifp->enable_dhcp_client ? MG_TCPIP_STATE_UP - : MG_TCPIP_STATE_READY; - if (!up && ifp->enable_dhcp_client) - ifp->ip = 0; - onstatechange(ifp); - } - } - if (ifp->state == MG_TCPIP_STATE_DOWN) - return; - - // DHCP RFC-2131 (4.4) - if (ifp->state == MG_TCPIP_STATE_UP && expired_1000ms) { - tx_dhcp_discover(ifp); // INIT (4.4.1) - } else if (expired_1000ms && ifp->state == MG_TCPIP_STATE_READY && ifp->lease_expire > 0) { // BOUND / RENEWING / REBINDING - if (ifp->now >= ifp->lease_expire) { - ifp->state = MG_TCPIP_STATE_UP, ifp->ip = 0; // expired, release IP - onstatechange(ifp); - } else if (ifp->now + 30UL * 60UL * 1000UL > ifp->lease_expire && ((ifp->now / 1000) % 60) == 0) { - // hack: 30 min before deadline, try to rebind (4.3.6) every min - tx_dhcp_request_re(ifp, (uint8_t*)broadcast, ifp->ip, 0xffffffff); - } // TODO(): Handle T1 (RENEWING) and T2 (REBINDING) (4.4.5) - } - - // Read data from the network - if (ifp->driver->rx != NULL) { // Polling driver. We must call it - size_t len = ifp->driver->rx(ifp->recv_queue.buf, ifp->recv_queue.size, ifp); - if (len > 0) - mg_tcpip_rx(ifp, ifp->recv_queue.buf, len); - } else { // Interrupt-based driver. Fills recv queue itself - char* buf; - size_t len = mg_queue_next(&ifp->recv_queue, &buf); - if (len > 0) { - mg_tcpip_rx(ifp, buf, len); - mg_queue_del(&ifp->recv_queue, len); - } - } - - // Process timeouts - for (struct mg_connection* c = ifp->mgr->conns; c != NULL; c = c->next) { - if (c->is_udp || c->is_listening || c->is_resolving) - continue; - struct connstate* s = (struct connstate*)(c + 1); - uint32_t rem_ip; - memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t)); - if (uptime_ms > s->timer) { - if (s->ttype == MIP_TTYPE_ACK) { - MG_VERBOSE(("%lu ack %x %x", c->id, s->seq, s->ack)); - tx_tcp(ifp, s->mac, rem_ip, TH_ACK, c->loc.port, c->rem.port, - mg_htonl(s->seq), mg_htonl(s->ack), "", 0); - } else if (s->ttype == MIP_TTYPE_ARP) { - mg_error(c, "ARP timeout"); - } else if (s->ttype == MIP_TTYPE_SYN) { - mg_error(c, "Connection timeout"); - } else if (s->ttype == MIP_TTYPE_FIN) { - c->is_closing = 1; - continue; - } else { - if (s->tmiss++ > 2) { - mg_error(c, "keepalive"); - } else { - MG_VERBOSE(("%lu keepalive", c->id)); - tx_tcp(ifp, s->mac, rem_ip, TH_ACK, c->loc.port, c->rem.port, - mg_htonl(s->seq - 1), mg_htonl(s->ack), "", 0); - } - } - - settmout(c, MIP_TTYPE_KEEPALIVE); - } - } -} - -// This function executes in interrupt context, thus it should copy data -// somewhere fast. Note that newlib's malloc is not thread safe, thus use -// our lock-free queue with preallocated buffer to copy data and return asap -void mg_tcpip_qwrite(void* buf, size_t len, struct mg_tcpip_if* ifp) -{ - char* p; - if (mg_queue_book(&ifp->recv_queue, &p, len) >= len) { - memcpy(p, buf, len); - mg_queue_add(&ifp->recv_queue, len); - ifp->nrecv++; - } else { - ifp->ndrop++; - } -} - -void mg_tcpip_init(struct mg_mgr* mgr, struct mg_tcpip_if* ifp) -{ - // If MAC address is not set, make a random one - if (ifp->mac[0] == 0 && ifp->mac[1] == 0 && ifp->mac[2] == 0 && ifp->mac[3] == 0 && ifp->mac[4] == 0 && ifp->mac[5] == 0) { - ifp->mac[0] = 0x02; // Locally administered, unicast - mg_random(&ifp->mac[1], sizeof(ifp->mac) - 1); - MG_INFO(("MAC not set. Generated random: %M", mg_print_mac, ifp->mac)); - } - - if (ifp->driver->init && !ifp->driver->init(ifp)) { - MG_ERROR(("driver init failed")); - } else { - size_t framesize = 1540; - ifp->tx.ptr = (char*)calloc(1, framesize), ifp->tx.len = framesize; - if (ifp->recv_queue.size == 0) - ifp->recv_queue.size = ifp->driver->rx ? framesize : 8192; - ifp->recv_queue.buf = (char*)calloc(1, ifp->recv_queue.size); - ifp->timer_1000ms = mg_millis(); - mgr->priv = ifp; - ifp->mgr = mgr; - ifp->mtu = MG_TCPIP_MTU_DEFAULT; - mgr->extraconnsize = sizeof(struct connstate); - if (ifp->ip == 0) - ifp->enable_dhcp_client = true; - memset(ifp->gwmac, 255, sizeof(ifp->gwmac)); // Set to broadcast - mg_random(&ifp->eport, sizeof(ifp->eport)); // Random from 0 to 65535 - ifp->eport |= MG_EPHEMERAL_PORT_BASE; // Random from - // MG_EPHEMERAL_PORT_BASE to 65535 - if (ifp->tx.ptr == NULL || ifp->recv_queue.buf == NULL) - MG_ERROR(("OOM")); - } -} - -void mg_tcpip_free(struct mg_tcpip_if* ifp) -{ - free(ifp->recv_queue.buf); - free((char*)ifp->tx.ptr); -} - -int mg_mkpipe(struct mg_mgr* m, mg_event_handler_t fn, void* d, bool udp) -{ - (void)m, (void)fn, (void)d, (void)udp; - MG_ERROR(("Not implemented")); - return -1; -} - -static void send_syn(struct mg_connection* c) -{ - struct connstate* s = (struct connstate*)(c + 1); - uint32_t isn = mg_htonl((uint32_t)mg_ntohs(c->loc.port)); - struct mg_tcpip_if* ifp = (struct mg_tcpip_if*)c->mgr->priv; - uint32_t rem_ip; - memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t)); - tx_tcp(ifp, s->mac, rem_ip, TH_SYN, c->loc.port, c->rem.port, isn, 0, NULL, - 0); -} - -void mg_connect_resolved(struct mg_connection* c) -{ - struct mg_tcpip_if* ifp = (struct mg_tcpip_if*)c->mgr->priv; - uint32_t rem_ip; - memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t)); - c->is_resolving = 0; - if (ifp->eport < MG_EPHEMERAL_PORT_BASE) - ifp->eport = MG_EPHEMERAL_PORT_BASE; - memcpy(c->loc.ip, &ifp->ip, sizeof(uint32_t)); - c->loc.port = mg_htons(ifp->eport++); - MG_DEBUG(("%lu %M -> %M", c->id, mg_print_ip_port, &c->loc, mg_print_ip_port, - &c->rem)); - mg_call(c, MG_EV_RESOLVE, NULL); - if (((rem_ip & ifp->mask) == (ifp->ip & ifp->mask))) { - // If we're in the same LAN, fire an ARP lookup. - MG_DEBUG(("%lu ARP lookup...", c->id)); - arp_ask(ifp, rem_ip); - settmout(c, MIP_TTYPE_ARP); - c->is_arplooking = 1; - c->is_connecting = 1; - } else if (rem_ip == (ifp->ip | ~ifp->mask)) { - struct connstate* s = (struct connstate*)(c + 1); - memset(s->mac, 0xFF, sizeof(s->mac)); // local broadcast - } else if ((*((uint8_t*)&rem_ip) & 0xE0) == 0xE0) { - struct connstate* s = (struct connstate*)(c + 1); // 224 to 239, E0 to EF - uint8_t mcastp[3] = { 0x01, 0x00, 0x5E }; // multicast group - memcpy(s->mac, mcastp, 3); - memcpy(s->mac + 3, ((uint8_t*)&rem_ip) + 1, 3); // 23 LSb - s->mac[3] &= 0x7F; - } else { - struct connstate* s = (struct connstate*)(c + 1); - memcpy(s->mac, ifp->gwmac, sizeof(ifp->gwmac)); - if (c->is_udp) { - mg_call(c, MG_EV_CONNECT, NULL); - } else { - send_syn(c); - settmout(c, MIP_TTYPE_SYN); - c->is_connecting = 1; - } - } -} - -bool mg_open_listener(struct mg_connection* c, const char* url) -{ - c->loc.port = mg_htons(mg_url_port(url)); - return true; -} - -static void write_conn(struct mg_connection* c) -{ - long len = c->is_tls ? mg_tls_send(c, c->send.buf, c->send.len) - : mg_io_send(c, c->send.buf, c->send.len); - if (len > 0) { - mg_iobuf_del(&c->send, 0, (size_t)len); - mg_call(c, MG_EV_WRITE, &len); - } -} - -static void init_closure(struct mg_connection* c) -{ - struct connstate* s = (struct connstate*)(c + 1); - if (c->is_udp == false && c->is_listening == false && c->is_connecting == false) { // For TCP conns, - struct mg_tcpip_if* ifp = (struct mg_tcpip_if*)c->mgr->priv; // send TCP FIN - uint32_t rem_ip; - memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t)); - tx_tcp(ifp, s->mac, rem_ip, TH_FIN | TH_ACK, c->loc.port, c->rem.port, - mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0); - settmout(c, MIP_TTYPE_FIN); - } -} - -static void close_conn(struct mg_connection* c) -{ - struct connstate* s = (struct connstate*)(c + 1); - mg_iobuf_free(&s->raw); // For TLS connections, release raw data - mg_close_conn(c); -} - -static bool can_write(struct mg_connection* c) -{ - return c->is_connecting == 0 && c->is_resolving == 0 && c->send.len > 0 && c->is_tls_hs == 0 && c->is_arplooking == 0; -} - -void mg_mgr_poll(struct mg_mgr* mgr, int ms) -{ - struct mg_connection *c, *tmp; - uint64_t now = mg_millis(); - mg_tcpip_poll((struct mg_tcpip_if*)mgr->priv, now); - mg_timer_poll(&mgr->timers, now); - for (c = mgr->conns; c != NULL; c = tmp) { - tmp = c->next; - struct connstate* s = (struct connstate*)(c + 1); - mg_call(c, MG_EV_POLL, &now); - MG_VERBOSE(("%lu .. %c%c%c%c%c", c->id, c->is_tls ? 'T' : 't', - c->is_connecting ? 'C' : 'c', c->is_tls_hs ? 'H' : 'h', - c->is_resolving ? 'R' : 'r', c->is_closing ? 'C' : 'c')); - if (c->is_tls_hs) - mg_tls_handshake(c); - if (can_write(c)) - write_conn(c); - if (c->is_draining && c->send.len == 0 && s->ttype != MIP_TTYPE_FIN) - init_closure(c); - if (c->is_closing) - close_conn(c); - } - (void)ms; -} - -bool mg_send(struct mg_connection* c, const void* buf, size_t len) -{ - struct mg_tcpip_if* ifp = (struct mg_tcpip_if*)c->mgr->priv; - bool res = false; - uint32_t rem_ip; - memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t)); - if (ifp->ip == 0 || ifp->state != MG_TCPIP_STATE_READY) { - mg_error(c, "net down"); - } else if (c->is_udp) { - struct connstate* s = (struct connstate*)(c + 1); - len = trim_len(c, len); // Trimming length if necessary - tx_udp(ifp, s->mac, ifp->ip, c->loc.port, rem_ip, c->rem.port, buf, len); - res = true; - } else { - res = mg_iobuf_add(&c->send, c->send.len, buf, len); - } - return res; -} -#endif // MG_ENABLE_TCPIP - -#ifdef MG_ENABLE_LINES -#line 1 "src/ota_dummy.c" -#endif - -#if MG_OTA == MG_OTA_NONE -bool mg_ota_begin(size_t new_firmware_size) -{ - (void)new_firmware_size; - return true; -} -bool mg_ota_write(const void* buf, size_t len) -{ - (void)buf, (void)len; - return true; -} -bool mg_ota_end(void) -{ - return true; -} -bool mg_ota_commit(void) -{ - return true; -} -bool mg_ota_rollback(void) -{ - return true; -} -int mg_ota_status(int fw) -{ - (void)fw; - return 0; -} -uint32_t mg_ota_crc32(int fw) -{ - (void)fw; - return 0; -} -uint32_t mg_ota_timestamp(int fw) -{ - (void)fw; - return 0; -} -size_t mg_ota_size(int fw) -{ - (void)fw; - return 0; -} -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/ota_flash.c" -#endif - -// This OTA implementation uses the internal flash API outlined in sys.h -// It splits flash into 2 equal partitions, and stores OTA status in the -// last sector of the partition. - -#if MG_OTA == MG_OTA_FLASH - -#define MG_OTADATA_KEY 0xb07afed0 - -static char* s_addr; // Current address to write to -static size_t s_size; // Firmware size to flash. In-progress indicator -static uint32_t s_crc32; // Firmware checksum - -struct mg_otadata { - uint32_t crc32, size, timestamp, status; -}; - -bool mg_ota_begin(size_t new_firmware_size) -{ - bool ok = false; - if (s_size) { - MG_ERROR(("OTA already in progress. Call mg_ota_end()")); - } else { - size_t half = mg_flash_size() / 2, max = half - mg_flash_sector_size(); - s_crc32 = 0; - s_addr = (char*)mg_flash_start() + half; - MG_DEBUG(("Firmware %lu bytes, max %lu", s_size, max)); - if (new_firmware_size < max) { - ok = true; - s_size = new_firmware_size; - MG_INFO(("Starting OTA, firmware size %lu", s_size)); - } else { - MG_ERROR(("Firmware %lu is too big to fit %lu", new_firmware_size, max)); - } - } - return ok; -} - -bool mg_ota_write(const void* buf, size_t len) -{ - bool ok = false; - if (s_size == 0) { - MG_ERROR(("OTA is not started, call mg_ota_begin()")); - } else { - size_t align = mg_flash_write_align(); - size_t len_aligned_down = MG_ROUND_DOWN(len, align); - if (len_aligned_down) - ok = mg_flash_write(s_addr, buf, len_aligned_down); - if (len_aligned_down < len) { - size_t left = len - len_aligned_down; - char tmp[align]; - memset(tmp, 0xff, sizeof(tmp)); - memcpy(tmp, (char*)buf + len_aligned_down, left); - ok = mg_flash_write(s_addr + len_aligned_down, tmp, sizeof(tmp)); - } - s_crc32 = mg_crc32(s_crc32, (char*)buf, len); // Update CRC - MG_DEBUG(("%#x %p %lu -> %d", s_addr - len, buf, len, ok)); - s_addr += len; - } - return ok; -} - -bool mg_ota_end(void) -{ - char* base = (char*)mg_flash_start() + mg_flash_size() / 2; - bool ok = false; - if (s_size) { - size_t size = s_addr - base; - uint32_t crc32 = mg_crc32(0, base, s_size); - if (size == s_size && crc32 == s_crc32) { - uint32_t now = (uint32_t)(mg_now() / 1000); - struct mg_otadata od = { crc32, size, now, MG_OTA_FIRST_BOOT }; - uint32_t key = MG_OTADATA_KEY + (mg_flash_bank() == 2 ? 1 : 2); - ok = mg_flash_save(NULL, key, &od, sizeof(od)); - } - MG_DEBUG(("CRC: %x/%x, size: %lu/%lu, status: %s", s_crc32, crc32, s_size, - size, ok ? "ok" : "fail")); - s_size = 0; - if (ok) - ok = mg_flash_swap_bank(); - } - MG_INFO(("Finishing OTA: %s", ok ? "ok" : "fail")); - return ok; -} - -static struct mg_otadata mg_otadata(int fw) -{ - struct mg_otadata od = {}; - int bank = mg_flash_bank(); - uint32_t key = MG_OTADATA_KEY + 1; - if ((fw == MG_FIRMWARE_CURRENT && bank == 2)) - key++; - if ((fw == MG_FIRMWARE_PREVIOUS && bank == 1)) - key++; - mg_flash_load(NULL, key, &od, sizeof(od)); - // MG_DEBUG(("Loaded OTA data. fw %d, bank %d, key %p", fw, bank, key)); - // mg_hexdump(&od, sizeof(od)); - return od; -} - -int mg_ota_status(int fw) -{ - struct mg_otadata od = mg_otadata(fw); - return od.status; -} -uint32_t mg_ota_crc32(int fw) -{ - struct mg_otadata od = mg_otadata(fw); - return od.crc32; -} -uint32_t mg_ota_timestamp(int fw) -{ - struct mg_otadata od = mg_otadata(fw); - return od.timestamp; -} -size_t mg_ota_size(int fw) -{ - struct mg_otadata od = mg_otadata(fw); - return od.size; -} - -bool mg_ota_commit(void) -{ - struct mg_otadata od = mg_otadata(MG_FIRMWARE_CURRENT); - od.status = MG_OTA_COMMITTED; - uint32_t key = MG_OTADATA_KEY + mg_flash_bank(); - return mg_flash_save(NULL, key, &od, sizeof(od)); -} - -bool mg_ota_rollback(void) -{ - MG_DEBUG(("Rolling firmware back")); - return mg_flash_swap_bank(); -} -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/printf.c" -#endif - -size_t mg_queue_vprintf(struct mg_queue* q, const char* fmt, va_list* ap) -{ - size_t len = mg_snprintf(NULL, 0, fmt, ap); - char* buf; - if (len == 0 || mg_queue_book(q, &buf, len + 1) < len + 1) { - len = 0; // Nah. Not enough space - } else { - len = mg_vsnprintf((char*)buf, len + 1, fmt, ap); - mg_queue_add(q, len); - } - return len; -} - -size_t mg_queue_printf(struct mg_queue* q, const char* fmt, ...) -{ - va_list ap; - size_t len; - va_start(ap, fmt); - len = mg_queue_vprintf(q, fmt, &ap); - va_end(ap); - return len; -} - -static void mg_pfn_iobuf_private(char ch, void* param, bool expand) -{ - struct mg_iobuf* io = (struct mg_iobuf*)param; - if (expand && io->len + 2 > io->size) - mg_iobuf_resize(io, io->len + 2); - if (io->len + 2 <= io->size) { - io->buf[io->len++] = (uint8_t)ch; - io->buf[io->len] = 0; - } else if (io->len < io->size) { - io->buf[io->len++] = 0; // Guarantee to 0-terminate - } -} - -static void mg_putchar_iobuf_static(char ch, void* param) -{ - mg_pfn_iobuf_private(ch, param, false); -} - -void mg_pfn_iobuf(char ch, void* param) -{ - mg_pfn_iobuf_private(ch, param, true); -} - -size_t mg_vsnprintf(char* buf, size_t len, const char* fmt, va_list* ap) -{ - struct mg_iobuf io = { (uint8_t*)buf, len, 0, 0 }; - size_t n = mg_vxprintf(mg_putchar_iobuf_static, &io, fmt, ap); - if (n < len) - buf[n] = '\0'; - return n; -} - -size_t mg_snprintf(char* buf, size_t len, const char* fmt, ...) -{ - va_list ap; - size_t n; - va_start(ap, fmt); - n = mg_vsnprintf(buf, len, fmt, &ap); - va_end(ap); - return n; -} - -char* mg_vmprintf(const char* fmt, va_list* ap) -{ - struct mg_iobuf io = { 0, 0, 0, 256 }; - mg_vxprintf(mg_pfn_iobuf, &io, fmt, ap); - return (char*)io.buf; -} - -char* mg_mprintf(const char* fmt, ...) -{ - char* s; - va_list ap; - va_start(ap, fmt); - s = mg_vmprintf(fmt, &ap); - va_end(ap); - return s; -} - -void mg_pfn_stdout(char c, void* param) -{ - putchar(c); - (void)param; -} - -static size_t print_ip4(void (*out)(char, void*), void* arg, uint8_t* p) -{ - return mg_xprintf(out, arg, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); -} - -static size_t print_ip6(void (*out)(char, void*), void* arg, uint16_t* p) -{ - return mg_xprintf(out, arg, "[%x:%x:%x:%x:%x:%x:%x:%x]", mg_ntohs(p[0]), - mg_ntohs(p[1]), mg_ntohs(p[2]), mg_ntohs(p[3]), - mg_ntohs(p[4]), mg_ntohs(p[5]), mg_ntohs(p[6]), - mg_ntohs(p[7])); -} - -size_t mg_print_ip4(void (*out)(char, void*), void* arg, va_list* ap) -{ - uint8_t* p = va_arg(*ap, uint8_t*); - return print_ip4(out, arg, p); -} - -size_t mg_print_ip6(void (*out)(char, void*), void* arg, va_list* ap) -{ - uint16_t* p = va_arg(*ap, uint16_t*); - return print_ip6(out, arg, p); -} - -size_t mg_print_ip(void (*out)(char, void*), void* arg, va_list* ap) -{ - struct mg_addr* addr = va_arg(*ap, struct mg_addr*); - if (addr->is_ip6) - return print_ip6(out, arg, (uint16_t*)addr->ip); - return print_ip4(out, arg, (uint8_t*)&addr->ip); -} - -size_t mg_print_ip_port(void (*out)(char, void*), void* arg, va_list* ap) -{ - struct mg_addr* a = va_arg(*ap, struct mg_addr*); - return mg_xprintf(out, arg, "%M:%hu", mg_print_ip, a, mg_ntohs(a->port)); -} - -size_t mg_print_mac(void (*out)(char, void*), void* arg, va_list* ap) -{ - uint8_t* p = va_arg(*ap, uint8_t*); - return mg_xprintf(out, arg, "%02x:%02x:%02x:%02x:%02x:%02x", p[0], p[1], p[2], - p[3], p[4], p[5]); -} - -static char mg_esc(int c, bool esc) -{ - const char *p, *esc1 = "\b\f\n\r\t\\\"", *esc2 = "bfnrt\\\""; - for (p = esc ? esc1 : esc2; *p != '\0'; p++) { - if (*p == c) - return esc ? esc2[p - esc1] : esc1[p - esc2]; - } - return 0; -} - -static char mg_escape(int c) -{ - return mg_esc(c, true); -} - -static size_t qcpy(void (*out)(char, void*), void* ptr, char* buf, - size_t len) -{ - size_t i = 0, extra = 0; - for (i = 0; i < len && buf[i] != '\0'; i++) { - char c = mg_escape(buf[i]); - if (c) { - out('\\', ptr), out(c, ptr), extra++; - } else { - out(buf[i], ptr); - } - } - return i + extra; -} - -static size_t bcpy(void (*out)(char, void*), void* arg, uint8_t* buf, - size_t len) -{ - size_t i, j, n = 0; - const char* t = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - for (i = 0; i < len; i += 3) { - uint8_t c1 = buf[i], c2 = i + 1 < len ? buf[i + 1] : 0, - c3 = i + 2 < len ? buf[i + 2] : 0; - char tmp[4] = { t[c1 >> 2], t[(c1 & 3) << 4 | (c2 >> 4)], '=', '=' }; - if (i + 1 < len) - tmp[2] = t[(c2 & 15) << 2 | (c3 >> 6)]; - if (i + 2 < len) - tmp[3] = t[c3 & 63]; - for (j = 0; j < sizeof(tmp) && tmp[j] != '\0'; j++) - out(tmp[j], arg); - n += j; - } - return n; -} - -size_t mg_print_hex(void (*out)(char, void*), void* arg, va_list* ap) -{ - size_t bl = (size_t)va_arg(*ap, int); - uint8_t* p = va_arg(*ap, uint8_t*); - const char* hex = "0123456789abcdef"; - size_t j; - for (j = 0; j < bl; j++) { - out(hex[(p[j] >> 4) & 0x0F], arg); - out(hex[p[j] & 0x0F], arg); - } - return 2 * bl; -} -size_t mg_print_base64(void (*out)(char, void*), void* arg, va_list* ap) -{ - size_t len = (size_t)va_arg(*ap, int); - uint8_t* buf = va_arg(*ap, uint8_t*); - return bcpy(out, arg, buf, len); -} - -size_t mg_print_esc(void (*out)(char, void*), void* arg, va_list* ap) -{ - size_t len = (size_t)va_arg(*ap, int); - char* p = va_arg(*ap, char*); - if (len == 0) - len = p == NULL ? 0 : strlen(p); - return qcpy(out, arg, p, len); -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/queue.c" -#endif - -#if defined(__GNUC__) || defined(__clang__) -#define MG_MEMORY_BARRIER() __sync_synchronize() -#elif defined(_MSC_VER) && _MSC_VER >= 1700 -#define MG_MEMORY_BARRIER() MemoryBarrier() -#elif !defined(MG_MEMORY_BARRIER) -#define MG_MEMORY_BARRIER() -#endif - -// Every message in a queue is prepended by a 32-bit message length (ML). -// If ML is 0, then it is the end, and reader must wrap to the beginning. -// -// Queue when q->tail <= q->head: -// |----- free -----| ML | message1 | ML | message2 | ----- free ------| -// ^ ^ ^ ^ -// buf tail head len -// -// Queue when q->tail > q->head: -// | ML | message2 |----- free ------| ML | message1 | 0 |---- free ----| -// ^ ^ ^ ^ -// buf head tail len - -void mg_queue_init(struct mg_queue* q, char* buf, size_t size) -{ - q->size = size; - q->buf = buf; - q->head = q->tail = 0; -} - -static size_t mg_queue_read_len(struct mg_queue* q) -{ - uint32_t n = 0; - MG_MEMORY_BARRIER(); - memcpy(&n, q->buf + q->tail, sizeof(n)); - assert(q->tail + n + sizeof(n) <= q->size); - return n; -} - -static void mg_queue_write_len(struct mg_queue* q, size_t len) -{ - uint32_t n = (uint32_t)len; - memcpy(q->buf + q->head, &n, sizeof(n)); - MG_MEMORY_BARRIER(); -} - -size_t mg_queue_book(struct mg_queue* q, char** buf, size_t len) -{ - size_t space = 0, hs = sizeof(uint32_t) * 2; // *2 is for the 0 marker - if (q->head >= q->tail && q->head + len + hs <= q->size) { - space = q->size - q->head - hs; // There is enough space - } else if (q->head >= q->tail && q->tail > hs) { - mg_queue_write_len(q, 0); // Not enough space ahead - q->head = 0; // Wrap head to the beginning - } - if (q->head + hs + len < q->tail) - space = q->tail - q->head - hs; - if (buf != NULL) - *buf = q->buf + q->head + sizeof(uint32_t); - return space; -} - -size_t mg_queue_next(struct mg_queue* q, char** buf) -{ - size_t len = 0; - if (q->tail != q->head) { - len = mg_queue_read_len(q); - if (len == 0) { // Zero (head wrapped) ? - q->tail = 0; // Reset tail to the start - if (q->head > q->tail) - len = mg_queue_read_len(q); // Read again - } - } - if (buf != NULL) - *buf = q->buf + q->tail + sizeof(uint32_t); - assert(q->tail + len <= q->size); - return len; -} - -void mg_queue_add(struct mg_queue* q, size_t len) -{ - assert(len > 0); - mg_queue_write_len(q, len); - assert(q->head + sizeof(uint32_t) * 2 + len <= q->size); - q->head += len + sizeof(uint32_t); -} - -void mg_queue_del(struct mg_queue* q, size_t len) -{ - q->tail += len + sizeof(uint32_t); - assert(q->tail + sizeof(uint32_t) <= q->size); -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/rpc.c" -#endif - -void mg_rpc_add(struct mg_rpc** head, struct mg_str method, - void (*fn)(struct mg_rpc_req*), void* fn_data) -{ - struct mg_rpc* rpc = (struct mg_rpc*)calloc(1, sizeof(*rpc)); - if (rpc != NULL) { - rpc->method = mg_strdup(method), rpc->fn = fn, rpc->fn_data = fn_data; - rpc->next = *head, *head = rpc; - } -} - -void mg_rpc_del(struct mg_rpc** head, void (*fn)(struct mg_rpc_req*)) -{ - struct mg_rpc* r; - while ((r = *head) != NULL) { - if (r->fn == fn || fn == NULL) { - *head = r->next; - free((void*)r->method.ptr); - free(r); - } else { - head = &(*head)->next; - } - } -} - -static void mg_rpc_call(struct mg_rpc_req* r, struct mg_str method) -{ - struct mg_rpc* h = r->head == NULL ? NULL : *r->head; - while (h != NULL && !mg_match(method, h->method, NULL)) - h = h->next; - if (h != NULL) { - r->rpc = h; - h->fn(r); - } else { - mg_rpc_err(r, -32601, "\"%.*s not found\"", (int)method.len, method.ptr); - } -} - -void mg_rpc_process(struct mg_rpc_req* r) -{ - int len, off = mg_json_get(r->frame, "$.method", &len); - if (off > 0 && r->frame.ptr[off] == '"') { - struct mg_str method = mg_str_n(&r->frame.ptr[off + 1], (size_t)len - 2); - mg_rpc_call(r, method); - } else if ((off = mg_json_get(r->frame, "$.result", &len)) > 0 || (off = mg_json_get(r->frame, "$.error", &len)) > 0) { - mg_rpc_call(r, mg_str("")); // JSON response! call "" method handler - } else { - mg_rpc_err(r, -32700, "%m", mg_print_esc, (int)r->frame.len, - r->frame.ptr); // Invalid - } -} - -void mg_rpc_vok(struct mg_rpc_req* r, const char* fmt, va_list* ap) -{ - int len, off = mg_json_get(r->frame, "$.id", &len); - if (off > 0) { - mg_xprintf(r->pfn, r->pfn_data, "{%m:%.*s,%m:", mg_print_esc, 0, "id", len, - &r->frame.ptr[off], mg_print_esc, 0, "result"); - mg_vxprintf(r->pfn, r->pfn_data, fmt == NULL ? "null" : fmt, ap); - mg_xprintf(r->pfn, r->pfn_data, "}"); - } -} - -void mg_rpc_ok(struct mg_rpc_req* r, const char* fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - mg_rpc_vok(r, fmt, &ap); - va_end(ap); -} - -void mg_rpc_verr(struct mg_rpc_req* r, int code, const char* fmt, va_list* ap) -{ - int len, off = mg_json_get(r->frame, "$.id", &len); - mg_xprintf(r->pfn, r->pfn_data, "{"); - if (off > 0) { - mg_xprintf(r->pfn, r->pfn_data, "%m:%.*s,", mg_print_esc, 0, "id", len, - &r->frame.ptr[off]); - } - mg_xprintf(r->pfn, r->pfn_data, "%m:{%m:%d,%m:", mg_print_esc, 0, "error", - mg_print_esc, 0, "code", code, mg_print_esc, 0, "message"); - mg_vxprintf(r->pfn, r->pfn_data, fmt == NULL ? "null" : fmt, ap); - mg_xprintf(r->pfn, r->pfn_data, "}}"); -} - -void mg_rpc_err(struct mg_rpc_req* r, int code, const char* fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - mg_rpc_verr(r, code, fmt, &ap); - va_end(ap); -} - -static size_t print_methods(mg_pfn_t pfn, void* pfn_data, va_list* ap) -{ - struct mg_rpc *h, **head = (struct mg_rpc**)va_arg(*ap, void**); - size_t len = 0; - for (h = *head; h != NULL; h = h->next) { - if (h->method.len == 0) - continue; // Ignore response handler - len += mg_xprintf(pfn, pfn_data, "%s%m", h == *head ? "" : ",", - mg_print_esc, (int)h->method.len, h->method.ptr); - } - return len; -} - -void mg_rpc_list(struct mg_rpc_req* r) -{ - mg_rpc_ok(r, "[%M]", print_methods, r->head); -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/sha1.c" -#endif -/* Copyright(c) By Steve Reid */ -/* 100% Public Domain */ - -union char64long16 { - unsigned char c[64]; - uint32_t l[16]; -}; - -#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) - -static uint32_t blk0(union char64long16* block, int i) -{ - if (MG_BIG_ENDIAN) { - } else { - block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | (rol(block->l[i], 8) & 0x00FF00FF); - } - return block->l[i]; -} - -/* Avoid redefine warning (ARM /usr/include/sys/ucontext.h define R0~R4) */ -#undef blk -#undef R0 -#undef R1 -#undef R2 -#undef R3 -#undef R4 - -#define blk(i) \ - (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ block->l[(i + 8) & 15] ^ block->l[(i + 2) & 15] ^ block->l[i & 15], \ - 1)) -#define R0(v, w, x, y, z, i) \ - z += ((w & (x ^ y)) ^ y) + blk0(block, i) + 0x5A827999 + rol(v, 5); \ - w = rol(w, 30); -#define R1(v, w, x, y, z, i) \ - z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \ - w = rol(w, 30); -#define R2(v, w, x, y, z, i) \ - z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); \ - w = rol(w, 30); -#define R3(v, w, x, y, z, i) \ - z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \ - w = rol(w, 30); -#define R4(v, w, x, y, z, i) \ - z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \ - w = rol(w, 30); - -static void mg_sha1_transform(uint32_t state[5], - const unsigned char* buffer) -{ - uint32_t a, b, c, d, e; - union char64long16 block[1]; - - memcpy(block, buffer, 64); - a = state[0]; - b = state[1]; - c = state[2]; - d = state[3]; - e = state[4]; - R0(a, b, c, d, e, 0); - R0(e, a, b, c, d, 1); - R0(d, e, a, b, c, 2); - R0(c, d, e, a, b, 3); - R0(b, c, d, e, a, 4); - R0(a, b, c, d, e, 5); - R0(e, a, b, c, d, 6); - R0(d, e, a, b, c, 7); - R0(c, d, e, a, b, 8); - R0(b, c, d, e, a, 9); - R0(a, b, c, d, e, 10); - R0(e, a, b, c, d, 11); - R0(d, e, a, b, c, 12); - R0(c, d, e, a, b, 13); - R0(b, c, d, e, a, 14); - R0(a, b, c, d, e, 15); - R1(e, a, b, c, d, 16); - R1(d, e, a, b, c, 17); - R1(c, d, e, a, b, 18); - R1(b, c, d, e, a, 19); - R2(a, b, c, d, e, 20); - R2(e, a, b, c, d, 21); - R2(d, e, a, b, c, 22); - R2(c, d, e, a, b, 23); - R2(b, c, d, e, a, 24); - R2(a, b, c, d, e, 25); - R2(e, a, b, c, d, 26); - R2(d, e, a, b, c, 27); - R2(c, d, e, a, b, 28); - R2(b, c, d, e, a, 29); - R2(a, b, c, d, e, 30); - R2(e, a, b, c, d, 31); - R2(d, e, a, b, c, 32); - R2(c, d, e, a, b, 33); - R2(b, c, d, e, a, 34); - R2(a, b, c, d, e, 35); - R2(e, a, b, c, d, 36); - R2(d, e, a, b, c, 37); - R2(c, d, e, a, b, 38); - R2(b, c, d, e, a, 39); - R3(a, b, c, d, e, 40); - R3(e, a, b, c, d, 41); - R3(d, e, a, b, c, 42); - R3(c, d, e, a, b, 43); - R3(b, c, d, e, a, 44); - R3(a, b, c, d, e, 45); - R3(e, a, b, c, d, 46); - R3(d, e, a, b, c, 47); - R3(c, d, e, a, b, 48); - R3(b, c, d, e, a, 49); - R3(a, b, c, d, e, 50); - R3(e, a, b, c, d, 51); - R3(d, e, a, b, c, 52); - R3(c, d, e, a, b, 53); - R3(b, c, d, e, a, 54); - R3(a, b, c, d, e, 55); - R3(e, a, b, c, d, 56); - R3(d, e, a, b, c, 57); - R3(c, d, e, a, b, 58); - R3(b, c, d, e, a, 59); - R4(a, b, c, d, e, 60); - R4(e, a, b, c, d, 61); - R4(d, e, a, b, c, 62); - R4(c, d, e, a, b, 63); - R4(b, c, d, e, a, 64); - R4(a, b, c, d, e, 65); - R4(e, a, b, c, d, 66); - R4(d, e, a, b, c, 67); - R4(c, d, e, a, b, 68); - R4(b, c, d, e, a, 69); - R4(a, b, c, d, e, 70); - R4(e, a, b, c, d, 71); - R4(d, e, a, b, c, 72); - R4(c, d, e, a, b, 73); - R4(b, c, d, e, a, 74); - R4(a, b, c, d, e, 75); - R4(e, a, b, c, d, 76); - R4(d, e, a, b, c, 77); - R4(c, d, e, a, b, 78); - R4(b, c, d, e, a, 79); - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - state[4] += e; - /* Erase working structures. The order of operations is important, - * used to ensure that compiler doesn't optimize those out. */ - memset(block, 0, sizeof(block)); - a = b = c = d = e = 0; - (void)a; - (void)b; - (void)c; - (void)d; - (void)e; -} - -void mg_sha1_init(mg_sha1_ctx* context) -{ - context->state[0] = 0x67452301; - context->state[1] = 0xEFCDAB89; - context->state[2] = 0x98BADCFE; - context->state[3] = 0x10325476; - context->state[4] = 0xC3D2E1F0; - context->count[0] = context->count[1] = 0; -} - -void mg_sha1_update(mg_sha1_ctx* context, const unsigned char* data, - size_t len) -{ - size_t i, j; - - j = context->count[0]; - if ((context->count[0] += (uint32_t)len << 3) < j) - context->count[1]++; - context->count[1] += (uint32_t)(len >> 29); - j = (j >> 3) & 63; - if ((j + len) > 63) { - memcpy(&context->buffer[j], data, (i = 64 - j)); - mg_sha1_transform(context->state, context->buffer); - for (; i + 63 < len; i += 64) { - mg_sha1_transform(context->state, &data[i]); - } - j = 0; - } else - i = 0; - memcpy(&context->buffer[j], &data[i], len - i); -} - -void mg_sha1_final(unsigned char digest[20], mg_sha1_ctx* context) -{ - unsigned i; - unsigned char finalcount[8], c; - - for (i = 0; i < 8; i++) { - finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 255); - } - c = 0200; - mg_sha1_update(context, &c, 1); - while ((context->count[0] & 504) != 448) { - c = 0000; - mg_sha1_update(context, &c, 1); - } - mg_sha1_update(context, finalcount, 8); - for (i = 0; i < 20; i++) { - digest[i] = (unsigned char)((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255); - } - memset(context, '\0', sizeof(*context)); - memset(&finalcount, '\0', sizeof(finalcount)); -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/sntp.c" -#endif - -#define SNTP_TIME_OFFSET 2208988800U // (1970 - 1900) in seconds -#define SNTP_MAX_FRAC 4294967295.0 // 2 ** 32 - 1 - -static int64_t gettimestamp(const uint32_t* data) -{ - uint32_t sec = mg_ntohl(data[0]), frac = mg_ntohl(data[1]); - if (sec) - sec -= SNTP_TIME_OFFSET; - return ((int64_t)sec) * 1000 + (int64_t)(frac / SNTP_MAX_FRAC * 1000.0); -} - -int64_t mg_sntp_parse(const unsigned char* buf, size_t len) -{ - int64_t res = -1; - int mode = len > 0 ? buf[0] & 7 : 0; - int version = len > 0 ? (buf[0] >> 3) & 7 : 0; - if (len < 48) { - MG_ERROR(("%s", "corrupt packet")); - } else if (mode != 4 && mode != 5) { - MG_ERROR(("%s", "not a server reply")); - } else if (buf[1] == 0) { - MG_ERROR(("%s", "server sent a kiss of death")); - } else if (version == 4 || version == 3) { - // int64_t ref = gettimestamp((uint32_t *) &buf[16]); - int64_t t0 = gettimestamp((uint32_t*)&buf[24]); - int64_t t1 = gettimestamp((uint32_t*)&buf[32]); - int64_t t2 = gettimestamp((uint32_t*)&buf[40]); - int64_t t3 = (int64_t)mg_millis(); - int64_t delta = (t3 - t0) - (t2 - t1); - MG_VERBOSE(("%lld %lld %lld %lld delta:%lld", t0, t1, t2, t3, delta)); - res = t2 + delta / 2; - } else { - MG_ERROR(("unexpected version: %d", version)); - } - return res; -} - -static void sntp_cb(struct mg_connection* c, int ev, void* evd, void* fnd) -{ - if (ev == MG_EV_READ) { - int64_t milliseconds = mg_sntp_parse(c->recv.buf, c->recv.len); - if (milliseconds > 0) { - MG_INFO(("%lu got time: %lld ms from epoch", c->id, milliseconds)); - mg_call(c, MG_EV_SNTP_TIME, (uint64_t*)&milliseconds); - MG_VERBOSE(("%u.%u", (unsigned)(milliseconds / 1000), - (unsigned)(milliseconds % 1000))); - } - mg_iobuf_del(&c->recv, 0, c->recv.len); // Free receive buffer - } else if (ev == MG_EV_CONNECT) { - mg_sntp_request(c); - } else if (ev == MG_EV_CLOSE) { - } - (void)fnd; - (void)evd; -} - -void mg_sntp_request(struct mg_connection* c) -{ - if (c->is_resolving) { - MG_ERROR(("%lu wait until resolved", c->id)); - } else { - int64_t now = (int64_t)mg_millis(); // Use int64_t, for vc98 - uint8_t buf[48] = { 0 }; - uint32_t* t = (uint32_t*)&buf[40]; - double frac = ((double)(now % 1000)) / 1000.0 * SNTP_MAX_FRAC; - buf[0] = (0 << 6) | (4 << 3) | 3; - t[0] = mg_htonl((uint32_t)(now / 1000) + SNTP_TIME_OFFSET); - t[1] = mg_htonl((uint32_t)frac); - mg_send(c, buf, sizeof(buf)); - } -} - -struct mg_connection* mg_sntp_connect(struct mg_mgr* mgr, const char* url, - mg_event_handler_t fn, void* fnd) -{ - struct mg_connection* c = NULL; - if (url == NULL) - url = "udp://time.google.com:123"; - if ((c = mg_connect(mgr, url, fn, fnd)) != NULL) - c->pfn = sntp_cb; - return c; -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/sock.c" -#endif - -#if MG_ENABLE_SOCKET - -#ifndef closesocket -#define closesocket(x) close(x) -#endif - -#define FD(c_) ((MG_SOCKET_TYPE)(size_t)(c_)->fd) -#define S2PTR(s_) ((void*)(size_t)(s_)) - -#ifndef MSG_NONBLOCKING -#define MSG_NONBLOCKING 0 -#endif - -#ifndef AF_INET6 -#define AF_INET6 10 -#endif - -#ifndef MG_SOCK_ERR -#define MG_SOCK_ERR(errcode) ((errcode) < 0 ? errno : 0) -#endif - -#ifndef MG_SOCK_INTR -#define MG_SOCK_INTR(fd) (fd == MG_INVALID_SOCKET && MG_SOCK_ERR(-1) == EINTR) -#endif - -#ifndef MG_SOCK_PENDING -#define MG_SOCK_PENDING(errcode) \ - (((errcode) < 0) && (errno == EINPROGRESS || errno == EWOULDBLOCK)) -#endif - -#ifndef MG_SOCK_RESET -#define MG_SOCK_RESET(errcode) \ - (((errcode) < 0) && (errno == EPIPE || errno == ECONNRESET)) -#endif - -union usa { - struct sockaddr sa; - struct sockaddr_in sin; -#if MG_ENABLE_IPV6 - struct sockaddr_in6 sin6; -#endif -}; - -static socklen_t tousa(struct mg_addr* a, union usa* usa) -{ - socklen_t len = sizeof(usa->sin); - memset(usa, 0, sizeof(*usa)); - usa->sin.sin_family = AF_INET; - usa->sin.sin_port = a->port; - memcpy(&usa->sin.sin_addr, a->ip, sizeof(uint32_t)); -#if MG_ENABLE_IPV6 - if (a->is_ip6) { - usa->sin.sin_family = AF_INET6; - usa->sin6.sin6_port = a->port; - memcpy(&usa->sin6.sin6_addr, a->ip, sizeof(a->ip)); - len = sizeof(usa->sin6); - } -#endif - return len; -} - -static void tomgaddr(union usa* usa, struct mg_addr* a, bool is_ip6) -{ - a->is_ip6 = is_ip6; - a->port = usa->sin.sin_port; - memcpy(&a->ip, &usa->sin.sin_addr, sizeof(uint32_t)); -#if MG_ENABLE_IPV6 - if (is_ip6) { - memcpy(a->ip, &usa->sin6.sin6_addr, sizeof(a->ip)); - a->port = usa->sin6.sin6_port; - } -#endif -} - -static void setlocaddr(MG_SOCKET_TYPE fd, struct mg_addr* addr) -{ - union usa usa; - socklen_t n = sizeof(usa); - if (getsockname(fd, &usa.sa, &n) == 0) { - tomgaddr(&usa, addr, n != sizeof(usa.sin)); - } -} - -static void iolog(struct mg_connection* c, char* buf, long n, bool r) -{ - if (n == MG_IO_WAIT) { - // Do nothing - } else if (n <= 0) { - c->is_closing = 1; // Termination. Don't call mg_error(): #1529 - } else if (n > 0) { - if (c->is_hexdumping) { - union usa usa; - socklen_t slen = sizeof(usa.sin); - if (getsockname(FD(c), &usa.sa, &slen) < 0) - (void)0; // Ignore result - MG_INFO(("\n-- %lu %M %s %M %ld", c->id, mg_print_ip_port, &c->loc, - r ? "<-" : "->", mg_print_ip_port, &c->rem, n)); - - mg_hexdump(buf, (size_t)n); - } - if (r) { - c->recv.len += (size_t)n; - mg_call(c, MG_EV_READ, &n); - } else { - mg_iobuf_del(&c->send, 0, (size_t)n); - // if (c->send.len == 0) mg_iobuf_resize(&c->send, 0); - if (c->send.len == 0) { - MG_EPOLL_MOD(c, 0); - } - mg_call(c, MG_EV_WRITE, &n); - } - } -} - -long mg_io_send(struct mg_connection* c, const void* buf, size_t len) -{ - long n; - if (c->is_udp) { - union usa usa; - socklen_t slen = tousa(&c->rem, &usa); - n = sendto(FD(c), (char*)buf, len, 0, &usa.sa, slen); - if (n > 0) - setlocaddr(FD(c), &c->loc); - } else { - n = send(FD(c), (char*)buf, len, MSG_NONBLOCKING); - } - if (MG_SOCK_PENDING(n)) - return MG_IO_WAIT; - if (MG_SOCK_RESET(n)) - return MG_IO_RESET; - if (n <= 0) - return MG_IO_ERR; - return n; -} - -bool mg_send(struct mg_connection* c, const void* buf, size_t len) -{ - if (c->is_udp) { - long n = mg_io_send(c, buf, len); - MG_DEBUG(("%lu %ld %d:%d %ld err %d", c->id, c->fd, (int)c->send.len, - (int)c->recv.len, n, MG_SOCK_ERR(n))); - iolog(c, (char*)buf, n, false); - return n > 0; - } else { - return mg_iobuf_add(&c->send, c->send.len, buf, len); - } -} - -static void mg_set_non_blocking_mode(MG_SOCKET_TYPE fd) -{ -#if defined(MG_CUSTOM_NONBLOCK) - MG_CUSTOM_NONBLOCK(fd); -#elif MG_ARCH == MG_ARCH_WIN32 && MG_ENABLE_WINSOCK - unsigned long on = 1; - ioctlsocket(fd, FIONBIO, &on); -#elif MG_ENABLE_RL - unsigned long on = 1; - ioctlsocket(fd, FIONBIO, &on); -#elif MG_ENABLE_FREERTOS_TCP - const BaseType_t off = 0; - if (setsockopt(fd, 0, FREERTOS_SO_RCVTIMEO, &off, sizeof(off)) != 0) - (void)0; - if (setsockopt(fd, 0, FREERTOS_SO_SNDTIMEO, &off, sizeof(off)) != 0) - (void)0; -#elif MG_ENABLE_LWIP - lwip_fcntl(fd, F_SETFL, O_NONBLOCK); -#elif MG_ARCH == MG_ARCH_AZURERTOS - fcntl(fd, F_SETFL, O_NONBLOCK); -#elif MG_ARCH == MG_ARCH_TIRTOS - int val = 0; - setsockopt(fd, SOL_SOCKET, SO_BLOCKING, &val, sizeof(val)); - // SPRU524J section 3.3.3 page 63, SO_SNDLOWAT - int sz = sizeof(val); - getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &val, &sz); - val /= 2; // set send low-water mark at half send buffer size - setsockopt(fd, SOL_SOCKET, SO_SNDLOWAT, &val, sizeof(val)); -#else - fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); // Non-blocking mode - fcntl(fd, F_SETFD, FD_CLOEXEC); // Set close-on-exec -#endif -} - -bool mg_open_listener(struct mg_connection* c, const char* url) -{ - MG_SOCKET_TYPE fd = MG_INVALID_SOCKET; - bool success = false; - c->loc.port = mg_htons(mg_url_port(url)); - if (!mg_aton(mg_url_host(url), &c->loc)) { - MG_ERROR(("invalid listening URL: %s", url)); - } else { - union usa usa; - socklen_t slen = tousa(&c->loc, &usa); - int rc, on = 1, af = c->loc.is_ip6 ? AF_INET6 : AF_INET; - int type = strncmp(url, "udp:", 4) == 0 ? SOCK_DGRAM : SOCK_STREAM; - int proto = type == SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP; - (void)on; - - if ((fd = socket(af, type, proto)) == MG_INVALID_SOCKET) { - MG_ERROR(("socket: %d", MG_SOCK_ERR(-1))); -#if defined(SO_EXCLUSIVEADDRUSE) - } else if ((rc = setsockopt(fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, - (char*)&on, sizeof(on))) - != 0) { - // "Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE" - MG_ERROR(("setsockopt(SO_EXCLUSIVEADDRUSE): %d %d", on, MG_SOCK_ERR(rc))); -#elif defined(SO_REUSEADDR) && (!defined(LWIP_SOCKET) || SO_REUSE) - } else if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, - sizeof(on))) - != 0) { - // 1. SO_REUSEADDR semantics on UNIX and Windows is different. On - // Windows, SO_REUSEADDR allows to bind a socket to a port without error - // even if the port is already open by another program. This is not the - // behavior SO_REUSEADDR was designed for, and leads to hard-to-track - // failure scenarios. - // - // 2. For LWIP, SO_REUSEADDR should be explicitly enabled by defining - // SO_REUSE = 1 in lwipopts.h, otherwise the code below will compile but - // won't work! (setsockopt will return EINVAL) - MG_ERROR(("setsockopt(SO_REUSEADDR): %d", MG_SOCK_ERR(rc))); -#endif -#if MG_IPV6_V6ONLY - // Bind only to the V6 address, not V4 address on this port - } else if (c->loc.is_ip6 && (rc = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&on, sizeof(on))) != 0) { - // See #2089. Allow to bind v4 and v6 sockets on the same port - MG_ERROR(("setsockopt(IPV6_V6ONLY): %d", MG_SOCK_ERR(rc))); -#endif - } else if ((rc = bind(fd, &usa.sa, slen)) != 0) { - MG_ERROR(("bind: %d", MG_SOCK_ERR(rc))); - } else if ((type == SOCK_STREAM && (rc = listen(fd, MG_SOCK_LISTEN_BACKLOG_SIZE)) != 0)) { - // NOTE(lsm): FreeRTOS uses backlog value as a connection limit - // In case port was set to 0, get the real port number - MG_ERROR(("listen: %d", MG_SOCK_ERR(rc))); - } else { - setlocaddr(fd, &c->loc); - mg_set_non_blocking_mode(fd); - c->fd = S2PTR(fd); - MG_EPOLL_ADD(c); - success = true; - } - } - if (success == false && fd != MG_INVALID_SOCKET) - closesocket(fd); - return success; -} - -long mg_io_recv(struct mg_connection* c, void* buf, size_t len) -{ - long n = 0; - if (c->is_udp) { - union usa usa; - socklen_t slen = tousa(&c->rem, &usa); - n = recvfrom(FD(c), (char*)buf, len, 0, &usa.sa, &slen); - if (n > 0) - tomgaddr(&usa, &c->rem, slen != sizeof(usa.sin)); - } else { - n = recv(FD(c), (char*)buf, len, MSG_NONBLOCKING); - } - if (MG_SOCK_PENDING(n)) - return MG_IO_WAIT; - if (MG_SOCK_RESET(n)) - return MG_IO_RESET; - if (n <= 0) - return MG_IO_ERR; - return n; -} - -// NOTE(lsm): do only one iteration of reads, cause some systems -// (e.g. FreeRTOS stack) return 0 instead of -1/EWOULDBLOCK when no data -static void read_conn(struct mg_connection* c) -{ - long n = -1; - if (c->recv.len >= MG_MAX_RECV_SIZE) { - mg_error(c, "max_recv_buf_size reached"); - } else if (c->recv.size <= c->recv.len && !mg_iobuf_resize(&c->recv, c->recv.size + MG_IO_SIZE)) { - mg_error(c, "oom"); - } else { - char* buf = (char*)&c->recv.buf[c->recv.len]; - size_t len = c->recv.size - c->recv.len; - n = c->is_tls ? mg_tls_recv(c, buf, len) : mg_io_recv(c, buf, len); - MG_DEBUG(("%lu %ld snd %ld/%ld rcv %ld/%ld n=%ld err=%d", c->id, c->fd, - (long)c->send.len, (long)c->send.size, (long)c->recv.len, - (long)c->recv.size, n, MG_SOCK_ERR(n))); - iolog(c, buf, n, true); - } -} - -static void write_conn(struct mg_connection* c) -{ - char* buf = (char*)c->send.buf; - size_t len = c->send.len; - long n = c->is_tls ? mg_tls_send(c, buf, len) : mg_io_send(c, buf, len); - MG_DEBUG(("%lu %ld snd %ld/%ld rcv %ld/%ld n=%ld err=%d", c->id, c->fd, - (long)c->send.len, (long)c->send.size, (long)c->recv.len, - (long)c->recv.size, n, MG_SOCK_ERR(n))); - iolog(c, buf, n, false); -} - -static void close_conn(struct mg_connection* c) -{ - if (FD(c) != MG_INVALID_SOCKET) { -#if MG_ENABLE_EPOLL - epoll_ctl(c->mgr->epoll_fd, EPOLL_CTL_DEL, FD(c), NULL); -#endif - closesocket(FD(c)); -#if MG_ENABLE_FREERTOS_TCP - FreeRTOS_FD_CLR(c->fd, c->mgr->ss, eSELECT_ALL); -#endif - } - mg_close_conn(c); -} - -static void connect_conn(struct mg_connection* c) -{ - union usa usa; - socklen_t n = sizeof(usa); - // Use getpeername() to test whether we have connected - if (getpeername(FD(c), &usa.sa, &n) == 0) { - c->is_connecting = 0; - mg_call(c, MG_EV_CONNECT, NULL); - MG_EPOLL_MOD(c, 0); - if (c->is_tls_hs) - mg_tls_handshake(c); - } else { - mg_error(c, "socket error"); - } -} - -static void setsockopts(struct mg_connection* c) -{ -#if MG_ENABLE_FREERTOS_TCP || MG_ARCH == MG_ARCH_AZURERTOS || MG_ARCH == MG_ARCH_TIRTOS - (void)c; -#else - int on = 1; -#if !defined(SOL_TCP) -#define SOL_TCP IPPROTO_TCP -#endif - if (setsockopt(FD(c), SOL_TCP, TCP_NODELAY, (char*)&on, sizeof(on)) != 0) - (void)0; - if (setsockopt(FD(c), SOL_SOCKET, SO_KEEPALIVE, (char*)&on, sizeof(on)) != 0) - (void)0; -#endif -} - -void mg_connect_resolved(struct mg_connection* c) -{ - int type = c->is_udp ? SOCK_DGRAM : SOCK_STREAM; - int rc, af = c->rem.is_ip6 ? AF_INET6 : AF_INET; // c->rem has resolved IP - c->fd = S2PTR(socket(af, type, 0)); // Create outbound socket - c->is_resolving = 0; // Clear resolving flag - if (FD(c) == MG_INVALID_SOCKET) { - mg_error(c, "socket(): %d", MG_SOCK_ERR(-1)); - } else if (c->is_udp) { - MG_EPOLL_ADD(c); -#if MG_ARCH == MG_ARCH_TIRTOS - union usa usa; // TI-RTOS NDK requires binding to receive on UDP sockets - socklen_t slen = tousa(&c->loc, &usa); - if ((rc = bind(c->fd, &usa.sa, slen)) != 0) - MG_ERROR(("bind: %d", MG_SOCK_ERR(rc))); -#endif - mg_call(c, MG_EV_RESOLVE, NULL); - mg_call(c, MG_EV_CONNECT, NULL); - } else { - union usa usa; - socklen_t slen = tousa(&c->rem, &usa); - mg_set_non_blocking_mode(FD(c)); - setsockopts(c); - MG_EPOLL_ADD(c); - mg_call(c, MG_EV_RESOLVE, NULL); - rc = connect(FD(c), &usa.sa, slen); // Attempt to connect - if (rc == 0) { // Success - mg_call(c, MG_EV_CONNECT, NULL); // Send MG_EV_CONNECT to the user - } else if (MG_SOCK_PENDING(rc)) { // Need to wait for TCP handshake - MG_DEBUG(("%lu %ld -> %M pend", c->id, c->fd, mg_print_ip_port, &c->rem)); - c->is_connecting = 1; - } else { - mg_error(c, "connect: %d", MG_SOCK_ERR(rc)); - } - } -} - -static MG_SOCKET_TYPE raccept(MG_SOCKET_TYPE sock, union usa* usa, - socklen_t* len) -{ - MG_SOCKET_TYPE fd = MG_INVALID_SOCKET; - do { - memset(usa, 0, sizeof(*usa)); - fd = accept(sock, &usa->sa, len); - } while (MG_SOCK_INTR(fd)); - return fd; -} - -static void accept_conn(struct mg_mgr* mgr, struct mg_connection* lsn) -{ - struct mg_connection* c = NULL; - union usa usa; - socklen_t sa_len = sizeof(usa); - MG_SOCKET_TYPE fd = raccept(FD(lsn), &usa, &sa_len); - if (fd == MG_INVALID_SOCKET) { -#if MG_ARCH == MG_ARCH_AZURERTOS - // AzureRTOS, in non-block socket mode can mark listening socket readable - // even it is not. See comment for 'select' func implementation in - // nx_bsd.c That's not an error, just should try later - if (errno != EAGAIN) -#endif - MG_ERROR(("%lu accept failed, errno %d", lsn->id, MG_SOCK_ERR(-1))); -#if (MG_ARCH != MG_ARCH_WIN32) && !MG_ENABLE_FREERTOS_TCP && (MG_ARCH != MG_ARCH_TIRTOS) && !MG_ENABLE_POLL && !MG_ENABLE_EPOLL - } else if ((long)fd >= FD_SETSIZE) { - MG_ERROR(("%ld > %ld", (long)fd, (long)FD_SETSIZE)); - closesocket(fd); -#endif - } else if ((c = mg_alloc_conn(mgr)) == NULL) { - MG_ERROR(("%lu OOM", lsn->id)); - closesocket(fd); - } else { - tomgaddr(&usa, &c->rem, sa_len != sizeof(usa.sin)); - LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c); - c->fd = S2PTR(fd); - MG_EPOLL_ADD(c); - mg_set_non_blocking_mode(FD(c)); - setsockopts(c); - c->is_accepted = 1; - c->is_hexdumping = lsn->is_hexdumping; - c->loc = lsn->loc; - c->pfn = lsn->pfn; - c->pfn_data = lsn->pfn_data; - c->fn = lsn->fn; - c->fn_data = lsn->fn_data; - MG_DEBUG(("%lu %ld accepted %M -> %M", c->id, c->fd, mg_print_ip_port, - &c->rem, mg_print_ip_port, &c->loc)); - mg_call(c, MG_EV_OPEN, NULL); - mg_call(c, MG_EV_ACCEPT, NULL); - } -} - -static bool can_read(const struct mg_connection* c) -{ - return c->is_full == false; -} - -static bool can_write(const struct mg_connection* c) -{ - return c->is_connecting || (c->send.len > 0 && c->is_tls_hs == 0); -} - -static bool skip_iotest(const struct mg_connection* c) -{ - return (c->is_closing || c->is_resolving || FD(c) == MG_INVALID_SOCKET) || (can_read(c) == false && can_write(c) == false); -} - -static void mg_iotest(struct mg_mgr* mgr, int ms) -{ -#if MG_ENABLE_FREERTOS_TCP - struct mg_connection* c; - for (c = mgr->conns; c != NULL; c = c->next) { - c->is_readable = c->is_writable = 0; - if (skip_iotest(c)) - continue; - if (can_read(c)) - FreeRTOS_FD_SET(c->fd, mgr->ss, eSELECT_READ | eSELECT_EXCEPT); - if (can_write(c)) - FreeRTOS_FD_SET(c->fd, mgr->ss, eSELECT_WRITE); - } - FreeRTOS_select(mgr->ss, pdMS_TO_TICKS(ms)); - for (c = mgr->conns; c != NULL; c = c->next) { - EventBits_t bits = FreeRTOS_FD_ISSET(c->fd, mgr->ss); - c->is_readable = bits & (eSELECT_READ | eSELECT_EXCEPT) ? 1U : 0; - c->is_writable = bits & eSELECT_WRITE ? 1U : 0; - if (c->fd != MG_INVALID_SOCKET) - FreeRTOS_FD_CLR(c->fd, mgr->ss, - eSELECT_READ | eSELECT_EXCEPT | eSELECT_WRITE); - } -#elif MG_ENABLE_EPOLL - size_t max = 1; - for (struct mg_connection* c = mgr->conns; c != NULL; c = c->next) { - c->is_readable = c->is_writable = 0; - if (mg_tls_pending(c) > 0) - ms = 1, c->is_readable = 1; - if (can_write(c)) - MG_EPOLL_MOD(c, 1); - max++; - } - struct epoll_event* evs = (struct epoll_event*)alloca(max * sizeof(evs[0])); - int n = epoll_wait(mgr->epoll_fd, evs, (int)max, ms); - for (int i = 0; i < n; i++) { - struct mg_connection* c = (struct mg_connection*)evs[i].data.ptr; - if (evs[i].events & EPOLLERR) { - mg_error(c, "socket error"); - } else if (c->is_readable == 0) { - bool rd = evs[i].events & (EPOLLIN | EPOLLHUP); - bool wr = evs[i].events & EPOLLOUT; - c->is_readable = can_read(c) && rd ? 1U : 0; - c->is_writable = can_write(c) && wr ? 1U : 0; - } - } - (void)skip_iotest; -#elif MG_ENABLE_POLL - nfds_t n = 0; - for (struct mg_connection* c = mgr->conns; c != NULL; c = c->next) - n++; - struct pollfd* fds = (struct pollfd*)alloca(n * sizeof(fds[0])); - memset(fds, 0, n * sizeof(fds[0])); - n = 0; - for (struct mg_connection* c = mgr->conns; c != NULL; c = c->next) { - c->is_readable = c->is_writable = 0; - if (skip_iotest(c)) { - // Socket not valid, ignore - } else if (mg_tls_pending(c) > 0) { - ms = 1; // Don't wait if TLS is ready - } else { - fds[n].fd = FD(c); - if (can_read(c)) - fds[n].events |= POLLIN; - if (can_write(c)) - fds[n].events |= POLLOUT; - n++; - } - } - - // MG_INFO(("poll n=%d ms=%d", (int) n, ms)); - if (poll(fds, n, ms) < 0) { -#if MG_ARCH == MG_ARCH_WIN32 - if (n == 0) - Sleep(ms); // On Windows, poll fails if no sockets -#endif - memset(fds, 0, n * sizeof(fds[0])); - } - n = 0; - for (struct mg_connection* c = mgr->conns; c != NULL; c = c->next) { - if (skip_iotest(c)) { - // Socket not valid, ignore - } else if (mg_tls_pending(c) > 0) { - c->is_readable = 1; - } else { - if (fds[n].revents & POLLERR) { - mg_error(c, "socket error"); - } else { - c->is_readable = (unsigned)(fds[n].revents & (POLLIN | POLLHUP) ? 1 : 0); - c->is_writable = (unsigned)(fds[n].revents & POLLOUT ? 1 : 0); - } - n++; - } - } -#else - struct timeval tv = { ms / 1000, (ms % 1000) * 1000 }, tv_zero = { 0, 0 }, *tvp; - struct mg_connection* c; - fd_set rset, wset, eset; - MG_SOCKET_TYPE maxfd = 0; - int rc; - - FD_ZERO(&rset); - FD_ZERO(&wset); - FD_ZERO(&eset); - tvp = ms < 0 ? NULL : &tv; - for (c = mgr->conns; c != NULL; c = c->next) { - c->is_readable = c->is_writable = 0; - if (skip_iotest(c)) - continue; - FD_SET(FD(c), &eset); - if (can_read(c)) - FD_SET(FD(c), &rset); - if (can_write(c)) - FD_SET(FD(c), &wset); - if (mg_tls_pending(c) > 0) - tvp = &tv_zero; - if (FD(c) > maxfd) - maxfd = FD(c); - } - - if ((rc = select((int)maxfd + 1, &rset, &wset, &eset, tvp)) < 0) { -#if MG_ARCH == MG_ARCH_WIN32 - if (maxfd == 0) - Sleep(ms); // On Windows, select fails if no sockets -#else - MG_ERROR(("select: %d %d", rc, MG_SOCK_ERR(rc))); -#endif - FD_ZERO(&rset); - FD_ZERO(&wset); - FD_ZERO(&eset); - } - - for (c = mgr->conns; c != NULL; c = c->next) { - if (FD(c) != MG_INVALID_SOCKET && FD_ISSET(FD(c), &eset)) { - mg_error(c, "socket error"); - } else { - c->is_readable = FD(c) != MG_INVALID_SOCKET && FD_ISSET(FD(c), &rset); - c->is_writable = FD(c) != MG_INVALID_SOCKET && FD_ISSET(FD(c), &wset); - if (mg_tls_pending(c) > 0) - c->is_readable = 1; - } - } -#endif -} - -void mg_mgr_poll(struct mg_mgr* mgr, int ms) -{ - struct mg_connection *c, *tmp; - uint64_t now; - - mg_iotest(mgr, ms); - now = mg_millis(); - mg_timer_poll(&mgr->timers, now); - - for (c = mgr->conns; c != NULL; c = tmp) { - bool is_resp = c->is_resp; - tmp = c->next; - mg_call(c, MG_EV_POLL, &now); - if (is_resp && !c->is_resp) { - long n = 0; - mg_call(c, MG_EV_READ, &n); - } - MG_VERBOSE(("%lu %c%c %c%c%c%c%c", c->id, c->is_readable ? 'r' : '-', - c->is_writable ? 'w' : '-', c->is_tls ? 'T' : 't', - c->is_connecting ? 'C' : 'c', c->is_tls_hs ? 'H' : 'h', - c->is_resolving ? 'R' : 'r', c->is_closing ? 'C' : 'c')); - if (c->is_resolving || c->is_closing) { - // Do nothing - } else if (c->is_listening && c->is_udp == 0) { - if (c->is_readable) - accept_conn(mgr, c); - } else if (c->is_connecting) { - if (c->is_readable || c->is_writable) - connect_conn(c); - } else if (c->is_tls_hs) { - if ((c->is_readable || c->is_writable)) - mg_tls_handshake(c); - } else { - if (c->is_readable) - read_conn(c); - if (c->is_writable) - write_conn(c); - } - - if (c->is_draining && c->send.len == 0) - c->is_closing = 1; - if (c->is_closing) - close_conn(c); - } -} -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/ssi.c" -#endif - -#ifndef MG_MAX_SSI_DEPTH -#define MG_MAX_SSI_DEPTH 5 -#endif - -#ifndef MG_SSI_BUFSIZ -#define MG_SSI_BUFSIZ 1024 -#endif - -#if MG_ENABLE_SSI -static char* mg_ssi(const char* path, const char* root, int depth) -{ - struct mg_iobuf b = { NULL, 0, 0, MG_IO_SIZE }; - FILE* fp = fopen(path, "rb"); - if (fp != NULL) { - char buf[MG_SSI_BUFSIZ], arg[sizeof(buf)]; - int ch, intag = 0; - size_t len = 0; - buf[0] = arg[0] = '\0'; - while ((ch = fgetc(fp)) != EOF) { - if (intag && ch == '>' && buf[len - 1] == '-' && buf[len - 2] == '-') { - buf[len++] = (char)(ch & 0xff); - buf[len] = '\0'; - if (sscanf(buf, " %#x %#x", s_txdesc[s_txno][1], tsr)); - if (!(s_txdesc[s_txno][1] & BIT(31))) - s_txdesc[s_txno][1] |= BIT(31); - } - - GMAC_REGS->GMAC_RSR = rsr; - GMAC_REGS->GMAC_TSR = tsr; -} - -struct mg_tcpip_driver mg_tcpip_driver_same54 = { - mg_tcpip_driver_same54_init, mg_tcpip_driver_same54_tx, NULL, - mg_tcpip_driver_same54_up -}; -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/drivers/stm32.c" -#endif - -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_STM32) && MG_ENABLE_DRIVER_STM32 -struct stm32_eth { - volatile uint32_t MACCR, MACFFR, MACHTHR, MACHTLR, MACMIIAR, MACMIIDR, MACFCR, - MACVLANTR, RESERVED0[2], MACRWUFFR, MACPMTCSR, RESERVED1, MACDBGR, MACSR, - MACIMR, MACA0HR, MACA0LR, MACA1HR, MACA1LR, MACA2HR, MACA2LR, MACA3HR, - MACA3LR, RESERVED2[40], MMCCR, MMCRIR, MMCTIR, MMCRIMR, MMCTIMR, - RESERVED3[14], MMCTGFSCCR, MMCTGFMSCCR, RESERVED4[5], MMCTGFCR, - RESERVED5[10], MMCRFCECR, MMCRFAECR, RESERVED6[10], MMCRGUFCR, - RESERVED7[334], PTPTSCR, PTPSSIR, PTPTSHR, PTPTSLR, PTPTSHUR, PTPTSLUR, - PTPTSAR, PTPTTHR, PTPTTLR, RESERVED8, PTPTSSR, PTPPPSCR, RESERVED9[564], - DMABMR, DMATPDR, DMARPDR, DMARDLAR, DMATDLAR, DMASR, DMAOMR, DMAIER, - DMAMFBOCR, DMARSWTR, RESERVED10[8], DMACHTDR, DMACHRDR, DMACHTBAR, - DMACHRBAR; -}; -#undef ETH -#define ETH ((struct stm32_eth*)(uintptr_t)0x40028000) - -#undef DSB -#if defined(__CC_ARM) -#define DSB() __dsb(0xF) -#elif defined(__ARMCC_VERSION) -#define DSB() __builtin_arm_dsb(0xF) -#elif defined(__GNUC__) && defined(__arm__) && defined(__thumb__) -#define DSB() asm("DSB 0xF") -#elif defined(__ICCARM__) -#define DSB() __iar_builtin_DSB() -#else -#define DSB() -#endif - -#undef BIT -#define BIT(x) ((uint32_t)1 << (x)) -#define ETH_PKT_SIZE 1540 // Max frame size -#define ETH_DESC_CNT 4 // Descriptors count -#define ETH_DS 4 // Descriptor size (words) - -static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors -static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors -static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers -static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers -static uint8_t s_txno; // Current TX descriptor -static uint8_t s_rxno; // Current RX descriptor - -static struct mg_tcpip_if* s_ifp; // MIP interface -enum { PHY_ADDR = 0, - PHY_BCR = 0, - PHY_BSR = 1, - PHY_CSCR = 31 }; - -static uint32_t eth_read_phy(uint8_t addr, uint8_t reg) -{ - ETH->MACMIIAR &= (7 << 2); - ETH->MACMIIAR |= ((uint32_t)addr << 11) | ((uint32_t)reg << 6); - ETH->MACMIIAR |= BIT(0); - while (ETH->MACMIIAR & BIT(0)) - (void)0; - return ETH->MACMIIDR; -} - -static void eth_write_phy(uint8_t addr, uint8_t reg, uint32_t val) -{ - ETH->MACMIIDR = val; - ETH->MACMIIAR &= (7 << 2); - ETH->MACMIIAR |= ((uint32_t)addr << 11) | ((uint32_t)reg << 6) | BIT(1); - ETH->MACMIIAR |= BIT(0); - while (ETH->MACMIIAR & BIT(0)) - (void)0; -} - -static uint32_t get_hclk(void) -{ - struct rcc { - volatile uint32_t CR, PLLCFGR, CFGR; - }* rcc = (struct rcc*)0x40023800; - uint32_t clk = 0, hsi = 16000000 /* 16 MHz */, hse = 8000000 /* 8MHz */; - - if (rcc->CFGR & (1 << 2)) { - clk = hse; - } else if (rcc->CFGR & (1 << 3)) { - uint32_t vco, m, n, p; - m = (rcc->PLLCFGR & (0x3f << 0)) >> 0; - n = (rcc->PLLCFGR & (0x1ff << 6)) >> 6; - p = (((rcc->PLLCFGR & (3 << 16)) >> 16) + 1) * 2; - clk = (rcc->PLLCFGR & (1 << 22)) ? hse : hsi; - vco = (uint32_t)((uint64_t)clk * n / m); - clk = vco / p; - } else { - clk = hsi; - } - uint32_t hpre = (rcc->CFGR & (15 << 4)) >> 4; - if (hpre < 8) - return clk; - - uint8_t ahbptab[8] = { 1, 2, 3, 4, 6, 7, 8, 9 }; // log2(div) - return ((uint32_t)clk) >> ahbptab[hpre - 8]; -} - -// Guess CR from HCLK. MDC clock is generated from HCLK (AHB); as per 802.3, -// it must not exceed 2.5MHz As the AHB clock can be (and usually is) derived -// from the HSI (internal RC), and it can go above specs, the datasheets -// specify a range of frequencies and activate one of a series of dividers to -// keep the MDC clock safely below 2.5MHz. We guess a divider setting based on -// HCLK with a +5% drift. If the user uses a different clock from our -// defaults, needs to set the macros on top Valid for STM32F74xxx/75xxx -// (38.8.1) and STM32F42xxx/43xxx (33.8.1) (both 4.5% worst case drift) -static int guess_mdc_cr(void) -{ - uint8_t crs[] = { 2, 3, 0, 1, 4, 5 }; // ETH->MACMIIAR::CR values - uint8_t div[] = { 16, 26, 42, 62, 102, 124 }; // Respective HCLK dividers - uint32_t hclk = get_hclk(); // Guess system HCLK - int result = -1; // Invalid CR value - if (hclk < 25000000) { - MG_ERROR(("HCLK too low")); - } else { - for (int i = 0; i < 6; i++) { - if (hclk / div[i] <= 2375000UL /* 2.5MHz - 5% */) { - result = crs[i]; - break; - } - } - if (result < 0) - MG_ERROR(("HCLK too high")); - } - MG_DEBUG(("HCLK: %u, CR: %d", hclk, result)); - return result; -} - -static bool mg_tcpip_driver_stm32_init(struct mg_tcpip_if* ifp) -{ - struct mg_tcpip_driver_stm32_data* d = (struct mg_tcpip_driver_stm32_data*)ifp->driver_data; - s_ifp = ifp; - - // Init RX descriptors - for (int i = 0; i < ETH_DESC_CNT; i++) { - s_rxdesc[i][0] = BIT(31); // Own - s_rxdesc[i][1] = sizeof(s_rxbuf[i]) | BIT(14); // 2nd address chained - s_rxdesc[i][2] = (uint32_t)(uintptr_t)s_rxbuf[i]; // Point to data buffer - s_rxdesc[i][3] = (uint32_t)(uintptr_t)s_rxdesc[(i + 1) % ETH_DESC_CNT]; // Chain - } - - // Init TX descriptors - for (int i = 0; i < ETH_DESC_CNT; i++) { - s_txdesc[i][2] = (uint32_t)(uintptr_t)s_txbuf[i]; // Buf pointer - s_txdesc[i][3] = (uint32_t)(uintptr_t)s_txdesc[(i + 1) % ETH_DESC_CNT]; // Chain - } - - ETH->DMABMR |= BIT(0); // Software reset - while ((ETH->DMABMR & BIT(0)) != 0) - (void)0; // Wait until done - - // Set MDC clock divider. If user told us the value, use it. Otherwise, guess - int cr = (d == NULL || d->mdc_cr < 0) ? guess_mdc_cr() : d->mdc_cr; - ETH->MACMIIAR = ((uint32_t)cr & 7) << 2; - - // NOTE(cpq): we do not use extended descriptor bit 7, and do not use - // hardware checksum. Therefore, descriptor size is 4, not 8 - // ETH->DMABMR = BIT(13) | BIT(16) | BIT(22) | BIT(23) | BIT(25); - ETH->MACIMR = BIT(3) | BIT(9); // Mask timestamp & PMT IT - ETH->MACFCR = BIT(7); // Disable zero quarta pause - // ETH->MACFFR = BIT(31); // Receive all - eth_write_phy(PHY_ADDR, PHY_BCR, BIT(15)); // Reset PHY - eth_write_phy(PHY_ADDR, PHY_BCR, BIT(12)); // Set autonegotiation - ETH->DMARDLAR = (uint32_t)(uintptr_t)s_rxdesc; // RX descriptors - ETH->DMATDLAR = (uint32_t)(uintptr_t)s_txdesc; // RX descriptors - ETH->DMAIER = BIT(6) | BIT(16); // RIE, NISE - ETH->MACCR = BIT(2) | BIT(3) | BIT(11) | BIT(14); // RE, TE, Duplex, Fast - ETH->DMAOMR = BIT(1) | BIT(13) | BIT(21) | BIT(25); // SR, ST, TSF, RSF - - // MAC address filtering - ETH->MACA0HR = ((uint32_t)ifp->mac[5] << 8U) | ifp->mac[4]; - ETH->MACA0LR = (uint32_t)(ifp->mac[3] << 24) | ((uint32_t)ifp->mac[2] << 16) | ((uint32_t)ifp->mac[1] << 8) | ifp->mac[0]; - return true; -} - -static size_t mg_tcpip_driver_stm32_tx(const void* buf, size_t len, - struct mg_tcpip_if* ifp) -{ - if (len > sizeof(s_txbuf[s_txno])) { - MG_ERROR(("Frame too big, %ld", (long)len)); - len = 0; // Frame is too big - } else if ((s_txdesc[s_txno][0] & BIT(31))) { - ifp->nerr++; - MG_ERROR(("No free descriptors")); - // printf("D0 %lx SR %lx\n", (long) s_txdesc[0][0], (long) ETH->DMASR); - len = 0; // All descriptors are busy, fail - } else { - memcpy(s_txbuf[s_txno], buf, len); // Copy data - s_txdesc[s_txno][1] = (uint32_t)len; // Set data len - s_txdesc[s_txno][0] = BIT(20) | BIT(28) | BIT(29); // Chain,FS,LS - s_txdesc[s_txno][0] |= BIT(31); // Set OWN bit - let DMA take over - if (++s_txno >= ETH_DESC_CNT) - s_txno = 0; - } - DSB(); // ensure descriptors have been written - ETH->DMASR = BIT(2) | BIT(5); // Clear any prior TBUS/TUS - ETH->DMATPDR = 0; // and resume - return len; -} - -static bool mg_tcpip_driver_stm32_up(struct mg_tcpip_if* ifp) -{ - uint32_t bsr = eth_read_phy(PHY_ADDR, PHY_BSR); - bool up = bsr & BIT(2) ? 1 : 0; - if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up - uint32_t scsr = eth_read_phy(PHY_ADDR, PHY_CSCR); - uint32_t maccr = ETH->MACCR | BIT(14) | BIT(11); // 100M, Full-duplex - if ((scsr & BIT(3)) == 0) - maccr &= ~BIT(14); // 10M - if ((scsr & BIT(4)) == 0) - maccr &= ~BIT(11); // Half-duplex - ETH->MACCR = maccr; // IRQ handler does not fiddle with this register - MG_DEBUG(("Link is %uM %s-duplex", maccr & BIT(14) ? 100 : 10, - maccr & BIT(11) ? "full" : "half")); - } - return up; -} - -void ETH_IRQHandler(void); -void ETH_IRQHandler(void) -{ - if (ETH->DMASR & BIT(6)) { // Frame received, loop - ETH->DMASR = BIT(16) | BIT(6); // Clear flag - for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever - if (s_rxdesc[s_rxno][0] & BIT(31)) - break; // exit when done - if (((s_rxdesc[s_rxno][0] & (BIT(8) | BIT(9))) == (BIT(8) | BIT(9))) && !(s_rxdesc[s_rxno][0] & BIT(15))) { // skip partial/errored frames - uint32_t len = ((s_rxdesc[s_rxno][0] >> 16) & (BIT(14) - 1)); - // printf("%lx %lu %lx %.8lx\n", s_rxno, len, s_rxdesc[s_rxno][0], - // ETH->DMASR); - mg_tcpip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp); - } - s_rxdesc[s_rxno][0] = BIT(31); - if (++s_rxno >= ETH_DESC_CNT) - s_rxno = 0; - } - } - ETH->DMASR = BIT(7); // Clear possible RBUS while processing - ETH->DMARPDR = 0; // and resume RX -} - -struct mg_tcpip_driver mg_tcpip_driver_stm32 = { mg_tcpip_driver_stm32_init, - mg_tcpip_driver_stm32_tx, NULL, - mg_tcpip_driver_stm32_up }; -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/drivers/stm32h.c" -#endif - -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_STM32H) && MG_ENABLE_DRIVER_STM32H -struct stm32h_eth { - volatile uint32_t MACCR, MACECR, MACPFR, MACWTR, MACHT0R, MACHT1R, - RESERVED1[14], MACVTR, RESERVED2, MACVHTR, RESERVED3, MACVIR, MACIVIR, - RESERVED4[2], MACTFCR, RESERVED5[7], MACRFCR, RESERVED6[7], MACISR, - MACIER, MACRXTXSR, RESERVED7, MACPCSR, MACRWKPFR, RESERVED8[2], MACLCSR, - MACLTCR, MACLETR, MAC1USTCR, RESERVED9[12], MACVR, MACDR, RESERVED10, - MACHWF0R, MACHWF1R, MACHWF2R, RESERVED11[54], MACMDIOAR, MACMDIODR, - RESERVED12[2], MACARPAR, RESERVED13[59], MACA0HR, MACA0LR, MACA1HR, - MACA1LR, MACA2HR, MACA2LR, MACA3HR, MACA3LR, RESERVED14[248], MMCCR, - MMCRIR, MMCTIR, MMCRIMR, MMCTIMR, RESERVED15[14], MMCTSCGPR, MMCTMCGPR, - RESERVED16[5], MMCTPCGR, RESERVED17[10], MMCRCRCEPR, MMCRAEPR, - RESERVED18[10], MMCRUPGR, RESERVED19[9], MMCTLPIMSTR, MMCTLPITCR, - MMCRLPIMSTR, MMCRLPITCR, RESERVED20[65], MACL3L4C0R, MACL4A0R, - RESERVED21[2], MACL3A0R0R, MACL3A1R0R, MACL3A2R0R, MACL3A3R0R, - RESERVED22[4], MACL3L4C1R, MACL4A1R, RESERVED23[2], MACL3A0R1R, - MACL3A1R1R, MACL3A2R1R, MACL3A3R1R, RESERVED24[108], MACTSCR, MACSSIR, - MACSTSR, MACSTNR, MACSTSUR, MACSTNUR, MACTSAR, RESERVED25, MACTSSR, - RESERVED26[3], MACTTSSNR, MACTTSSSR, RESERVED27[2], MACACR, RESERVED28, - MACATSNR, MACATSSR, MACTSIACR, MACTSEACR, MACTSICNR, MACTSECNR, - RESERVED29[4], MACPPSCR, RESERVED30[3], MACPPSTTSR, MACPPSTTNR, MACPPSIR, - MACPPSWR, RESERVED31[12], MACPOCR, MACSPI0R, MACSPI1R, MACSPI2R, MACLMIR, - RESERVED32[11], MTLOMR, RESERVED33[7], MTLISR, RESERVED34[55], MTLTQOMR, - MTLTQUR, MTLTQDR, RESERVED35[8], MTLQICSR, MTLRQOMR, MTLRQMPOCR, MTLRQDR, - RESERVED36[177], DMAMR, DMASBMR, DMAISR, DMADSR, RESERVED37[60], DMACCR, - DMACTCR, DMACRCR, RESERVED38[2], DMACTDLAR, RESERVED39, DMACRDLAR, - DMACTDTPR, RESERVED40, DMACRDTPR, DMACTDRLR, DMACRDRLR, DMACIER, - DMACRIWTR, DMACSFCSR, RESERVED41, DMACCATDR, RESERVED42, DMACCARDR, - RESERVED43, DMACCATBR, RESERVED44, DMACCARBR, DMACSR, RESERVED45[2], - DMACMFCR; -}; -#undef ETH -#define ETH \ - ((struct stm32h_eth*)(uintptr_t)(0x40000000UL + 0x00020000UL + 0x8000UL)) - -#undef BIT -#define BIT(x) ((uint32_t)1 << (x)) -#define ETH_PKT_SIZE 1540 // Max frame size -#define ETH_DESC_CNT 4 // Descriptors count -#define ETH_DS 4 // Descriptor size (words) - -static volatile uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors -static volatile uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors -static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers -static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers -static struct mg_tcpip_if* s_ifp; // MIP interface -enum { - PHY_ADDR = 0, - PHY_BCR = 0, - PHY_BSR = 1, - PHY_CSCR = 31 -}; // PHY constants - -static uint32_t eth_read_phy(uint8_t addr, uint8_t reg) -{ - ETH->MACMDIOAR &= (0xF << 8); - ETH->MACMDIOAR |= ((uint32_t)addr << 21) | ((uint32_t)reg << 16) | 3 << 2; - ETH->MACMDIOAR |= BIT(0); - while (ETH->MACMDIOAR & BIT(0)) - (void)0; - return ETH->MACMDIODR; -} - -static void eth_write_phy(uint8_t addr, uint8_t reg, uint32_t val) -{ - ETH->MACMDIODR = val; - ETH->MACMDIOAR &= (0xF << 8); - ETH->MACMDIOAR |= ((uint32_t)addr << 21) | ((uint32_t)reg << 16) | 1 << 2; - ETH->MACMDIOAR |= BIT(0); - while (ETH->MACMDIOAR & BIT(0)) - (void)0; -} - -static uint32_t get_hclk(void) -{ - struct rcc { - volatile uint32_t CR, HSICFGR, CRRCR, CSICFGR, CFGR, RESERVED1, D1CFGR, - D2CFGR, D3CFGR, RESERVED2, PLLCKSELR, PLLCFGR, PLL1DIVR, PLL1FRACR, - PLL2DIVR, PLL2FRACR, PLL3DIVR, PLL3FRACR, RESERVED3, D1CCIPR, D2CCIP1R, - D2CCIP2R, D3CCIPR, RESERVED4, CIER, CIFR, CICR, RESERVED5, BDCR, CSR, - RESERVED6, AHB3RSTR, AHB1RSTR, AHB2RSTR, AHB4RSTR, APB3RSTR, APB1LRSTR, - APB1HRSTR, APB2RSTR, APB4RSTR, GCR, RESERVED8, D3AMR, RESERVED11[9], - RSR, AHB3ENR, AHB1ENR, AHB2ENR, AHB4ENR, APB3ENR, APB1LENR, APB1HENR, - APB2ENR, APB4ENR, RESERVED12, AHB3LPENR, AHB1LPENR, AHB2LPENR, - AHB4LPENR, APB3LPENR, APB1LLPENR, APB1HLPENR, APB2LPENR, APB4LPENR, - RESERVED13[4]; - }* rcc = ((struct rcc*)(0x40000000 + 0x18020000 + 0x4400)); - uint32_t clk = 0, hsi = 64000000 /* 64 MHz */, hse = 8000000 /* 8MHz */, - csi = 4000000 /* 4MHz */; - unsigned int sel = (rcc->CFGR & (7 << 3)) >> 3; - - if (sel == 1) { - clk = csi; - } else if (sel == 2) { - clk = hse; - } else if (sel == 3) { - uint32_t vco, m, n, p; - unsigned int src = (rcc->PLLCKSELR & (3 << 0)) >> 0; - m = ((rcc->PLLCKSELR & (0x3F << 4)) >> 4); - n = ((rcc->PLL1DIVR & (0x1FF << 0)) >> 0) + 1 + ((rcc->PLLCFGR & BIT(0)) ? 1 : 0); // round-up in fractional mode - p = ((rcc->PLL1DIVR & (0x7F << 9)) >> 9) + 1; - if (src == 1) { - clk = csi; - } else if (src == 2) { - clk = hse; - } else { - clk = hsi; - clk >>= ((rcc->CR & 3) >> 3); - } - vco = (uint32_t)((uint64_t)clk * n / m); - clk = vco / p; - } else { - clk = hsi; - clk >>= ((rcc->CR & 3) >> 3); - } - const uint8_t cptab[12] = { 1, 2, 3, 4, 6, 7, 8, 9 }; // log2(div) - uint32_t d1cpre = (rcc->D1CFGR & (0x0F << 8)) >> 8; - if (d1cpre >= 8) - clk >>= cptab[d1cpre - 8]; - MG_DEBUG(("D1 CLK: %u", clk)); - uint32_t hpre = (rcc->D1CFGR & (0x0F << 0)) >> 0; - if (hpre < 8) - return clk; - return ((uint32_t)clk) >> cptab[hpre - 8]; -} - -// Guess CR from AHB1 clock. MDC clock is generated from the ETH peripheral -// clock (AHB1); as per 802.3, it must not exceed 2. As the AHB clock can -// be derived from HSI or CSI (internal RC) clocks, and those can go above -// specs, the datasheets specify a range of frequencies and activate one of a -// series of dividers to keep the MDC clock safely below 2.5MHz. We guess a -// divider setting based on HCLK with some drift. If the user uses a different -// clock from our defaults, needs to set the macros on top. Valid for -// STM32H74xxx/75xxx (58.11.4)(4.5% worst case drift)(CSI clock has a 7.5 % -// worst case drift @ max temp) -static int guess_mdc_cr(void) -{ - const uint8_t crs[] = { 2, 3, 0, 1, 4, 5 }; // ETH->MACMDIOAR::CR values - const uint8_t div[] = { 16, 26, 42, 62, 102, 124 }; // Respective HCLK dividers - uint32_t hclk = get_hclk(); // Guess system HCLK - int result = -1; // Invalid CR value - for (int i = 0; i < 6; i++) { - if (hclk / div[i] <= 2375000UL /* 2.5MHz - 5% */) { - result = crs[i]; - break; - } - } - if (result < 0) - MG_ERROR(("HCLK too high")); - MG_DEBUG(("HCLK: %u, CR: %d", hclk, result)); - return result; -} - -static bool mg_tcpip_driver_stm32h_init(struct mg_tcpip_if* ifp) -{ - struct mg_tcpip_driver_stm32h_data* d = (struct mg_tcpip_driver_stm32h_data*)ifp->driver_data; - s_ifp = ifp; - - // Init RX descriptors - for (int i = 0; i < ETH_DESC_CNT; i++) { - s_rxdesc[i][0] = (uint32_t)(uintptr_t)s_rxbuf[i]; // Point to data buffer - s_rxdesc[i][3] = BIT(31) | BIT(30) | BIT(24); // OWN, IOC, BUF1V - } - - // Init TX descriptors - for (int i = 0; i < ETH_DESC_CNT; i++) { - s_txdesc[i][0] = (uint32_t)(uintptr_t)s_txbuf[i]; // Buf pointer - } - - ETH->DMAMR |= BIT(0); // Software reset - while ((ETH->DMAMR & BIT(0)) != 0) - (void)0; // Wait until done - - // Set MDC clock divider. If user told us the value, use it. Otherwise, guess - int cr = (d == NULL || d->mdc_cr < 0) ? guess_mdc_cr() : d->mdc_cr; - ETH->MACMDIOAR = ((uint32_t)cr & 0xF) << 8; - - // NOTE(scaprile): We do not use timing facilities so the DMA engine does not - // re-write buffer address - ETH->DMAMR = 0 << 16; // use interrupt mode 0 (58.8.1) (reset value) - ETH->DMASBMR |= BIT(12); // AAL NOTE(scaprile): is this actually needed - ETH->MACIER = 0; // Do not enable additional irq sources (reset value) - ETH->MACTFCR = BIT(7); // Disable zero-quanta pause - // ETH->MACPFR = BIT(31); // Receive all - eth_write_phy(PHY_ADDR, PHY_BCR, BIT(15)); // Reset PHY - eth_write_phy(PHY_ADDR, PHY_BCR, BIT(12)); // Set autonegotiation - ETH->DMACRDLAR = (uint32_t)(uintptr_t)s_rxdesc; // RX descriptors start address - ETH->DMACRDRLR = ETH_DESC_CNT - 1; // ring length - ETH->DMACRDTPR = (uint32_t)(uintptr_t)&s_rxdesc[ETH_DESC_CNT - 1]; // last valid descriptor address - ETH->DMACTDLAR = (uint32_t)(uintptr_t)s_txdesc; // TX descriptors start address - ETH->DMACTDRLR = ETH_DESC_CNT - 1; // ring length - ETH->DMACTDTPR = (uint32_t)(uintptr_t)s_txdesc; // first available descriptor address - ETH->DMACCR = 0; // DSL = 0 (contiguous descriptor table) (reset value) - ETH->DMACIER = BIT(6) | BIT(15); // RIE, NIE - ETH->MACCR = BIT(0) | BIT(1) | BIT(13) | BIT(14) | BIT(15); // RE, TE, Duplex, Fast, Reserved - ETH->MTLTQOMR |= BIT(1); // TSF - ETH->MTLRQOMR |= BIT(5); // RSF - ETH->DMACTCR |= BIT(0); // ST - ETH->DMACRCR |= BIT(0); // SR - - // MAC address filtering - ETH->MACA0HR = ((uint32_t)ifp->mac[5] << 8U) | ifp->mac[4]; - ETH->MACA0LR = (uint32_t)(ifp->mac[3] << 24) | ((uint32_t)ifp->mac[2] << 16) | ((uint32_t)ifp->mac[1] << 8) | ifp->mac[0]; - return true; -} - -static uint32_t s_txno; -static size_t mg_tcpip_driver_stm32h_tx(const void* buf, size_t len, - struct mg_tcpip_if* ifp) -{ - if (len > sizeof(s_txbuf[s_txno])) { - MG_ERROR(("Frame too big, %ld", (long)len)); - len = 0; // Frame is too big - } else if ((s_txdesc[s_txno][3] & BIT(31))) { - MG_ERROR(("No free descriptors: %u %08X %08X %08X", s_txno, - s_txdesc[s_txno][3], ETH->DMACSR, ETH->DMACTCR)); - for (int i = 0; i < ETH_DESC_CNT; i++) - MG_ERROR(("%08X", s_txdesc[i][3])); - len = 0; // All descriptors are busy, fail - } else { - memcpy(s_txbuf[s_txno], buf, len); // Copy data - s_txdesc[s_txno][2] = (uint32_t)len; // Set data len - s_txdesc[s_txno][3] = BIT(28) | BIT(29); // FD, LD - s_txdesc[s_txno][3] |= BIT(31); // Set OWN bit - let DMA take over - if (++s_txno >= ETH_DESC_CNT) - s_txno = 0; - } - ETH->DMACSR |= BIT(2) | BIT(1); // Clear any prior TBU, TPS - ETH->DMACTDTPR = (uint32_t)(uintptr_t)&s_txdesc[s_txno]; // and resume - return len; - (void)ifp; -} - -static bool mg_tcpip_driver_stm32h_up(struct mg_tcpip_if* ifp) -{ - uint32_t bsr = eth_read_phy(PHY_ADDR, PHY_BSR); - bool up = bsr & BIT(2) ? 1 : 0; - if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up - uint32_t scsr = eth_read_phy(PHY_ADDR, PHY_CSCR); - uint32_t maccr = ETH->MACCR | BIT(14) | BIT(13); // 100M, Full-duplex - if ((scsr & BIT(3)) == 0) - maccr &= ~BIT(14); // 10M - if ((scsr & BIT(4)) == 0) - maccr &= ~BIT(13); // Half-duplex - ETH->MACCR = maccr; // IRQ handler does not fiddle with this register - MG_DEBUG(("Link is %uM %s-duplex", maccr & BIT(14) ? 100 : 10, - maccr & BIT(13) ? "full" : "half")); - } - return up; -} - -void ETH_IRQHandler(void); -static uint32_t s_rxno; -void ETH_IRQHandler(void) -{ - if (ETH->DMACSR & BIT(6)) { // Frame received, loop - ETH->DMACSR = BIT(15) | BIT(6); // Clear flag - for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever - if (s_rxdesc[s_rxno][3] & BIT(31)) - break; // exit when done - if (((s_rxdesc[s_rxno][3] & (BIT(28) | BIT(29))) == (BIT(28) | BIT(29))) && !(s_rxdesc[s_rxno][3] & BIT(15))) { // skip partial/errored frames - uint32_t len = s_rxdesc[s_rxno][3] & (BIT(15) - 1); - // MG_DEBUG(("%lx %lu %lx %08lx", s_rxno, len, s_rxdesc[s_rxno][3], - // ETH->DMACSR)); - mg_tcpip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp); - } - s_rxdesc[s_rxno][3] = BIT(31) | BIT(30) | BIT(24); // OWN, IOC, BUF1V - if (++s_rxno >= ETH_DESC_CNT) - s_rxno = 0; - } - } - ETH->DMACSR = BIT(7) | BIT(8); // Clear possible RBU RPS while processing - ETH->DMACRDTPR = (uint32_t)(uintptr_t)&s_rxdesc[ETH_DESC_CNT - 1]; // and resume RX -} - -struct mg_tcpip_driver mg_tcpip_driver_stm32h = { - mg_tcpip_driver_stm32h_init, mg_tcpip_driver_stm32h_tx, NULL, - mg_tcpip_driver_stm32h_up -}; -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/drivers/tm4c.c" -#endif - -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_TM4C) && MG_ENABLE_DRIVER_TM4C -struct tm4c_emac { - volatile uint32_t EMACCFG, EMACFRAMEFLTR, EMACHASHTBLH, EMACHASHTBLL, - EMACMIIADDR, EMACMIIDATA, EMACFLOWCTL, EMACVLANTG, RESERVED0, EMACSTATUS, - EMACRWUFF, EMACPMTCTLSTAT, RESERVED1[2], EMACRIS, EMACIM, EMACADDR0H, - EMACADDR0L, EMACADDR1H, EMACADDR1L, EMACADDR2H, EMACADDR2L, EMACADDR3H, - EMACADDR3L, RESERVED2[31], EMACWDOGTO, RESERVED3[8], EMACMMCCTRL, - EMACMMCRXRIS, EMACMMCTXRIS, EMACMMCRXIM, EMACMMCTXIM, RESERVED4, - EMACTXCNTGB, RESERVED5[12], EMACTXCNTSCOL, EMACTXCNTMCOL, RESERVED6[4], - EMACTXOCTCNTG, RESERVED7[6], EMACRXCNTGB, RESERVED8[4], EMACRXCNTCRCERR, - EMACRXCNTALGNERR, RESERVED9[10], EMACRXCNTGUNI, RESERVED10[239], - EMACVLNINCREP, EMACVLANHASH, RESERVED11[93], EMACTIMSTCTRL, EMACSUBSECINC, - EMACTIMSEC, EMACTIMNANO, EMACTIMSECU, EMACTIMNANOU, EMACTIMADD, - EMACTARGSEC, EMACTARGNANO, EMACHWORDSEC, EMACTIMSTAT, EMACPPSCTRL, - RESERVED12[12], EMACPPS0INTVL, EMACPPS0WIDTH, RESERVED13[294], - EMACDMABUSMOD, EMACTXPOLLD, EMACRXPOLLD, EMACRXDLADDR, EMACTXDLADDR, - EMACDMARIS, EMACDMAOPMODE, EMACDMAIM, EMACMFBOC, EMACRXINTWDT, - RESERVED14[8], EMACHOSTXDESC, EMACHOSRXDESC, EMACHOSTXBA, EMACHOSRXBA, - RESERVED15[218], EMACPP, EMACPC, EMACCC, RESERVED16, EMACEPHYRIS, - EMACEPHYIM, EMACEPHYIMSC; -}; -#undef EMAC -#define EMAC ((struct tm4c_emac*)(uintptr_t)0x400EC000) - -#undef BIT -#define BIT(x) ((uint32_t)1 << (x)) -#define ETH_PKT_SIZE 1540 // Max frame size -#define ETH_DESC_CNT 4 // Descriptors count -#define ETH_DS 4 // Descriptor size (words) - -static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors -static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors -static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers -static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers -static struct mg_tcpip_if* s_ifp; // MIP interface -enum { - EPHY_ADDR = 0, - EPHYBMCR = 0, - EPHYBMSR = 1, - EPHYSTS = 16 -}; // PHY constants - -static inline void tm4cspin(volatile uint32_t count) -{ - while (count--) - (void)0; -} - -static uint32_t emac_read_phy(uint8_t addr, uint8_t reg) -{ - EMAC->EMACMIIADDR &= (0xf << 2); - EMAC->EMACMIIADDR |= ((uint32_t)addr << 11) | ((uint32_t)reg << 6); - EMAC->EMACMIIADDR |= BIT(0); - while (EMAC->EMACMIIADDR & BIT(0)) - tm4cspin(1); - return EMAC->EMACMIIDATA; -} - -static void emac_write_phy(uint8_t addr, uint8_t reg, uint32_t val) -{ - EMAC->EMACMIIDATA = val; - EMAC->EMACMIIADDR &= (0xf << 2); - EMAC->EMACMIIADDR |= ((uint32_t)addr << 11) | ((uint32_t)reg << 6) | BIT(1); - EMAC->EMACMIIADDR |= BIT(0); - while (EMAC->EMACMIIADDR & BIT(0)) - tm4cspin(1); -} - -static uint32_t get_sysclk(void) -{ - struct sysctl { - volatile uint32_t DONTCARE0[44], RSCLKCFG, DONTCARE1[43], PLLFREQ0, - PLLFREQ1; - }* sysctl = (struct sysctl*)0x400FE000; - uint32_t clk = 0, piosc = 16000000 /* 16 MHz */, mosc = 25000000 /* 25MHz */; - if (sysctl->RSCLKCFG & (1 << 28)) { // USEPLL - uint32_t fin, vco, mdiv, n, q, psysdiv; - uint32_t pllsrc = (sysctl->RSCLKCFG & (0xf << 24)) >> 24; - if (pllsrc == 0) { - clk = piosc; - } else if (pllsrc == 3) { - clk = mosc; - } else { - MG_ERROR(("Unsupported clock source")); - } - q = (sysctl->PLLFREQ1 & (0x1f << 8)) >> 8; - n = (sysctl->PLLFREQ1 & (0x1f << 0)) >> 0; - fin = clk / ((q + 1) * (n + 1)); - mdiv = (sysctl->PLLFREQ0 & (0x3ff << 0)) >> 0; // mint + (mfrac / 1024); MFRAC not supported - psysdiv = (sysctl->RSCLKCFG & (0x3f << 0)) >> 0; - vco = (uint32_t)((uint64_t)fin * mdiv); - return vco / (psysdiv + 1); - } - uint32_t oscsrc = (sysctl->RSCLKCFG & (0xf << 20)) >> 20; - if (oscsrc == 0) { - clk = piosc; - } else if (oscsrc == 3) { - clk = mosc; - } else { - MG_ERROR(("Unsupported clock source")); - } - uint32_t osysdiv = (sysctl->RSCLKCFG & (0xf << 16)) >> 16; - return clk / (osysdiv + 1); -} - -// Guess CR from SYSCLK. MDC clock is generated from SYSCLK (AHB); as per -// 802.3, it must not exceed 2.5MHz (also 20.4.2.6) As the AHB clock can be -// derived from the PIOSC (internal RC), and it can go above specs, the -// datasheets specify a range of frequencies and activate one of a series of -// dividers to keep the MDC clock safely below 2.5MHz. We guess a divider -// setting based on SYSCLK with a +5% drift. If the user uses a different clock -// from our defaults, needs to set the macros on top Valid for TM4C129x (20.7) -// (4.5% worst case drift) -// The PHY receives the main oscillator (MOSC) (20.3.1) -static int guess_mdc_cr(void) -{ - uint8_t crs[] = { 2, 3, 0, 1 }; // EMAC->MACMIIAR::CR values - uint8_t div[] = { 16, 26, 42, 62 }; // Respective HCLK dividers - uint32_t sysclk = get_sysclk(); // Guess system SYSCLK - int result = -1; // Invalid CR value - if (sysclk < 25000000) { - MG_ERROR(("SYSCLK too low")); - } else { - for (int i = 0; i < 4; i++) { - if (sysclk / div[i] <= 2375000UL /* 2.5MHz - 5% */) { - result = crs[i]; - break; - } - } - if (result < 0) - MG_ERROR(("SYSCLK too high")); - } - MG_DEBUG(("SYSCLK: %u, CR: %d", sysclk, result)); - return result; -} - -static bool mg_tcpip_driver_tm4c_init(struct mg_tcpip_if* ifp) -{ - struct mg_tcpip_driver_tm4c_data* d = (struct mg_tcpip_driver_tm4c_data*)ifp->driver_data; - s_ifp = ifp; - - // Init RX descriptors - for (int i = 0; i < ETH_DESC_CNT; i++) { - s_rxdesc[i][0] = BIT(31); // Own - s_rxdesc[i][1] = sizeof(s_rxbuf[i]) | BIT(14); // 2nd address chained - s_rxdesc[i][2] = (uint32_t)(uintptr_t)s_rxbuf[i]; // Point to data buffer - s_rxdesc[i][3] = (uint32_t)(uintptr_t)s_rxdesc[(i + 1) % ETH_DESC_CNT]; // Chain - // MG_DEBUG(("%d %p", i, s_rxdesc[i])); - } - - // Init TX descriptors - for (int i = 0; i < ETH_DESC_CNT; i++) { - s_txdesc[i][2] = (uint32_t)(uintptr_t)s_txbuf[i]; // Buf pointer - s_txdesc[i][3] = (uint32_t)(uintptr_t)s_txdesc[(i + 1) % ETH_DESC_CNT]; // Chain - } - - EMAC->EMACDMABUSMOD |= BIT(0); // Software reset - while ((EMAC->EMACDMABUSMOD & BIT(0)) != 0) - tm4cspin(1); // Wait until done - - // Set MDC clock divider. If user told us the value, use it. Otherwise, guess - int cr = (d == NULL || d->mdc_cr < 0) ? guess_mdc_cr() : d->mdc_cr; - EMAC->EMACMIIADDR = ((uint32_t)cr & 0xf) << 2; - - // NOTE(cpq): we do not use extended descriptor bit 7, and do not use - // hardware checksum. Therefore, descriptor size is 4, not 8 - // EMAC->EMACDMABUSMOD = BIT(13) | BIT(16) | BIT(22) | BIT(23) | BIT(25); - EMAC->EMACIM = BIT(3) | BIT(9); // Mask timestamp & PMT IT - EMAC->EMACFLOWCTL = BIT(7); // Disable zero-quanta pause - // EMAC->EMACFRAMEFLTR = BIT(31); // Receive all - // EMAC->EMACPC defaults to internal PHY (EPHY) in MMI mode - emac_write_phy(EPHY_ADDR, EPHYBMCR, BIT(15)); // Reset internal PHY (EPHY) - emac_write_phy(EPHY_ADDR, EPHYBMCR, BIT(12)); // Set autonegotiation - EMAC->EMACRXDLADDR = (uint32_t)(uintptr_t)s_rxdesc; // RX descriptors - EMAC->EMACTXDLADDR = (uint32_t)(uintptr_t)s_txdesc; // TX descriptors - EMAC->EMACDMAIM = BIT(6) | BIT(16); // RIE, NIE - EMAC->EMACCFG = BIT(2) | BIT(3) | BIT(11) | BIT(14); // RE, TE, Duplex, Fast - EMAC->EMACDMAOPMODE = BIT(1) | BIT(13) | BIT(21) | BIT(25); // SR, ST, TSF, RSF - EMAC->EMACADDR0H = ((uint32_t)ifp->mac[5] << 8U) | ifp->mac[4]; - EMAC->EMACADDR0L = (uint32_t)(ifp->mac[3] << 24) | ((uint32_t)ifp->mac[2] << 16) | ((uint32_t)ifp->mac[1] << 8) | ifp->mac[0]; - // NOTE(scaprile) There are 3 additional slots for filtering, disabled by - // default. This also applies to the STM32 driver (at least for F7) - return true; -} - -static uint32_t s_txno; -static size_t mg_tcpip_driver_tm4c_tx(const void* buf, size_t len, - struct mg_tcpip_if* ifp) -{ - if (len > sizeof(s_txbuf[s_txno])) { - MG_ERROR(("Frame too big, %ld", (long)len)); - len = 0; // fail - } else if ((s_txdesc[s_txno][0] & BIT(31))) { - MG_ERROR(("No descriptors available")); - // printf("D0 %lx SR %lx\n", (long) s_txdesc[0][0], (long) - // EMAC->EMACDMARIS); - len = 0; // fail - } else { - memcpy(s_txbuf[s_txno], buf, len); // Copy data - s_txdesc[s_txno][1] = (uint32_t)len; // Set data len - s_txdesc[s_txno][0] = BIT(20) | BIT(28) | BIT(29) | BIT(30); // Chain,FS,LS,IC - s_txdesc[s_txno][0] |= BIT(31); // Set OWN bit - let DMA take over - if (++s_txno >= ETH_DESC_CNT) - s_txno = 0; - } - EMAC->EMACDMARIS = BIT(2) | BIT(5); // Clear any prior TU/UNF - EMAC->EMACTXPOLLD = 0; // and resume - return len; - (void)ifp; -} - -static bool mg_tcpip_driver_tm4c_up(struct mg_tcpip_if* ifp) -{ - uint32_t bmsr = emac_read_phy(EPHY_ADDR, EPHYBMSR); - bool up = (bmsr & BIT(2)) ? 1 : 0; - if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up - uint32_t sts = emac_read_phy(EPHY_ADDR, EPHYSTS); - uint32_t emaccfg = EMAC->EMACCFG | BIT(14) | BIT(11); // 100M, Full-duplex - if (sts & BIT(1)) - emaccfg &= ~BIT(14); // 10M - if ((sts & BIT(2)) == 0) - emaccfg &= ~BIT(11); // Half-duplex - EMAC->EMACCFG = emaccfg; // IRQ handler does not fiddle with this register - MG_DEBUG(("Link is %uM %s-duplex", emaccfg & BIT(14) ? 100 : 10, - emaccfg & BIT(11) ? "full" : "half")); - } - return up; -} - -void EMAC0_IRQHandler(void); -static uint32_t s_rxno; -void EMAC0_IRQHandler(void) -{ - if (EMAC->EMACDMARIS & BIT(6)) { // Frame received, loop - EMAC->EMACDMARIS = BIT(16) | BIT(6); // Clear flag - for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever - if (s_rxdesc[s_rxno][0] & BIT(31)) - break; // exit when done - if (((s_rxdesc[s_rxno][0] & (BIT(8) | BIT(9))) == (BIT(8) | BIT(9))) && !(s_rxdesc[s_rxno][0] & BIT(15))) { // skip partial/errored frames - uint32_t len = ((s_rxdesc[s_rxno][0] >> 16) & (BIT(14) - 1)); - // printf("%lx %lu %lx %.8lx\n", s_rxno, len, s_rxdesc[s_rxno][0], - // EMAC->EMACDMARIS); - mg_tcpip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp); - } - s_rxdesc[s_rxno][0] = BIT(31); - if (++s_rxno >= ETH_DESC_CNT) - s_rxno = 0; - } - } - EMAC->EMACDMARIS = BIT(7); // Clear possible RU while processing - EMAC->EMACRXPOLLD = 0; // and resume RX -} - -struct mg_tcpip_driver mg_tcpip_driver_tm4c = { mg_tcpip_driver_tm4c_init, - mg_tcpip_driver_tm4c_tx, NULL, - mg_tcpip_driver_tm4c_up }; -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/drivers/w5500.c" -#endif - -#if MG_ENABLE_TCPIP - -enum { W5500_CR = 0, - W5500_S0 = 1, - W5500_TX0 = 2, - W5500_RX0 = 3 }; - -static void w5500_txn(struct mg_tcpip_spi* s, uint8_t block, uint16_t addr, bool wr, - void* buf, size_t len) -{ - uint8_t* p = (uint8_t*)buf; - uint8_t cmd[] = { (uint8_t)(addr >> 8), (uint8_t)(addr & 255), - (uint8_t)((block << 3) | (wr ? 4 : 0)) }; - s->begin(s->spi); - for (size_t i = 0; i < sizeof(cmd); i++) - s->txn(s->spi, cmd[i]); - for (size_t i = 0; i < len; i++) { - uint8_t r = s->txn(s->spi, p[i]); - if (!wr) - p[i] = r; - } - s->end(s->spi); -} - -// clang-format off -static void w5500_wn(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, void *buf, size_t len) { w5500_txn(s, block, addr, true, buf, len); } -static void w5500_w1(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, uint8_t val) { w5500_wn(s, block, addr, &val, 1); } -static void w5500_w2(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, uint16_t val) { uint8_t buf[2] = {(uint8_t) (val >> 8), (uint8_t) (val & 255)}; w5500_wn(s, block, addr, buf, sizeof(buf)); } -static void w5500_rn(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, void *buf, size_t len) { w5500_txn(s, block, addr, false, buf, len); } -static uint8_t w5500_r1(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr) { uint8_t r = 0; w5500_rn(s, block, addr, &r, 1); return r; } -static uint16_t w5500_r2(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr) { uint8_t buf[2] = {0, 0}; w5500_rn(s, block, addr, buf, sizeof(buf)); return (uint16_t) ((buf[0] << 8) | buf[1]); } -// clang-format on - -static size_t w5500_rx(void* buf, size_t buflen, struct mg_tcpip_if* ifp) -{ - struct mg_tcpip_spi* s = (struct mg_tcpip_spi*)ifp->driver_data; - uint16_t r = 0, n = 0, len = (uint16_t)buflen, n2; // Read recv len - while ((n2 = w5500_r2(s, W5500_S0, 0x26)) > n) - n = n2; // Until it is stable - // printf("RSR: %d\n", (int) n); - if (n > 0) { - uint16_t ptr = w5500_r2(s, W5500_S0, 0x28); // Get read pointer - n = w5500_r2(s, W5500_RX0, ptr); // Read frame length - if (n <= len + 2 && n > 1) { - r = (uint16_t)(n - 2); - w5500_rn(s, W5500_RX0, (uint16_t)(ptr + 2), buf, r); - } - w5500_w2(s, W5500_S0, 0x28, (uint16_t)(ptr + n)); // Advance read pointer - w5500_w1(s, W5500_S0, 1, 0x40); // Sock0 CR -> RECV - // printf(" RX_RD: tot=%u n=%u r=%u\n", n2, n, r); - } - return r; -} - -static size_t w5500_tx(const void* buf, size_t buflen, struct mg_tcpip_if* ifp) -{ - struct mg_tcpip_spi* s = (struct mg_tcpip_spi*)ifp->driver_data; - uint16_t n = 0, len = (uint16_t)buflen; - while (n < len) - n = w5500_r2(s, W5500_S0, 0x20); // Wait for space - uint16_t ptr = w5500_r2(s, W5500_S0, 0x24); // Get write pointer - w5500_wn(s, W5500_TX0, ptr, (void*)buf, len); // Write data - w5500_w2(s, W5500_S0, 0x24, (uint16_t)(ptr + len)); // Advance write pointer - w5500_w1(s, W5500_S0, 1, 0x20); // Sock0 CR -> SEND - for (int i = 0; i < 40; i++) { - uint8_t ir = w5500_r1(s, W5500_S0, 2); // Read S0 IR - if (ir == 0) - continue; - // printf("IR %d, len=%d, free=%d, ptr %d\n", ir, (int) len, (int) n, ptr); - w5500_w1(s, W5500_S0, 2, ir); // Write S0 IR: clear it! - if (ir & 8) - len = 0; // Timeout. Report error - if (ir & (16 | 8)) - break; // Stop on SEND_OK or timeout - } - return len; -} - -static bool w5500_init(struct mg_tcpip_if* ifp) -{ - struct mg_tcpip_spi* s = (struct mg_tcpip_spi*)ifp->driver_data; - s->end(s->spi); - w5500_w1(s, W5500_CR, 0, 0x80); // Reset chip: CR -> 0x80 - w5500_w1(s, W5500_CR, 0x2e, 0); // CR PHYCFGR -> reset - w5500_w1(s, W5500_CR, 0x2e, 0xf8); // CR PHYCFGR -> set - // w5500_wn(s, W5500_CR, 9, s->mac, 6); // Set source MAC - w5500_w1(s, W5500_S0, 0x1e, 16); // Sock0 RX buf size - w5500_w1(s, W5500_S0, 0x1f, 16); // Sock0 TX buf size - w5500_w1(s, W5500_S0, 0, 4); // Sock0 MR -> MACRAW - w5500_w1(s, W5500_S0, 1, 1); // Sock0 CR -> OPEN - return w5500_r1(s, W5500_S0, 3) == 0x42; // Sock0 SR == MACRAW -} - -static bool w5500_up(struct mg_tcpip_if* ifp) -{ - struct mg_tcpip_spi* spi = (struct mg_tcpip_spi*)ifp->driver_data; - uint8_t phycfgr = w5500_r1(spi, W5500_CR, 0x2e); - return phycfgr & 1; // Bit 0 of PHYCFGR is LNK (0 - down, 1 - up) -} - -struct mg_tcpip_driver mg_tcpip_driver_w5500 = { w5500_init, w5500_tx, w5500_rx, w5500_up }; -#endif \ No newline at end of file diff --git a/APP_Framework/Applications/mongoose/netsetting.c b/APP_Framework/Applications/mongoose/netsetting.c deleted file mode 100644 index 5e16fe9e4..000000000 --- a/APP_Framework/Applications/mongoose/netsetting.c +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) 2022 Cesanta Software Limited -// All rights reserved -// -// UI example -// It implements the following endpoints: -// /api/config/get - respond with current config -// /api/config/set - POST a config change -// any other URI serves static files from s_root_dir -// Data and results are JSON strings - -#include "ip_addr.h" -#include "mongoose.h" -#include "netdev.h" - -static const char* s_http_addr = "http://0.0.0.0:8000"; // HTTP port -static const char* s_root_dir = "netsetting"; - -static struct netdev* p_netdev; - -static struct config { - char *ip, *mask, *gw, *dns; -} s_config; - -// Try to update a single configuration value -static void update_config(struct mg_str json, const char* path, char** value) -{ - char* jval; - if ((jval = mg_json_get_str(json, path)) != NULL) { - free(*value); - *value = strdup(jval); - } -} - -static void fn(struct mg_connection* c, int ev, void* ev_data, void* fn_data) -{ - if (ev == MG_EV_OPEN && c->is_listening) { - s_config.ip = strdup(inet_ntoa(p_netdev->ip_addr)); - s_config.mask = strdup(inet_ntoa(p_netdev->netmask)); - s_config.gw = strdup(inet_ntoa(p_netdev->gw)); - s_config.dns = strdup(inet_ntoa(p_netdev->dns_servers[0])); - } else if (ev == MG_EV_HTTP_MSG) { - struct mg_http_message* hm = (struct mg_http_message*)ev_data; - if (mg_http_match_uri(hm, "/api/config/get")) { - mg_http_reply(c, 200, "Content-Type: application/json\r\n", - "{%m:%m,%m:%m,%m:%m,%m:%m}\n", - MG_ESC("ip"), MG_ESC(s_config.ip), - MG_ESC("mask"), MG_ESC(s_config.mask), - MG_ESC("gw"), MG_ESC(s_config.gw), - MG_ESC("dns"), MG_ESC(s_config.dns)); - } else if (mg_http_match_uri(hm, "/api/config/set")) { - struct mg_str json = hm->body; - printf("json: %s\n", json.ptr); - update_config(json, "$.ip", &s_config.ip); - update_config(json, "$.mask", &s_config.mask); - update_config(json, "$.gw", &s_config.gw); - update_config(json, "$.dns", &s_config.dns); - mg_http_reply(c, 200, "", "ok\n"); - - ip_addr_t ipaddr, maskaddr, gwaddr; - inet_aton(s_config.ip, &ipaddr); - inet_aton(s_config.mask, &maskaddr); - inet_aton(s_config.gw, &gwaddr); - p_netdev->ops->set_addr_info(p_netdev, &ipaddr, &maskaddr, &gwaddr); - - printf("Board Net Configuration changed to [IP: %s, Mask: %s, GW: %s]\n", - s_config.ip, - s_config.mask, - s_config.gw); - } else { - struct mg_http_serve_opts opts = { .root_dir = s_root_dir }; - mg_http_serve_dir(c, ev_data, &opts); - } - } - (void)fn_data; -} - -static void* do_net_setting_demo(void* none) -{ - p_netdev = NETDEV_DEFAULT; - - struct mg_mgr mgr; // Event manager - mg_log_set(MG_LL_DEBUG); // Set to 3 to enable debug - mg_mgr_init(&mgr); // Initialise event manager - mg_http_listen(&mgr, s_http_addr, fn, NULL); // Create HTTP listener - for (;;) - mg_mgr_poll(&mgr, 10); // Infinite event loop - mg_mgr_free(&mgr); - return NULL; -} - -int net_setting_demo(int argc, char* argv[]) -{ - pthread_t tid = -1; - pthread_attr_t attr; - attr.schedparam.sched_priority = 30; - attr.stacksize = 16384; - - PrivTaskCreate(&tid, &attr, do_net_setting_demo, NULL); - - return 0; -} -SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | SHELL_CMD_PARAM_NUM(5), NetSettingDemo, net_setting_demo, webserver to set net configurations); \ No newline at end of file diff --git a/APP_Framework/Applications/mongoose/project.c b/APP_Framework/Applications/mongoose/project.c index ab3ba96db..cf05a4af9 100644 --- a/APP_Framework/Applications/mongoose/project.c +++ b/APP_Framework/Applications/mongoose/project.c @@ -14,7 +14,7 @@ char index_path[] = "login.html"; -static const char* s_http_addr = "http://0.0.0.0:8000"; // HTTP port +static const char* s_http_addr = "http://192.168.131.88:8000"; // HTTP port static const char* s_root_dir = "webserver"; static const char* s_enable_hexdump = "no"; static const char* s_ssi_pattern = "#.html"; @@ -104,18 +104,22 @@ static void fn(struct mg_connection* c, int ev, void* ev_data, void* fn_data) (void)fn_data; } -extern void LwipSetIPTest(int argc, char* argv[]); static void* do_webserver_demo(void* none) { - p_netdev = NETDEV_DEFAULT; - s_config.ip = strdup(inet_ntoa(p_netdev->ip_addr)); - s_config.mask = strdup(inet_ntoa(p_netdev->netmask)); - s_config.gw = strdup(inet_ntoa(p_netdev->gw)); + p_netdev = netdev_get_by_name("wz"); + if (p_netdev == NULL) { + MG_INFO(("Did nto find wz netdev, use default.\n")); + p_netdev = NETDEV_DEFAULT; + } + MG_INFO(("Use Netdev %s", p_netdev->name)); + s_config.ip = strdup(inet_ntoa(*p_netdev->ip_addr)); + s_config.mask = strdup(inet_ntoa(*p_netdev->netmask)); + s_config.gw = strdup(inet_ntoa(*p_netdev->gw)); s_config.dns = strdup(inet_ntoa(p_netdev->dns_servers[0])); struct mg_mgr mgr; // Event manager - mg_log_set(MG_LL_INFO); // Set to 3 to enable debug - // mg_log_set(MG_LL_DEBUG); // Set to 3 to enable debug + // mg_log_set(MG_LL_INFO); // Set to 3 to enable debug + mg_log_set(MG_LL_DEBUG); // Set to 3 to enable debug mg_mgr_init(&mgr); // Initialise event manager mg_http_listen(&mgr, s_http_addr, fn, NULL); // Create HTTP listener for (;;) @@ -126,12 +130,10 @@ static void* do_webserver_demo(void* none) int webserver_demo(int argc, char* argv[]) { - LwipSetIPTest(1, NULL); - pthread_t tid = -1; pthread_attr_t attr; attr.schedparam.sched_priority = 30; - attr.stacksize = 0x4000; + attr.stacksize = 0x2000; PrivTaskCreate(&tid, &attr, do_webserver_demo, NULL); diff --git a/Ubiquitous/XiZi_IIoT/Makefile b/Ubiquitous/XiZi_IIoT/Makefile index de3ea3e86..4ecef27a7 100755 --- a/Ubiquitous/XiZi_IIoT/Makefile +++ b/Ubiquitous/XiZi_IIoT/Makefile @@ -50,6 +50,7 @@ export SRC_DIR:= $(SRC_APP_DIR) $(SRC_KERNEL_DIR) export LIBCC export MUSL_DIR := $(KERNEL_ROOT)/lib/musllib export LWIP_DIR := $(KERNEL_ROOT)/resources/ethernet +export MONGOOSE_DIR := $(KERNEL_ROOT)/../../APP_Framework/Applications/mongoose/lib PART:= @@ -74,6 +75,10 @@ ifeq ($(CONFIG_RESOURCES_LWIP), y) PART += COMPILE_LWIP endif +ifeq ($(CONFIG_USE_MONGOOSE), y) +# PART += COMPILE_MONGOOSE +endif + ifeq ($(CONFIG_MCUBOOT_BOOTLOADER), y) PART += COMPILE_BOOTLOADER else ifeq ($(CONFIG_MCUBOOT_APPLICATION), y) @@ -130,6 +135,15 @@ COMPILE_LWIP: @cp build/liblwip.a $(KERNEL_ROOT)/resources/ethernet/LwIP/liblwip.a @rm build/Makefile build/make.obj +COMPILE_MONGOOSE: + @for dir in $(MONGOOSE_DIR);do \ + $(MAKE) -C $$dir COMPILE_TYPE=$@; \ + done + @cp link_mongoose.mk build/Makefile + @$(MAKE) -C build TARGET=mongoose.a LINK_FLAGS=LFLAGS + @cp build/mongoose.a $(KERNEL_ROOT)/../../APP_Framework/Applications/mongoose/mongoose.a + @rm build/Makefile build/make.obj + COMPILE_KERNEL: @for dir in $(SRC_KERNEL_DIR);do \ $(MAKE) -C $$dir; \ diff --git a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/eth_driver.c b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/eth_driver.c index 25d106209..9b07b399d 100644 --- a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/eth_driver.c +++ b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/eth_driver.c @@ -278,10 +278,7 @@ struct pbuf* low_level_input(struct netif* netif) return p; } -extern void LwipSetIPTest(int argc, char* argv[]); int HwEthInit(void) { - // lwip_config_tcp(0, lwip_ipaddr, lwip_netmask, lwip_gwaddr); - LwipSetIPTest(1, NULL); return EOK; } diff --git a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/eth_netdev.c b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/eth_netdev.c index 1c5c9d3c5..875a35db0 100644 --- a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/eth_netdev.c +++ b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/eth_netdev.c @@ -76,6 +76,7 @@ static int lwip_netdev_set_addr_info(struct netdev* netdev, ip_addr_t* ip_addr, netif_set_gw((struct netif*)netdev->user_data, ip_2_ip4(gw)); } } + return 0; } #ifdef LWIP_DNS @@ -92,7 +93,7 @@ static int lwip_netdev_set_dns_server(struct netdev* netdev, uint8_t dns_num, ip } #endif -#ifdef LWIP_DHCP +#if LWIP_DHCP static int lwip_netdev_set_dhcp(struct netdev* netdev, bool is_enabled) { netdev_low_level_set_dhcp_status(netdev, is_enabled); @@ -120,7 +121,7 @@ static const struct netdev_ops lwip_netdev_ops = { #ifdef LWIP_DNS .set_dns_server = lwip_netdev_set_dns_server, #endif -#ifdef LWIP_DHCP +#if LWIP_DHCP .set_dhcp = lwip_netdev_set_dhcp, #endif .set_default = lwip_netdev_set_default, @@ -179,9 +180,9 @@ int lwip_netdev_add(struct netif* lwip_netif) netdev->ops = &lwip_netdev_ops; netdev->hwaddr_len = lwip_netif->hwaddr_len; memcpy(netdev->hwaddr, lwip_netif->hwaddr, lwip_netif->hwaddr_len); - netdev->ip_addr = lwip_netif->ip_addr; - netdev->gw = lwip_netif->gw; - netdev->netmask = lwip_netif->netmask; + netdev->ip_addr = &lwip_netif->ip_addr; + netdev->gw = &lwip_netif->gw; + netdev->netmask = &lwip_netif->netmask; return result; } diff --git a/Ubiquitous/XiZi_IIoT/board/edu-riscv64/third_party_driver/ethernet/Makefile b/Ubiquitous/XiZi_IIoT/board/edu-riscv64/third_party_driver/ethernet/Makefile index e994ce1ad..a7dae3fce 100644 --- a/Ubiquitous/XiZi_IIoT/board/edu-riscv64/third_party_driver/ethernet/Makefile +++ b/Ubiquitous/XiZi_IIoT/board/edu-riscv64/third_party_driver/ethernet/Makefile @@ -1,4 +1,4 @@ # SRC_FILES := socket.c connect_w5500.c w5500.c wizchip_conf.c spi_interface.c wiz_ping.c connect_w5500_test.c wiz_iperf.c -SRC_FILES := socket.c connect_w5500.c w5500.c wizchip_conf.c spi_interface.c wiz_ping.c connect_w5500_test.c w5x00_lwip.c wiz_iperf.c +SRC_FILES := socket.c connect_w5500.c w5500.c wizchip_conf.c spi_interface.c w5x00_lwip.c include $(KERNEL_ROOT)/compiler.mk \ No newline at end of file diff --git a/Ubiquitous/XiZi_IIoT/board/edu-riscv64/third_party_driver/ethernet/connect_w5500.c b/Ubiquitous/XiZi_IIoT/board/edu-riscv64/third_party_driver/ethernet/connect_w5500.c index dc29ff22d..ee99dcb78 100644 --- a/Ubiquitous/XiZi_IIoT/board/edu-riscv64/third_party_driver/ethernet/connect_w5500.c +++ b/Ubiquitous/XiZi_IIoT/board/edu-riscv64/third_party_driver/ethernet/connect_w5500.c @@ -328,8 +328,12 @@ int HwWiznetInit(void) setSHAR(wiz_mac); ctlwizchip(CW_RESET_PHY, 0); + network_init(); + wiz_interrupt_init(0, wiz_irq_handler); + network_init(); + setSn_RXBUF_SIZE(0, 16); setSn_TXBUF_SIZE(0, 16); #define SOCK_ANY_PORT_NUM 0xC000 @@ -349,7 +353,5 @@ int HwWiznetInit(void) } } - network_init(); - return EOK; } \ No newline at end of file diff --git a/Ubiquitous/XiZi_IIoT/board/edu-riscv64/third_party_driver/ethernet/connect_w5500_test.c b/Ubiquitous/XiZi_IIoT/board/edu-riscv64/third_party_driver/ethernet/test/connect_w5500_test.c similarity index 100% rename from Ubiquitous/XiZi_IIoT/board/edu-riscv64/third_party_driver/ethernet/connect_w5500_test.c rename to Ubiquitous/XiZi_IIoT/board/edu-riscv64/third_party_driver/ethernet/test/connect_w5500_test.c diff --git a/Ubiquitous/XiZi_IIoT/board/edu-riscv64/third_party_driver/ethernet/wiz_iperf.c b/Ubiquitous/XiZi_IIoT/board/edu-riscv64/third_party_driver/ethernet/test/wiz_iperf.c similarity index 100% rename from Ubiquitous/XiZi_IIoT/board/edu-riscv64/third_party_driver/ethernet/wiz_iperf.c rename to Ubiquitous/XiZi_IIoT/board/edu-riscv64/third_party_driver/ethernet/test/wiz_iperf.c diff --git a/Ubiquitous/XiZi_IIoT/board/edu-riscv64/third_party_driver/ethernet/wiz_ping.c b/Ubiquitous/XiZi_IIoT/board/edu-riscv64/third_party_driver/ethernet/test/wiz_ping.c similarity index 100% rename from Ubiquitous/XiZi_IIoT/board/edu-riscv64/third_party_driver/ethernet/wiz_ping.c rename to Ubiquitous/XiZi_IIoT/board/edu-riscv64/third_party_driver/ethernet/test/wiz_ping.c diff --git a/Ubiquitous/XiZi_IIoT/board/edu-riscv64/third_party_driver/ethernet/wiz_ping.h b/Ubiquitous/XiZi_IIoT/board/edu-riscv64/third_party_driver/ethernet/test/wiz_ping.h similarity index 100% rename from Ubiquitous/XiZi_IIoT/board/edu-riscv64/third_party_driver/ethernet/wiz_ping.h rename to Ubiquitous/XiZi_IIoT/board/edu-riscv64/third_party_driver/ethernet/test/wiz_ping.h diff --git a/Ubiquitous/XiZi_IIoT/board/edu-riscv64/third_party_driver/spi/connect_spi.c b/Ubiquitous/XiZi_IIoT/board/edu-riscv64/third_party_driver/spi/connect_spi.c index 8811d2856..75e666a6b 100644 --- a/Ubiquitous/XiZi_IIoT/board/edu-riscv64/third_party_driver/spi/connect_spi.c +++ b/Ubiquitous/XiZi_IIoT/board/edu-riscv64/third_party_driver/spi/connect_spi.c @@ -152,6 +152,9 @@ static uint32 SpiDrvConfigure(void *drv, struct BusConfigureInfo *configure_info static uint32 SpiWriteData(struct SpiHardwareDevice *spi_dev, struct SpiDataStandard *spi_datacfg) { +#define WRITE_BUF_SIZE 1600 + static uint32_t write_buf[4 * WRITE_BUF_SIZE] = { 0 }; + SpiDeviceParam *dev_param = (SpiDeviceParam *)(spi_dev->haldev.private_data); uint8 device_id = dev_param->spi_slave_param->spi_slave_id; @@ -184,10 +187,15 @@ static uint32 SpiWriteData(struct SpiHardwareDevice *spi_dev, struct SpiDataStan dmac_set_single_mode(dev_param->spi_dma_param->spi_dmac_txchannel, &dummy, (void *)(&spi_instance[device_master_id]->dr[0]), DMAC_ADDR_NOCHANGE, DMAC_ADDR_NOCHANGE, DMAC_MSIZE_4, DMAC_TRANS_WIDTH_32, spi_datacfg->length); } else { - tx_buff = x_malloc(spi_datacfg->length * 4); - if (!tx_buff) { - goto transfer_done; + if (spi_datacfg->length > WRITE_BUF_SIZE) { + tx_buff = x_malloc(spi_datacfg->length * 4); + if (!tx_buff) { + goto transfer_done; + } + } else { + tx_buff = write_buf; } + for (i = 0; i < spi_datacfg->length; i++) { tx_buff[i] = ((uint8_t *)spi_datacfg->tx_buff)[i]; } @@ -201,10 +209,10 @@ static uint32 SpiWriteData(struct SpiHardwareDevice *spi_dev, struct SpiDataStan spi_instance[device_master_id]->ser = 0x00; spi_instance[device_master_id]->ssienr = 0x00; - transfer_done: - if (tx_buff != NULL) { - x_free(tx_buff); - } + transfer_done: + if (tx_buff != NULL && spi_datacfg->length > WRITE_BUF_SIZE) { + x_free(tx_buff); + } } if (spi_datacfg->spi_cs_release) { @@ -219,6 +227,9 @@ static uint32 SpiWriteData(struct SpiHardwareDevice *spi_dev, struct SpiDataStan static uint32 SpiReadData(struct SpiHardwareDevice *spi_dev, struct SpiDataStandard *spi_datacfg) { +#define READ_BUF_SIZE 1600 + static uint32_t read_buf[4 * READ_BUF_SIZE] = { 0 }; + SpiDeviceParam *dev_param = (SpiDeviceParam *)(spi_dev->haldev.private_data); uint32 spi_read_length = 0;; @@ -251,12 +262,15 @@ static uint32 SpiReadData(struct SpiHardwareDevice *spi_dev, struct SpiDataStand dmac_set_single_mode(dev_param->spi_dma_param->spi_dmac_rxchannel, (void *)(&spi_instance[device_master_id]->dr[0]), &dummy, DMAC_ADDR_NOCHANGE, DMAC_ADDR_NOCHANGE, DMAC_MSIZE_1, DMAC_TRANS_WIDTH_32, spi_datacfg->length); } else { - rx_buff = x_calloc(spi_datacfg->length * 4, 1); - if(!rx_buff) - { - goto transfer_done; + if (spi_datacfg->length > READ_BUF_SIZE) { + rx_buff = x_calloc(spi_datacfg->length * 4, 1); + if (!rx_buff) { + goto transfer_done; + } + } else { + rx_buff = read_buf; } - + dmac_set_single_mode(dev_param->spi_dma_param->spi_dmac_rxchannel, (void *)(&spi_instance[device_master_id]->dr[0]), rx_buff, DMAC_ADDR_NOCHANGE, DMAC_ADDR_INCREMENT, DMAC_MSIZE_1, DMAC_TRANS_WIDTH_32, spi_datacfg->length); } @@ -273,8 +287,8 @@ static uint32 SpiReadData(struct SpiHardwareDevice *spi_dev, struct SpiDataStand } } - transfer_done: - if (rx_buff) { + transfer_done: + if (rx_buff && spi_datacfg->length > READ_BUF_SIZE) { x_free(rx_buff); } } diff --git a/Ubiquitous/XiZi_IIoT/board/imxrt1176-sbc/third_party_driver/ethernet/eth_netdev.c b/Ubiquitous/XiZi_IIoT/board/imxrt1176-sbc/third_party_driver/ethernet/eth_netdev.c index 03ca27594..09957b854 100644 --- a/Ubiquitous/XiZi_IIoT/board/imxrt1176-sbc/third_party_driver/ethernet/eth_netdev.c +++ b/Ubiquitous/XiZi_IIoT/board/imxrt1176-sbc/third_party_driver/ethernet/eth_netdev.c @@ -76,6 +76,7 @@ static int lwip_netdev_set_addr_info(struct netdev* netdev, ip_addr_t* ip_addr, netif_set_gw((struct netif*)netdev->user_data, ip_2_ip4(gw)); } } + return 0; } #ifdef LWIP_DNS @@ -92,7 +93,7 @@ static int lwip_netdev_set_dns_server(struct netdev* netdev, uint8_t dns_num, ip } #endif -#ifdef LWIP_DHCP +#if LWIP_DHCP static int lwip_netdev_set_dhcp(struct netdev* netdev, bool is_enabled) { netdev_low_level_set_dhcp_status(netdev, is_enabled); @@ -120,7 +121,7 @@ static const struct netdev_ops lwip_netdev_ops = { #ifdef LWIP_DNS .set_dns_server = lwip_netdev_set_dns_server, #endif -#ifdef LWIP_DHCP +#if LWIP_DHCP .set_dhcp = lwip_netdev_set_dhcp, #endif .set_default = lwip_netdev_set_default, @@ -180,9 +181,9 @@ int lwip_netdev_add(struct netif* lwip_netif) netdev->ops = &lwip_netdev_ops; netdev->hwaddr_len = lwip_netif->hwaddr_len; memcpy(netdev->hwaddr, lwip_netif->hwaddr, lwip_netif->hwaddr_len); - netdev->ip_addr = lwip_netif->ip_addr; - netdev->gw = lwip_netif->gw; - netdev->netmask = lwip_netif->netmask; + netdev->ip_addr = &lwip_netif->ip_addr; + netdev->gw = &lwip_netif->gw; + netdev->netmask = &lwip_netif->netmask; return result; } diff --git a/Ubiquitous/XiZi_IIoT/board/ok1052-c/third_party_driver/ethernet/eth_netdev.c b/Ubiquitous/XiZi_IIoT/board/ok1052-c/third_party_driver/ethernet/eth_netdev.c index 03ca27594..09957b854 100644 --- a/Ubiquitous/XiZi_IIoT/board/ok1052-c/third_party_driver/ethernet/eth_netdev.c +++ b/Ubiquitous/XiZi_IIoT/board/ok1052-c/third_party_driver/ethernet/eth_netdev.c @@ -76,6 +76,7 @@ static int lwip_netdev_set_addr_info(struct netdev* netdev, ip_addr_t* ip_addr, netif_set_gw((struct netif*)netdev->user_data, ip_2_ip4(gw)); } } + return 0; } #ifdef LWIP_DNS @@ -92,7 +93,7 @@ static int lwip_netdev_set_dns_server(struct netdev* netdev, uint8_t dns_num, ip } #endif -#ifdef LWIP_DHCP +#if LWIP_DHCP static int lwip_netdev_set_dhcp(struct netdev* netdev, bool is_enabled) { netdev_low_level_set_dhcp_status(netdev, is_enabled); @@ -120,7 +121,7 @@ static const struct netdev_ops lwip_netdev_ops = { #ifdef LWIP_DNS .set_dns_server = lwip_netdev_set_dns_server, #endif -#ifdef LWIP_DHCP +#if LWIP_DHCP .set_dhcp = lwip_netdev_set_dhcp, #endif .set_default = lwip_netdev_set_default, @@ -180,9 +181,9 @@ int lwip_netdev_add(struct netif* lwip_netif) netdev->ops = &lwip_netdev_ops; netdev->hwaddr_len = lwip_netif->hwaddr_len; memcpy(netdev->hwaddr, lwip_netif->hwaddr, lwip_netif->hwaddr_len); - netdev->ip_addr = lwip_netif->ip_addr; - netdev->gw = lwip_netif->gw; - netdev->netmask = lwip_netif->netmask; + netdev->ip_addr = &lwip_netif->ip_addr; + netdev->gw = &lwip_netif->gw; + netdev->netmask = &lwip_netif->netmask; return result; } diff --git a/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/ethernet/eth_netdev.c b/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/ethernet/eth_netdev.c index 03ca27594..09957b854 100644 --- a/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/ethernet/eth_netdev.c +++ b/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/ethernet/eth_netdev.c @@ -76,6 +76,7 @@ static int lwip_netdev_set_addr_info(struct netdev* netdev, ip_addr_t* ip_addr, netif_set_gw((struct netif*)netdev->user_data, ip_2_ip4(gw)); } } + return 0; } #ifdef LWIP_DNS @@ -92,7 +93,7 @@ static int lwip_netdev_set_dns_server(struct netdev* netdev, uint8_t dns_num, ip } #endif -#ifdef LWIP_DHCP +#if LWIP_DHCP static int lwip_netdev_set_dhcp(struct netdev* netdev, bool is_enabled) { netdev_low_level_set_dhcp_status(netdev, is_enabled); @@ -120,7 +121,7 @@ static const struct netdev_ops lwip_netdev_ops = { #ifdef LWIP_DNS .set_dns_server = lwip_netdev_set_dns_server, #endif -#ifdef LWIP_DHCP +#if LWIP_DHCP .set_dhcp = lwip_netdev_set_dhcp, #endif .set_default = lwip_netdev_set_default, @@ -180,9 +181,9 @@ int lwip_netdev_add(struct netif* lwip_netif) netdev->ops = &lwip_netdev_ops; netdev->hwaddr_len = lwip_netif->hwaddr_len; memcpy(netdev->hwaddr, lwip_netif->hwaddr, lwip_netif->hwaddr_len); - netdev->ip_addr = lwip_netif->ip_addr; - netdev->gw = lwip_netif->gw; - netdev->netmask = lwip_netif->netmask; + netdev->ip_addr = &lwip_netif->ip_addr; + netdev->gw = &lwip_netif->gw; + netdev->netmask = &lwip_netif->netmask; return result; } diff --git a/Ubiquitous/XiZi_IIoT/board/xishutong-arm32/config.mk b/Ubiquitous/XiZi_IIoT/board/xishutong-arm32/config.mk index af0254884..ad12a5c9b 100644 --- a/Ubiquitous/XiZi_IIoT/board/xishutong-arm32/config.mk +++ b/Ubiquitous/XiZi_IIoT/board/xishutong-arm32/config.mk @@ -15,4 +15,8 @@ ifeq ($(CONFIG_RESOURCES_LWIP), y) export LINK_LWIP := $(KERNEL_ROOT)/resources/ethernet/LwIP/liblwip.a endif +ifeq ($(CONFIG_USE_MONGOOSE), y) +export LINK_MONGOOSE := $(KERNEL_ROOT)/../../APP_Framework/Applications/mongoose/mongoose.a +endif + export ARCH = arm diff --git a/Ubiquitous/XiZi_IIoT/board/xishutong-arm32/third_party_driver/ethernet/eth_driver.c b/Ubiquitous/XiZi_IIoT/board/xishutong-arm32/third_party_driver/ethernet/eth_driver.c index 9ad1468d0..a1e94307c 100644 --- a/Ubiquitous/XiZi_IIoT/board/xishutong-arm32/third_party_driver/ethernet/eth_driver.c +++ b/Ubiquitous/XiZi_IIoT/board/xishutong-arm32/third_party_driver/ethernet/eth_driver.c @@ -277,10 +277,7 @@ struct pbuf* low_level_input(struct netif* netif) return p; } -extern void LwipSetIPTest(int argc, char* argv[]); int HwEthInit(void) { - // lwip_config_tcp(0, lwip_ipaddr, lwip_netmask, lwip_gwaddr); - LwipSetIPTest(1, NULL); return EOK; } diff --git a/Ubiquitous/XiZi_IIoT/board/xishutong-arm32/third_party_driver/ethernet/eth_netdev.c b/Ubiquitous/XiZi_IIoT/board/xishutong-arm32/third_party_driver/ethernet/eth_netdev.c index 862d80bd8..0f6a8df37 100644 --- a/Ubiquitous/XiZi_IIoT/board/xishutong-arm32/third_party_driver/ethernet/eth_netdev.c +++ b/Ubiquitous/XiZi_IIoT/board/xishutong-arm32/third_party_driver/ethernet/eth_netdev.c @@ -76,6 +76,7 @@ static int lwip_netdev_set_addr_info(struct netdev* netdev, ip_addr_t* ip_addr, netif_set_gw((struct netif*)netdev->user_data, ip_2_ip4(gw)); } } + return 0; } #ifdef LWIP_DNS @@ -92,7 +93,7 @@ static int lwip_netdev_set_dns_server(struct netdev* netdev, uint8_t dns_num, ip } #endif -#ifdef LWIP_DHCP +#if LWIP_DHCP static int lwip_netdev_set_dhcp(struct netdev* netdev, bool is_enabled) { netdev_low_level_set_dhcp_status(netdev, is_enabled); @@ -120,7 +121,7 @@ static const struct netdev_ops lwip_netdev_ops = { #ifdef LWIP_DNS .set_dns_server = lwip_netdev_set_dns_server, #endif -#ifdef LWIP_DHCP +#if LWIP_DHCP .set_dhcp = lwip_netdev_set_dhcp, #endif .set_default = lwip_netdev_set_default, @@ -183,9 +184,9 @@ int lwip_netdev_add(struct netif* lwip_netif) netdev->ops = &lwip_netdev_ops; netdev->hwaddr_len = lwip_netif->hwaddr_len; memcpy(netdev->hwaddr, lwip_netif->hwaddr, lwip_netif->hwaddr_len); - netdev->ip_addr = lwip_netif->ip_addr; - netdev->gw = lwip_netif->gw; - netdev->netmask = lwip_netif->netmask; + netdev->ip_addr = &lwip_netif->ip_addr; + netdev->gw = &lwip_netif->gw; + netdev->netmask = &lwip_netif->netmask; return result; } diff --git a/Ubiquitous/XiZi_IIoT/board/xishutong-arm32/third_party_driver/ethernet/ethernetif.c b/Ubiquitous/XiZi_IIoT/board/xishutong-arm32/third_party_driver/ethernet/ethernetif.c index 8347c4c50..15a14bcb7 100644 --- a/Ubiquitous/XiZi_IIoT/board/xishutong-arm32/third_party_driver/ethernet/ethernetif.c +++ b/Ubiquitous/XiZi_IIoT/board/xishutong-arm32/third_party_driver/ethernet/ethernetif.c @@ -204,7 +204,6 @@ void ethernetif_input(void* netif_arg) /* Move received packet into a new pbuf */ while (1) { sys_arch_sem_wait(get_eth_recv_sem(), WAITING_FOREVER); - KPrintf("%s -->\n", netif->name); while (1) { p = low_level_input(netif); #ifndef ETHERNET_LOOPBACK_TEST diff --git a/Ubiquitous/XiZi_IIoT/board/xishutong-arm32/third_party_driver/ethernet_wiz/w5x00_lwip.c b/Ubiquitous/XiZi_IIoT/board/xishutong-arm32/third_party_driver/ethernet_wiz/w5x00_lwip.c index 2494cdb32..357a9e404 100644 --- a/Ubiquitous/XiZi_IIoT/board/xishutong-arm32/third_party_driver/ethernet_wiz/w5x00_lwip.c +++ b/Ubiquitous/XiZi_IIoT/board/xishutong-arm32/third_party_driver/ethernet_wiz/w5x00_lwip.c @@ -113,7 +113,6 @@ void ethernetif_input2(void* netif_arg) struct pbuf* p = NULL; for (;;) { sys_arch_sem_wait(get_eth_recv_sem2(), WAITING_FOREVER); - KPrintf("%s -->\n", netif->name); sys_mutex_lock(&wiz_trans_mtx); while (1) { diff --git a/Ubiquitous/XiZi_IIoT/board/xiwangtong-arm32/third_party_driver/ethernet/eth_netdev.c b/Ubiquitous/XiZi_IIoT/board/xiwangtong-arm32/third_party_driver/ethernet/eth_netdev.c index 03ca27594..09957b854 100644 --- a/Ubiquitous/XiZi_IIoT/board/xiwangtong-arm32/third_party_driver/ethernet/eth_netdev.c +++ b/Ubiquitous/XiZi_IIoT/board/xiwangtong-arm32/third_party_driver/ethernet/eth_netdev.c @@ -76,6 +76,7 @@ static int lwip_netdev_set_addr_info(struct netdev* netdev, ip_addr_t* ip_addr, netif_set_gw((struct netif*)netdev->user_data, ip_2_ip4(gw)); } } + return 0; } #ifdef LWIP_DNS @@ -92,7 +93,7 @@ static int lwip_netdev_set_dns_server(struct netdev* netdev, uint8_t dns_num, ip } #endif -#ifdef LWIP_DHCP +#if LWIP_DHCP static int lwip_netdev_set_dhcp(struct netdev* netdev, bool is_enabled) { netdev_low_level_set_dhcp_status(netdev, is_enabled); @@ -120,7 +121,7 @@ static const struct netdev_ops lwip_netdev_ops = { #ifdef LWIP_DNS .set_dns_server = lwip_netdev_set_dns_server, #endif -#ifdef LWIP_DHCP +#if LWIP_DHCP .set_dhcp = lwip_netdev_set_dhcp, #endif .set_default = lwip_netdev_set_default, @@ -180,9 +181,9 @@ int lwip_netdev_add(struct netif* lwip_netif) netdev->ops = &lwip_netdev_ops; netdev->hwaddr_len = lwip_netif->hwaddr_len; memcpy(netdev->hwaddr, lwip_netif->hwaddr, lwip_netif->hwaddr_len); - netdev->ip_addr = lwip_netif->ip_addr; - netdev->gw = lwip_netif->gw; - netdev->netmask = lwip_netif->netmask; + netdev->ip_addr = &lwip_netif->ip_addr; + netdev->gw = &lwip_netif->gw; + netdev->netmask = &lwip_netif->netmask; return result; } diff --git a/Ubiquitous/XiZi_IIoT/compiler.mk b/Ubiquitous/XiZi_IIoT/compiler.mk index 87aba0280..4a91b1ba0 100644 --- a/Ubiquitous/XiZi_IIoT/compiler.mk +++ b/Ubiquitous/XiZi_IIoT/compiler.mk @@ -2,6 +2,8 @@ ifeq ($(COMPILE_TYPE), COMPILE_MUSL) SRC_DIR_TEMP := $(MUSL_DIR) else ifeq ($(COMPILE_TYPE), COMPILE_LWIP) SRC_DIR_TEMP := $(LWIP_DIR) +else ifeq ($(COMPILE_TYPE), COMPILE_MONGOOSE) +SRC_DIR_TEMP := $(MONGOOSE_DIR) else SRC_DIR_TEMP := $(SRC_DIR) endif @@ -9,6 +11,7 @@ endif SRC_DIR := MUSL_DIR := LWIP_DIR := +MONGOOSE_DIR := ifeq ($(USE_APP_INCLUDEPATH), y) include $(KERNEL_ROOT)/path_app.mk diff --git a/Ubiquitous/XiZi_IIoT/kernel/include/xs_kdbg.h b/Ubiquitous/XiZi_IIoT/kernel/include/xs_kdbg.h index afd6dd617..c4cf2c162 100644 --- a/Ubiquitous/XiZi_IIoT/kernel/include/xs_kdbg.h +++ b/Ubiquitous/XiZi_IIoT/kernel/include/xs_kdbg.h @@ -44,15 +44,16 @@ extern "C" { #define MSGQUEUE_DEBUG 0 #define FILESYS_DEBUG 0 -#define NETDEV_DEBUG 0 +#define NETDEV_DEBUG 1 #define WEBNET_DEBUG 0 #define WIZNET_DEBUG 0 -#define SYS_KDEBUG_LOG(section, information) \ - do { \ - if (section) { \ - KPrintf information; \ - } \ +#define SYS_KDEBUG_LOG(section, information) \ + do { \ + if (section) { \ + KPrintf("[%s:%d] ", __func__, __LINE__); \ + KPrintf information; \ + } \ } while (0) #define KDYN_NONE 0 diff --git a/Ubiquitous/XiZi_IIoT/link.mk b/Ubiquitous/XiZi_IIoT/link.mk index a2bdd07d4..09e643226 100644 --- a/Ubiquitous/XiZi_IIoT/link.mk +++ b/Ubiquitous/XiZi_IIoT/link.mk @@ -3,7 +3,7 @@ OBJS := $(shell cat make.obj) $(TARGET): $(OBJS) @echo ------------------------------------------------ @echo link $(TARGET) - @$(CROSS_COMPILE)g++ -o $@ $($(LINK_FLAGS)) $(OBJS) $(LINK_LWIP) $(LINK_MUSLLIB) $(LIBCC) + @$(CROSS_COMPILE)g++ -o $@ $($(LINK_FLAGS)) $(OBJS) $(LINK_LWIP) $(LINK_MUSLLIB) $(LINK_MONGOOSE) $(LIBCC) @echo ------------------------------------------------ @$(CROSS_COMPILE)objcopy -O binary $@ XiZi-$(BOARD)$(COMPILE_TYPE).bin @$(CROSS_COMPILE)objdump -S $@ > XiZi-$(BOARD)$(COMPILE_TYPE).asm diff --git a/Ubiquitous/XiZi_IIoT/link_mongoose.mk b/Ubiquitous/XiZi_IIoT/link_mongoose.mk new file mode 100644 index 000000000..568c910b0 --- /dev/null +++ b/Ubiquitous/XiZi_IIoT/link_mongoose.mk @@ -0,0 +1,9 @@ +OBJS := $(shell cat make.obj) + +$(TARGET): $(OBJS) + @echo ------------------------------------------------ + @echo link $(TARGET) + @$(CROSS_COMPILE)ar -r $@ $(OBJS) + @echo ------------------------------------------------ + @$(CROSS_COMPILE)objcopy $@ $(TARGET) + @$(CROSS_COMPILE)size $@ \ No newline at end of file diff --git a/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/api/sockets.c b/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/api/sockets.c index 3ddd778f5..5f458d8f0 100644 --- a/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/api/sockets.c +++ b/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/api/sockets.c @@ -640,7 +640,8 @@ lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen) LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d)...\n", s)); sock = get_socket(s); if (!sock) { - return -1; + KPrintf("sock = get_socket(s);\n"); + return -1; } /* wait for a new connection */ @@ -655,6 +656,7 @@ lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen) sock_set_errno(sock, err_to_errno(err)); } done_socket(sock); + KPrintf("err = netconn_accept(sock->conn, &newconn);\n"); return -1; } LWIP_ASSERT("newconn != NULL", newconn != NULL); @@ -664,6 +666,7 @@ lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen) netconn_delete(newconn); sock_set_errno(sock, ENFILE); done_socket(sock); + KPrintf("newsock = alloc_socket(newconn, 1);\n"); return -1; } LWIP_ASSERT("invalid socket index", (newsock >= LWIP_SOCKET_OFFSET) && (newsock < NUM_SOCKETS + LWIP_SOCKET_OFFSET)); @@ -701,6 +704,7 @@ lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen) free_socket(nsock, 1); sock_set_errno(sock, err_to_errno(err)); done_socket(sock); + KPrintf("err = netconn_peer(newconn, &naddr, &port);\n"); return -1; } @@ -1242,6 +1246,8 @@ lwip_recvfrom(int s, void *mem, size_t len, int flags, if (err != ERR_OK) { LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom[UDP/RAW](%d): buf == NULL, error is \"%s\"!\n", s, lwip_strerr(err))); + KPrintf("lwip_recvfrom[UDP/RAW](%d): buf == NULL, error is \"%s\"!\n", + s, lwip_strerr(err)); sock_set_errno(sock, err_to_errno(err)); done_socket(sock); return -1; diff --git a/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/arch/lwipopts.h b/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/arch/lwipopts.h index feded6b5a..ffbb51d74 100644 --- a/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/arch/lwipopts.h +++ b/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/arch/lwipopts.h @@ -272,13 +272,16 @@ a lot of data that needs to be copied, this should be set high. */ #define MEMP_NUM_PBUF 32 /* MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One per active UDP "connection". */ -#define MEMP_NUM_UDP_PCB 4 +#define MEMP_NUM_UDP_PCB 32 /* MEMP_NUM_TCP_PCB: the number of simulatenously active TCP connections. */ -#define MEMP_NUM_TCP_PCB 64 +#define MEMP_NUM_TCP_PCB 32 +#define MEMP_NUM_RAW_PCB 32 +#define MEMP_NUM_REASSDATA 32 +#define MEMP_NUM_NETCONN 32 /* MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP connections. */ -#define MEMP_NUM_TCP_PCB_LISTEN 2 +#define MEMP_NUM_TCP_PCB_LISTEN 32 /* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP segments. */ #define MEMP_NUM_TCP_SEG 256 @@ -384,12 +387,6 @@ a lot of data that needs to be copied, this should be set high. */ /* ---------- ICMP options ---------- */ #define LWIP_ICMP 1 -/* ---------- DHCP options ---------- */ -/* Define LWIP_DHCP to 1 if you want DHCP configuration of - interfaces. DHCP is not implemented in lwIP 0.5.1, however, so - turning this on does currently not work. */ -#define LWIP_DHCP 1 - /* ---------- UDP options ---------- */ #define LWIP_UDP 1 #define UDP_TTL 255 @@ -521,7 +518,7 @@ The STM32F4x7 allows computing and verifying the IP, UDP, TCP and ICMP checksums /* ---------- DHCP options ---------- */ /* Define LWIP_DHCP to 1 if you want DHCP configuration of interfaces. */ -#define LWIP_DHCP 1 +#define LWIP_DHCP 0 /* 1 if you want to do an ARP check on the offered address (recommended). */ diff --git a/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/arch/sys_arch.c b/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/arch/sys_arch.c index 68e3c0ff6..788e7e3e3 100644 --- a/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/arch/sys_arch.c +++ b/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/arch/sys_arch.c @@ -76,7 +76,7 @@ char lwip_eth0_ipaddr[20] = { 192, 168, 130, 77 }; char lwip_eth0_netmask[20] = { 255, 255, 254, 0 }; char lwip_eth0_gwaddr[20] = { 192, 168, 130, 1 }; -char lwip_eth1_ipaddr[20] = { 192, 168, 130, 88 }; +char lwip_eth1_ipaddr[20] = { 192, 168, 131, 88 }; char lwip_eth1_netmask[20] = { 255, 255, 254, 0 }; char lwip_eth1_gwaddr[20] = { 192, 168, 130, 1 }; @@ -346,7 +346,9 @@ void lwip_config_input(int eport, struct netif* net) if (eport == 0) { th_id = sys_thread_new("eth_input", ethernetif_input, net, LWIP_TASK_STACK_SIZE, 30); } else if (eport == 1) { +#ifdef NETIF_ENET1_INIT_FUNC th_id = sys_thread_new("eth_input2", ethernetif_input2, net, LWIP_TASK_STACK_SIZE, 30); +#endif } } @@ -360,7 +362,6 @@ void lwip_config_tcp(uint8_t enet_port, char* ip, char* mask, char* gw) if (chk_lwip_bit(LWIP_INIT_FLAG)) { lw_print("lw: [%s] already ...\n", __func__); - return; } set_lwip_bit(LWIP_INIT_FLAG); @@ -384,12 +385,13 @@ void lwip_config_tcp(uint8_t enet_port, char* ip, char* mask, char* gw) lw_print("Not Netif driver for Eport 0\n"); return; #endif +#ifdef NETIF_ENET0_INIT_FUNC lw_print("Add netif eport 0\n"); netif_add(&gnetif, &net_ipaddr, &net_netmask, &net_gw, eth_cfg, NETIF_ENET0_INIT_FUNC, tcpip_input); - // netif_set_default(&gnetif); - // netif_set_up(&gnetif); + netif_set_default(&gnetif); + netif_set_up(&gnetif); lw_print("\r\n************************************************\r\n"); lw_print(" Network Configuration\r\n"); @@ -404,6 +406,7 @@ void lwip_config_tcp(uint8_t enet_port, char* ip, char* mask, char* gw) lwip_config_input(enet_port, &gnetif); is_init_0 = 1; +#endif } else if (1 == enet_port) { if (is_init_1 == 1) { @@ -413,11 +416,12 @@ void lwip_config_tcp(uint8_t enet_port, char* ip, char* mask, char* gw) lw_print("Not Netif driver for Eport 1\n"); return; #endif +#ifdef NETIF_ENET1_INIT_FUNC lw_print("Add netif eport 1\n"); netif_add(&gnetif2, &net_ipaddr, &net_netmask, &net_gw, eth_cfg, NETIF_ENET1_INIT_FUNC, tcpip_input); - netif_set_default(&gnetif2); + // netif_set_default(&gnetif2); netif_set_up(&gnetif2); lw_print("\r\n************************************************\r\n"); @@ -433,5 +437,6 @@ void lwip_config_tcp(uint8_t enet_port, char* ip, char* mask, char* gw) lwip_config_input(enet_port, &gnetif2); is_init_1 = 1; +#endif } } \ No newline at end of file diff --git a/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/core/ipv4/ip4.c b/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/core/ipv4/ip4.c index 036457afe..3cb0ca16f 100644 --- a/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/core/ipv4/ip4.c +++ b/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/core/ipv4/ip4.c @@ -228,6 +228,95 @@ ip4_route(const ip4_addr_t *dest) return netif_default; } +/** + * Finds the appropriate network interface for a given IP address. It + * searches the list of network interfaces linearly. A match is found + * if the masked IP address of the network interface equals the masked + * IP address given to the function. + * + * @param dest the destination IP address for which to find the route + * @return the netif on which to send to reach dest + */ +struct netif* +ip4_route2(const ip4_addr_t* src, const ip4_addr_t* dest) +{ +#if !LWIP_SINGLE_NETIF + struct netif* netif; + LWIP_ASSERT_CORE_LOCKED(); + +#if LWIP_MULTICAST_TX_OPTIONS + /* Use administratively selected interface for multicast by default */ + if (ip4_addr_ismulticast(dest) && ip4_default_multicast_netif) { + return ip4_default_multicast_netif; + } +#endif /* LWIP_MULTICAST_TX_OPTIONS */ + + /* bug #54569: in case LWIP_SINGLE_NETIF=1 and LWIP_DEBUGF() disabled, the following loop is optimized away */ + LWIP_UNUSED_ARG(dest); + + /* iterate through netifs */ + NETIF_FOREACH(netif) + { + /* is the netif up, does it have a link and a valid address? */ + if (netif_is_up(netif) && netif_is_link_up(netif) && !ip4_addr_isany_val(*netif_ip4_addr(netif))) { + + /* network mask matches? */ + // if (ip4_addr_netcmp(dest, netif_ip4_addr(netif), netif_ip4_netmask(netif))) { + if (ip4_addr_cmp(src, netif_ip4_addr(netif))) { + /* return netif on which to forward IP packet */ + return netif; + } + /* gateway matches on a non broadcast interface? (i.e. peer in a point to point interface) */ + if (((netif->flags & NETIF_FLAG_BROADCAST) == 0) && ip4_addr_cmp(dest, netif_ip4_gw(netif))) { + /* return netif on which to forward IP packet */ + return netif; + } + } + } + +#if LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF + /* loopif is disabled, looopback traffic is passed through any netif */ + if (ip4_addr_isloopback(dest)) { + /* don't check for link on loopback traffic */ + if (netif_default != NULL && netif_is_up(netif_default)) { + return netif_default; + } + /* default netif is not up, just use any netif for loopback traffic */ + NETIF_FOREACH(netif) + { + if (netif_is_up(netif)) { + return netif; + } + } + return NULL; + } +#endif /* LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF */ + +#ifdef LWIP_HOOK_IP4_ROUTE_SRC + netif = LWIP_HOOK_IP4_ROUTE_SRC(NULL, dest); + if (netif != NULL) { + return netif; + } +#elif defined(LWIP_HOOK_IP4_ROUTE) + netif = LWIP_HOOK_IP4_ROUTE(dest); + if (netif != NULL) { + return netif; + } +#endif +#endif /* !LWIP_SINGLE_NETIF */ + + if ((netif_default == NULL) || !netif_is_up(netif_default) || !netif_is_link_up(netif_default) || ip4_addr_isany_val(*netif_ip4_addr(netif_default)) || ip4_addr_isloopback(dest)) { + /* No matching netif found and default netif is not usable. + If this is not good enough for you, use LWIP_HOOK_IP4_ROUTE() */ + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_route: No route to %" U16_F ".%" U16_F ".%" U16_F ".%" U16_F "\n", ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest))); + IP_STATS_INC(ip.rterr); + MIB2_STATS_INC(mib2.ipoutnoroutes); + return NULL; + } + + return netif_default; +} + #if IP_FORWARD /** * Determine whether an IP address is in a reserved set of addresses diff --git a/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/include/lwip/ip4.h b/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/include/lwip/ip4.h index fd35a3369..d3789a9be 100644 --- a/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/include/lwip/ip4.h +++ b/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/include/lwip/ip4.h @@ -63,39 +63,41 @@ extern "C" { #define ip_init() /* Compatibility define, no init needed. */ struct netif *ip4_route(const ip4_addr_t *dest); +struct netif* ip4_route2(const ip4_addr_t* src, const ip4_addr_t* dest); #if LWIP_IPV4_SRC_ROUTING -struct netif *ip4_route_src(const ip4_addr_t *src, const ip4_addr_t *dest); +struct netif* ip4_route_src(const ip4_addr_t* src, const ip4_addr_t* dest); #else /* LWIP_IPV4_SRC_ROUTING */ -#define ip4_route_src(src, dest) ip4_route(dest) +// #define ip4_route_src(src, dest) ip4_route(dest) +#define ip4_route_src(src, dest) ip4_route2(src, dest) #endif /* LWIP_IPV4_SRC_ROUTING */ -err_t ip4_input(struct pbuf *p, struct netif *inp); -err_t ip4_output(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, - u8_t ttl, u8_t tos, u8_t proto); -err_t ip4_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, - u8_t ttl, u8_t tos, u8_t proto, struct netif *netif); -err_t ip4_output_if_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, - u8_t ttl, u8_t tos, u8_t proto, struct netif *netif); +err_t ip4_input(struct pbuf* p, struct netif* inp); +err_t ip4_output(struct pbuf* p, const ip4_addr_t* src, const ip4_addr_t* dest, + u8_t ttl, u8_t tos, u8_t proto); +err_t ip4_output_if(struct pbuf* p, const ip4_addr_t* src, const ip4_addr_t* dest, + u8_t ttl, u8_t tos, u8_t proto, struct netif* netif); +err_t ip4_output_if_src(struct pbuf* p, const ip4_addr_t* src, const ip4_addr_t* dest, + u8_t ttl, u8_t tos, u8_t proto, struct netif* netif); #if LWIP_NETIF_USE_HINTS -err_t ip4_output_hinted(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, - u8_t ttl, u8_t tos, u8_t proto, struct netif_hint *netif_hint); + err_t ip4_output_hinted(struct pbuf* p, const ip4_addr_t* src, const ip4_addr_t* dest, + u8_t ttl, u8_t tos, u8_t proto, struct netif_hint* netif_hint); #endif /* LWIP_NETIF_USE_HINTS */ #if IP_OPTIONS_SEND -err_t ip4_output_if_opt(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, - u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options, - u16_t optlen); -err_t ip4_output_if_opt_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, - u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options, - u16_t optlen); + err_t ip4_output_if_opt(struct pbuf* p, const ip4_addr_t* src, const ip4_addr_t* dest, + u8_t ttl, u8_t tos, u8_t proto, struct netif* netif, void* ip_options, + u16_t optlen); + err_t ip4_output_if_opt_src(struct pbuf* p, const ip4_addr_t* src, const ip4_addr_t* dest, + u8_t ttl, u8_t tos, u8_t proto, struct netif* netif, void* ip_options, + u16_t optlen); #endif /* IP_OPTIONS_SEND */ #if LWIP_MULTICAST_TX_OPTIONS -void ip4_set_default_multicast_netif(struct netif* default_multicast_netif); + void ip4_set_default_multicast_netif(struct netif* default_multicast_netif); #endif /* LWIP_MULTICAST_TX_OPTIONS */ #define ip4_netif_get_local_ip(netif) (((netif) != NULL) ? netif_ip_addr4(netif) : NULL) #if IP_DEBUG -void ip4_debug_print(struct pbuf *p); + void ip4_debug_print(struct pbuf* p); #else #define ip4_debug_print(p) #endif /* IP_DEBUG */ diff --git a/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/include/lwip/opt.h b/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/include/lwip/opt.h index a304c67c2..55c840233 100644 --- a/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/include/lwip/opt.h +++ b/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/include/lwip/opt.h @@ -521,7 +521,7 @@ * (only needed if you use the sequential API, like api_lib.c) */ #if !defined MEMP_NUM_NETBUF || defined __DOXYGEN__ -#define MEMP_NUM_NETBUF 2 +#define MEMP_NUM_NETBUF 8 #endif /** @@ -529,7 +529,7 @@ * (only needed if you use the sequential API, like api_lib.c) */ #if !defined MEMP_NUM_NETCONN || defined __DOXYGEN__ -#define MEMP_NUM_NETCONN 4 +#define MEMP_NUM_NETCONN 8 #endif /** @@ -538,7 +538,7 @@ * In that case, you need one per thread calling lwip_select.) */ #if !defined MEMP_NUM_SELECT_CB || defined __DOXYGEN__ -#define MEMP_NUM_SELECT_CB 4 +#define MEMP_NUM_SELECT_CB 4 #endif /** @@ -547,7 +547,7 @@ * (only needed if you use tcpip.c) */ #if !defined MEMP_NUM_TCPIP_MSG_API || defined __DOXYGEN__ -#define MEMP_NUM_TCPIP_MSG_API 8 +#define MEMP_NUM_TCPIP_MSG_API 8 #endif /** @@ -556,7 +556,7 @@ * (only needed if you use tcpip.c) */ #if !defined MEMP_NUM_TCPIP_MSG_INPKT || defined __DOXYGEN__ -#define MEMP_NUM_TCPIP_MSG_INPKT 8 +#define MEMP_NUM_TCPIP_MSG_INPKT 8 #endif /** @@ -564,7 +564,7 @@ * (before freeing the corresponding memory using lwip_freeaddrinfo()). */ #if !defined MEMP_NUM_NETDB || defined __DOXYGEN__ -#define MEMP_NUM_NETDB 1 +#define MEMP_NUM_NETDB 2 #endif /** @@ -572,7 +572,7 @@ * if DNS_LOCAL_HOSTLIST_IS_DYNAMIC==1. */ #if !defined MEMP_NUM_LOCALHOSTLIST || defined __DOXYGEN__ -#define MEMP_NUM_LOCALHOSTLIST 1 +#define MEMP_NUM_LOCALHOSTLIST 2 #endif /** diff --git a/Ubiquitous/XiZi_IIoT/resources/ethernet/cmd_lwip/lwip_config_demo.c b/Ubiquitous/XiZi_IIoT/resources/ethernet/cmd_lwip/lwip_config_demo.c index f6b6c6d01..1d6aa94e8 100755 --- a/Ubiquitous/XiZi_IIoT/resources/ethernet/cmd_lwip/lwip_config_demo.c +++ b/Ubiquitous/XiZi_IIoT/resources/ethernet/cmd_lwip/lwip_config_demo.c @@ -25,102 +25,145 @@ #include #include -#include +#include "argparse.h" + +#include "inet.h" +#include "netdev.h" /******************************************************************************/ -uint8_t enet_id = 0; -static void LwipSetIPTask(void* param) -{ - lwip_config_tcp(0, lwip_eth0_ipaddr, lwip_eth0_netmask, lwip_eth0_gwaddr); - lwip_config_tcp(1, lwip_eth1_ipaddr, lwip_eth1_netmask, lwip_eth1_gwaddr); -} +enum NETWORK_ACTIVE_E { + LWIP_ACTIVE = 'e', + LWIP_ACTIVE_ALL = 'a', +}; -void LwipSetIPTest(int argc, char* argv[]) +void LwipNetworkActive(int argc, char* argv[]) { - if (argc >= 4) { - printf("lw: [%s] ip %s mask %s gw %s netport %s\n", __func__, argv[1], argv[2], argv[3], argv[4]); - sscanf(argv[1], "%hhd.%hhd.%hhd.%hhd", &lwip_ipaddr[0], &lwip_ipaddr[1], &lwip_ipaddr[2], &lwip_ipaddr[3]); - sscanf(argv[2], "%hhd.%hhd.%hhd.%hhd", &lwip_netmask[0], &lwip_netmask[1], &lwip_netmask[2], &lwip_netmask[3]); - sscanf(argv[3], "%hhd.%hhd.%hhd.%hhd", &lwip_gwaddr[0], &lwip_gwaddr[1], &lwip_gwaddr[2], &lwip_gwaddr[3]); - sscanf(argv[4], "%hhd", &enet_id); + static char usage_info[] = "select a eport to start."; + static char program_info[] = "Active lwip network function."; + static const char* const net_active_usages[] = { + "LwipNetworkActive -e 0", + "LwipNetworkActive -e 1", + "LwipNetworkActive -a", + }; - if (0 == enet_id) { - printf("save eth0 info\n"); - memcpy(lwip_eth0_ipaddr, lwip_ipaddr, 20); - memcpy(lwip_eth0_netmask, lwip_netmask, 20); - memcpy(lwip_eth0_gwaddr, lwip_gwaddr, 20); - } else if (1 == enet_id) { - printf("save eth1 info\n"); - memcpy(lwip_eth1_ipaddr, lwip_ipaddr, 20); - memcpy(lwip_eth1_netmask, lwip_netmask, 20); - memcpy(lwip_eth1_gwaddr, lwip_gwaddr, 20); - } - } else if (argc == 2) { - printf("lw: [%s] set eth0 ipaddr %s \n", __func__, argv[1]); - sscanf(argv[1], "%hhd.%hhd.%hhd.%hhd", &lwip_ipaddr[0], &lwip_ipaddr[1], &lwip_ipaddr[2], &lwip_ipaddr[3]); - memcpy(lwip_eth0_ipaddr, lwip_ipaddr, strlen(lwip_ipaddr)); + int eport = -1; + bool all = false; + bool is_help = false; + struct argparse_option options[] = { + OPT_HELP(&is_help), + OPT_INTEGER(LWIP_ACTIVE, "eport", &eport, "eport to start network.", NULL, 0, 0), + OPT_BIT(LWIP_ACTIVE_ALL, "all", &all, "start network at both eport 0 and 1.", NULL, true, 0), + OPT_END(), + }; + + struct argparse argparse; + argparse_init(&argparse, options, net_active_usages, 0); + argparse_describe(&argparse, usage_info, program_info); + argc = argparse_parse(&argparse, argc, (const char**)argv); + if (is_help) { + return; } - // sys_thread_new("SET ip address", LwipSetIPTask, &enet_id, LWIP_TASK_STACK_SIZE, LWIP_DEMO_TASK_PRIO); - LwipSetIPTask(&enet_id); -} + if (all) { + lwip_config_tcp(0, lwip_eth0_ipaddr, lwip_eth0_netmask, lwip_eth0_gwaddr); + lwip_config_tcp(1, lwip_eth1_ipaddr, lwip_eth1_netmask, lwip_eth1_gwaddr); + return; + } + + if (eport != 0 && eport != 1) { + printf("[%s] Err: Support only eport 0 and eport 1.\n", __func__); + } + + if (eport == 0) { + lwip_config_tcp(0, lwip_eth0_ipaddr, lwip_eth0_netmask, lwip_eth0_gwaddr); + } else if (eport == 1) { + lwip_config_tcp(1, lwip_eth1_ipaddr, lwip_eth1_netmask, lwip_eth1_gwaddr); + } +} SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | SHELL_CMD_PARAM_NUM(5), - setip, LwipSetIPTest, setip[IP][Netmask][Gateway][port]); + LwipNetworkActive, LwipNetworkActive, start lwip); -void LwipShowIPTask(int argc, char* argv[]) +enum NETWORK_CONFIG_E { + NETWORK_DEV = 'd', + NETWORK_IP = 'i', + NETWORK_NM = 'n', + NETWORK_GW = 'g', +}; + +void LwipSetNetwork(int argc, char* argv[]) { -#ifdef configMAC_ADDR - char mac_addr0[] = configMAC_ADDR; -#endif + static char usage_info[] = "config network information."; + static char program_info[] = "set network ip, netmask, gateway"; + static const char* const net_set_usages[] = { + "LwipSetNetwork -d [dev] -i [ip]", + "LwipSetNetwork -d [dev] -n [netmask]", + "LwipSetNetwork -d [dev] -g [gw]", + "LwipSetNetwork -d [dev] -i [ip] -n [netmask] -g [gw]", + }; - // find default netdev - struct netdev* netdev = netdev_get_by_name("en\0"); + char* dev_ptr = NULL; + char* ip_ptr = NULL; + char* nm_ptr = NULL; + char* gw_ptr = NULL; + bool is_help = false; + struct argparse_option options[] = { + OPT_HELP(&is_help), + OPT_GROUP("Param Options"), + OPT_STRING(NETWORK_DEV, "dev", &dev_ptr, "netdev", NULL, 0, 0), + OPT_STRING(NETWORK_IP, "ip", &ip_ptr, "change ip", NULL, 0, 0), + OPT_STRING(NETWORK_NM, "nm", &nm_ptr, "change netmask", NULL, 0, 0), + OPT_STRING(NETWORK_GW, "gw", &gw_ptr, "change gateway", NULL, 0, 0), + OPT_END(), + }; + + struct argparse argparse; + argparse_init(&argparse, options, net_set_usages, 0); + argparse_describe(&argparse, usage_info, program_info); + argc = argparse_parse(&argparse, argc, (const char**)argv); + /* help task */ + if (is_help) { + return; + } + + if (dev_ptr == NULL) { + printf("[%s] Err: must given a netdev name.\n", __func__); + return; + } + struct netdev* netdev = netdev_get_by_name(dev_ptr); if (netdev == NULL) { - lw_notice("[%s] Netdev not found by name en\n"); - struct netdev* default_netdev = NETDEV_DEFAULT; + printf("[%s] Err: Netdev not found by name: %s\n", __func__, dev_ptr); + return; } - lw_notice("\r\n************************************************\r\n"); - lw_notice(" Network Configuration\r\n"); - lw_notice("************************************************\r\n"); - // lw_notice(" ETH0 IPv4 Address : %u.%u.%u.%u\r\n", ((u8_t*)&lwip_eth0_ipaddr)[0], ((u8_t*)&lwip_eth0_ipaddr)[1], - // ((u8_t*)&lwip_eth0_ipaddr)[2], ((u8_t*)&lwip_eth0_ipaddr)[3]); - // lw_notice(" ETH0 IPv4 Subnet mask : %u.%u.%u.%u\r\n", ((u8_t*)&lwip_eth0_netmask)[0], ((u8_t*)&lwip_eth0_netmask)[1], - // ((u8_t*)&lwip_eth0_netmask)[2], ((u8_t*)&lwip_eth0_netmask)[3]); - // lw_notice(" ETH0 IPv4 Gateway : %u.%u.%u.%u\r\n", ((u8_t*)&lwip_gwaddr)[0], ((u8_t*)&lwip_eth0_gwaddr)[1], - // ((u8_t*)&lwip_eth0_gwaddr)[2], ((u8_t*)&lwip_eth0_gwaddr)[3]); - // #ifdef configMAC_ADDR - // lw_notice(" ETH0 MAC Address : %x:%x:%x:%x:%x:%x\r\n", mac_addr0[0], mac_addr0[1], mac_addr0[2], - // mac_addr0[3], mac_addr0[4], mac_addr0[5]); - // #endif - - lw_notice(" ETH0 IPv4 Address : %u.%u.%u.%u\r\n", ((u8_t*)&lwip_eth0_ipaddr)[0], ((u8_t*)&lwip_eth0_ipaddr)[1], - ((u8_t*)&lwip_eth0_ipaddr)[2], ((u8_t*)&lwip_eth0_ipaddr)[3]); - lw_notice(" ETH0 IPv4 Subnet mask : %u.%u.%u.%u\r\n", ((u8_t*)&lwip_eth0_netmask)[0], ((u8_t*)&lwip_eth0_netmask)[1], - ((u8_t*)&lwip_eth0_netmask)[2], ((u8_t*)&lwip_eth0_netmask)[3]); - lw_notice(" ETH0 IPv4 Gateway : %u.%u.%u.%u\r\n", ((u8_t*)&lwip_gwaddr)[0], ((u8_t*)&lwip_eth0_gwaddr)[1], - ((u8_t*)&lwip_eth0_gwaddr)[2], ((u8_t*)&lwip_eth0_gwaddr)[3]); -#ifdef configMAC_ADDR - lw_notice(" ETH0 MAC Address : %x:%x:%x:%x:%x:%x\r\n", mac_addr0[0], mac_addr0[1], mac_addr0[2], - mac_addr0[3], mac_addr0[4], mac_addr0[5]); -#endif - -#ifdef BOARD_NET_COUNT - if (BOARD_NET_COUNT > 1) { - char mac_addr1[] = configMAC_ADDR_ETH1; - lw_notice("\r\n"); - lw_notice(" ETH1 IPv4 Address : %u.%u.%u.%u\r\n", ((u8_t*)&lwip_eth1_ipaddr)[0], ((u8_t*)&lwip_eth1_ipaddr)[1], - ((u8_t*)&lwip_eth1_ipaddr)[2], ((u8_t*)&lwip_eth1_ipaddr)[3]); - lw_notice(" ETH1 IPv4 Subnet mask : %u.%u.%u.%u\r\n", ((u8_t*)&lwip_eth1_netmask)[0], ((u8_t*)&lwip_eth1_netmask)[1], - ((u8_t*)&lwip_eth1_netmask)[2], ((u8_t*)&lwip_eth1_netmask)[3]); - lw_notice(" ETH1 IPv4 Gateway : %u.%u.%u.%u\r\n", ((u8_t*)&lwip_eth1_gwaddr)[0], ((u8_t*)&lwip_eth1_gwaddr)[1], - ((u8_t*)&lwip_eth1_gwaddr)[2], ((u8_t*)&lwip_eth1_gwaddr)[3]); - lw_notice(" ETH1 MAC Address : %x:%x:%x:%x:%x:%x\r\n", mac_addr1[0], mac_addr1[1], mac_addr1[2], - mac_addr1[3], mac_addr1[4], mac_addr1[5]); + ip_addr_t ipaddr, maskaddr, gwaddr; + if (ip_ptr != NULL) { + inet_aton(ip_ptr, &ipaddr); + if (0 != netdev_set_ipaddr(netdev, &ipaddr)) { + printf("[%s] Err: set ip: %s failed.\n", __func__, ip_ptr); + } } -#endif - lw_notice("************************************************\r\n"); + if (nm_ptr != NULL) { + inet_aton(nm_ptr, &maskaddr); + if (0 != netdev_set_netmask(netdev, &maskaddr)) { + printf("[%s] Err: set netmask: %s failed.\n", __func__, nm_ptr); + } + } + if (gw_ptr != NULL) { + inet_aton(gw_ptr, &gwaddr); + if (0 != netdev_set_gw(netdev, &gwaddr)) { + printf("[%s] Err: set gateway: %s failed.\n", __func__, gw_ptr); + } + } +} + +SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | SHELL_CMD_PARAM_NUM(11), + LwipSetNetwork, LwipSetNetwork, config lwip); + +void LwipShowNetwork(int argc, char* argv[]) +{ + extern void netdev_list_dev(); + netdev_list_dev(); } SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | SHELL_CMD_PARAM_NUM(0), - showip, LwipShowIPTask, GetIp[IP][Netmask][Gateway]); + LwipShowNetwork, LwipShowNetwork, show lwip network configuration); diff --git a/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_lowlevel.c b/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_lowlevel.c index c1d4e0fa4..8cd2b0fde 100644 --- a/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_lowlevel.c +++ b/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_lowlevel.c @@ -43,8 +43,8 @@ void netdev_low_level_set_ipaddr(struct netdev* netdev, const ip_addr_t* ip_addr { CHECK(ip_addr); - if (netdev && ip_addr_cmp(&(netdev->ip_addr), ip_addr) == 0) { - ip_addr_copy(netdev->ip_addr, *ip_addr); + if (netdev && ip_addr_cmp((netdev->ip_addr), ip_addr) == 0) { + ip_addr_copy(*netdev->ip_addr, *ip_addr); /* execute IP address change callback function */ if (netdev->addr_callback) { @@ -64,8 +64,8 @@ void netdev_low_level_set_netmask(struct netdev* netdev, const ip_addr_t* netmas { CHECK(netmask); - if (netdev && ip_addr_cmp(&(netdev->netmask), netmask) == 0) { - ip_addr_copy(netdev->netmask, *netmask); + if (netdev && ip_addr_cmp((netdev->netmask), netmask) == 0) { + ip_addr_copy(*netdev->netmask, *netmask); /* execute netmask address change callback function */ if (netdev->addr_callback) { @@ -85,8 +85,8 @@ void netdev_low_level_set_gw(struct netdev* netdev, const ip_addr_t* gw) { CHECK(gw); - if (netdev && ip_addr_cmp(&(netdev->gw), gw) == 0) { - ip_addr_copy(netdev->gw, *gw); + if (netdev && ip_addr_cmp((netdev->gw), gw) == 0) { + ip_addr_copy(*netdev->gw, *gw); /* execute gateway address change callback function */ if (netdev->addr_callback) { diff --git a/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_manipulate.c b/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_manipulate.c index fee6adeb2..283261a1b 100644 --- a/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_manipulate.c +++ b/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_manipulate.c @@ -277,7 +277,7 @@ struct netdev* netdev_get_by_ipaddr(ip_addr_t* ip_addr) struct netdev* current_dev = NETDEV_LISTHEAD; SINGLE_LINKLIST_FOR_EACH_ENTRY(current_dev, &(NETDEV_LISTHEAD->list), list) { - if (NULL != current_dev && ip_addr_cmp(&(current_dev->ip_addr), ip_addr)) { + if (NULL != current_dev && ip_addr_cmp((current_dev->ip_addr), ip_addr)) { ENABLE_INTERRUPT(lock); return current_dev; } @@ -325,10 +325,10 @@ struct netdev* netdev_get_by_name(const char* name) return NULL; } -struct netdev* netdev_list_dev() +void netdev_list_dev() { if (NETDEV_LISTHEAD == NULL) { - return NULL; + return; } char ip[IP4ADDR_STRLEN_MAX], netmask[IP4ADDR_STRLEN_MAX], gw[IP4ADDR_STRLEN_MAX], dns[IP4ADDR_STRLEN_MAX]; @@ -342,9 +342,9 @@ struct netdev* netdev_list_dev() continue; } - strncpy(ip, inet_ntoa(current_dev->ip_addr), IP4ADDR_STRLEN_MAX); - strncpy(netmask, inet_ntoa(current_dev->netmask), IP4ADDR_STRLEN_MAX); - strncpy(gw, inet_ntoa(current_dev->gw), IP4ADDR_STRLEN_MAX); + strncpy(ip, inet_ntoa(*current_dev->ip_addr), IP4ADDR_STRLEN_MAX); + strncpy(netmask, inet_ntoa(*current_dev->netmask), IP4ADDR_STRLEN_MAX); + strncpy(gw, inet_ntoa(*current_dev->gw), IP4ADDR_STRLEN_MAX); strncpy(dns, inet_ntoa(current_dev->dns_servers[0]), IP4ADDR_STRLEN_MAX); KPrintf("Netdev %s: ip: %s, mask: %s, gw: %s, dns: %s\n", current_dev->name, @@ -352,7 +352,7 @@ struct netdev* netdev_list_dev() } ENABLE_INTERRUPT(lock); - return NULL; + return; } SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | SHELL_CMD_PARAM_NUM(5), netdev_list, netdev_list_dev, list sys netdev); \ No newline at end of file diff --git a/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_register.c b/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_register.c index 0fa3b1bb7..e6196feb6 100644 --- a/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_register.c +++ b/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_register.c @@ -65,17 +65,13 @@ int netdev_register(struct netdev* netdev, const char* name, void* user_data) // set flag mask, assert network is down uint16_t flag_mask = 0; - flag_mask = NETDEV_FLAG_UP | NETDEV_FLAG_LINK_UP | NETDEV_FLAG_INTERNET_UP | NETDEV_FLAG_DHCP; + flag_mask = NETDEV_FLAG_UP | NETDEV_FLAG_LINK_UP | NETDEV_FLAG_INTERNET_UP; netdev->flags &= ~flag_mask; // clear dev setting - ip_addr_set_zero(&(netdev->ip_addr)); - ip_addr_set_zero(&(netdev->netmask)); - ip_addr_set_zero(&(netdev->gw)); - - IP_SET_TYPE_VAL(netdev->ip_addr, IPADDR_TYPE_V4); - IP_SET_TYPE_VAL(netdev->netmask, IPADDR_TYPE_V4); - IP_SET_TYPE_VAL(netdev->gw, IPADDR_TYPE_V4); + netdev->ip_addr = NULL; + netdev->netmask = NULL; + netdev->gw = NULL; #if NETDEV_IPV6 for (index = 0; index < NETDEV_IPV6_NUM_ADDRESSES; index++) { @@ -87,7 +83,6 @@ int netdev_register(struct netdev* netdev, const char* name, void* user_data) // clear DNS servers for (uint16_t idx = 0; idx < NETDEV_DNS_SERVERS_NUM; idx++) { ip_addr_set_zero(&(netdev->dns_servers[idx])); - IP_SET_TYPE_VAL(netdev->ip_addr, IPADDR_TYPE_V4); } // clear callback fn netdev->addr_callback = NULL; diff --git a/Ubiquitous/XiZi_IIoT/resources/include/netdev/netdev.h b/Ubiquitous/XiZi_IIoT/resources/include/netdev/netdev.h index e3007fd9e..468a1a68e 100644 --- a/Ubiquitous/XiZi_IIoT/resources/include/netdev/netdev.h +++ b/Ubiquitous/XiZi_IIoT/resources/include/netdev/netdev.h @@ -93,9 +93,9 @@ struct netdev { SysSingleLinklistType list; char name[NAME_NUM_MAX]; /* network interface device name */ - ip_addr_t ip_addr; /* IP address */ - ip_addr_t netmask; /* subnet mask */ - ip_addr_t gw; /* gateway */ + ip_addr_t* ip_addr; /* IP address */ + ip_addr_t* netmask; /* subnet mask */ + ip_addr_t* gw; /* gateway */ #if NETDEV_IPV6 ip_addr_t ip6_addr[NETDEV_IPV6_NUM_ADDRESSES]; /* array of IPv6 addresses */ #endif /* NETDEV_IPV6 */