| 1 | // Copyright (c) 2004-2013 Sergey Lyubka |
| 2 | // Copyright (c) 2013-2022 Cesanta Software Limited |
| 3 | // All rights reserved |
| 4 | // |
| 5 | // This software is dual-licensed: you can redistribute it and/or modify |
| 6 | // it under the terms of the GNU General Public License version 2 as |
| 7 | // published by the Free Software Foundation. For the terms of this |
| 8 | // license, see http://www.gnu.org/licenses/ |
| 9 | // |
| 10 | // You are free to use this software under the terms of the GNU General |
| 11 | // Public License, but WITHOUT ANY WARRANTY; without even the implied |
| 12 | // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| 13 | // See the GNU General Public License for more details. |
| 14 | // |
| 15 | // Alternatively, you can license this software under a commercial |
| 16 | // license, as set out in https://www.mongoose.ws/licensing/ |
| 17 | // |
| 18 | // SPDX-License-Identifier: GPL-2.0-only or commercial |
| 19 | |
| 20 | #include "mongoose.h" |
| 21 | |
| 22 | #if defined(__sunos__) |
| 23 | #include <alloca.h> |
| 24 | #endif |
| 25 | |
| 26 | #ifdef MG_ENABLE_LINES |
| 27 | #line 1 "src/base64.c" |
| 28 | #endif |
| 29 | |
| 30 | |
| 31 | |
| 32 | static int mg_b64idx(int c) { |
| 33 | if (c < 26) { |
| 34 | return c + 'A'; |
| 35 | } else if (c < 52) { |
| 36 | return c - 26 + 'a'; |
| 37 | } else if (c < 62) { |
| 38 | return c - 52 + '0'; |
| 39 | } else { |
| 40 | return c == 62 ? '+' : '/'; |
| 41 | } |
| 42 | } |
| 43 | |
| 44 | static int mg_b64rev(int c) { |
| 45 | if (c >= 'A' && c <= 'Z') { |
| 46 | return c - 'A'; |
| 47 | } else if (c >= 'a' && c <= 'z') { |
| 48 | return c + 26 - 'a'; |
| 49 | } else if (c >= '0' && c <= '9') { |
| 50 | return c + 52 - '0'; |
| 51 | } else if (c == '+') { |
| 52 | return 62; |
| 53 | } else if (c == '/') { |
| 54 | return 63; |
| 55 | } else if (c == '=') { |
| 56 | return 64; |
| 57 | } else { |
| 58 | return -1; |
| 59 | } |
| 60 | } |
| 61 | |
| 62 | int mg_base64_update(unsigned char ch, char *to, int n) { |
| 63 | int rem = (n & 3) % 3; |
| 64 | if (rem == 0) { |
| 65 | to[n] = (char) mg_b64idx(ch >> 2); |
| 66 | to[++n] = (char) ((ch & 3) << 4); |
| 67 | } else if (rem == 1) { |
| 68 | to[n] = (char) mg_b64idx(to[n] | (ch >> 4)); |
| 69 | to[++n] = (char) ((ch & 15) << 2); |
| 70 | } else { |
| 71 | to[n] = (char) mg_b64idx(to[n] | (ch >> 6)); |
| 72 | to[++n] = (char) mg_b64idx(ch & 63); |
| 73 | n++; |
| 74 | } |
| 75 | return n; |
| 76 | } |
| 77 | |
| 78 | int mg_base64_final(char *to, int n) { |
| 79 | int saved = n; |
| 80 | // printf("---[%.*s]\n", n, to); |
| 81 | if (n & 3) n = mg_base64_update(0, to, n); |
| 82 | if ((saved & 3) == 2) n--; |
| 83 | // printf(" %d[%.*s]\n", n, n, to); |
| 84 | while (n & 3) to[n++] = '='; |
| 85 | to[n] = '\0'; |
| 86 | return n; |
| 87 | } |
| 88 | |
| 89 | int mg_base64_encode(const unsigned char *p, int n, char *to) { |
| 90 | int i, len = 0; |
| 91 | for (i = 0; i < n; i++) len = mg_base64_update(p[i], to, len); |
| 92 | len = mg_base64_final(to, len); |
| 93 | return len; |
| 94 | } |
| 95 | |
| 96 | int mg_base64_decode(const char *src, int n, char *dst) { |
| 97 | const char *end = src == NULL ? NULL : src + n; // Cannot add to NULL |
| 98 | int len = 0; |
| 99 | while (src != NULL && src + 3 < end) { |
| 100 | int a = mg_b64rev(src[0]), b = mg_b64rev(src[1]), c = mg_b64rev(src[2]), |
| 101 | d = mg_b64rev(src[3]); |
| 102 | if (a == 64 || a < 0 || b == 64 || b < 0 || c < 0 || d < 0) return 0; |
| 103 | dst[len++] = (char) ((a << 2) | (b >> 4)); |
| 104 | if (src[2] != '=') { |
| 105 | dst[len++] = (char) ((b << 4) | (c >> 2)); |
| 106 | if (src[3] != '=') dst[len++] = (char) ((c << 6) | d); |
| 107 | } |
| 108 | src += 4; |
| 109 | } |
| 110 | dst[len] = '\0'; |
| 111 | return len; |
| 112 | } |
| 113 | |
| 114 | #ifdef MG_ENABLE_LINES |
| 115 | #line 1 "src/dns.c" |
| 116 | #endif |
| 117 | |
| 118 | |
| 119 | |
| 120 | |
| 121 | |
| 122 | |
| 123 | |
| 124 | |
| 125 | struct dns_data { |
| 126 | struct dns_data *next; |
| 127 | struct mg_connection *c; |
| 128 | uint64_t expire; |
| 129 | uint16_t txnid; |
| 130 | }; |
| 131 | |
| 132 | static void mg_sendnsreq(struct mg_connection *, struct mg_str *, int, |
| 133 | struct mg_dns *, bool); |
| 134 | |
| 135 | static void mg_dns_free(struct mg_connection *c, struct dns_data *d) { |
| 136 | LIST_DELETE(struct dns_data, |
| 137 | (struct dns_data **) &c->mgr->active_dns_requests, d); |
| 138 | free(d); |
| 139 | } |
| 140 | |
| 141 | void mg_resolve_cancel(struct mg_connection *c) { |
| 142 | struct dns_data *tmp, *d = (struct dns_data *) c->mgr->active_dns_requests; |
| 143 | for (; d != NULL; d = tmp) { |
| 144 | tmp = d->next; |
| 145 | if (d->c == c) mg_dns_free(c, d); |
| 146 | } |
| 147 | } |
| 148 | |
| 149 | static size_t mg_dns_parse_name_depth(const uint8_t *s, size_t len, size_t ofs, |
| 150 | char *to, size_t tolen, size_t j, |
| 151 | int depth) { |
| 152 | size_t i = 0; |
| 153 | if (tolen > 0 && depth == 0) to[0] = '\0'; |
| 154 | if (depth > 5) return 0; |
| 155 | // MG_INFO(("ofs %lx %x %x", (unsigned long) ofs, s[ofs], s[ofs + 1])); |
| 156 | while (ofs + i + 1 < len) { |
| 157 | size_t n = s[ofs + i]; |
| 158 | if (n == 0) { |
| 159 | i++; |
| 160 | break; |
| 161 | } |
| 162 | if (n & 0xc0) { |
| 163 | size_t ptr = (((n & 0x3f) << 8) | s[ofs + i + 1]); // 12 is hdr len |
| 164 | // MG_INFO(("PTR %lx", (unsigned long) ptr)); |
| 165 | if (ptr + 1 < len && (s[ptr] & 0xc0) == 0 && |
| 166 | mg_dns_parse_name_depth(s, len, ptr, to, tolen, j, depth + 1) == 0) |
| 167 | return 0; |
| 168 | i += 2; |
| 169 | break; |
| 170 | } |
| 171 | if (ofs + i + n + 1 >= len) return 0; |
| 172 | if (j > 0) { |
| 173 | if (j < tolen) to[j] = '.'; |
| 174 | j++; |
| 175 | } |
| 176 | if (j + n < tolen) memcpy(&to[j], &s[ofs + i + 1], n); |
| 177 | j += n; |
| 178 | i += n + 1; |
| 179 | if (j < tolen) to[j] = '\0'; // Zero-terminate this chunk |
| 180 | // MG_INFO(("--> [%s]", to)); |
| 181 | } |
| 182 | if (tolen > 0) to[tolen - 1] = '\0'; // Make sure make sure it is nul-term |
| 183 | return i; |
| 184 | } |
| 185 | |
| 186 | static size_t mg_dns_parse_name(const uint8_t *s, size_t n, size_t ofs, |
| 187 | char *dst, size_t dstlen) { |
| 188 | return mg_dns_parse_name_depth(s, n, ofs, dst, dstlen, 0, 0); |
| 189 | } |
| 190 | |
| 191 | size_t mg_dns_parse_rr(const uint8_t *buf, size_t len, size_t ofs, |
| 192 | bool is_question, struct mg_dns_rr *rr) { |
| 193 | const uint8_t *s = buf + ofs, *e = &buf[len]; |
| 194 | |
| 195 | memset(rr, 0, sizeof(*rr)); |
| 196 | if (len < sizeof(struct mg_dns_header)) return 0; // Too small |
| 197 | if (len > 512) return 0; // Too large, we don't expect that |
| 198 | if (s >= e) return 0; // Overflow |
| 199 | |
| 200 | if ((rr->nlen = (uint16_t) mg_dns_parse_name(buf, len, ofs, NULL, 0)) == 0) |
| 201 | return 0; |
| 202 | s += rr->nlen + 4; |
| 203 | if (s > e) return 0; |
| 204 | rr->atype = (uint16_t) (((uint16_t) s[-4] << 8) | s[-3]); |
| 205 | rr->aclass = (uint16_t) (((uint16_t) s[-2] << 8) | s[-1]); |
| 206 | if (is_question) return (size_t) (rr->nlen + 4); |
| 207 | |
| 208 | s += 6; |
| 209 | if (s > e) return 0; |
| 210 | rr->alen = (uint16_t) (((uint16_t) s[-2] << 8) | s[-1]); |
| 211 | if (s + rr->alen > e) return 0; |
| 212 | return (size_t) (rr->nlen + rr->alen + 10); |
| 213 | } |
| 214 | |
| 215 | bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *dm) { |
| 216 | const struct mg_dns_header *h = (struct mg_dns_header *) buf; |
| 217 | struct mg_dns_rr rr; |
| 218 | size_t i, n, ofs = sizeof(*h); |
| 219 | memset(dm, 0, sizeof(*dm)); |
| 220 | |
| 221 | if (len < sizeof(*h)) return 0; // Too small, headers dont fit |
| 222 | if (mg_ntohs(h->num_questions) > 1) return 0; // Sanity |
| 223 | if (mg_ntohs(h->num_answers) > 10) return 0; // Sanity |
| 224 | dm->txnid = mg_ntohs(h->txnid); |
| 225 | |
| 226 | for (i = 0; i < mg_ntohs(h->num_questions); i++) { |
| 227 | if ((n = mg_dns_parse_rr(buf, len, ofs, true, &rr)) == 0) return false; |
| 228 | // MG_INFO(("Q %lu %lu %hu/%hu", ofs, n, rr.atype, rr.aclass)); |
| 229 | ofs += n; |
| 230 | } |
| 231 | for (i = 0; i < mg_ntohs(h->num_answers); i++) { |
| 232 | if ((n = mg_dns_parse_rr(buf, len, ofs, false, &rr)) == 0) return false; |
| 233 | // MG_INFO(("A -- %lu %lu %hu/%hu %s", ofs, n, rr.atype, rr.aclass, |
| 234 | // dm->name)); |
| 235 | mg_dns_parse_name(buf, len, ofs, dm->name, sizeof(dm->name)); |
| 236 | ofs += n; |
| 237 | |
| 238 | if (rr.alen == 4 && rr.atype == 1 && rr.aclass == 1) { |
| 239 | dm->addr.is_ip6 = false; |
| 240 | memcpy(&dm->addr.ip, &buf[ofs - 4], 4); |
| 241 | dm->resolved = true; |
| 242 | break; // Return success |
| 243 | } else if (rr.alen == 16 && rr.atype == 28 && rr.aclass == 1) { |
| 244 | dm->addr.is_ip6 = true; |
| 245 | memcpy(&dm->addr.ip, &buf[ofs - 16], 16); |
| 246 | dm->resolved = true; |
| 247 | break; // Return success |
| 248 | } |
| 249 | } |
| 250 | return true; |
| 251 | } |
| 252 | |
| 253 | static void dns_cb(struct mg_connection *c, int ev, void *ev_data, |
| 254 | void *fn_data) { |
| 255 | struct dns_data *d, *tmp; |
| 256 | if (ev == MG_EV_POLL) { |
| 257 | uint64_t now = *(uint64_t *) ev_data; |
| 258 | for (d = (struct dns_data *) c->mgr->active_dns_requests; d != NULL; |
| 259 | d = tmp) { |
| 260 | tmp = d->next; |
| 261 | // MG_DEBUG ("%lu %lu dns poll", d->expire, now)); |
| 262 | if (now > d->expire) mg_error(d->c, "DNS timeout" ); |
| 263 | } |
| 264 | } else if (ev == MG_EV_READ) { |
| 265 | struct mg_dns_message dm; |
| 266 | int resolved = 0; |
| 267 | if (mg_dns_parse(c->recv.buf, c->recv.len, &dm) == false) { |
| 268 | MG_ERROR(("Unexpected DNS response:" )); |
| 269 | mg_hexdump(c->recv.buf, c->recv.len); |
| 270 | } else { |
| 271 | // MG_VERBOSE(("%s %d", dm.name, dm.resolved)); |
| 272 | for (d = (struct dns_data *) c->mgr->active_dns_requests; d != NULL; |
| 273 | d = tmp) { |
| 274 | tmp = d->next; |
| 275 | // MG_INFO(("d %p %hu %hu", d, d->txnid, dm.txnid)); |
| 276 | if (dm.txnid != d->txnid) continue; |
| 277 | if (d->c->is_resolving) { |
| 278 | if (dm.resolved) { |
| 279 | dm.addr.port = d->c->rem.port; // Save port |
| 280 | d->c->rem = dm.addr; // Copy resolved address |
| 281 | MG_DEBUG( |
| 282 | ("%lu %s is %M" , d->c->id, dm.name, mg_print_ip, &d->c->rem)); |
| 283 | mg_connect_resolved(d->c); |
| 284 | #if MG_ENABLE_IPV6 |
| 285 | } else if (dm.addr.is_ip6 == false && dm.name[0] != '\0' && |
| 286 | c->mgr->use_dns6 == false) { |
| 287 | struct mg_str x = mg_str(dm.name); |
| 288 | mg_sendnsreq(d->c, &x, c->mgr->dnstimeout, &c->mgr->dns6, true); |
| 289 | #endif |
| 290 | } else { |
| 291 | mg_error(d->c, "%s DNS lookup failed" , dm.name); |
| 292 | } |
| 293 | } else { |
| 294 | MG_ERROR(("%lu already resolved" , d->c->id)); |
| 295 | } |
| 296 | mg_dns_free(c, d); |
| 297 | resolved = 1; |
| 298 | } |
| 299 | } |
| 300 | if (!resolved) MG_ERROR(("stray DNS reply" )); |
| 301 | c->recv.len = 0; |
| 302 | } else if (ev == MG_EV_CLOSE) { |
| 303 | for (d = (struct dns_data *) c->mgr->active_dns_requests; d != NULL; |
| 304 | d = tmp) { |
| 305 | tmp = d->next; |
| 306 | mg_error(d->c, "DNS error" ); |
| 307 | mg_dns_free(c, d); |
| 308 | } |
| 309 | } |
| 310 | (void) fn_data; |
| 311 | } |
| 312 | |
| 313 | static bool mg_dns_send(struct mg_connection *c, const struct mg_str *name, |
| 314 | uint16_t txnid, bool ipv6) { |
| 315 | struct { |
| 316 | struct mg_dns_header ; |
| 317 | uint8_t data[256]; |
| 318 | } pkt; |
| 319 | size_t i, n; |
| 320 | memset(&pkt, 0, sizeof(pkt)); |
| 321 | pkt.header.txnid = mg_htons(txnid); |
| 322 | pkt.header.flags = mg_htons(0x100); |
| 323 | pkt.header.num_questions = mg_htons(1); |
| 324 | for (i = n = 0; i < sizeof(pkt.data) - 5; i++) { |
| 325 | if (name->ptr[i] == '.' || i >= name->len) { |
| 326 | pkt.data[n] = (uint8_t) (i - n); |
| 327 | memcpy(&pkt.data[n + 1], name->ptr + n, i - n); |
| 328 | n = i + 1; |
| 329 | } |
| 330 | if (i >= name->len) break; |
| 331 | } |
| 332 | memcpy(&pkt.data[n], "\x00\x00\x01\x00\x01" , 5); // A query |
| 333 | n += 5; |
| 334 | if (ipv6) pkt.data[n - 3] = 0x1c; // AAAA query |
| 335 | // memcpy(&pkt.data[n], "\xc0\x0c\x00\x1c\x00\x01", 6); // AAAA query |
| 336 | // n += 6; |
| 337 | return mg_send(c, &pkt, sizeof(pkt.header) + n); |
| 338 | } |
| 339 | |
| 340 | static void mg_sendnsreq(struct mg_connection *c, struct mg_str *name, int ms, |
| 341 | struct mg_dns *dnsc, bool ipv6) { |
| 342 | struct dns_data *d = NULL; |
| 343 | if (dnsc->url == NULL) { |
| 344 | mg_error(c, "DNS server URL is NULL. Call mg_mgr_init()" ); |
| 345 | } else if (dnsc->c == NULL) { |
| 346 | dnsc->c = mg_connect(c->mgr, dnsc->url, NULL, NULL); |
| 347 | if (dnsc->c != NULL) { |
| 348 | dnsc->c->pfn = dns_cb; |
| 349 | // dnsc->c->is_hexdumping = 1; |
| 350 | } |
| 351 | } |
| 352 | if (dnsc->c == NULL) { |
| 353 | mg_error(c, "resolver" ); |
| 354 | } else if ((d = (struct dns_data *) calloc(1, sizeof(*d))) == NULL) { |
| 355 | mg_error(c, "resolve OOM" ); |
| 356 | } else { |
| 357 | struct dns_data *reqs = (struct dns_data *) c->mgr->active_dns_requests; |
| 358 | d->txnid = reqs ? (uint16_t) (reqs->txnid + 1) : 1; |
| 359 | d->next = (struct dns_data *) c->mgr->active_dns_requests; |
| 360 | c->mgr->active_dns_requests = d; |
| 361 | d->expire = mg_millis() + (uint64_t) ms; |
| 362 | d->c = c; |
| 363 | c->is_resolving = 1; |
| 364 | MG_VERBOSE(("%lu resolving %.*s @ %s, txnid %hu" , c->id, (int) name->len, |
| 365 | name->ptr, dnsc->url, d->txnid)); |
| 366 | if (!mg_dns_send(dnsc->c, name, d->txnid, ipv6)) { |
| 367 | mg_error(dnsc->c, "DNS send" ); |
| 368 | } |
| 369 | } |
| 370 | } |
| 371 | |
| 372 | void mg_resolve(struct mg_connection *c, const char *url) { |
| 373 | struct mg_str host = mg_url_host(url); |
| 374 | c->rem.port = mg_htons(mg_url_port(url)); |
| 375 | if (mg_aton(host, &c->rem)) { |
| 376 | // host is an IP address, do not fire name resolution |
| 377 | mg_connect_resolved(c); |
| 378 | } else { |
| 379 | // host is not an IP, send DNS resolution request |
| 380 | struct mg_dns *dns = c->mgr->use_dns6 ? &c->mgr->dns6 : &c->mgr->dns4; |
| 381 | mg_sendnsreq(c, &host, c->mgr->dnstimeout, dns, c->mgr->use_dns6); |
| 382 | } |
| 383 | } |
| 384 | |
| 385 | #ifdef MG_ENABLE_LINES |
| 386 | #line 1 "src/event.c" |
| 387 | #endif |
| 388 | |
| 389 | |
| 390 | |
| 391 | |
| 392 | |
| 393 | void mg_call(struct mg_connection *c, int ev, void *ev_data) { |
| 394 | // Run user-defined handler first, in order to give it an ability |
| 395 | // to intercept processing (e.g. clean input buffer) before the |
| 396 | // protocol handler kicks in |
| 397 | if (c->fn != NULL) c->fn(c, ev, ev_data, c->fn_data); |
| 398 | if (c->pfn != NULL) c->pfn(c, ev, ev_data, c->pfn_data); |
| 399 | } |
| 400 | |
| 401 | void mg_error(struct mg_connection *c, const char *fmt, ...) { |
| 402 | char buf[64]; |
| 403 | va_list ap; |
| 404 | va_start(ap, fmt); |
| 405 | mg_vsnprintf(buf, sizeof(buf), fmt, &ap); |
| 406 | va_end(ap); |
| 407 | MG_ERROR(("%lu %p %s" , c->id, c->fd, buf)); |
| 408 | c->is_closing = 1; // Set is_closing before sending MG_EV_CALL |
| 409 | mg_call(c, MG_EV_ERROR, buf); // Let user handler to override it |
| 410 | } |
| 411 | |
| 412 | #ifdef MG_ENABLE_LINES |
| 413 | #line 1 "src/fmt.c" |
| 414 | #endif |
| 415 | |
| 416 | |
| 417 | |
| 418 | |
| 419 | static bool is_digit(int c) { |
| 420 | return c >= '0' && c <= '9'; |
| 421 | } |
| 422 | |
| 423 | static int addexp(char *buf, int e, int sign) { |
| 424 | int n = 0; |
| 425 | buf[n++] = 'e'; |
| 426 | buf[n++] = (char) sign; |
| 427 | if (e > 400) return 0; |
| 428 | if (e < 10) buf[n++] = '0'; |
| 429 | if (e >= 100) buf[n++] = (char) (e / 100 + '0'), e -= 100 * (e / 100); |
| 430 | if (e >= 10) buf[n++] = (char) (e / 10 + '0'), e -= 10 * (e / 10); |
| 431 | buf[n++] = (char) (e + '0'); |
| 432 | return n; |
| 433 | } |
| 434 | |
| 435 | static int xisinf(double x) { |
| 436 | union { |
| 437 | double f; |
| 438 | uint64_t u; |
| 439 | } ieee754 = {x}; |
| 440 | return ((unsigned) (ieee754.u >> 32) & 0x7fffffff) == 0x7ff00000 && |
| 441 | ((unsigned) ieee754.u == 0); |
| 442 | } |
| 443 | |
| 444 | static int xisnan(double x) { |
| 445 | union { |
| 446 | double f; |
| 447 | uint64_t u; |
| 448 | } ieee754 = {x}; |
| 449 | return ((unsigned) (ieee754.u >> 32) & 0x7fffffff) + |
| 450 | ((unsigned) ieee754.u != 0) > |
| 451 | 0x7ff00000; |
| 452 | } |
| 453 | |
| 454 | static size_t mg_dtoa(char *dst, size_t dstlen, double d, int width, bool tz) { |
| 455 | char buf[40]; |
| 456 | int i, s = 0, n = 0, e = 0; |
| 457 | double t, mul, saved; |
| 458 | if (d == 0.0) return mg_snprintf(dst, dstlen, "%s" , "0" ); |
| 459 | if (xisinf(d)) return mg_snprintf(dst, dstlen, "%s" , d > 0 ? "inf" : "-inf" ); |
| 460 | if (xisnan(d)) return mg_snprintf(dst, dstlen, "%s" , "nan" ); |
| 461 | if (d < 0.0) d = -d, buf[s++] = '-'; |
| 462 | |
| 463 | // Round |
| 464 | saved = d; |
| 465 | mul = 1.0; |
| 466 | while (d >= 10.0 && d / mul >= 10.0) mul *= 10.0; |
| 467 | while (d <= 1.0 && d / mul <= 1.0) mul /= 10.0; |
| 468 | for (i = 0, t = mul * 5; i < width; i++) t /= 10.0; |
| 469 | d += t; |
| 470 | // Calculate exponent, and 'mul' for scientific representation |
| 471 | mul = 1.0; |
| 472 | while (d >= 10.0 && d / mul >= 10.0) mul *= 10.0, e++; |
| 473 | while (d < 1.0 && d / mul < 1.0) mul /= 10.0, e--; |
| 474 | // printf(" --> %g %d %g %g\n", saved, e, t, mul); |
| 475 | |
| 476 | if (e >= width && width > 1) { |
| 477 | n = (int) mg_dtoa(buf, sizeof(buf), saved / mul, width, tz); |
| 478 | // printf(" --> %.*g %d [%.*s]\n", 10, d / t, e, n, buf); |
| 479 | n += addexp(buf + s + n, e, '+'); |
| 480 | return mg_snprintf(dst, dstlen, "%.*s" , n, buf); |
| 481 | } else if (e <= -width && width > 1) { |
| 482 | n = (int) mg_dtoa(buf, sizeof(buf), saved / mul, width, tz); |
| 483 | // printf(" --> %.*g %d [%.*s]\n", 10, d / mul, e, n, buf); |
| 484 | n += addexp(buf + s + n, -e, '-'); |
| 485 | return mg_snprintf(dst, dstlen, "%.*s" , n, buf); |
| 486 | } else { |
| 487 | for (i = 0, t = mul; t >= 1.0 && s + n < (int) sizeof(buf); i++) { |
| 488 | int ch = (int) (d / t); |
| 489 | if (n > 0 || ch > 0) buf[s + n++] = (char) (ch + '0'); |
| 490 | d -= ch * t; |
| 491 | t /= 10.0; |
| 492 | } |
| 493 | // printf(" --> [%g] -> %g %g (%d) [%.*s]\n", saved, d, t, n, s + n, buf); |
| 494 | if (n == 0) buf[s++] = '0'; |
| 495 | while (t >= 1.0 && n + s < (int) sizeof(buf)) buf[n++] = '0', t /= 10.0; |
| 496 | if (s + n < (int) sizeof(buf)) buf[n + s++] = '.'; |
| 497 | // printf(" 1--> [%g] -> [%.*s]\n", saved, s + n, buf); |
| 498 | for (i = 0, t = 0.1; s + n < (int) sizeof(buf) && n < width; i++) { |
| 499 | int ch = (int) (d / t); |
| 500 | buf[s + n++] = (char) (ch + '0'); |
| 501 | d -= ch * t; |
| 502 | t /= 10.0; |
| 503 | } |
| 504 | } |
| 505 | while (tz && n > 0 && buf[s + n - 1] == '0') n--; // Trim trailing zeroes |
| 506 | if (n > 0 && buf[s + n - 1] == '.') n--; // Trim trailing dot |
| 507 | n += s; |
| 508 | if (n >= (int) sizeof(buf)) n = (int) sizeof(buf) - 1; |
| 509 | buf[n] = '\0'; |
| 510 | return mg_snprintf(dst, dstlen, "%s" , buf); |
| 511 | } |
| 512 | |
| 513 | static size_t mg_lld(char *buf, int64_t val, bool is_signed, bool is_hex) { |
| 514 | const char *letters = "0123456789abcdef" ; |
| 515 | uint64_t v = (uint64_t) val; |
| 516 | size_t s = 0, n, i; |
| 517 | if (is_signed && val < 0) buf[s++] = '-', v = (uint64_t) (-val); |
| 518 | // This loop prints a number in reverse order. I guess this is because we |
| 519 | // write numbers from right to left: least significant digit comes last. |
| 520 | // Maybe because we use Arabic numbers, and Arabs write RTL? |
| 521 | if (is_hex) { |
| 522 | for (n = 0; v; v >>= 4) buf[s + n++] = letters[v & 15]; |
| 523 | } else { |
| 524 | for (n = 0; v; v /= 10) buf[s + n++] = letters[v % 10]; |
| 525 | } |
| 526 | // Reverse a string |
| 527 | for (i = 0; i < n / 2; i++) { |
| 528 | char t = buf[s + i]; |
| 529 | buf[s + i] = buf[s + n - i - 1], buf[s + n - i - 1] = t; |
| 530 | } |
| 531 | if (val == 0) buf[n++] = '0'; // Handle special case |
| 532 | return n + s; |
| 533 | } |
| 534 | |
| 535 | static size_t scpy(void (*out)(char, void *), void *ptr, char *buf, |
| 536 | size_t len) { |
| 537 | size_t i = 0; |
| 538 | while (i < len && buf[i] != '\0') out(buf[i++], ptr); |
| 539 | return i; |
| 540 | } |
| 541 | |
| 542 | size_t mg_xprintf(void (*out)(char, void *), void *ptr, const char *fmt, ...) { |
| 543 | size_t len = 0; |
| 544 | va_list ap; |
| 545 | va_start(ap, fmt); |
| 546 | len = mg_vxprintf(out, ptr, fmt, &ap); |
| 547 | va_end(ap); |
| 548 | return len; |
| 549 | } |
| 550 | |
| 551 | size_t mg_vxprintf(void (*out)(char, void *), void *param, const char *fmt, |
| 552 | va_list *ap) { |
| 553 | size_t i = 0, n = 0; |
| 554 | while (fmt[i] != '\0') { |
| 555 | if (fmt[i] == '%') { |
| 556 | size_t j, k, x = 0, is_long = 0, w = 0 /* width */, pr = ~0U /* prec */; |
| 557 | char pad = ' ', minus = 0, c = fmt[++i]; |
| 558 | if (c == '#') x++, c = fmt[++i]; |
| 559 | if (c == '-') minus++, c = fmt[++i]; |
| 560 | if (c == '0') pad = '0', c = fmt[++i]; |
| 561 | while (is_digit(c)) w *= 10, w += (size_t) (c - '0'), c = fmt[++i]; |
| 562 | if (c == '.') { |
| 563 | c = fmt[++i]; |
| 564 | if (c == '*') { |
| 565 | pr = (size_t) va_arg(*ap, int); |
| 566 | c = fmt[++i]; |
| 567 | } else { |
| 568 | pr = 0; |
| 569 | while (is_digit(c)) pr *= 10, pr += (size_t) (c - '0'), c = fmt[++i]; |
| 570 | } |
| 571 | } |
| 572 | while (c == 'h') c = fmt[++i]; // Treat h and hh as int |
| 573 | if (c == 'l') { |
| 574 | is_long++, c = fmt[++i]; |
| 575 | if (c == 'l') is_long++, c = fmt[++i]; |
| 576 | } |
| 577 | if (c == 'p') x = 1, is_long = 1; |
| 578 | if (c == 'd' || c == 'u' || c == 'x' || c == 'X' || c == 'p' || |
| 579 | c == 'g' || c == 'f') { |
| 580 | bool s = (c == 'd'), h = (c == 'x' || c == 'X' || c == 'p'); |
| 581 | char tmp[40]; |
| 582 | size_t xl = x ? 2 : 0; |
| 583 | if (c == 'g' || c == 'f') { |
| 584 | double v = va_arg(*ap, double); |
| 585 | if (pr == ~0U) pr = 6; |
| 586 | k = mg_dtoa(tmp, sizeof(tmp), v, (int) pr, c == 'g'); |
| 587 | } else if (is_long == 2) { |
| 588 | int64_t v = va_arg(*ap, int64_t); |
| 589 | k = mg_lld(tmp, v, s, h); |
| 590 | } else if (is_long == 1) { |
| 591 | long v = va_arg(*ap, long); |
| 592 | k = mg_lld(tmp, s ? (int64_t) v : (int64_t) (unsigned long) v, s, h); |
| 593 | } else { |
| 594 | int v = va_arg(*ap, int); |
| 595 | k = mg_lld(tmp, s ? (int64_t) v : (int64_t) (unsigned) v, s, h); |
| 596 | } |
| 597 | for (j = 0; j < xl && w > 0; j++) w--; |
| 598 | for (j = 0; pad == ' ' && !minus && k < w && j + k < w; j++) |
| 599 | n += scpy(out, param, &pad, 1); |
| 600 | n += scpy(out, param, (char *) "0x" , xl); |
| 601 | for (j = 0; pad == '0' && k < w && j + k < w; j++) |
| 602 | n += scpy(out, param, &pad, 1); |
| 603 | n += scpy(out, param, tmp, k); |
| 604 | for (j = 0; pad == ' ' && minus && k < w && j + k < w; j++) |
| 605 | n += scpy(out, param, &pad, 1); |
| 606 | } else if (c == 'm' || c == 'M') { |
| 607 | mg_pm_t f = va_arg(*ap, mg_pm_t); |
| 608 | if (c == 'm') out('"', param); |
| 609 | n += f(out, param, ap); |
| 610 | if (c == 'm') n += 2, out('"', param); |
| 611 | } else if (c == 'c') { |
| 612 | int ch = va_arg(*ap, int); |
| 613 | out((char) ch, param); |
| 614 | n++; |
| 615 | } else if (c == 's') { |
| 616 | char *p = va_arg(*ap, char *); |
| 617 | if (pr == ~0U) pr = p == NULL ? 0 : strlen(p); |
| 618 | for (j = 0; !minus && pr < w && j + pr < w; j++) |
| 619 | n += scpy(out, param, &pad, 1); |
| 620 | n += scpy(out, param, p, pr); |
| 621 | for (j = 0; minus && pr < w && j + pr < w; j++) |
| 622 | n += scpy(out, param, &pad, 1); |
| 623 | } else if (c == '%') { |
| 624 | out('%', param); |
| 625 | n++; |
| 626 | } else { |
| 627 | out('%', param); |
| 628 | out(c, param); |
| 629 | n += 2; |
| 630 | } |
| 631 | i++; |
| 632 | } else { |
| 633 | out(fmt[i], param), n++, i++; |
| 634 | } |
| 635 | } |
| 636 | return n; |
| 637 | } |
| 638 | |
| 639 | #ifdef MG_ENABLE_LINES |
| 640 | #line 1 "src/fs.c" |
| 641 | #endif |
| 642 | |
| 643 | |
| 644 | |
| 645 | struct mg_fd *mg_fs_open(struct mg_fs *fs, const char *path, int flags) { |
| 646 | struct mg_fd *fd = (struct mg_fd *) calloc(1, sizeof(*fd)); |
| 647 | if (fd != NULL) { |
| 648 | fd->fd = fs->op(path, flags); |
| 649 | fd->fs = fs; |
| 650 | if (fd->fd == NULL) { |
| 651 | free(fd); |
| 652 | fd = NULL; |
| 653 | } |
| 654 | } |
| 655 | return fd; |
| 656 | } |
| 657 | |
| 658 | void mg_fs_close(struct mg_fd *fd) { |
| 659 | if (fd != NULL) { |
| 660 | fd->fs->cl(fd->fd); |
| 661 | free(fd); |
| 662 | } |
| 663 | } |
| 664 | |
| 665 | char *mg_file_read(struct mg_fs *fs, const char *path, size_t *sizep) { |
| 666 | struct mg_fd *fd; |
| 667 | char *data = NULL; |
| 668 | size_t size = 0; |
| 669 | fs->st(path, &size, NULL); |
| 670 | if ((fd = mg_fs_open(fs, path, MG_FS_READ)) != NULL) { |
| 671 | data = (char *) calloc(1, size + 1); |
| 672 | if (data != NULL) { |
| 673 | if (fs->rd(fd->fd, data, size) != size) { |
| 674 | free(data); |
| 675 | data = NULL; |
| 676 | } else { |
| 677 | data[size] = '\0'; |
| 678 | if (sizep != NULL) *sizep = size; |
| 679 | } |
| 680 | } |
| 681 | mg_fs_close(fd); |
| 682 | } |
| 683 | return data; |
| 684 | } |
| 685 | |
| 686 | bool mg_file_write(struct mg_fs *fs, const char *path, const void *buf, |
| 687 | size_t len) { |
| 688 | bool result = false; |
| 689 | struct mg_fd *fd; |
| 690 | char tmp[MG_PATH_MAX]; |
| 691 | mg_snprintf(tmp, sizeof(tmp), "%s..%d" , path, rand()); |
| 692 | if ((fd = mg_fs_open(fs, tmp, MG_FS_WRITE)) != NULL) { |
| 693 | result = fs->wr(fd->fd, buf, len) == len; |
| 694 | mg_fs_close(fd); |
| 695 | if (result) { |
| 696 | fs->rm(path); |
| 697 | fs->mv(tmp, path); |
| 698 | } else { |
| 699 | fs->rm(tmp); |
| 700 | } |
| 701 | } |
| 702 | return result; |
| 703 | } |
| 704 | |
| 705 | bool mg_file_printf(struct mg_fs *fs, const char *path, const char *fmt, ...) { |
| 706 | va_list ap; |
| 707 | char *data; |
| 708 | bool result = false; |
| 709 | va_start(ap, fmt); |
| 710 | data = mg_vmprintf(fmt, &ap); |
| 711 | va_end(ap); |
| 712 | result = mg_file_write(fs, path, data, strlen(data)); |
| 713 | free(data); |
| 714 | return result; |
| 715 | } |
| 716 | |
| 717 | #ifdef MG_ENABLE_LINES |
| 718 | #line 1 "src/fs_fat.c" |
| 719 | #endif |
| 720 | |
| 721 | |
| 722 | |
| 723 | #if MG_ENABLE_FATFS |
| 724 | #include <ff.h> |
| 725 | |
| 726 | static int mg_days_from_epoch(int y, int m, int d) { |
| 727 | y -= m <= 2; |
| 728 | int era = y / 400; |
| 729 | int yoe = y - era * 400; |
| 730 | int doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1; |
| 731 | int doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; |
| 732 | return era * 146097 + doe - 719468; |
| 733 | } |
| 734 | |
| 735 | static time_t mg_timegm(const struct tm *t) { |
| 736 | int year = t->tm_year + 1900; |
| 737 | int month = t->tm_mon; // 0-11 |
| 738 | if (month > 11) { |
| 739 | year += month / 12; |
| 740 | month %= 12; |
| 741 | } else if (month < 0) { |
| 742 | int years_diff = (11 - month) / 12; |
| 743 | year -= years_diff; |
| 744 | month += 12 * years_diff; |
| 745 | } |
| 746 | int x = mg_days_from_epoch(year, month + 1, t->tm_mday); |
| 747 | return 60 * (60 * (24L * x + t->tm_hour) + t->tm_min) + t->tm_sec; |
| 748 | } |
| 749 | |
| 750 | static time_t ff_time_to_epoch(uint16_t fdate, uint16_t ftime) { |
| 751 | struct tm tm; |
| 752 | memset(&tm, 0, sizeof(struct tm)); |
| 753 | tm.tm_sec = (ftime << 1) & 0x3e; |
| 754 | tm.tm_min = ((ftime >> 5) & 0x3f); |
| 755 | tm.tm_hour = ((ftime >> 11) & 0x1f); |
| 756 | tm.tm_mday = (fdate & 0x1f); |
| 757 | tm.tm_mon = ((fdate >> 5) & 0x0f) - 1; |
| 758 | tm.tm_year = ((fdate >> 9) & 0x7f) + 80; |
| 759 | return mg_timegm(&tm); |
| 760 | } |
| 761 | |
| 762 | static int ff_stat(const char *path, size_t *size, time_t *mtime) { |
| 763 | FILINFO fi; |
| 764 | if (path[0] == '\0') { |
| 765 | if (size) *size = 0; |
| 766 | if (mtime) *mtime = 0; |
| 767 | return MG_FS_DIR; |
| 768 | } else if (f_stat(path, &fi) == 0) { |
| 769 | if (size) *size = (size_t) fi.fsize; |
| 770 | if (mtime) *mtime = ff_time_to_epoch(fi.fdate, fi.ftime); |
| 771 | return MG_FS_READ | MG_FS_WRITE | ((fi.fattrib & AM_DIR) ? MG_FS_DIR : 0); |
| 772 | } else { |
| 773 | return 0; |
| 774 | } |
| 775 | } |
| 776 | |
| 777 | static void ff_list(const char *dir, void (*fn)(const char *, void *), |
| 778 | void *userdata) { |
| 779 | DIR d; |
| 780 | FILINFO fi; |
| 781 | if (f_opendir(&d, dir) == FR_OK) { |
| 782 | while (f_readdir(&d, &fi) == FR_OK && fi.fname[0] != '\0') { |
| 783 | if (!strcmp(fi.fname, "." ) || !strcmp(fi.fname, ".." )) continue; |
| 784 | fn(fi.fname, userdata); |
| 785 | } |
| 786 | f_closedir(&d); |
| 787 | } |
| 788 | } |
| 789 | |
| 790 | static void *ff_open(const char *path, int flags) { |
| 791 | FIL f; |
| 792 | unsigned char mode = FA_READ; |
| 793 | if (flags & MG_FS_WRITE) mode |= FA_WRITE | FA_OPEN_ALWAYS | FA_OPEN_APPEND; |
| 794 | if (f_open(&f, path, mode) == 0) { |
| 795 | FIL *fp; |
| 796 | if ((fp = calloc(1, sizeof(*fp))) != NULL) { |
| 797 | memcpy(fp, &f, sizeof(*fp)); |
| 798 | return fp; |
| 799 | } |
| 800 | } |
| 801 | return NULL; |
| 802 | } |
| 803 | |
| 804 | static void ff_close(void *fp) { |
| 805 | if (fp != NULL) { |
| 806 | f_close((FIL *) fp); |
| 807 | free(fp); |
| 808 | } |
| 809 | } |
| 810 | |
| 811 | static size_t ff_read(void *fp, void *buf, size_t len) { |
| 812 | UINT n = 0, misalign = ((size_t) buf) & 3; |
| 813 | if (misalign) { |
| 814 | char aligned[4]; |
| 815 | f_read((FIL *) fp, aligned, len > misalign ? misalign : len, &n); |
| 816 | memcpy(buf, aligned, n); |
| 817 | } else { |
| 818 | f_read((FIL *) fp, buf, len, &n); |
| 819 | } |
| 820 | return n; |
| 821 | } |
| 822 | |
| 823 | static size_t ff_write(void *fp, const void *buf, size_t len) { |
| 824 | UINT n = 0; |
| 825 | return f_write((FIL *) fp, (char *) buf, len, &n) == FR_OK ? n : 0; |
| 826 | } |
| 827 | |
| 828 | static size_t ff_seek(void *fp, size_t offset) { |
| 829 | f_lseek((FIL *) fp, offset); |
| 830 | return offset; |
| 831 | } |
| 832 | |
| 833 | static bool ff_rename(const char *from, const char *to) { |
| 834 | return f_rename(from, to) == FR_OK; |
| 835 | } |
| 836 | |
| 837 | static bool ff_remove(const char *path) { |
| 838 | return f_unlink(path) == FR_OK; |
| 839 | } |
| 840 | |
| 841 | static bool ff_mkdir(const char *path) { |
| 842 | return f_mkdir(path) == FR_OK; |
| 843 | } |
| 844 | |
| 845 | struct mg_fs mg_fs_fat = {ff_stat, ff_list, ff_open, ff_close, ff_read, |
| 846 | ff_write, ff_seek, ff_rename, ff_remove, ff_mkdir}; |
| 847 | #endif |
| 848 | |
| 849 | #ifdef MG_ENABLE_LINES |
| 850 | #line 1 "src/fs_packed.c" |
| 851 | #endif |
| 852 | |
| 853 | |
| 854 | |
| 855 | |
| 856 | struct packed_file { |
| 857 | const char *data; |
| 858 | size_t size; |
| 859 | size_t pos; |
| 860 | }; |
| 861 | |
| 862 | const char *mg_unpack(const char *path, size_t *size, time_t *mtime); |
| 863 | const char *mg_unlist(size_t no); |
| 864 | |
| 865 | #if MG_ENABLE_PACKED_FS |
| 866 | #else |
| 867 | const char *mg_unpack(const char *path, size_t *size, time_t *mtime) { |
| 868 | (void) path, (void) size, (void) mtime; |
| 869 | return NULL; |
| 870 | } |
| 871 | const char *mg_unlist(size_t no) { |
| 872 | (void) no; |
| 873 | return NULL; |
| 874 | } |
| 875 | #endif |
| 876 | |
| 877 | static int is_dir_prefix(const char *prefix, size_t n, const char *path) { |
| 878 | // MG_INFO(("[%.*s] [%s] %c", (int) n, prefix, path, path[n])); |
| 879 | return n < strlen(path) && strncmp(prefix, path, n) == 0 && |
| 880 | (n == 0 || path[n] == '/' || path[n - 1] == '/'); |
| 881 | } |
| 882 | |
| 883 | static int packed_stat(const char *path, size_t *size, time_t *mtime) { |
| 884 | const char *p; |
| 885 | size_t i, n = strlen(path); |
| 886 | if (mg_unpack(path, size, mtime)) return MG_FS_READ; // Regular file |
| 887 | // Scan all files. If `path` is a dir prefix for any of them, it's a dir |
| 888 | for (i = 0; (p = mg_unlist(i)) != NULL; i++) { |
| 889 | if (is_dir_prefix(path, n, p)) return MG_FS_DIR; |
| 890 | } |
| 891 | return 0; |
| 892 | } |
| 893 | |
| 894 | static void packed_list(const char *dir, void (*fn)(const char *, void *), |
| 895 | void *userdata) { |
| 896 | char buf[MG_PATH_MAX], tmp[sizeof(buf)]; |
| 897 | const char *path, *begin, *end; |
| 898 | size_t i, n = strlen(dir); |
| 899 | tmp[0] = '\0'; // Previously listed entry |
| 900 | for (i = 0; (path = mg_unlist(i)) != NULL; i++) { |
| 901 | if (!is_dir_prefix(dir, n, path)) continue; |
| 902 | begin = &path[n + 1]; |
| 903 | end = strchr(begin, '/'); |
| 904 | if (end == NULL) end = begin + strlen(begin); |
| 905 | mg_snprintf(buf, sizeof(buf), "%.*s" , (int) (end - begin), begin); |
| 906 | buf[sizeof(buf) - 1] = '\0'; |
| 907 | // If this entry has been already listed, skip |
| 908 | // NOTE: we're assuming that file list is sorted alphabetically |
| 909 | if (strcmp(buf, tmp) == 0) continue; |
| 910 | fn(buf, userdata); // Not yet listed, call user function |
| 911 | strcpy(tmp, buf); // And save this entry as listed |
| 912 | } |
| 913 | } |
| 914 | |
| 915 | static void *packed_open(const char *path, int flags) { |
| 916 | size_t size = 0; |
| 917 | const char *data = mg_unpack(path, &size, NULL); |
| 918 | struct packed_file *fp = NULL; |
| 919 | if (data == NULL) return NULL; |
| 920 | if (flags & MG_FS_WRITE) return NULL; |
| 921 | if ((fp = (struct packed_file *) calloc(1, sizeof(*fp))) != NULL) { |
| 922 | fp->size = size; |
| 923 | fp->data = data; |
| 924 | } |
| 925 | return (void *) fp; |
| 926 | } |
| 927 | |
| 928 | static void packed_close(void *fp) { |
| 929 | if (fp != NULL) free(fp); |
| 930 | } |
| 931 | |
| 932 | static size_t packed_read(void *fd, void *buf, size_t len) { |
| 933 | struct packed_file *fp = (struct packed_file *) fd; |
| 934 | if (fp->pos + len > fp->size) len = fp->size - fp->pos; |
| 935 | memcpy(buf, &fp->data[fp->pos], len); |
| 936 | fp->pos += len; |
| 937 | return len; |
| 938 | } |
| 939 | |
| 940 | static size_t packed_write(void *fd, const void *buf, size_t len) { |
| 941 | (void) fd, (void) buf, (void) len; |
| 942 | return 0; |
| 943 | } |
| 944 | |
| 945 | static size_t packed_seek(void *fd, size_t offset) { |
| 946 | struct packed_file *fp = (struct packed_file *) fd; |
| 947 | fp->pos = offset; |
| 948 | if (fp->pos > fp->size) fp->pos = fp->size; |
| 949 | return fp->pos; |
| 950 | } |
| 951 | |
| 952 | static bool packed_rename(const char *from, const char *to) { |
| 953 | (void) from, (void) to; |
| 954 | return false; |
| 955 | } |
| 956 | |
| 957 | static bool packed_remove(const char *path) { |
| 958 | (void) path; |
| 959 | return false; |
| 960 | } |
| 961 | |
| 962 | static bool packed_mkdir(const char *path) { |
| 963 | (void) path; |
| 964 | return false; |
| 965 | } |
| 966 | |
| 967 | struct mg_fs mg_fs_packed = { |
| 968 | packed_stat, packed_list, packed_open, packed_close, packed_read, |
| 969 | packed_write, packed_seek, packed_rename, packed_remove, packed_mkdir}; |
| 970 | |
| 971 | #ifdef MG_ENABLE_LINES |
| 972 | #line 1 "src/fs_posix.c" |
| 973 | #endif |
| 974 | |
| 975 | |
| 976 | #if MG_ENABLE_FILE |
| 977 | |
| 978 | #ifndef MG_STAT_STRUCT |
| 979 | #define MG_STAT_STRUCT stat |
| 980 | #endif |
| 981 | |
| 982 | #ifndef MG_STAT_FUNC |
| 983 | #define MG_STAT_FUNC stat |
| 984 | #endif |
| 985 | |
| 986 | static int p_stat(const char *path, size_t *size, time_t *mtime) { |
| 987 | #if !defined(S_ISDIR) |
| 988 | MG_ERROR(("stat() API is not supported. %p %p %p" , path, size, mtime)); |
| 989 | return 0; |
| 990 | #else |
| 991 | #if MG_ARCH == MG_ARCH_WIN32 |
| 992 | struct _stati64 st; |
| 993 | wchar_t tmp[MG_PATH_MAX]; |
| 994 | MultiByteToWideChar(CP_UTF8, 0, path, -1, tmp, sizeof(tmp) / sizeof(tmp[0])); |
| 995 | if (_wstati64(tmp, &st) != 0) return 0; |
| 996 | // If path is a symlink, windows reports 0 in st.st_size. |
| 997 | // Get a real file size by opening it and jumping to the end |
| 998 | if (st.st_size == 0 && (st.st_mode & _S_IFREG)) { |
| 999 | FILE *fp = _wfopen(tmp, L"rb" ); |
| 1000 | if (fp != NULL) { |
| 1001 | fseek(fp, 0, SEEK_END); |
| 1002 | if (ftell(fp) > 0) st.st_size = ftell(fp); // Use _ftelli64 on win10+ |
| 1003 | fclose(fp); |
| 1004 | } |
| 1005 | } |
| 1006 | #else |
| 1007 | struct MG_STAT_STRUCT st; |
| 1008 | if (MG_STAT_FUNC(path, &st) != 0) return 0; |
| 1009 | #endif |
| 1010 | if (size) *size = (size_t) st.st_size; |
| 1011 | if (mtime) *mtime = st.st_mtime; |
| 1012 | return MG_FS_READ | MG_FS_WRITE | (S_ISDIR(st.st_mode) ? MG_FS_DIR : 0); |
| 1013 | #endif |
| 1014 | } |
| 1015 | |
| 1016 | #if MG_ARCH == MG_ARCH_WIN32 |
| 1017 | struct dirent { |
| 1018 | char d_name[MAX_PATH]; |
| 1019 | }; |
| 1020 | |
| 1021 | typedef struct win32_dir { |
| 1022 | HANDLE handle; |
| 1023 | WIN32_FIND_DATAW info; |
| 1024 | struct dirent result; |
| 1025 | } DIR; |
| 1026 | |
| 1027 | int gettimeofday(struct timeval *tv, void *tz) { |
| 1028 | FILETIME ft; |
| 1029 | unsigned __int64 tmpres = 0; |
| 1030 | |
| 1031 | if (tv != NULL) { |
| 1032 | GetSystemTimeAsFileTime(&ft); |
| 1033 | tmpres |= ft.dwHighDateTime; |
| 1034 | tmpres <<= 32; |
| 1035 | tmpres |= ft.dwLowDateTime; |
| 1036 | tmpres /= 10; // convert into microseconds |
| 1037 | tmpres -= (int64_t) 11644473600000000; |
| 1038 | tv->tv_sec = (long) (tmpres / 1000000UL); |
| 1039 | tv->tv_usec = (long) (tmpres % 1000000UL); |
| 1040 | } |
| 1041 | (void) tz; |
| 1042 | return 0; |
| 1043 | } |
| 1044 | |
| 1045 | static int to_wchar(const char *path, wchar_t *wbuf, size_t wbuf_len) { |
| 1046 | int ret; |
| 1047 | char buf[MAX_PATH * 2], buf2[MAX_PATH * 2], *p; |
| 1048 | strncpy(buf, path, sizeof(buf)); |
| 1049 | buf[sizeof(buf) - 1] = '\0'; |
| 1050 | // Trim trailing slashes. Leave backslash for paths like "X:\" |
| 1051 | p = buf + strlen(buf) - 1; |
| 1052 | while (p > buf && p[-1] != ':' && (p[0] == '\\' || p[0] == '/')) *p-- = '\0'; |
| 1053 | memset(wbuf, 0, wbuf_len * sizeof(wchar_t)); |
| 1054 | ret = MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len); |
| 1055 | // Convert back to Unicode. If doubly-converted string does not match the |
| 1056 | // original, something is fishy, reject. |
| 1057 | WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2), |
| 1058 | NULL, NULL); |
| 1059 | if (strcmp(buf, buf2) != 0) { |
| 1060 | wbuf[0] = L'\0'; |
| 1061 | ret = 0; |
| 1062 | } |
| 1063 | return ret; |
| 1064 | } |
| 1065 | |
| 1066 | DIR *opendir(const char *name) { |
| 1067 | DIR *d = NULL; |
| 1068 | wchar_t wpath[MAX_PATH]; |
| 1069 | DWORD attrs; |
| 1070 | |
| 1071 | if (name == NULL) { |
| 1072 | SetLastError(ERROR_BAD_ARGUMENTS); |
| 1073 | } else if ((d = (DIR *) calloc(1, sizeof(*d))) == NULL) { |
| 1074 | SetLastError(ERROR_NOT_ENOUGH_MEMORY); |
| 1075 | } else { |
| 1076 | to_wchar(name, wpath, sizeof(wpath) / sizeof(wpath[0])); |
| 1077 | attrs = GetFileAttributesW(wpath); |
| 1078 | if (attrs != 0Xffffffff && (attrs & FILE_ATTRIBUTE_DIRECTORY)) { |
| 1079 | (void) wcscat(wpath, L"\\*" ); |
| 1080 | d->handle = FindFirstFileW(wpath, &d->info); |
| 1081 | d->result.d_name[0] = '\0'; |
| 1082 | } else { |
| 1083 | free(d); |
| 1084 | d = NULL; |
| 1085 | } |
| 1086 | } |
| 1087 | return d; |
| 1088 | } |
| 1089 | |
| 1090 | int closedir(DIR *d) { |
| 1091 | int result = 0; |
| 1092 | if (d != NULL) { |
| 1093 | if (d->handle != INVALID_HANDLE_VALUE) |
| 1094 | result = FindClose(d->handle) ? 0 : -1; |
| 1095 | free(d); |
| 1096 | } else { |
| 1097 | result = -1; |
| 1098 | SetLastError(ERROR_BAD_ARGUMENTS); |
| 1099 | } |
| 1100 | return result; |
| 1101 | } |
| 1102 | |
| 1103 | struct dirent *readdir(DIR *d) { |
| 1104 | struct dirent *result = NULL; |
| 1105 | if (d != NULL) { |
| 1106 | memset(&d->result, 0, sizeof(d->result)); |
| 1107 | if (d->handle != INVALID_HANDLE_VALUE) { |
| 1108 | result = &d->result; |
| 1109 | WideCharToMultiByte(CP_UTF8, 0, d->info.cFileName, -1, result->d_name, |
| 1110 | sizeof(result->d_name), NULL, NULL); |
| 1111 | if (!FindNextFileW(d->handle, &d->info)) { |
| 1112 | FindClose(d->handle); |
| 1113 | d->handle = INVALID_HANDLE_VALUE; |
| 1114 | } |
| 1115 | } else { |
| 1116 | SetLastError(ERROR_FILE_NOT_FOUND); |
| 1117 | } |
| 1118 | } else { |
| 1119 | SetLastError(ERROR_BAD_ARGUMENTS); |
| 1120 | } |
| 1121 | return result; |
| 1122 | } |
| 1123 | #endif |
| 1124 | |
| 1125 | static void p_list(const char *dir, void (*fn)(const char *, void *), |
| 1126 | void *userdata) { |
| 1127 | #if MG_ENABLE_DIRLIST |
| 1128 | struct dirent *dp; |
| 1129 | DIR *dirp; |
| 1130 | if ((dirp = (opendir(dir))) == NULL) return; |
| 1131 | while ((dp = readdir(dirp)) != NULL) { |
| 1132 | if (!strcmp(dp->d_name, "." ) || !strcmp(dp->d_name, ".." )) continue; |
| 1133 | fn(dp->d_name, userdata); |
| 1134 | } |
| 1135 | closedir(dirp); |
| 1136 | #else |
| 1137 | (void) dir, (void) fn, (void) userdata; |
| 1138 | #endif |
| 1139 | } |
| 1140 | |
| 1141 | static void *p_open(const char *path, int flags) { |
| 1142 | const char *mode = flags == MG_FS_READ ? "rb" : "a+b" ; |
| 1143 | #if MG_ARCH == MG_ARCH_WIN32 |
| 1144 | wchar_t b1[MG_PATH_MAX], b2[10]; |
| 1145 | MultiByteToWideChar(CP_UTF8, 0, path, -1, b1, sizeof(b1) / sizeof(b1[0])); |
| 1146 | MultiByteToWideChar(CP_UTF8, 0, mode, -1, b2, sizeof(b2) / sizeof(b2[0])); |
| 1147 | return (void *) _wfopen(b1, b2); |
| 1148 | #else |
| 1149 | return (void *) fopen(path, mode); |
| 1150 | #endif |
| 1151 | } |
| 1152 | |
| 1153 | static void p_close(void *fp) { |
| 1154 | fclose((FILE *) fp); |
| 1155 | } |
| 1156 | |
| 1157 | static size_t p_read(void *fp, void *buf, size_t len) { |
| 1158 | return fread(buf, 1, len, (FILE *) fp); |
| 1159 | } |
| 1160 | |
| 1161 | static size_t p_write(void *fp, const void *buf, size_t len) { |
| 1162 | return fwrite(buf, 1, len, (FILE *) fp); |
| 1163 | } |
| 1164 | |
| 1165 | static size_t p_seek(void *fp, size_t offset) { |
| 1166 | #if (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64) || \ |
| 1167 | (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L) || \ |
| 1168 | (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 600) |
| 1169 | if (fseeko((FILE *) fp, (off_t) offset, SEEK_SET) != 0) (void) 0; |
| 1170 | #else |
| 1171 | if (fseek((FILE *) fp, (long) offset, SEEK_SET) != 0) (void) 0; |
| 1172 | #endif |
| 1173 | return (size_t) ftell((FILE *) fp); |
| 1174 | } |
| 1175 | |
| 1176 | static bool p_rename(const char *from, const char *to) { |
| 1177 | return rename(from, to) == 0; |
| 1178 | } |
| 1179 | |
| 1180 | static bool p_remove(const char *path) { |
| 1181 | return remove(path) == 0; |
| 1182 | } |
| 1183 | |
| 1184 | static bool p_mkdir(const char *path) { |
| 1185 | return mkdir(path, 0775) == 0; |
| 1186 | } |
| 1187 | |
| 1188 | #else |
| 1189 | |
| 1190 | static int p_stat(const char *path, size_t *size, time_t *mtime) { |
| 1191 | (void) path, (void) size, (void) mtime; |
| 1192 | return 0; |
| 1193 | } |
| 1194 | static void p_list(const char *path, void (*fn)(const char *, void *), |
| 1195 | void *userdata) { |
| 1196 | (void) path, (void) fn, (void) userdata; |
| 1197 | } |
| 1198 | static void *p_open(const char *path, int flags) { |
| 1199 | (void) path, (void) flags; |
| 1200 | return NULL; |
| 1201 | } |
| 1202 | static void p_close(void *fp) { |
| 1203 | (void) fp; |
| 1204 | } |
| 1205 | static size_t p_read(void *fd, void *buf, size_t len) { |
| 1206 | (void) fd, (void) buf, (void) len; |
| 1207 | return 0; |
| 1208 | } |
| 1209 | static size_t p_write(void *fd, const void *buf, size_t len) { |
| 1210 | (void) fd, (void) buf, (void) len; |
| 1211 | return 0; |
| 1212 | } |
| 1213 | static size_t p_seek(void *fd, size_t offset) { |
| 1214 | (void) fd, (void) offset; |
| 1215 | return (size_t) ~0; |
| 1216 | } |
| 1217 | static bool p_rename(const char *from, const char *to) { |
| 1218 | (void) from, (void) to; |
| 1219 | return false; |
| 1220 | } |
| 1221 | static bool p_remove(const char *path) { |
| 1222 | (void) path; |
| 1223 | return false; |
| 1224 | } |
| 1225 | static bool p_mkdir(const char *path) { |
| 1226 | (void) path; |
| 1227 | return false; |
| 1228 | } |
| 1229 | #endif |
| 1230 | |
| 1231 | struct mg_fs mg_fs_posix = {p_stat, p_list, p_open, p_close, p_read, |
| 1232 | p_write, p_seek, p_rename, p_remove, p_mkdir}; |
| 1233 | |
| 1234 | #ifdef MG_ENABLE_LINES |
| 1235 | #line 1 "src/http.c" |
| 1236 | #endif |
| 1237 | |
| 1238 | |
| 1239 | |
| 1240 | |
| 1241 | |
| 1242 | |
| 1243 | |
| 1244 | |
| 1245 | |
| 1246 | |
| 1247 | |
| 1248 | |
| 1249 | |
| 1250 | bool mg_to_size_t(struct mg_str str, size_t *val); |
| 1251 | bool mg_to_size_t(struct mg_str str, size_t *val) { |
| 1252 | uint64_t result = 0, max = 1844674407370955160 /* (UINT64_MAX-9)/10 */; |
| 1253 | size_t i = 0; |
| 1254 | while (i < str.len && (str.ptr[i] == ' ' || str.ptr[i] == '\t')) i++; |
| 1255 | if (i < str.len && str.ptr[i] == '-') return false; |
| 1256 | while (i < str.len && str.ptr[i] >= '0' && str.ptr[i] <= '9') { |
| 1257 | if (result > max) return false; |
| 1258 | result *= 10; |
| 1259 | result += (unsigned) (str.ptr[i] - '0'); |
| 1260 | i++; |
| 1261 | } |
| 1262 | *val = (size_t) result; |
| 1263 | return true; |
| 1264 | } |
| 1265 | |
| 1266 | // Chunk deletion marker is the MSB in the "processed" counter |
| 1267 | #define MG_DMARK ((size_t) 1 << (sizeof(size_t) * 8 - 1)) |
| 1268 | |
| 1269 | // Multipart POST example: |
| 1270 | // --xyz |
| 1271 | // Content-Disposition: form-data; name="val" |
| 1272 | // |
| 1273 | // abcdef |
| 1274 | // --xyz |
| 1275 | // Content-Disposition: form-data; name="foo"; filename="a.txt" |
| 1276 | // Content-Type: text/plain |
| 1277 | // |
| 1278 | // hello world |
| 1279 | // |
| 1280 | // --xyz-- |
| 1281 | size_t mg_http_next_multipart(struct mg_str body, size_t ofs, |
| 1282 | struct mg_http_part *part) { |
| 1283 | struct mg_str cd = mg_str_n("Content-Disposition" , 19); |
| 1284 | const char *s = body.ptr; |
| 1285 | size_t b = ofs, h1, h2, b1, b2, max = body.len; |
| 1286 | |
| 1287 | // Init part params |
| 1288 | if (part != NULL) part->name = part->filename = part->body = mg_str_n(0, 0); |
| 1289 | |
| 1290 | // Skip boundary |
| 1291 | while (b + 2 < max && s[b] != '\r' && s[b + 1] != '\n') b++; |
| 1292 | if (b <= ofs || b + 2 >= max) return 0; |
| 1293 | // MG_INFO(("B: %zu %zu [%.*s]", ofs, b - ofs, (int) (b - ofs), s)); |
| 1294 | |
| 1295 | // Skip headers |
| 1296 | h1 = h2 = b + 2; |
| 1297 | for (;;) { |
| 1298 | while (h2 + 2 < max && s[h2] != '\r' && s[h2 + 1] != '\n') h2++; |
| 1299 | if (h2 == h1) break; |
| 1300 | if (h2 + 2 >= max) return 0; |
| 1301 | // MG_INFO(("Header: [%.*s]", (int) (h2 - h1), &s[h1])); |
| 1302 | if (part != NULL && h1 + cd.len + 2 < h2 && s[h1 + cd.len] == ':' && |
| 1303 | mg_ncasecmp(&s[h1], cd.ptr, cd.len) == 0) { |
| 1304 | struct mg_str v = mg_str_n(&s[h1 + cd.len + 2], h2 - (h1 + cd.len + 2)); |
| 1305 | part->name = mg_http_get_header_var(v, mg_str_n("name" , 4)); |
| 1306 | part->filename = mg_http_get_header_var(v, mg_str_n("filename" , 8)); |
| 1307 | } |
| 1308 | h1 = h2 = h2 + 2; |
| 1309 | } |
| 1310 | b1 = b2 = h2 + 2; |
| 1311 | while (b2 + 2 + (b - ofs) + 2 < max && !(s[b2] == '\r' && s[b2 + 1] == '\n' && |
| 1312 | memcmp(&s[b2 + 2], s, b - ofs) == 0)) |
| 1313 | b2++; |
| 1314 | |
| 1315 | if (b2 + 2 >= max) return 0; |
| 1316 | if (part != NULL) part->body = mg_str_n(&s[b1], b2 - b1); |
| 1317 | // MG_INFO(("Body: [%.*s]", (int) (b2 - b1), &s[b1])); |
| 1318 | return b2 + 2; |
| 1319 | } |
| 1320 | |
| 1321 | void mg_http_bauth(struct mg_connection *c, const char *user, |
| 1322 | const char *pass) { |
| 1323 | struct mg_str u = mg_str(user), p = mg_str(pass); |
| 1324 | size_t need = c->send.len + 36 + (u.len + p.len) * 2; |
| 1325 | if (c->send.size < need) mg_iobuf_resize(&c->send, need); |
| 1326 | if (c->send.size >= need) { |
| 1327 | int i, n = 0; |
| 1328 | char *buf = (char *) &c->send.buf[c->send.len]; |
| 1329 | memcpy(buf, "Authorization: Basic " , 21); // DON'T use mg_send! |
| 1330 | for (i = 0; i < (int) u.len; i++) { |
| 1331 | n = mg_base64_update(((unsigned char *) u.ptr)[i], buf + 21, n); |
| 1332 | } |
| 1333 | if (p.len > 0) { |
| 1334 | n = mg_base64_update(':', buf + 21, n); |
| 1335 | for (i = 0; i < (int) p.len; i++) { |
| 1336 | n = mg_base64_update(((unsigned char *) p.ptr)[i], buf + 21, n); |
| 1337 | } |
| 1338 | } |
| 1339 | n = mg_base64_final(buf + 21, n); |
| 1340 | c->send.len += 21 + (size_t) n + 2; |
| 1341 | memcpy(&c->send.buf[c->send.len - 2], "\r\n" , 2); |
| 1342 | } else { |
| 1343 | MG_ERROR(("%lu oom %d->%d " , c->id, (int) c->send.size, (int) need)); |
| 1344 | } |
| 1345 | } |
| 1346 | |
| 1347 | struct mg_str mg_http_var(struct mg_str buf, struct mg_str name) { |
| 1348 | struct mg_str k, v, result = mg_str_n(NULL, 0); |
| 1349 | while (mg_split(&buf, &k, &v, '&')) { |
| 1350 | if (name.len == k.len && mg_ncasecmp(name.ptr, k.ptr, k.len) == 0) { |
| 1351 | result = v; |
| 1352 | break; |
| 1353 | } |
| 1354 | } |
| 1355 | return result; |
| 1356 | } |
| 1357 | |
| 1358 | int mg_http_get_var(const struct mg_str *buf, const char *name, char *dst, |
| 1359 | size_t dst_len) { |
| 1360 | int len; |
| 1361 | if (dst == NULL || dst_len == 0) { |
| 1362 | len = -2; // Bad destination |
| 1363 | } else if (buf->ptr == NULL || name == NULL || buf->len == 0) { |
| 1364 | len = -1; // Bad source |
| 1365 | dst[0] = '\0'; |
| 1366 | } else { |
| 1367 | struct mg_str v = mg_http_var(*buf, mg_str(name)); |
| 1368 | if (v.ptr == NULL) { |
| 1369 | len = -4; // Name does not exist |
| 1370 | } else { |
| 1371 | len = mg_url_decode(v.ptr, v.len, dst, dst_len, 1); |
| 1372 | if (len < 0) len = -3; // Failed to decode |
| 1373 | } |
| 1374 | } |
| 1375 | return len; |
| 1376 | } |
| 1377 | |
| 1378 | static bool isx(int c) { |
| 1379 | return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || |
| 1380 | (c >= 'A' && c <= 'F'); |
| 1381 | } |
| 1382 | |
| 1383 | int mg_url_decode(const char *src, size_t src_len, char *dst, size_t dst_len, |
| 1384 | int is_form_url_encoded) { |
| 1385 | size_t i, j; |
| 1386 | for (i = j = 0; i < src_len && j + 1 < dst_len; i++, j++) { |
| 1387 | if (src[i] == '%') { |
| 1388 | // Use `i + 2 < src_len`, not `i < src_len - 2`, note small src_len |
| 1389 | if (i + 2 < src_len && isx(src[i + 1]) && isx(src[i + 2])) { |
| 1390 | mg_unhex(src + i + 1, 2, (uint8_t *) &dst[j]); |
| 1391 | i += 2; |
| 1392 | } else { |
| 1393 | return -1; |
| 1394 | } |
| 1395 | } else if (is_form_url_encoded && src[i] == '+') { |
| 1396 | dst[j] = ' '; |
| 1397 | } else { |
| 1398 | dst[j] = src[i]; |
| 1399 | } |
| 1400 | } |
| 1401 | if (j < dst_len) dst[j] = '\0'; // Null-terminate the destination |
| 1402 | return i >= src_len && j < dst_len ? (int) j : -1; |
| 1403 | } |
| 1404 | |
| 1405 | static bool isok(uint8_t c) { |
| 1406 | return c == '\n' || c == '\r' || c >= ' '; |
| 1407 | } |
| 1408 | |
| 1409 | int mg_http_get_request_len(const unsigned char *buf, size_t buf_len) { |
| 1410 | size_t i; |
| 1411 | for (i = 0; i < buf_len; i++) { |
| 1412 | if (!isok(buf[i])) return -1; |
| 1413 | if ((i > 0 && buf[i] == '\n' && buf[i - 1] == '\n') || |
| 1414 | (i > 3 && buf[i] == '\n' && buf[i - 1] == '\r' && buf[i - 2] == '\n')) |
| 1415 | return (int) i + 1; |
| 1416 | } |
| 1417 | return 0; |
| 1418 | } |
| 1419 | |
| 1420 | static const char *skip(const char *s, const char *e, const char *d, |
| 1421 | struct mg_str *v) { |
| 1422 | v->ptr = s; |
| 1423 | while (s < e && *s != '\n' && strchr(d, *s) == NULL) s++; |
| 1424 | v->len = (size_t) (s - v->ptr); |
| 1425 | while (s < e && strchr(d, *s) != NULL) s++; |
| 1426 | return s; |
| 1427 | } |
| 1428 | |
| 1429 | struct mg_str *(struct mg_http_message *h, const char *name) { |
| 1430 | size_t i, n = strlen(name), max = sizeof(h->headers) / sizeof(h->headers[0]); |
| 1431 | for (i = 0; i < max && h->headers[i].name.len > 0; i++) { |
| 1432 | struct mg_str *k = &h->headers[i].name, *v = &h->headers[i].value; |
| 1433 | if (n == k->len && mg_ncasecmp(k->ptr, name, n) == 0) return v; |
| 1434 | } |
| 1435 | return NULL; |
| 1436 | } |
| 1437 | |
| 1438 | static bool (const char *s, const char *end, |
| 1439 | struct mg_http_header *h, int ) { |
| 1440 | int i; |
| 1441 | for (i = 0; i < max_headers; i++) { |
| 1442 | struct mg_str k, v, tmp; |
| 1443 | const char *he = skip(s, end, "\r\n" , &tmp); |
| 1444 | if (tmp.len == 0) break; // empty header = EOH |
| 1445 | s = skip(s, he, ": \r\n" , &k); |
| 1446 | s = skip(s, he, "\r\n" , &v); |
| 1447 | if (k.len == tmp.len) continue; |
| 1448 | while (v.len > 0 && v.ptr[v.len - 1] == ' ') v.len--; // Trim spaces |
| 1449 | if (k.len == 0) return false; // empty name |
| 1450 | // MG_INFO(("--HH [%.*s] [%.*s] [%.*s]", (int) tmp.len - 1, tmp.ptr, |
| 1451 | //(int) k.len, k.ptr, (int) v.len, v.ptr)); |
| 1452 | h[i].name = k; |
| 1453 | h[i].value = v; |
| 1454 | } |
| 1455 | return true; |
| 1456 | } |
| 1457 | |
| 1458 | int mg_http_parse(const char *s, size_t len, struct mg_http_message *hm) { |
| 1459 | int is_response, req_len = mg_http_get_request_len((unsigned char *) s, len); |
| 1460 | const char *end = s == NULL ? NULL : s + req_len, *qs; // Cannot add to NULL |
| 1461 | struct mg_str *cl; |
| 1462 | |
| 1463 | memset(hm, 0, sizeof(*hm)); |
| 1464 | if (req_len <= 0) return req_len; |
| 1465 | |
| 1466 | hm->message.ptr = hm->head.ptr = s; |
| 1467 | hm->body.ptr = end; |
| 1468 | hm->head.len = (size_t) req_len; |
| 1469 | hm->chunk.ptr = end; |
| 1470 | hm->message.len = hm->body.len = (size_t) ~0; // Set body length to infinite |
| 1471 | |
| 1472 | // Parse request line |
| 1473 | s = skip(s, end, " " , &hm->method); |
| 1474 | s = skip(s, end, " " , &hm->uri); |
| 1475 | s = skip(s, end, "\r\n" , &hm->proto); |
| 1476 | |
| 1477 | // Sanity check. Allow protocol/reason to be empty |
| 1478 | if (hm->method.len == 0 || hm->uri.len == 0) return -1; |
| 1479 | |
| 1480 | // If URI contains '?' character, setup query string |
| 1481 | if ((qs = (const char *) memchr(hm->uri.ptr, '?', hm->uri.len)) != NULL) { |
| 1482 | hm->query.ptr = qs + 1; |
| 1483 | hm->query.len = (size_t) (&hm->uri.ptr[hm->uri.len] - (qs + 1)); |
| 1484 | hm->uri.len = (size_t) (qs - hm->uri.ptr); |
| 1485 | } |
| 1486 | |
| 1487 | if (!mg_http_parse_headers(s, end, hm->headers, |
| 1488 | sizeof(hm->headers) / sizeof(hm->headers[0]))) |
| 1489 | return -1; // error when parsing |
| 1490 | if ((cl = mg_http_get_header(hm, "Content-Length" )) != NULL) { |
| 1491 | if (mg_to_size_t(*cl, &hm->body.len) == false) return -1; |
| 1492 | hm->message.len = (size_t) req_len + hm->body.len; |
| 1493 | } |
| 1494 | |
| 1495 | // mg_http_parse() is used to parse both HTTP requests and HTTP |
| 1496 | // responses. If HTTP response does not have Content-Length set, then |
| 1497 | // body is read until socket is closed, i.e. body.len is infinite (~0). |
| 1498 | // |
| 1499 | // For HTTP requests though, according to |
| 1500 | // http://tools.ietf.org/html/rfc7231#section-8.1.3, |
| 1501 | // only POST and PUT methods have defined body semantics. |
| 1502 | // Therefore, if Content-Length is not specified and methods are |
| 1503 | // not one of PUT or POST, set body length to 0. |
| 1504 | // |
| 1505 | // So, if it is HTTP request, and Content-Length is not set, |
| 1506 | // and method is not (PUT or POST) then reset body length to zero. |
| 1507 | is_response = mg_ncasecmp(hm->method.ptr, "HTTP/" , 5) == 0; |
| 1508 | if (hm->body.len == (size_t) ~0 && !is_response && |
| 1509 | mg_vcasecmp(&hm->method, "PUT" ) != 0 && |
| 1510 | mg_vcasecmp(&hm->method, "POST" ) != 0) { |
| 1511 | hm->body.len = 0; |
| 1512 | hm->message.len = (size_t) req_len; |
| 1513 | } |
| 1514 | |
| 1515 | // The 204 (No content) responses also have 0 body length |
| 1516 | if (hm->body.len == (size_t) ~0 && is_response && |
| 1517 | mg_vcasecmp(&hm->uri, "204" ) == 0) { |
| 1518 | hm->body.len = 0; |
| 1519 | hm->message.len = (size_t) req_len; |
| 1520 | } |
| 1521 | |
| 1522 | return req_len; |
| 1523 | } |
| 1524 | |
| 1525 | static void mg_http_vprintf_chunk(struct mg_connection *c, const char *fmt, |
| 1526 | va_list *ap) { |
| 1527 | size_t len = c->send.len; |
| 1528 | mg_send(c, " \r\n" , 10); |
| 1529 | mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, ap); |
| 1530 | if (c->send.len >= len + 10) { |
| 1531 | mg_snprintf((char *) c->send.buf + len, 9, "%08lx" , c->send.len - len - 10); |
| 1532 | c->send.buf[len + 8] = '\r'; |
| 1533 | if (c->send.len == len + 10) c->is_resp = 0; // Last chunk, reset marker |
| 1534 | } |
| 1535 | mg_send(c, "\r\n" , 2); |
| 1536 | } |
| 1537 | |
| 1538 | void mg_http_printf_chunk(struct mg_connection *c, const char *fmt, ...) { |
| 1539 | va_list ap; |
| 1540 | va_start(ap, fmt); |
| 1541 | mg_http_vprintf_chunk(c, fmt, &ap); |
| 1542 | va_end(ap); |
| 1543 | } |
| 1544 | |
| 1545 | void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len) { |
| 1546 | mg_printf(c, "%lx\r\n" , (unsigned long) len); |
| 1547 | mg_send(c, buf, len); |
| 1548 | mg_send(c, "\r\n" , 2); |
| 1549 | if (len == 0) c->is_resp = 0; |
| 1550 | } |
| 1551 | |
| 1552 | // clang-format off |
| 1553 | static const char *mg_http_status_code_str(int status_code) { |
| 1554 | switch (status_code) { |
| 1555 | case 100: return "Continue" ; |
| 1556 | case 101: return "Switching Protocols" ; |
| 1557 | case 102: return "Processing" ; |
| 1558 | case 200: return "OK" ; |
| 1559 | case 201: return "Created" ; |
| 1560 | case 202: return "Accepted" ; |
| 1561 | case 203: return "Non-authoritative Information" ; |
| 1562 | case 204: return "No Content" ; |
| 1563 | case 205: return "Reset Content" ; |
| 1564 | case 206: return "Partial Content" ; |
| 1565 | case 207: return "Multi-Status" ; |
| 1566 | case 208: return "Already Reported" ; |
| 1567 | case 226: return "IM Used" ; |
| 1568 | case 300: return "Multiple Choices" ; |
| 1569 | case 301: return "Moved Permanently" ; |
| 1570 | case 302: return "Found" ; |
| 1571 | case 303: return "See Other" ; |
| 1572 | case 304: return "Not Modified" ; |
| 1573 | case 305: return "Use Proxy" ; |
| 1574 | case 307: return "Temporary Redirect" ; |
| 1575 | case 308: return "Permanent Redirect" ; |
| 1576 | case 400: return "Bad Request" ; |
| 1577 | case 401: return "Unauthorized" ; |
| 1578 | case 402: return "Payment Required" ; |
| 1579 | case 403: return "Forbidden" ; |
| 1580 | case 404: return "Not Found" ; |
| 1581 | case 405: return "Method Not Allowed" ; |
| 1582 | case 406: return "Not Acceptable" ; |
| 1583 | case 407: return "Proxy Authentication Required" ; |
| 1584 | case 408: return "Request Timeout" ; |
| 1585 | case 409: return "Conflict" ; |
| 1586 | case 410: return "Gone" ; |
| 1587 | case 411: return "Length Required" ; |
| 1588 | case 412: return "Precondition Failed" ; |
| 1589 | case 413: return "Payload Too Large" ; |
| 1590 | case 414: return "Request-URI Too Long" ; |
| 1591 | case 415: return "Unsupported Media Type" ; |
| 1592 | case 416: return "Requested Range Not Satisfiable" ; |
| 1593 | case 417: return "Expectation Failed" ; |
| 1594 | case 418: return "I'm a teapot" ; |
| 1595 | case 421: return "Misdirected Request" ; |
| 1596 | case 422: return "Unprocessable Entity" ; |
| 1597 | case 423: return "Locked" ; |
| 1598 | case 424: return "Failed Dependency" ; |
| 1599 | case 426: return "Upgrade Required" ; |
| 1600 | case 428: return "Precondition Required" ; |
| 1601 | case 429: return "Too Many Requests" ; |
| 1602 | case 431: return "Request Header Fields Too Large" ; |
| 1603 | case 444: return "Connection Closed Without Response" ; |
| 1604 | case 451: return "Unavailable For Legal Reasons" ; |
| 1605 | case 499: return "Client Closed Request" ; |
| 1606 | case 500: return "Internal Server Error" ; |
| 1607 | case 501: return "Not Implemented" ; |
| 1608 | case 502: return "Bad Gateway" ; |
| 1609 | case 503: return "Service Unavailable" ; |
| 1610 | case 504: return "Gateway Timeout" ; |
| 1611 | case 505: return "HTTP Version Not Supported" ; |
| 1612 | case 506: return "Variant Also Negotiates" ; |
| 1613 | case 507: return "Insufficient Storage" ; |
| 1614 | case 508: return "Loop Detected" ; |
| 1615 | case 510: return "Not Extended" ; |
| 1616 | case 511: return "Network Authentication Required" ; |
| 1617 | case 599: return "Network Connect Timeout Error" ; |
| 1618 | default: return "" ; |
| 1619 | } |
| 1620 | } |
| 1621 | // clang-format on |
| 1622 | |
| 1623 | void mg_http_reply(struct mg_connection *c, int code, const char *, |
| 1624 | const char *fmt, ...) { |
| 1625 | va_list ap; |
| 1626 | size_t len; |
| 1627 | mg_printf(c, "HTTP/1.1 %d %s\r\n%sContent-Length: \r\n\r\n" , code, |
| 1628 | mg_http_status_code_str(code), headers == NULL ? "" : headers); |
| 1629 | len = c->send.len; |
| 1630 | va_start(ap, fmt); |
| 1631 | mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, &ap); |
| 1632 | va_end(ap); |
| 1633 | if (c->send.len > 16) { |
| 1634 | size_t n = mg_snprintf((char *) &c->send.buf[len - 15], 11, "%-10lu" , |
| 1635 | (unsigned long) (c->send.len - len)); |
| 1636 | c->send.buf[len - 15 + n] = ' '; // Change ending 0 to space |
| 1637 | } |
| 1638 | c->is_resp = 0; |
| 1639 | } |
| 1640 | |
| 1641 | static void http_cb(struct mg_connection *, int, void *, void *); |
| 1642 | static void restore_http_cb(struct mg_connection *c) { |
| 1643 | mg_fs_close((struct mg_fd *) c->pfn_data); |
| 1644 | c->pfn_data = NULL; |
| 1645 | c->pfn = http_cb; |
| 1646 | c->is_resp = 0; |
| 1647 | } |
| 1648 | |
| 1649 | char *mg_http_etag(char *buf, size_t len, size_t size, time_t mtime); |
| 1650 | char *mg_http_etag(char *buf, size_t len, size_t size, time_t mtime) { |
| 1651 | mg_snprintf(buf, len, "\"%lld.%lld\"" , (int64_t) mtime, (int64_t) size); |
| 1652 | return buf; |
| 1653 | } |
| 1654 | |
| 1655 | static void static_cb(struct mg_connection *c, int ev, void *ev_data, |
| 1656 | void *fn_data) { |
| 1657 | if (ev == MG_EV_WRITE || ev == MG_EV_POLL) { |
| 1658 | struct mg_fd *fd = (struct mg_fd *) fn_data; |
| 1659 | // Read to send IO buffer directly, avoid extra on-stack buffer |
| 1660 | size_t n, max = MG_IO_SIZE, space; |
| 1661 | size_t *cl = (size_t *) &c->data[(sizeof(c->data) - sizeof(size_t)) / |
| 1662 | sizeof(size_t) * sizeof(size_t)]; |
| 1663 | if (c->send.size < max) mg_iobuf_resize(&c->send, max); |
| 1664 | if (c->send.len >= c->send.size) return; // Rate limit |
| 1665 | if ((space = c->send.size - c->send.len) > *cl) space = *cl; |
| 1666 | n = fd->fs->rd(fd->fd, c->send.buf + c->send.len, space); |
| 1667 | c->send.len += n; |
| 1668 | *cl -= n; |
| 1669 | if (n == 0) restore_http_cb(c); |
| 1670 | } else if (ev == MG_EV_CLOSE) { |
| 1671 | restore_http_cb(c); |
| 1672 | } |
| 1673 | (void) ev_data; |
| 1674 | } |
| 1675 | |
| 1676 | // Known mime types. Keep it outside guess_content_type() function, since |
| 1677 | // some environments don't like it defined there. |
| 1678 | // clang-format off |
| 1679 | static struct mg_str s_known_types[] = { |
| 1680 | MG_C_STR("html" ), MG_C_STR("text/html; charset=utf-8" ), |
| 1681 | MG_C_STR("htm" ), MG_C_STR("text/html; charset=utf-8" ), |
| 1682 | MG_C_STR("css" ), MG_C_STR("text/css; charset=utf-8" ), |
| 1683 | MG_C_STR("js" ), MG_C_STR("text/javascript; charset=utf-8" ), |
| 1684 | MG_C_STR("gif" ), MG_C_STR("image/gif" ), |
| 1685 | MG_C_STR("png" ), MG_C_STR("image/png" ), |
| 1686 | MG_C_STR("jpg" ), MG_C_STR("image/jpeg" ), |
| 1687 | MG_C_STR("jpeg" ), MG_C_STR("image/jpeg" ), |
| 1688 | MG_C_STR("woff" ), MG_C_STR("font/woff" ), |
| 1689 | MG_C_STR("ttf" ), MG_C_STR("font/ttf" ), |
| 1690 | MG_C_STR("svg" ), MG_C_STR("image/svg+xml" ), |
| 1691 | MG_C_STR("txt" ), MG_C_STR("text/plain; charset=utf-8" ), |
| 1692 | MG_C_STR("avi" ), MG_C_STR("video/x-msvideo" ), |
| 1693 | MG_C_STR("csv" ), MG_C_STR("text/csv" ), |
| 1694 | MG_C_STR("doc" ), MG_C_STR("application/msword" ), |
| 1695 | MG_C_STR("exe" ), MG_C_STR("application/octet-stream" ), |
| 1696 | MG_C_STR("gz" ), MG_C_STR("application/gzip" ), |
| 1697 | MG_C_STR("ico" ), MG_C_STR("image/x-icon" ), |
| 1698 | MG_C_STR("json" ), MG_C_STR("application/json" ), |
| 1699 | MG_C_STR("mov" ), MG_C_STR("video/quicktime" ), |
| 1700 | MG_C_STR("mp3" ), MG_C_STR("audio/mpeg" ), |
| 1701 | MG_C_STR("mp4" ), MG_C_STR("video/mp4" ), |
| 1702 | MG_C_STR("mpeg" ), MG_C_STR("video/mpeg" ), |
| 1703 | MG_C_STR("pdf" ), MG_C_STR("application/pdf" ), |
| 1704 | MG_C_STR("shtml" ), MG_C_STR("text/html; charset=utf-8" ), |
| 1705 | MG_C_STR("tgz" ), MG_C_STR("application/tar-gz" ), |
| 1706 | MG_C_STR("wav" ), MG_C_STR("audio/wav" ), |
| 1707 | MG_C_STR("webp" ), MG_C_STR("image/webp" ), |
| 1708 | MG_C_STR("zip" ), MG_C_STR("application/zip" ), |
| 1709 | MG_C_STR("3gp" ), MG_C_STR("video/3gpp" ), |
| 1710 | {0, 0}, |
| 1711 | }; |
| 1712 | // clang-format on |
| 1713 | |
| 1714 | static struct mg_str guess_content_type(struct mg_str path, const char *) { |
| 1715 | struct mg_str k, v, s = mg_str(extra); |
| 1716 | size_t i = 0; |
| 1717 | |
| 1718 | // Shrink path to its extension only |
| 1719 | while (i < path.len && path.ptr[path.len - i - 1] != '.') i++; |
| 1720 | path.ptr += path.len - i; |
| 1721 | path.len = i; |
| 1722 | |
| 1723 | // Process user-provided mime type overrides, if any |
| 1724 | while (mg_commalist(&s, &k, &v)) { |
| 1725 | if (mg_strcmp(path, k) == 0) return v; |
| 1726 | } |
| 1727 | |
| 1728 | // Process built-in mime types |
| 1729 | for (i = 0; s_known_types[i].ptr != NULL; i += 2) { |
| 1730 | if (mg_strcmp(path, s_known_types[i]) == 0) return s_known_types[i + 1]; |
| 1731 | } |
| 1732 | |
| 1733 | return mg_str("text/plain; charset=utf-8" ); |
| 1734 | } |
| 1735 | |
| 1736 | static int getrange(struct mg_str *s, size_t *a, size_t *b) { |
| 1737 | size_t i, numparsed = 0; |
| 1738 | // MG_INFO(("%.*s", (int) s->len, s->ptr)); |
| 1739 | for (i = 0; i + 6 < s->len; i++) { |
| 1740 | if (memcmp(&s->ptr[i], "bytes=" , 6) == 0) { |
| 1741 | struct mg_str p = mg_str_n(s->ptr + i + 6, s->len - i - 6); |
| 1742 | if (p.len > 0 && p.ptr[0] >= '0' && p.ptr[0] <= '9') numparsed++; |
| 1743 | if (!mg_to_size_t(p, a)) return 0; |
| 1744 | // MG_INFO(("PPP [%.*s] %d", (int) p.len, p.ptr, numparsed)); |
| 1745 | while (p.len && p.ptr[0] >= '0' && p.ptr[0] <= '9') p.ptr++, p.len--; |
| 1746 | if (p.len && p.ptr[0] == '-') p.ptr++, p.len--; |
| 1747 | if (!mg_to_size_t(p, b)) return 0; |
| 1748 | if (p.len > 0 && p.ptr[0] >= '0' && p.ptr[0] <= '9') numparsed++; |
| 1749 | // MG_INFO(("PPP [%.*s] %d", (int) p.len, p.ptr, numparsed)); |
| 1750 | break; |
| 1751 | } |
| 1752 | } |
| 1753 | return (int) numparsed; |
| 1754 | } |
| 1755 | |
| 1756 | void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm, |
| 1757 | const char *path, |
| 1758 | const struct mg_http_serve_opts *opts) { |
| 1759 | char etag[64], tmp[MG_PATH_MAX]; |
| 1760 | struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs; |
| 1761 | struct mg_fd *fd = NULL; |
| 1762 | size_t size = 0; |
| 1763 | time_t mtime = 0; |
| 1764 | struct mg_str *inm = NULL; |
| 1765 | struct mg_str mime = guess_content_type(mg_str(path), opts->mime_types); |
| 1766 | bool gzip = false; |
| 1767 | |
| 1768 | if (path != NULL) { |
| 1769 | // If a browser sends us "Accept-Encoding: gzip", try to open .gz first |
| 1770 | struct mg_str *ae = mg_http_get_header(hm, "Accept-Encoding" ); |
| 1771 | if (ae != NULL && mg_strstr(*ae, mg_str("gzip" )) != NULL) { |
| 1772 | mg_snprintf(tmp, sizeof(tmp), "%s.gz" , path); |
| 1773 | fd = mg_fs_open(fs, tmp, MG_FS_READ); |
| 1774 | if (fd != NULL) gzip = true, path = tmp; |
| 1775 | } |
| 1776 | // No luck opening .gz? Open what we've told to open |
| 1777 | if (fd == NULL) fd = mg_fs_open(fs, path, MG_FS_READ); |
| 1778 | } |
| 1779 | |
| 1780 | // Failed to open, and page404 is configured? Open it, then |
| 1781 | if (fd == NULL && opts->page404 != NULL) { |
| 1782 | fd = mg_fs_open(fs, opts->page404, MG_FS_READ); |
| 1783 | mime = guess_content_type(mg_str(path), opts->mime_types); |
| 1784 | path = opts->page404; |
| 1785 | } |
| 1786 | |
| 1787 | if (fd == NULL || fs->st(path, &size, &mtime) == 0) { |
| 1788 | mg_http_reply(c, 404, opts->extra_headers, "Not found\n" ); |
| 1789 | mg_fs_close(fd); |
| 1790 | // NOTE: mg_http_etag() call should go first! |
| 1791 | } else if (mg_http_etag(etag, sizeof(etag), size, mtime) != NULL && |
| 1792 | (inm = mg_http_get_header(hm, "If-None-Match" )) != NULL && |
| 1793 | mg_vcasecmp(inm, etag) == 0) { |
| 1794 | mg_fs_close(fd); |
| 1795 | mg_http_reply(c, 304, opts->extra_headers, "" ); |
| 1796 | } else { |
| 1797 | int n, status = 200; |
| 1798 | char range[100]; |
| 1799 | size_t r1 = 0, r2 = 0, cl = size; |
| 1800 | |
| 1801 | // Handle Range header |
| 1802 | struct mg_str *rh = mg_http_get_header(hm, "Range" ); |
| 1803 | range[0] = '\0'; |
| 1804 | if (rh != NULL && (n = getrange(rh, &r1, &r2)) > 0) { |
| 1805 | // If range is specified like "400-", set second limit to content len |
| 1806 | if (n == 1) r2 = cl - 1; |
| 1807 | if (r1 > r2 || r2 >= cl) { |
| 1808 | status = 416; |
| 1809 | cl = 0; |
| 1810 | mg_snprintf(range, sizeof(range), "Content-Range: bytes */%lld\r\n" , |
| 1811 | (int64_t) size); |
| 1812 | } else { |
| 1813 | status = 206; |
| 1814 | cl = r2 - r1 + 1; |
| 1815 | mg_snprintf(range, sizeof(range), |
| 1816 | "Content-Range: bytes %llu-%llu/%llu\r\n" , (uint64_t) r1, |
| 1817 | (uint64_t) (r1 + cl - 1), (uint64_t) size); |
| 1818 | fs->sk(fd->fd, r1); |
| 1819 | } |
| 1820 | } |
| 1821 | mg_printf(c, |
| 1822 | "HTTP/1.1 %d %s\r\n" |
| 1823 | "Content-Type: %.*s\r\n" |
| 1824 | "Etag: %s\r\n" |
| 1825 | "Content-Length: %llu\r\n" |
| 1826 | "%s%s%s\r\n" , |
| 1827 | status, mg_http_status_code_str(status), (int) mime.len, mime.ptr, |
| 1828 | etag, (uint64_t) cl, gzip ? "Content-Encoding: gzip\r\n" : "" , |
| 1829 | range, opts->extra_headers ? opts->extra_headers : "" ); |
| 1830 | if (mg_vcasecmp(&hm->method, "HEAD" ) == 0) { |
| 1831 | c->is_draining = 1; |
| 1832 | c->is_resp = 0; |
| 1833 | mg_fs_close(fd); |
| 1834 | } else { |
| 1835 | // Track to-be-sent content length at the end of c->data, aligned |
| 1836 | size_t *clp = (size_t *) &c->data[(sizeof(c->data) - sizeof(size_t)) / |
| 1837 | sizeof(size_t) * sizeof(size_t)]; |
| 1838 | c->pfn = static_cb; |
| 1839 | c->pfn_data = fd; |
| 1840 | *clp = cl; |
| 1841 | } |
| 1842 | } |
| 1843 | } |
| 1844 | |
| 1845 | struct printdirentrydata { |
| 1846 | struct mg_connection *c; |
| 1847 | struct mg_http_message *hm; |
| 1848 | const struct mg_http_serve_opts *opts; |
| 1849 | const char *dir; |
| 1850 | }; |
| 1851 | |
| 1852 | static void printdirentry(const char *name, void *userdata) { |
| 1853 | struct printdirentrydata *d = (struct printdirentrydata *) userdata; |
| 1854 | struct mg_fs *fs = d->opts->fs == NULL ? &mg_fs_posix : d->opts->fs; |
| 1855 | size_t size = 0; |
| 1856 | time_t t = 0; |
| 1857 | char path[MG_PATH_MAX], sz[40], mod[40]; |
| 1858 | int flags, n = 0; |
| 1859 | |
| 1860 | // MG_DEBUG(("[%s] [%s]", d->dir, name)); |
| 1861 | if (mg_snprintf(path, sizeof(path), "%s%c%s" , d->dir, '/', name) > |
| 1862 | sizeof(path)) { |
| 1863 | MG_ERROR(("%s truncated" , name)); |
| 1864 | } else if ((flags = fs->st(path, &size, &t)) == 0) { |
| 1865 | MG_ERROR(("%lu stat(%s): %d" , d->c->id, path, errno)); |
| 1866 | } else { |
| 1867 | const char *slash = flags & MG_FS_DIR ? "/" : "" ; |
| 1868 | if (flags & MG_FS_DIR) { |
| 1869 | mg_snprintf(sz, sizeof(sz), "%s" , "[DIR]" ); |
| 1870 | } else { |
| 1871 | mg_snprintf(sz, sizeof(sz), "%lld" , (uint64_t) size); |
| 1872 | } |
| 1873 | #if defined(MG_HTTP_DIRLIST_TIME_FMT) |
| 1874 | { |
| 1875 | char time_str[40]; |
| 1876 | struct tm *time_info = localtime(&t); |
| 1877 | strftime(time_str, sizeof time_str, "%Y/%m/%d %H:%M:%S" , time_info); |
| 1878 | mg_snprintf(mod, sizeof(mod), "%s" , time_str); |
| 1879 | } |
| 1880 | #else |
| 1881 | mg_snprintf(mod, sizeof(mod), "%lu" , (unsigned long) t); |
| 1882 | #endif |
| 1883 | n = (int) mg_url_encode(name, strlen(name), path, sizeof(path)); |
| 1884 | mg_printf(d->c, |
| 1885 | " <tr><td><a href=\"%.*s%s\">%s%s</a></td>" |
| 1886 | "<td name=%lu>%s</td><td name=%lld>%s</td></tr>\n" , |
| 1887 | n, path, slash, name, slash, (unsigned long) t, mod, |
| 1888 | flags & MG_FS_DIR ? (int64_t) -1 : (int64_t) size, sz); |
| 1889 | } |
| 1890 | } |
| 1891 | |
| 1892 | static void listdir(struct mg_connection *c, struct mg_http_message *hm, |
| 1893 | const struct mg_http_serve_opts *opts, char *dir) { |
| 1894 | const char *sort_js_code = |
| 1895 | "<script>function srt(tb, sc, so, d) {" |
| 1896 | "var tr = Array.prototype.slice.call(tb.rows, 0)," |
| 1897 | "tr = tr.sort(function (a, b) { var c1 = a.cells[sc], c2 = b.cells[sc]," |
| 1898 | "n1 = c1.getAttribute('name'), n2 = c2.getAttribute('name'), " |
| 1899 | "t1 = a.cells[2].getAttribute('name'), " |
| 1900 | "t2 = b.cells[2].getAttribute('name'); " |
| 1901 | "return so * (t1 < 0 && t2 >= 0 ? -1 : t2 < 0 && t1 >= 0 ? 1 : " |
| 1902 | "n1 ? parseInt(n2) - parseInt(n1) : " |
| 1903 | "c1.textContent.trim().localeCompare(c2.textContent.trim())); });" ; |
| 1904 | const char *sort_js_code2 = |
| 1905 | "for (var i = 0; i < tr.length; i++) tb.appendChild(tr[i]); " |
| 1906 | "if (!d) window.location.hash = ('sc=' + sc + '&so=' + so); " |
| 1907 | "};" |
| 1908 | "window.onload = function() {" |
| 1909 | "var tb = document.getElementById('tb');" |
| 1910 | "var m = /sc=([012]).so=(1|-1)/.exec(window.location.hash) || [0, 2, 1];" |
| 1911 | "var sc = m[1], so = m[2]; document.onclick = function(ev) { " |
| 1912 | "var c = ev.target.rel; if (c) {if (c == sc) so *= -1; srt(tb, c, so); " |
| 1913 | "sc = c; ev.preventDefault();}};" |
| 1914 | "srt(tb, sc, so, true);" |
| 1915 | "}" |
| 1916 | "</script>" ; |
| 1917 | struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs; |
| 1918 | struct printdirentrydata d = {c, hm, opts, dir}; |
| 1919 | char tmp[10], buf[MG_PATH_MAX]; |
| 1920 | size_t off, n; |
| 1921 | int len = mg_url_decode(hm->uri.ptr, hm->uri.len, buf, sizeof(buf), 0); |
| 1922 | struct mg_str uri = len > 0 ? mg_str_n(buf, (size_t) len) : hm->uri; |
| 1923 | |
| 1924 | mg_printf(c, |
| 1925 | "HTTP/1.1 200 OK\r\n" |
| 1926 | "Content-Type: text/html; charset=utf-8\r\n" |
| 1927 | "%s" |
| 1928 | "Content-Length: \r\n\r\n" , |
| 1929 | opts->extra_headers == NULL ? "" : opts->extra_headers); |
| 1930 | off = c->send.len; // Start of body |
| 1931 | mg_printf(c, |
| 1932 | "<!DOCTYPE html><html><head><title>Index of %.*s</title>%s%s" |
| 1933 | "<style>th,td {text-align: left; padding-right: 1em; " |
| 1934 | "font-family: monospace; }</style></head>" |
| 1935 | "<body><h1>Index of %.*s</h1><table cellpadding=\"0\"><thead>" |
| 1936 | "<tr><th><a href=\"#\" rel=\"0\">Name</a></th><th>" |
| 1937 | "<a href=\"#\" rel=\"1\">Modified</a></th>" |
| 1938 | "<th><a href=\"#\" rel=\"2\">Size</a></th></tr>" |
| 1939 | "<tr><td colspan=\"3\"><hr></td></tr>" |
| 1940 | "</thead>" |
| 1941 | "<tbody id=\"tb\">\n" , |
| 1942 | (int) uri.len, uri.ptr, sort_js_code, sort_js_code2, (int) uri.len, |
| 1943 | uri.ptr); |
| 1944 | mg_printf(c, "%s" , |
| 1945 | " <tr><td><a href=\"..\">..</a></td>" |
| 1946 | "<td name=-1></td><td name=-1>[DIR]</td></tr>\n" ); |
| 1947 | |
| 1948 | fs->ls(dir, printdirentry, &d); |
| 1949 | mg_printf(c, |
| 1950 | "</tbody><tfoot><tr><td colspan=\"3\"><hr></td></tr></tfoot>" |
| 1951 | "</table><address>Mongoose v.%s</address></body></html>\n" , |
| 1952 | MG_VERSION); |
| 1953 | n = mg_snprintf(tmp, sizeof(tmp), "%lu" , (unsigned long) (c->send.len - off)); |
| 1954 | if (n > sizeof(tmp)) n = 0; |
| 1955 | memcpy(c->send.buf + off - 12, tmp, n); // Set content length |
| 1956 | c->is_resp = 0; // Mark response end |
| 1957 | } |
| 1958 | |
| 1959 | // Resolve requested file into `path` and return its fs->st() result |
| 1960 | static int uri_to_path2(struct mg_connection *c, struct mg_http_message *hm, |
| 1961 | struct mg_fs *fs, struct mg_str url, struct mg_str dir, |
| 1962 | char *path, size_t path_size) { |
| 1963 | int flags, tmp; |
| 1964 | // Append URI to the root_dir, and sanitize it |
| 1965 | size_t n = mg_snprintf(path, path_size, "%.*s" , (int) dir.len, dir.ptr); |
| 1966 | if (n > path_size) n = path_size; |
| 1967 | path[path_size - 1] = '\0'; |
| 1968 | if (n + 2 < path_size) path[n++] = '/', path[n] = '\0'; |
| 1969 | mg_url_decode(hm->uri.ptr + url.len, hm->uri.len - url.len, path + n, |
| 1970 | path_size - n, 0); |
| 1971 | path[path_size - 1] = '\0'; // Double-check |
| 1972 | mg_remove_double_dots(path); |
| 1973 | n = strlen(path); |
| 1974 | while (n > 1 && path[n - 1] == '/') path[--n] = 0; // Trim trailing slashes |
| 1975 | flags = mg_vcmp(&hm->uri, "/" ) == 0 ? MG_FS_DIR : fs->st(path, NULL, NULL); |
| 1976 | MG_VERBOSE(("%lu %.*s -> %s %d" , c->id, (int) hm->uri.len, hm->uri.ptr, path, |
| 1977 | flags)); |
| 1978 | if (flags == 0) { |
| 1979 | // Do nothing - let's caller decide |
| 1980 | } else if ((flags & MG_FS_DIR) && hm->uri.len > 0 && |
| 1981 | hm->uri.ptr[hm->uri.len - 1] != '/') { |
| 1982 | mg_printf(c, |
| 1983 | "HTTP/1.1 301 Moved\r\n" |
| 1984 | "Location: %.*s/\r\n" |
| 1985 | "Content-Length: 0\r\n" |
| 1986 | "\r\n" , |
| 1987 | (int) hm->uri.len, hm->uri.ptr); |
| 1988 | c->is_resp = 0; |
| 1989 | flags = -1; |
| 1990 | } else if (flags & MG_FS_DIR) { |
| 1991 | if (((mg_snprintf(path + n, path_size - n, "/" MG_HTTP_INDEX) > 0 && |
| 1992 | (tmp = fs->st(path, NULL, NULL)) != 0) || |
| 1993 | (mg_snprintf(path + n, path_size - n, "/index.shtml" ) > 0 && |
| 1994 | (tmp = fs->st(path, NULL, NULL)) != 0))) { |
| 1995 | flags = tmp; |
| 1996 | } else if ((mg_snprintf(path + n, path_size - n, "/" MG_HTTP_INDEX ".gz" ) > |
| 1997 | 0 && |
| 1998 | (tmp = fs->st(path, NULL, NULL)) != |
| 1999 | 0)) { // check for gzipped index |
| 2000 | flags = tmp; |
| 2001 | path[n + 1 + strlen(MG_HTTP_INDEX)] = |
| 2002 | '\0'; // Remove appended .gz in index file name |
| 2003 | } else { |
| 2004 | path[n] = '\0'; // Remove appended index file name |
| 2005 | } |
| 2006 | } |
| 2007 | return flags; |
| 2008 | } |
| 2009 | |
| 2010 | static int uri_to_path(struct mg_connection *c, struct mg_http_message *hm, |
| 2011 | const struct mg_http_serve_opts *opts, char *path, |
| 2012 | size_t path_size) { |
| 2013 | struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs; |
| 2014 | struct mg_str k, v, s = mg_str(opts->root_dir), u = {0, 0}, p = {0, 0}; |
| 2015 | while (mg_commalist(&s, &k, &v)) { |
| 2016 | if (v.len == 0) v = k, k = mg_str("/" ); |
| 2017 | if (hm->uri.len < k.len) continue; |
| 2018 | if (mg_strcmp(k, mg_str_n(hm->uri.ptr, k.len)) != 0) continue; |
| 2019 | u = k, p = v; |
| 2020 | } |
| 2021 | return uri_to_path2(c, hm, fs, u, p, path, path_size); |
| 2022 | } |
| 2023 | |
| 2024 | void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm, |
| 2025 | const struct mg_http_serve_opts *opts) { |
| 2026 | char path[MG_PATH_MAX]; |
| 2027 | const char *sp = opts->ssi_pattern; |
| 2028 | int flags = uri_to_path(c, hm, opts, path, sizeof(path)); |
| 2029 | if (flags < 0) { |
| 2030 | // Do nothing: the response has already been sent by uri_to_path() |
| 2031 | } else if (flags & MG_FS_DIR) { |
| 2032 | listdir(c, hm, opts, path); |
| 2033 | } else if (flags && sp != NULL && |
| 2034 | mg_globmatch(sp, strlen(sp), path, strlen(path))) { |
| 2035 | mg_http_serve_ssi(c, opts->root_dir, path); |
| 2036 | } else { |
| 2037 | mg_http_serve_file(c, hm, path, opts); |
| 2038 | } |
| 2039 | } |
| 2040 | |
| 2041 | static bool mg_is_url_safe(int c) { |
| 2042 | return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || |
| 2043 | (c >= 'A' && c <= 'Z') || c == '.' || c == '_' || c == '-' || c == '~'; |
| 2044 | } |
| 2045 | |
| 2046 | size_t mg_url_encode(const char *s, size_t sl, char *buf, size_t len) { |
| 2047 | size_t i, n = 0; |
| 2048 | for (i = 0; i < sl; i++) { |
| 2049 | int c = *(unsigned char *) &s[i]; |
| 2050 | if (n + 4 >= len) return 0; |
| 2051 | if (mg_is_url_safe(c)) { |
| 2052 | buf[n++] = s[i]; |
| 2053 | } else { |
| 2054 | buf[n++] = '%'; |
| 2055 | mg_hex(&s[i], 1, &buf[n]); |
| 2056 | n += 2; |
| 2057 | } |
| 2058 | } |
| 2059 | if (len > 0 && n < len - 1) buf[n] = '\0'; // Null-terminate the destination |
| 2060 | if (len > 0) buf[len - 1] = '\0'; // Always. |
| 2061 | return n; |
| 2062 | } |
| 2063 | |
| 2064 | void mg_http_creds(struct mg_http_message *hm, char *user, size_t userlen, |
| 2065 | char *pass, size_t passlen) { |
| 2066 | struct mg_str *v = mg_http_get_header(hm, "Authorization" ); |
| 2067 | user[0] = pass[0] = '\0'; |
| 2068 | if (v != NULL && v->len > 6 && memcmp(v->ptr, "Basic " , 6) == 0) { |
| 2069 | char buf[256]; |
| 2070 | int n = mg_base64_decode(v->ptr + 6, (int) v->len - 6, buf); |
| 2071 | const char *p = (const char *) memchr(buf, ':', n > 0 ? (size_t) n : 0); |
| 2072 | if (p != NULL) { |
| 2073 | mg_snprintf(user, userlen, "%.*s" , (int) (p - buf), buf); |
| 2074 | mg_snprintf(pass, passlen, "%.*s" , n - (int) (p - buf) - 1, p + 1); |
| 2075 | } |
| 2076 | } else if (v != NULL && v->len > 7 && memcmp(v->ptr, "Bearer " , 7) == 0) { |
| 2077 | mg_snprintf(pass, passlen, "%.*s" , (int) v->len - 7, v->ptr + 7); |
| 2078 | } else if ((v = mg_http_get_header(hm, "Cookie" )) != NULL) { |
| 2079 | struct mg_str t = mg_http_get_header_var(*v, mg_str_n("access_token" , 12)); |
| 2080 | if (t.len > 0) mg_snprintf(pass, passlen, "%.*s" , (int) t.len, t.ptr); |
| 2081 | } else { |
| 2082 | mg_http_get_var(&hm->query, "access_token" , pass, passlen); |
| 2083 | } |
| 2084 | } |
| 2085 | |
| 2086 | static struct mg_str stripquotes(struct mg_str s) { |
| 2087 | return s.len > 1 && s.ptr[0] == '"' && s.ptr[s.len - 1] == '"' |
| 2088 | ? mg_str_n(s.ptr + 1, s.len - 2) |
| 2089 | : s; |
| 2090 | } |
| 2091 | |
| 2092 | struct mg_str (struct mg_str s, struct mg_str v) { |
| 2093 | size_t i; |
| 2094 | for (i = 0; v.len > 0 && i + v.len + 2 < s.len; i++) { |
| 2095 | if (s.ptr[i + v.len] == '=' && memcmp(&s.ptr[i], v.ptr, v.len) == 0) { |
| 2096 | const char *p = &s.ptr[i + v.len + 1], *b = p, *x = &s.ptr[s.len]; |
| 2097 | int q = p < x && *p == '"' ? 1 : 0; |
| 2098 | while (p < x && |
| 2099 | (q ? p == b || *p != '"' : *p != ';' && *p != ' ' && *p != ',')) |
| 2100 | p++; |
| 2101 | // MG_INFO(("[%.*s] [%.*s] [%.*s]", (int) s.len, s.ptr, (int) v.len, |
| 2102 | // v.ptr, (int) (p - b), b)); |
| 2103 | return stripquotes(mg_str_n(b, (size_t) (p - b + q))); |
| 2104 | } |
| 2105 | } |
| 2106 | return mg_str_n(NULL, 0); |
| 2107 | } |
| 2108 | |
| 2109 | bool mg_http_match_uri(const struct mg_http_message *hm, const char *glob) { |
| 2110 | return mg_match(hm->uri, mg_str(glob), NULL); |
| 2111 | } |
| 2112 | |
| 2113 | long mg_http_upload(struct mg_connection *c, struct mg_http_message *hm, |
| 2114 | struct mg_fs *fs, const char *path, size_t max_size) { |
| 2115 | char buf[20] = "0" ; |
| 2116 | long res = 0, offset; |
| 2117 | mg_http_get_var(&hm->query, "offset" , buf, sizeof(buf)); |
| 2118 | offset = strtol(buf, NULL, 0); |
| 2119 | if (hm->body.len == 0) { |
| 2120 | mg_http_reply(c, 200, "" , "%ld" , res); // Nothing to write |
| 2121 | } else { |
| 2122 | struct mg_fd *fd; |
| 2123 | size_t current_size = 0; |
| 2124 | MG_DEBUG(("%s -> %d bytes @ %ld" , path, (int) hm->body.len, offset)); |
| 2125 | if (offset == 0) fs->rm(path); // If offset if 0, truncate file |
| 2126 | fs->st(path, ¤t_size, NULL); |
| 2127 | if (offset < 0) { |
| 2128 | mg_http_reply(c, 400, "" , "offset required" ); |
| 2129 | res = -1; |
| 2130 | } else if (offset > 0 && current_size != (size_t) offset) { |
| 2131 | mg_http_reply(c, 400, "" , "%s: offset mismatch" , path); |
| 2132 | res = -2; |
| 2133 | } else if ((size_t) offset + hm->body.len > max_size) { |
| 2134 | mg_http_reply(c, 400, "" , "%s: over max size of %lu" , path, |
| 2135 | (unsigned long) max_size); |
| 2136 | res = -3; |
| 2137 | } else if ((fd = mg_fs_open(fs, path, MG_FS_WRITE)) == NULL) { |
| 2138 | mg_http_reply(c, 400, "" , "open(%s): %d" , path, errno); |
| 2139 | res = -4; |
| 2140 | } else { |
| 2141 | res = offset + (long) fs->wr(fd->fd, hm->body.ptr, hm->body.len); |
| 2142 | mg_fs_close(fd); |
| 2143 | mg_http_reply(c, 200, "" , "%ld" , res); |
| 2144 | } |
| 2145 | } |
| 2146 | return res; |
| 2147 | } |
| 2148 | |
| 2149 | int mg_http_status(const struct mg_http_message *hm) { |
| 2150 | return atoi(hm->uri.ptr); |
| 2151 | } |
| 2152 | |
| 2153 | // If a server sends data to the client using chunked encoding, Mongoose strips |
| 2154 | // off the chunking prefix (hex length and \r\n) and suffix (\r\n), appends the |
| 2155 | // stripped data to the body, and fires the MG_EV_HTTP_CHUNK event. When zero |
| 2156 | // chunk is received, we fire MG_EV_HTTP_MSG, and the body already has all |
| 2157 | // chunking prefixes/suffixes stripped. |
| 2158 | // |
| 2159 | // If a server sends data without chunked encoding, we also fire a series of |
| 2160 | // MG_EV_HTTP_CHUNK events for every received piece of data, and then we fire |
| 2161 | // MG_EV_HTTP_MSG event in the end. |
| 2162 | // |
| 2163 | // We track total processed length in the c->pfn_data, which is a void * |
| 2164 | // pointer: we store a size_t value there. |
| 2165 | static bool getchunk(struct mg_str s, size_t *prefixlen, size_t *datalen) { |
| 2166 | size_t i = 0, n; |
| 2167 | while (i < s.len && s.ptr[i] != '\r' && s.ptr[i] != '\n') i++; |
| 2168 | n = mg_unhexn(s.ptr, i); |
| 2169 | // MG_INFO(("%d %d", (int) (i + n + 4), (int) s.len)); |
| 2170 | if (s.len < i + n + 4) return false; // Chunk not yet fully buffered |
| 2171 | if (s.ptr[i] != '\r' || s.ptr[i + 1] != '\n') return false; |
| 2172 | if (s.ptr[i + n + 2] != '\r' || s.ptr[i + n + 3] != '\n') return false; |
| 2173 | *prefixlen = i + 2; |
| 2174 | *datalen = n; |
| 2175 | return true; |
| 2176 | } |
| 2177 | |
| 2178 | static bool mg_is_chunked(struct mg_http_message *hm) { |
| 2179 | const char *needle = "chunked" ; |
| 2180 | struct mg_str *te = mg_http_get_header(hm, "Transfer-Encoding" ); |
| 2181 | return te != NULL && mg_vcasecmp(te, needle) == 0; |
| 2182 | } |
| 2183 | |
| 2184 | void mg_http_delete_chunk(struct mg_connection *c, struct mg_http_message *hm) { |
| 2185 | size_t ofs = (size_t) (hm->chunk.ptr - (char *) c->recv.buf); |
| 2186 | mg_iobuf_del(&c->recv, ofs, hm->chunk.len); |
| 2187 | c->pfn_data = (void *) ((size_t) c->pfn_data | MG_DMARK); |
| 2188 | } |
| 2189 | |
| 2190 | static void deliver_chunked_chunks(struct mg_connection *c, size_t hlen, |
| 2191 | struct mg_http_message *hm, bool *next) { |
| 2192 | // | ... headers ... | HEXNUM\r\n ..data.. \r\n | ...... |
| 2193 | // +------------------+--------------------------+---- |
| 2194 | // | hlen | chunk1 | ...... |
| 2195 | char *buf = (char *) &c->recv.buf[hlen], *p = buf; |
| 2196 | size_t len = c->recv.len - hlen; |
| 2197 | size_t processed = ((size_t) c->pfn_data) & ~MG_DMARK; |
| 2198 | size_t mark, pl, dl, del = 0, ofs = 0; |
| 2199 | bool last = false; |
| 2200 | if (processed <= len) len -= processed, buf += processed; |
| 2201 | while (!last && getchunk(mg_str_n(buf + ofs, len - ofs), &pl, &dl)) { |
| 2202 | size_t saved = c->recv.len; |
| 2203 | memmove(p + processed, buf + ofs + pl, dl); |
| 2204 | // MG_INFO(("P2 [%.*s]", (int) (processed + dl), p)); |
| 2205 | hm->chunk = mg_str_n(p + processed, dl); |
| 2206 | mg_call(c, MG_EV_HTTP_CHUNK, hm); |
| 2207 | ofs += pl + dl + 2, del += pl + 2; // 2 is for \r\n suffix |
| 2208 | processed += dl; |
| 2209 | if (c->recv.len != saved) processed -= dl, buf -= dl; |
| 2210 | // mg_hexdump(c->recv.buf, hlen + processed); |
| 2211 | last = (dl == 0); |
| 2212 | } |
| 2213 | mg_iobuf_del(&c->recv, hlen + processed, del); |
| 2214 | mark = ((size_t) c->pfn_data) & MG_DMARK; |
| 2215 | c->pfn_data = (void *) (processed | mark); |
| 2216 | if (last) { |
| 2217 | hm->body.len = processed; |
| 2218 | hm->message.len = hlen + processed; |
| 2219 | c->pfn_data = NULL; |
| 2220 | if (mark) mg_iobuf_del(&c->recv, 0, hlen), *next = true; |
| 2221 | // MG_INFO(("LAST, mark: %lx", mark)); |
| 2222 | // mg_hexdump(c->recv.buf, c->recv.len); |
| 2223 | } |
| 2224 | } |
| 2225 | |
| 2226 | static void deliver_normal_chunks(struct mg_connection *c, size_t hlen, |
| 2227 | struct mg_http_message *hm, bool *next) { |
| 2228 | size_t left, processed = ((size_t) c->pfn_data) & ~MG_DMARK; |
| 2229 | size_t deleted = ((size_t) c->pfn_data) & MG_DMARK; |
| 2230 | hm->chunk = mg_str_n((char *) &c->recv.buf[hlen], c->recv.len - hlen); |
| 2231 | if (processed <= hm->chunk.len && !deleted) { |
| 2232 | hm->chunk.len -= processed; |
| 2233 | hm->chunk.ptr += processed; |
| 2234 | } |
| 2235 | left = hm->body.len < processed ? 0 : hm->body.len - processed; |
| 2236 | if (hm->chunk.len > left) hm->chunk.len = left; |
| 2237 | if (hm->chunk.len > 0) mg_call(c, MG_EV_HTTP_CHUNK, hm); |
| 2238 | processed += hm->chunk.len; |
| 2239 | deleted = ((size_t) c->pfn_data) & MG_DMARK; // Re-evaluate after user call |
| 2240 | if (processed >= hm->body.len) { // Last, 0-len chunk |
| 2241 | hm->chunk.len = 0; // Reset length |
| 2242 | mg_call(c, MG_EV_HTTP_CHUNK, hm); // Call user handler |
| 2243 | c->pfn_data = NULL; // Reset processed counter |
| 2244 | if (processed && deleted) mg_iobuf_del(&c->recv, 0, hlen), *next = true; |
| 2245 | } else { |
| 2246 | c->pfn_data = (void *) (processed | deleted); // if it is set |
| 2247 | } |
| 2248 | } |
| 2249 | |
| 2250 | static void http_cb(struct mg_connection *c, int ev, void *evd, void *fnd) { |
| 2251 | if (ev == MG_EV_READ || ev == MG_EV_CLOSE) { |
| 2252 | struct mg_http_message hm; |
| 2253 | // mg_hexdump(c->recv.buf, c->recv.len); |
| 2254 | while (c->recv.buf != NULL && c->recv.len > 0) { |
| 2255 | bool next = false; |
| 2256 | int hlen = mg_http_parse((char *) c->recv.buf, c->recv.len, &hm); |
| 2257 | if (hlen < 0) { |
| 2258 | mg_error(c, "HTTP parse:\n%.*s" , (int) c->recv.len, c->recv.buf); |
| 2259 | break; |
| 2260 | } |
| 2261 | if (c->is_resp) break; // Response is still generated |
| 2262 | if (hlen == 0) break; // Request is not buffered yet |
| 2263 | if (ev == MG_EV_CLOSE) { // If client did not set Content-Length |
| 2264 | hm.message.len = c->recv.len; // and closes now, deliver a MSG |
| 2265 | hm.body.len = hm.message.len - (size_t) (hm.body.ptr - hm.message.ptr); |
| 2266 | } |
| 2267 | if (mg_is_chunked(&hm)) { |
| 2268 | deliver_chunked_chunks(c, (size_t) hlen, &hm, &next); |
| 2269 | } else { |
| 2270 | deliver_normal_chunks(c, (size_t) hlen, &hm, &next); |
| 2271 | } |
| 2272 | if (next) continue; // Chunks & request were deleted |
| 2273 | // Chunk events are delivered. If we have full body, deliver MSG |
| 2274 | if (c->recv.len < hm.message.len) break; |
| 2275 | if (c->is_accepted) c->is_resp = 1; // Start generating response |
| 2276 | mg_call(c, MG_EV_HTTP_MSG, &hm); // User handler can clear is_resp |
| 2277 | mg_iobuf_del(&c->recv, 0, hm.message.len); |
| 2278 | } |
| 2279 | } |
| 2280 | (void) evd, (void) fnd; |
| 2281 | } |
| 2282 | |
| 2283 | static void mg_hfn(struct mg_connection *c, int ev, void *ev_data, void *fnd) { |
| 2284 | if (ev == MG_EV_HTTP_MSG) { |
| 2285 | struct mg_http_message *hm = (struct mg_http_message *) ev_data; |
| 2286 | if (mg_http_match_uri(hm, "/quit" )) { |
| 2287 | mg_http_reply(c, 200, "" , "ok\n" ); |
| 2288 | c->is_draining = 1; |
| 2289 | c->data[0] = 'X'; |
| 2290 | } else if (mg_http_match_uri(hm, "/debug" )) { |
| 2291 | int level = (int) mg_json_get_long(hm->body, "$.level" , MG_LL_DEBUG); |
| 2292 | mg_log_set(level); |
| 2293 | mg_http_reply(c, 200, "" , "Debug level set to %d\n" , level); |
| 2294 | } else { |
| 2295 | mg_http_reply(c, 200, "" , "hi\n" ); |
| 2296 | } |
| 2297 | } else if (ev == MG_EV_CLOSE) { |
| 2298 | if (c->data[0] == 'X') *(bool *) fnd = true; |
| 2299 | } |
| 2300 | } |
| 2301 | |
| 2302 | void mg_hello(const char *url) { |
| 2303 | struct mg_mgr mgr; |
| 2304 | bool done = false; |
| 2305 | mg_mgr_init(&mgr); |
| 2306 | if (mg_http_listen(&mgr, url, mg_hfn, &done) == NULL) done = true; |
| 2307 | while (done == false) mg_mgr_poll(&mgr, 100); |
| 2308 | mg_mgr_free(&mgr); |
| 2309 | } |
| 2310 | |
| 2311 | struct mg_connection *mg_http_connect(struct mg_mgr *mgr, const char *url, |
| 2312 | mg_event_handler_t fn, void *fn_data) { |
| 2313 | struct mg_connection *c = mg_connect(mgr, url, fn, fn_data); |
| 2314 | if (c != NULL) c->pfn = http_cb; |
| 2315 | return c; |
| 2316 | } |
| 2317 | |
| 2318 | struct mg_connection *mg_http_listen(struct mg_mgr *mgr, const char *url, |
| 2319 | mg_event_handler_t fn, void *fn_data) { |
| 2320 | struct mg_connection *c = mg_listen(mgr, url, fn, fn_data); |
| 2321 | if (c != NULL) c->pfn = http_cb; |
| 2322 | return c; |
| 2323 | } |
| 2324 | |
| 2325 | #ifdef MG_ENABLE_LINES |
| 2326 | #line 1 "src/iobuf.c" |
| 2327 | #endif |
| 2328 | |
| 2329 | |
| 2330 | |
| 2331 | |
| 2332 | // Not using memset for zeroing memory, cause it can be dropped by compiler |
| 2333 | // See https://github.com/cesanta/mongoose/pull/1265 |
| 2334 | static void zeromem(volatile unsigned char *buf, size_t len) { |
| 2335 | if (buf != NULL) { |
| 2336 | while (len--) *buf++ = 0; |
| 2337 | } |
| 2338 | } |
| 2339 | |
| 2340 | static size_t roundup(size_t size, size_t align) { |
| 2341 | return align == 0 ? size : (size + align - 1) / align * align; |
| 2342 | } |
| 2343 | |
| 2344 | int mg_iobuf_resize(struct mg_iobuf *io, size_t new_size) { |
| 2345 | int ok = 1; |
| 2346 | new_size = roundup(new_size, io->align); |
| 2347 | if (new_size == 0) { |
| 2348 | zeromem(io->buf, io->size); |
| 2349 | free(io->buf); |
| 2350 | io->buf = NULL; |
| 2351 | io->len = io->size = 0; |
| 2352 | } else if (new_size != io->size) { |
| 2353 | // NOTE(lsm): do not use realloc here. Use calloc/free only, to ease the |
| 2354 | // porting to some obscure platforms like FreeRTOS |
| 2355 | void *p = calloc(1, new_size); |
| 2356 | if (p != NULL) { |
| 2357 | size_t len = new_size < io->len ? new_size : io->len; |
| 2358 | if (len > 0 && io->buf != NULL) memmove(p, io->buf, len); |
| 2359 | zeromem(io->buf, io->size); |
| 2360 | free(io->buf); |
| 2361 | io->buf = (unsigned char *) p; |
| 2362 | io->size = new_size; |
| 2363 | } else { |
| 2364 | ok = 0; |
| 2365 | MG_ERROR(("%lld->%lld" , (uint64_t) io->size, (uint64_t) new_size)); |
| 2366 | } |
| 2367 | } |
| 2368 | return ok; |
| 2369 | } |
| 2370 | |
| 2371 | int mg_iobuf_init(struct mg_iobuf *io, size_t size, size_t align) { |
| 2372 | io->buf = NULL; |
| 2373 | io->align = align; |
| 2374 | io->size = io->len = 0; |
| 2375 | return mg_iobuf_resize(io, size); |
| 2376 | } |
| 2377 | |
| 2378 | size_t mg_iobuf_add(struct mg_iobuf *io, size_t ofs, const void *buf, |
| 2379 | size_t len) { |
| 2380 | size_t new_size = roundup(io->len + len, io->align); |
| 2381 | mg_iobuf_resize(io, new_size); // Attempt to resize |
| 2382 | if (new_size != io->size) len = 0; // Resize failure, append nothing |
| 2383 | if (ofs < io->len) memmove(io->buf + ofs + len, io->buf + ofs, io->len - ofs); |
| 2384 | if (buf != NULL) memmove(io->buf + ofs, buf, len); |
| 2385 | if (ofs > io->len) io->len += ofs - io->len; |
| 2386 | io->len += len; |
| 2387 | return len; |
| 2388 | } |
| 2389 | |
| 2390 | size_t mg_iobuf_del(struct mg_iobuf *io, size_t ofs, size_t len) { |
| 2391 | if (ofs > io->len) ofs = io->len; |
| 2392 | if (ofs + len > io->len) len = io->len - ofs; |
| 2393 | if (io->buf) memmove(io->buf + ofs, io->buf + ofs + len, io->len - ofs - len); |
| 2394 | if (io->buf) zeromem(io->buf + io->len - len, len); |
| 2395 | io->len -= len; |
| 2396 | return len; |
| 2397 | } |
| 2398 | |
| 2399 | void mg_iobuf_free(struct mg_iobuf *io) { |
| 2400 | mg_iobuf_resize(io, 0); |
| 2401 | } |
| 2402 | |
| 2403 | #ifdef MG_ENABLE_LINES |
| 2404 | #line 1 "src/json.c" |
| 2405 | #endif |
| 2406 | |
| 2407 | |
| 2408 | |
| 2409 | |
| 2410 | static const char *escapeseq(int esc) { |
| 2411 | return esc ? "\b\f\n\r\t\\\"" : "bfnrt\\\"" ; |
| 2412 | } |
| 2413 | |
| 2414 | static char json_esc(int c, int esc) { |
| 2415 | const char *p, *esc1 = escapeseq(esc), *esc2 = escapeseq(!esc); |
| 2416 | for (p = esc1; *p != '\0'; p++) { |
| 2417 | if (*p == c) return esc2[p - esc1]; |
| 2418 | } |
| 2419 | return 0; |
| 2420 | } |
| 2421 | |
| 2422 | static int mg_pass_string(const char *s, int len) { |
| 2423 | int i; |
| 2424 | for (i = 0; i < len; i++) { |
| 2425 | if (s[i] == '\\' && i + 1 < len && json_esc(s[i + 1], 1)) { |
| 2426 | i++; |
| 2427 | } else if (s[i] == '\0') { |
| 2428 | return MG_JSON_INVALID; |
| 2429 | } else if (s[i] == '"') { |
| 2430 | return i; |
| 2431 | } |
| 2432 | } |
| 2433 | return MG_JSON_INVALID; |
| 2434 | } |
| 2435 | |
| 2436 | static double mg_atod(const char *p, int len, int *numlen) { |
| 2437 | double d = 0.0; |
| 2438 | int i = 0, sign = 1; |
| 2439 | |
| 2440 | // Sign |
| 2441 | if (i < len && *p == '-') { |
| 2442 | sign = -1, i++; |
| 2443 | } else if (i < len && *p == '+') { |
| 2444 | i++; |
| 2445 | } |
| 2446 | |
| 2447 | // Decimal |
| 2448 | for (; i < len && p[i] >= '0' && p[i] <= '9'; i++) { |
| 2449 | d *= 10.0; |
| 2450 | d += p[i] - '0'; |
| 2451 | } |
| 2452 | d *= sign; |
| 2453 | |
| 2454 | // Fractional |
| 2455 | if (i < len && p[i] == '.') { |
| 2456 | double frac = 0.0, base = 0.1; |
| 2457 | i++; |
| 2458 | for (; i < len && p[i] >= '0' && p[i] <= '9'; i++) { |
| 2459 | frac += base * (p[i] - '0'); |
| 2460 | base /= 10.0; |
| 2461 | } |
| 2462 | d += frac * sign; |
| 2463 | } |
| 2464 | |
| 2465 | // Exponential |
| 2466 | if (i < len && (p[i] == 'e' || p[i] == 'E')) { |
| 2467 | int j, exp = 0, minus = 0; |
| 2468 | i++; |
| 2469 | if (i < len && p[i] == '-') minus = 1, i++; |
| 2470 | if (i < len && p[i] == '+') i++; |
| 2471 | while (i < len && p[i] >= '0' && p[i] <= '9' && exp < 308) |
| 2472 | exp = exp * 10 + (p[i++] - '0'); |
| 2473 | if (minus) exp = -exp; |
| 2474 | for (j = 0; j < exp; j++) d *= 10.0; |
| 2475 | for (j = 0; j < -exp; j++) d /= 10.0; |
| 2476 | } |
| 2477 | |
| 2478 | if (numlen != NULL) *numlen = i; |
| 2479 | return d; |
| 2480 | } |
| 2481 | |
| 2482 | int mg_json_get(struct mg_str json, const char *path, int *toklen) { |
| 2483 | const char *s = json.ptr; |
| 2484 | int len = (int) json.len; |
| 2485 | enum { S_VALUE, S_KEY, S_COLON, S_COMMA_OR_EOO } expecting = S_VALUE; |
| 2486 | unsigned char nesting[MG_JSON_MAX_DEPTH]; |
| 2487 | int i = 0; // Current offset in `s` |
| 2488 | int j = 0; // Offset in `s` we're looking for (return value) |
| 2489 | int depth = 0; // Current depth (nesting level) |
| 2490 | int ed = 0; // Expected depth |
| 2491 | int pos = 1; // Current position in `path` |
| 2492 | int ci = -1, ei = -1; // Current and expected index in array |
| 2493 | |
| 2494 | if (toklen) *toklen = 0; |
| 2495 | if (path[0] != '$') return MG_JSON_INVALID; |
| 2496 | |
| 2497 | #define MG_CHECKRET(x) \ |
| 2498 | do { \ |
| 2499 | if (depth == ed && path[pos] == '\0' && ci == ei) { \ |
| 2500 | if (toklen) *toklen = i - j + 1; \ |
| 2501 | return j; \ |
| 2502 | } \ |
| 2503 | } while (0) |
| 2504 | |
| 2505 | // In the ascii table, the distance between `[` and `]` is 2. |
| 2506 | // Ditto for `{` and `}`. Hence +2 in the code below. |
| 2507 | #define MG_EOO(x) \ |
| 2508 | do { \ |
| 2509 | if (depth == ed && ci != ei) return MG_JSON_NOT_FOUND; \ |
| 2510 | if (c != nesting[depth - 1] + 2) return MG_JSON_INVALID; \ |
| 2511 | depth--; \ |
| 2512 | MG_CHECKRET(x); \ |
| 2513 | } while (0) |
| 2514 | |
| 2515 | for (i = 0; i < len; i++) { |
| 2516 | unsigned char c = ((unsigned char *) s)[i]; |
| 2517 | if (c == ' ' || c == '\t' || c == '\n' || c == '\r') continue; |
| 2518 | switch (expecting) { |
| 2519 | case S_VALUE: |
| 2520 | // p("V %s [%.*s] %d %d %d %d\n", path, pos, path, depth, ed, ci, ei); |
| 2521 | if (depth == ed) j = i; |
| 2522 | if (c == '{') { |
| 2523 | if (depth >= (int) sizeof(nesting)) return MG_JSON_TOO_DEEP; |
| 2524 | if (depth == ed && path[pos] == '.' && ci == ei) { |
| 2525 | // If we start the object, reset array indices |
| 2526 | ed++, pos++, ci = ei = -1; |
| 2527 | } |
| 2528 | nesting[depth++] = c; |
| 2529 | expecting = S_KEY; |
| 2530 | break; |
| 2531 | } else if (c == '[') { |
| 2532 | if (depth >= (int) sizeof(nesting)) return MG_JSON_TOO_DEEP; |
| 2533 | if (depth == ed && path[pos] == '[' && ei == ci) { |
| 2534 | ed++, pos++, ci = 0; |
| 2535 | for (ei = 0; path[pos] != ']' && path[pos] != '\0'; pos++) { |
| 2536 | ei *= 10; |
| 2537 | ei += path[pos] - '0'; |
| 2538 | } |
| 2539 | if (path[pos] != 0) pos++; |
| 2540 | } |
| 2541 | nesting[depth++] = c; |
| 2542 | break; |
| 2543 | } else if (c == ']' && depth > 0) { // Empty array |
| 2544 | MG_EOO(']'); |
| 2545 | } else if (c == 't' && i + 3 < len && memcmp(&s[i], "true" , 4) == 0) { |
| 2546 | i += 3; |
| 2547 | } else if (c == 'n' && i + 3 < len && memcmp(&s[i], "null" , 4) == 0) { |
| 2548 | i += 3; |
| 2549 | } else if (c == 'f' && i + 4 < len && memcmp(&s[i], "false" , 5) == 0) { |
| 2550 | i += 4; |
| 2551 | } else if (c == '-' || ((c >= '0' && c <= '9'))) { |
| 2552 | int numlen = 0; |
| 2553 | mg_atod(&s[i], len - i, &numlen); |
| 2554 | i += numlen - 1; |
| 2555 | } else if (c == '"') { |
| 2556 | int n = mg_pass_string(&s[i + 1], len - i - 1); |
| 2557 | if (n < 0) return n; |
| 2558 | i += n + 1; |
| 2559 | } else { |
| 2560 | return MG_JSON_INVALID; |
| 2561 | } |
| 2562 | MG_CHECKRET('V'); |
| 2563 | if (depth == ed && ei >= 0) ci++; |
| 2564 | expecting = S_COMMA_OR_EOO; |
| 2565 | break; |
| 2566 | |
| 2567 | case S_KEY: |
| 2568 | if (c == '"') { |
| 2569 | int n = mg_pass_string(&s[i + 1], len - i - 1); |
| 2570 | if (n < 0) return n; |
| 2571 | if (i + 1 + n >= len) return MG_JSON_NOT_FOUND; |
| 2572 | if (depth < ed) return MG_JSON_NOT_FOUND; |
| 2573 | if (depth == ed && path[pos - 1] != '.') return MG_JSON_NOT_FOUND; |
| 2574 | // printf("K %s [%.*s] [%.*s] %d %d %d\n", path, pos, path, n, |
| 2575 | // &s[i + 1], n, depth, ed); |
| 2576 | // NOTE(cpq): in the check sequence below is important. |
| 2577 | // strncmp() must go first: it fails fast if the remaining length of |
| 2578 | // the path is smaller than `n`. |
| 2579 | if (depth == ed && path[pos - 1] == '.' && |
| 2580 | strncmp(&s[i + 1], &path[pos], (size_t) n) == 0 && |
| 2581 | (path[pos + n] == '\0' || path[pos + n] == '.' || |
| 2582 | path[pos + n] == '[')) { |
| 2583 | pos += n; |
| 2584 | } |
| 2585 | i += n + 1; |
| 2586 | expecting = S_COLON; |
| 2587 | } else if (c == '}') { // Empty object |
| 2588 | MG_EOO('}'); |
| 2589 | expecting = S_COMMA_OR_EOO; |
| 2590 | if (depth == ed && ei >= 0) ci++; |
| 2591 | } else { |
| 2592 | return MG_JSON_INVALID; |
| 2593 | } |
| 2594 | break; |
| 2595 | |
| 2596 | case S_COLON: |
| 2597 | if (c == ':') { |
| 2598 | expecting = S_VALUE; |
| 2599 | } else { |
| 2600 | return MG_JSON_INVALID; |
| 2601 | } |
| 2602 | break; |
| 2603 | |
| 2604 | case S_COMMA_OR_EOO: |
| 2605 | if (depth <= 0) { |
| 2606 | return MG_JSON_INVALID; |
| 2607 | } else if (c == ',') { |
| 2608 | expecting = (nesting[depth - 1] == '{') ? S_KEY : S_VALUE; |
| 2609 | } else if (c == ']' || c == '}') { |
| 2610 | MG_EOO('O'); |
| 2611 | if (depth == ed && ei >= 0) ci++; |
| 2612 | } else { |
| 2613 | return MG_JSON_INVALID; |
| 2614 | } |
| 2615 | break; |
| 2616 | } |
| 2617 | } |
| 2618 | return MG_JSON_NOT_FOUND; |
| 2619 | } |
| 2620 | |
| 2621 | bool mg_json_get_num(struct mg_str json, const char *path, double *v) { |
| 2622 | int n, toklen, found = 0; |
| 2623 | if ((n = mg_json_get(json, path, &toklen)) >= 0 && |
| 2624 | (json.ptr[n] == '-' || (json.ptr[n] >= '0' && json.ptr[n] <= '9'))) { |
| 2625 | if (v != NULL) *v = mg_atod(json.ptr + n, toklen, NULL); |
| 2626 | found = 1; |
| 2627 | } |
| 2628 | return found; |
| 2629 | } |
| 2630 | |
| 2631 | bool mg_json_get_bool(struct mg_str json, const char *path, bool *v) { |
| 2632 | int found = 0, off = mg_json_get(json, path, NULL); |
| 2633 | if (off >= 0 && (json.ptr[off] == 't' || json.ptr[off] == 'f')) { |
| 2634 | if (v != NULL) *v = json.ptr[off] == 't'; |
| 2635 | found = 1; |
| 2636 | } |
| 2637 | return found; |
| 2638 | } |
| 2639 | |
| 2640 | bool mg_json_unescape(struct mg_str s, char *to, size_t n) { |
| 2641 | size_t i, j; |
| 2642 | for (i = 0, j = 0; i < s.len && j < n; i++, j++) { |
| 2643 | if (s.ptr[i] == '\\' && i + 5 < s.len && s.ptr[i + 1] == 'u') { |
| 2644 | // \uXXXX escape. We could process a simple one-byte chars |
| 2645 | // \u00xx from the ASCII range. More complex chars would require |
| 2646 | // dragging in a UTF8 library, which is too much for us |
| 2647 | if (s.ptr[i + 2] != '0' || s.ptr[i + 3] != '0') return false; // Give up |
| 2648 | ((unsigned char *) to)[j] = (unsigned char) mg_unhexn(s.ptr + i + 4, 2); |
| 2649 | |
| 2650 | i += 5; |
| 2651 | } else if (s.ptr[i] == '\\' && i + 1 < s.len) { |
| 2652 | char c = json_esc(s.ptr[i + 1], 0); |
| 2653 | if (c == 0) return false; |
| 2654 | to[j] = c; |
| 2655 | i++; |
| 2656 | } else { |
| 2657 | to[j] = s.ptr[i]; |
| 2658 | } |
| 2659 | } |
| 2660 | if (j >= n) return false; |
| 2661 | if (n > 0) to[j] = '\0'; |
| 2662 | return true; |
| 2663 | } |
| 2664 | |
| 2665 | char *mg_json_get_str(struct mg_str json, const char *path) { |
| 2666 | char *result = NULL; |
| 2667 | int len = 0, off = mg_json_get(json, path, &len); |
| 2668 | if (off >= 0 && len > 1 && json.ptr[off] == '"') { |
| 2669 | if ((result = (char *) calloc(1, (size_t) len)) != NULL && |
| 2670 | !mg_json_unescape(mg_str_n(json.ptr + off + 1, (size_t) (len - 2)), |
| 2671 | result, (size_t) len)) { |
| 2672 | free(result); |
| 2673 | result = NULL; |
| 2674 | } |
| 2675 | } |
| 2676 | return result; |
| 2677 | } |
| 2678 | |
| 2679 | char *mg_json_get_b64(struct mg_str json, const char *path, int *slen) { |
| 2680 | char *result = NULL; |
| 2681 | int len = 0, off = mg_json_get(json, path, &len); |
| 2682 | if (off >= 0 && json.ptr[off] == '"' && len > 1 && |
| 2683 | (result = (char *) calloc(1, (size_t) len)) != NULL) { |
| 2684 | int k = mg_base64_decode(json.ptr + off + 1, len - 2, result); |
| 2685 | if (slen != NULL) *slen = k; |
| 2686 | } |
| 2687 | return result; |
| 2688 | } |
| 2689 | |
| 2690 | char *mg_json_get_hex(struct mg_str json, const char *path, int *slen) { |
| 2691 | char *result = NULL; |
| 2692 | int len = 0, off = mg_json_get(json, path, &len); |
| 2693 | if (off >= 0 && json.ptr[off] == '"' && len > 1 && |
| 2694 | (result = (char *) calloc(1, (size_t) len / 2)) != NULL) { |
| 2695 | mg_unhex(json.ptr + off + 1, (size_t) (len - 2), (uint8_t *) result); |
| 2696 | result[len / 2 - 1] = '\0'; |
| 2697 | if (slen != NULL) *slen = len / 2 - 1; |
| 2698 | } |
| 2699 | return result; |
| 2700 | } |
| 2701 | |
| 2702 | long mg_json_get_long(struct mg_str json, const char *path, long dflt) { |
| 2703 | double dv; |
| 2704 | long result = dflt; |
| 2705 | if (mg_json_get_num(json, path, &dv)) result = (long) dv; |
| 2706 | return result; |
| 2707 | } |
| 2708 | |
| 2709 | #ifdef MG_ENABLE_LINES |
| 2710 | #line 1 "src/log.c" |
| 2711 | #endif |
| 2712 | |
| 2713 | |
| 2714 | |
| 2715 | |
| 2716 | |
| 2717 | static int s_level = MG_LL_INFO; |
| 2718 | static mg_pfn_t s_log_func = mg_pfn_stdout; |
| 2719 | static void *s_log_func_param = NULL; |
| 2720 | |
| 2721 | void mg_log_set_fn(mg_pfn_t fn, void *param) { |
| 2722 | s_log_func = fn; |
| 2723 | s_log_func_param = param; |
| 2724 | } |
| 2725 | |
| 2726 | static void logc(unsigned char c) { |
| 2727 | s_log_func((char) c, s_log_func_param); |
| 2728 | } |
| 2729 | |
| 2730 | static void logs(const char *buf, size_t len) { |
| 2731 | size_t i; |
| 2732 | for (i = 0; i < len; i++) logc(((unsigned char *) buf)[i]); |
| 2733 | } |
| 2734 | |
| 2735 | void mg_log_set(int log_level) { |
| 2736 | MG_DEBUG(("Setting log level to %d" , log_level)); |
| 2737 | s_level = log_level; |
| 2738 | } |
| 2739 | |
| 2740 | bool mg_log_prefix(int level, const char *file, int line, const char *fname) { |
| 2741 | if (level <= s_level) { |
| 2742 | const char *p = strrchr(file, '/'); |
| 2743 | char buf[41]; |
| 2744 | size_t n; |
| 2745 | if (p == NULL) p = strrchr(file, '\\'); |
| 2746 | n = mg_snprintf(buf, sizeof(buf), "%-6llx %d %s:%d:%s" , mg_millis(), level, |
| 2747 | p == NULL ? file : p + 1, line, fname); |
| 2748 | if (n > sizeof(buf) - 2) n = sizeof(buf) - 2; |
| 2749 | while (n < sizeof(buf)) buf[n++] = ' '; |
| 2750 | logs(buf, n - 1); |
| 2751 | return true; |
| 2752 | } else { |
| 2753 | return false; |
| 2754 | } |
| 2755 | } |
| 2756 | |
| 2757 | void mg_log(const char *fmt, ...) { |
| 2758 | va_list ap; |
| 2759 | va_start(ap, fmt); |
| 2760 | mg_vxprintf(s_log_func, s_log_func_param, fmt, &ap); |
| 2761 | va_end(ap); |
| 2762 | logc((unsigned char) '\n'); |
| 2763 | } |
| 2764 | |
| 2765 | static unsigned char nibble(unsigned c) { |
| 2766 | return (unsigned char) (c < 10 ? c + '0' : c + 'W'); |
| 2767 | } |
| 2768 | |
| 2769 | #define ISPRINT(x) ((x) >= ' ' && (x) <= '~') |
| 2770 | void mg_hexdump(const void *buf, size_t len) { |
| 2771 | const unsigned char *p = (const unsigned char *) buf; |
| 2772 | unsigned char ascii[16], alen = 0; |
| 2773 | size_t i; |
| 2774 | for (i = 0; i < len; i++) { |
| 2775 | if ((i % 16) == 0) { |
| 2776 | // Print buffered ascii chars |
| 2777 | if (i > 0) logs(" " , 2), logs((char *) ascii, 16), logc('\n'), alen = 0; |
| 2778 | // Print hex address, then \t |
| 2779 | logc(nibble((i >> 12) & 15)), logc(nibble((i >> 8) & 15)), |
| 2780 | logc(nibble((i >> 4) & 15)), logc('0'), logs(" " , 3); |
| 2781 | } |
| 2782 | logc(nibble(p[i] >> 4)), logc(nibble(p[i] & 15)); // Two nibbles, e.g. c5 |
| 2783 | logc(' '); // Space after hex number |
| 2784 | ascii[alen++] = ISPRINT(p[i]) ? p[i] : '.'; // Add to the ascii buf |
| 2785 | } |
| 2786 | while (alen < 16) logs(" " , 3), ascii[alen++] = ' '; |
| 2787 | logs(" " , 2), logs((char *) ascii, 16), logc('\n'); |
| 2788 | } |
| 2789 | |
| 2790 | #ifdef MG_ENABLE_LINES |
| 2791 | #line 1 "src/md5.c" |
| 2792 | #endif |
| 2793 | |
| 2794 | |
| 2795 | |
| 2796 | #if defined(MG_ENABLE_MD5) && MG_ENABLE_MD5 |
| 2797 | |
| 2798 | static void mg_byte_reverse(unsigned char *buf, unsigned longs) { |
| 2799 | if (MG_BIG_ENDIAN) { |
| 2800 | do { |
| 2801 | uint32_t t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 | |
| 2802 | ((unsigned) buf[1] << 8 | buf[0]); |
| 2803 | *(uint32_t *) buf = t; |
| 2804 | buf += 4; |
| 2805 | } while (--longs); |
| 2806 | } else { |
| 2807 | (void) buf, (void) longs; // Little endian. Do nothing |
| 2808 | } |
| 2809 | } |
| 2810 | |
| 2811 | #define F1(x, y, z) (z ^ (x & (y ^ z))) |
| 2812 | #define F2(x, y, z) F1(z, x, y) |
| 2813 | #define F3(x, y, z) (x ^ y ^ z) |
| 2814 | #define F4(x, y, z) (y ^ (x | ~z)) |
| 2815 | |
| 2816 | #define MD5STEP(f, w, x, y, z, data, s) \ |
| 2817 | (w += f(x, y, z) + data, w = w << s | w >> (32 - s), w += x) |
| 2818 | |
| 2819 | /* |
| 2820 | * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious |
| 2821 | * initialization constants. |
| 2822 | */ |
| 2823 | void mg_md5_init(mg_md5_ctx *ctx) { |
| 2824 | ctx->buf[0] = 0x67452301; |
| 2825 | ctx->buf[1] = 0xefcdab89; |
| 2826 | ctx->buf[2] = 0x98badcfe; |
| 2827 | ctx->buf[3] = 0x10325476; |
| 2828 | |
| 2829 | ctx->bits[0] = 0; |
| 2830 | ctx->bits[1] = 0; |
| 2831 | } |
| 2832 | |
| 2833 | static void mg_md5_transform(uint32_t buf[4], uint32_t const in[16]) { |
| 2834 | uint32_t a, b, c, d; |
| 2835 | |
| 2836 | a = buf[0]; |
| 2837 | b = buf[1]; |
| 2838 | c = buf[2]; |
| 2839 | d = buf[3]; |
| 2840 | |
| 2841 | MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); |
| 2842 | MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); |
| 2843 | MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); |
| 2844 | MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); |
| 2845 | MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); |
| 2846 | MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); |
| 2847 | MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); |
| 2848 | MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); |
| 2849 | MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); |
| 2850 | MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); |
| 2851 | MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); |
| 2852 | MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); |
| 2853 | MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); |
| 2854 | MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); |
| 2855 | MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); |
| 2856 | MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); |
| 2857 | |
| 2858 | MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); |
| 2859 | MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); |
| 2860 | MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); |
| 2861 | MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); |
| 2862 | MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); |
| 2863 | MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); |
| 2864 | MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); |
| 2865 | MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); |
| 2866 | MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); |
| 2867 | MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); |
| 2868 | MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); |
| 2869 | MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); |
| 2870 | MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); |
| 2871 | MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); |
| 2872 | MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); |
| 2873 | MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); |
| 2874 | |
| 2875 | MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); |
| 2876 | MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); |
| 2877 | MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); |
| 2878 | MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); |
| 2879 | MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); |
| 2880 | MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); |
| 2881 | MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); |
| 2882 | MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); |
| 2883 | MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); |
| 2884 | MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); |
| 2885 | MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); |
| 2886 | MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); |
| 2887 | MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); |
| 2888 | MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); |
| 2889 | MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); |
| 2890 | MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); |
| 2891 | |
| 2892 | MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); |
| 2893 | MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); |
| 2894 | MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); |
| 2895 | MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); |
| 2896 | MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); |
| 2897 | MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); |
| 2898 | MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); |
| 2899 | MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); |
| 2900 | MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); |
| 2901 | MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); |
| 2902 | MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); |
| 2903 | MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); |
| 2904 | MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); |
| 2905 | MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); |
| 2906 | MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); |
| 2907 | MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); |
| 2908 | |
| 2909 | buf[0] += a; |
| 2910 | buf[1] += b; |
| 2911 | buf[2] += c; |
| 2912 | buf[3] += d; |
| 2913 | } |
| 2914 | |
| 2915 | void mg_md5_update(mg_md5_ctx *ctx, const unsigned char *buf, size_t len) { |
| 2916 | uint32_t t; |
| 2917 | |
| 2918 | t = ctx->bits[0]; |
| 2919 | if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t) ctx->bits[1]++; |
| 2920 | ctx->bits[1] += (uint32_t) len >> 29; |
| 2921 | |
| 2922 | t = (t >> 3) & 0x3f; |
| 2923 | |
| 2924 | if (t) { |
| 2925 | unsigned char *p = (unsigned char *) ctx->in + t; |
| 2926 | |
| 2927 | t = 64 - t; |
| 2928 | if (len < t) { |
| 2929 | memcpy(p, buf, len); |
| 2930 | return; |
| 2931 | } |
| 2932 | memcpy(p, buf, t); |
| 2933 | mg_byte_reverse(ctx->in, 16); |
| 2934 | mg_md5_transform(ctx->buf, (uint32_t *) ctx->in); |
| 2935 | buf += t; |
| 2936 | len -= t; |
| 2937 | } |
| 2938 | |
| 2939 | while (len >= 64) { |
| 2940 | memcpy(ctx->in, buf, 64); |
| 2941 | mg_byte_reverse(ctx->in, 16); |
| 2942 | mg_md5_transform(ctx->buf, (uint32_t *) ctx->in); |
| 2943 | buf += 64; |
| 2944 | len -= 64; |
| 2945 | } |
| 2946 | |
| 2947 | memcpy(ctx->in, buf, len); |
| 2948 | } |
| 2949 | |
| 2950 | void mg_md5_final(mg_md5_ctx *ctx, unsigned char digest[16]) { |
| 2951 | unsigned count; |
| 2952 | unsigned char *p; |
| 2953 | uint32_t *a; |
| 2954 | |
| 2955 | count = (ctx->bits[0] >> 3) & 0x3F; |
| 2956 | |
| 2957 | p = ctx->in + count; |
| 2958 | *p++ = 0x80; |
| 2959 | count = 64 - 1 - count; |
| 2960 | if (count < 8) { |
| 2961 | memset(p, 0, count); |
| 2962 | mg_byte_reverse(ctx->in, 16); |
| 2963 | mg_md5_transform(ctx->buf, (uint32_t *) ctx->in); |
| 2964 | memset(ctx->in, 0, 56); |
| 2965 | } else { |
| 2966 | memset(p, 0, count - 8); |
| 2967 | } |
| 2968 | mg_byte_reverse(ctx->in, 14); |
| 2969 | |
| 2970 | a = (uint32_t *) ctx->in; |
| 2971 | a[14] = ctx->bits[0]; |
| 2972 | a[15] = ctx->bits[1]; |
| 2973 | |
| 2974 | mg_md5_transform(ctx->buf, (uint32_t *) ctx->in); |
| 2975 | mg_byte_reverse((unsigned char *) ctx->buf, 4); |
| 2976 | memcpy(digest, ctx->buf, 16); |
| 2977 | memset((char *) ctx, 0, sizeof(*ctx)); |
| 2978 | } |
| 2979 | #endif |
| 2980 | |
| 2981 | #ifdef MG_ENABLE_LINES |
| 2982 | #line 1 "src/mqtt.c" |
| 2983 | #endif |
| 2984 | |
| 2985 | |
| 2986 | |
| 2987 | |
| 2988 | |
| 2989 | |
| 2990 | |
| 2991 | |
| 2992 | #define MQTT_CLEAN_SESSION 0x02 |
| 2993 | #define MQTT_HAS_WILL 0x04 |
| 2994 | #define MQTT_WILL_RETAIN 0x20 |
| 2995 | #define MQTT_HAS_PASSWORD 0x40 |
| 2996 | #define MQTT_HAS_USER_NAME 0x80 |
| 2997 | |
| 2998 | struct mg_mqtt_pmap { |
| 2999 | uint8_t id; |
| 3000 | uint8_t type; |
| 3001 | }; |
| 3002 | |
| 3003 | static const struct mg_mqtt_pmap s_prop_map[] = { |
| 3004 | {MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, MQTT_PROP_TYPE_BYTE}, |
| 3005 | {MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, MQTT_PROP_TYPE_INT}, |
| 3006 | {MQTT_PROP_CONTENT_TYPE, MQTT_PROP_TYPE_STRING}, |
| 3007 | {MQTT_PROP_RESPONSE_TOPIC, MQTT_PROP_TYPE_STRING}, |
| 3008 | {MQTT_PROP_CORRELATION_DATA, MQTT_PROP_TYPE_BINARY_DATA}, |
| 3009 | {MQTT_PROP_SUBSCRIPTION_IDENTIFIER, MQTT_PROP_TYPE_VARIABLE_INT}, |
| 3010 | {MQTT_PROP_SESSION_EXPIRY_INTERVAL, MQTT_PROP_TYPE_INT}, |
| 3011 | {MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, MQTT_PROP_TYPE_STRING}, |
| 3012 | {MQTT_PROP_SERVER_KEEP_ALIVE, MQTT_PROP_TYPE_SHORT}, |
| 3013 | {MQTT_PROP_AUTHENTICATION_METHOD, MQTT_PROP_TYPE_STRING}, |
| 3014 | {MQTT_PROP_AUTHENTICATION_DATA, MQTT_PROP_TYPE_BINARY_DATA}, |
| 3015 | {MQTT_PROP_REQUEST_PROBLEM_INFORMATION, MQTT_PROP_TYPE_BYTE}, |
| 3016 | {MQTT_PROP_WILL_DELAY_INTERVAL, MQTT_PROP_TYPE_INT}, |
| 3017 | {MQTT_PROP_REQUEST_RESPONSE_INFORMATION, MQTT_PROP_TYPE_BYTE}, |
| 3018 | {MQTT_PROP_RESPONSE_INFORMATION, MQTT_PROP_TYPE_STRING}, |
| 3019 | {MQTT_PROP_SERVER_REFERENCE, MQTT_PROP_TYPE_STRING}, |
| 3020 | {MQTT_PROP_REASON_STRING, MQTT_PROP_TYPE_STRING}, |
| 3021 | {MQTT_PROP_RECEIVE_MAXIMUM, MQTT_PROP_TYPE_SHORT}, |
| 3022 | {MQTT_PROP_TOPIC_ALIAS_MAXIMUM, MQTT_PROP_TYPE_SHORT}, |
| 3023 | {MQTT_PROP_TOPIC_ALIAS, MQTT_PROP_TYPE_SHORT}, |
| 3024 | {MQTT_PROP_MAXIMUM_QOS, MQTT_PROP_TYPE_BYTE}, |
| 3025 | {MQTT_PROP_RETAIN_AVAILABLE, MQTT_PROP_TYPE_BYTE}, |
| 3026 | {MQTT_PROP_USER_PROPERTY, MQTT_PROP_TYPE_STRING_PAIR}, |
| 3027 | {MQTT_PROP_MAXIMUM_PACKET_SIZE, MQTT_PROP_TYPE_INT}, |
| 3028 | {MQTT_PROP_WILDCARD_SUBSCRIPTION_AVAILABLE, MQTT_PROP_TYPE_BYTE}, |
| 3029 | {MQTT_PROP_SUBSCRIPTION_IDENTIFIER_AVAILABLE, MQTT_PROP_TYPE_BYTE}, |
| 3030 | {MQTT_PROP_SHARED_SUBSCRIPTION_AVAILABLE, MQTT_PROP_TYPE_BYTE}}; |
| 3031 | |
| 3032 | void (struct mg_connection *c, uint8_t cmd, uint8_t flags, |
| 3033 | uint32_t len) { |
| 3034 | uint8_t buf[1 + sizeof(len)], *vlen = &buf[1]; |
| 3035 | buf[0] = (uint8_t) ((cmd << 4) | flags); |
| 3036 | do { |
| 3037 | *vlen = len % 0x80; |
| 3038 | len /= 0x80; |
| 3039 | if (len > 0) *vlen |= 0x80; |
| 3040 | vlen++; |
| 3041 | } while (len > 0 && vlen < &buf[sizeof(buf)]); |
| 3042 | mg_send(c, buf, (size_t) (vlen - buf)); |
| 3043 | } |
| 3044 | |
| 3045 | static void mg_send_u16(struct mg_connection *c, uint16_t value) { |
| 3046 | mg_send(c, &value, sizeof(value)); |
| 3047 | } |
| 3048 | |
| 3049 | static void mg_send_u32(struct mg_connection *c, uint32_t value) { |
| 3050 | mg_send(c, &value, sizeof(value)); |
| 3051 | } |
| 3052 | |
| 3053 | static uint8_t compute_variable_length_size(size_t length) { |
| 3054 | uint8_t bytes_needed = 0; |
| 3055 | do { |
| 3056 | bytes_needed++; |
| 3057 | length /= 0x80; |
| 3058 | } while (length > 0); |
| 3059 | return bytes_needed; |
| 3060 | } |
| 3061 | |
| 3062 | static int encode_variable_length(uint8_t *buf, size_t value) { |
| 3063 | int len = 0; |
| 3064 | |
| 3065 | do { |
| 3066 | uint8_t byte = (uint8_t) (value % 128); |
| 3067 | value /= 128; |
| 3068 | if (value > 0) byte |= 0x80; |
| 3069 | buf[len++] = byte; |
| 3070 | } while (value > 0); |
| 3071 | |
| 3072 | return len; |
| 3073 | } |
| 3074 | |
| 3075 | static size_t decode_varint(const uint8_t *buf, size_t len, size_t *value) { |
| 3076 | uint32_t multiplier = 1; |
| 3077 | size_t offset; |
| 3078 | *value = 0; |
| 3079 | |
| 3080 | for (offset = 0; offset < 4 && offset < len; offset++) { |
| 3081 | uint8_t encoded_byte = buf[offset]; |
| 3082 | *value += (encoded_byte & 0x7F) * multiplier; |
| 3083 | multiplier *= 128; |
| 3084 | |
| 3085 | if (!(encoded_byte & 0x80)) return offset + 1; |
| 3086 | } |
| 3087 | |
| 3088 | return 0; |
| 3089 | } |
| 3090 | |
| 3091 | static int mqtt_prop_type_by_id(uint8_t prop_id) { |
| 3092 | size_t i, num_properties = sizeof(s_prop_map) / sizeof(s_prop_map[0]); |
| 3093 | for (i = 0; i < num_properties; ++i) { |
| 3094 | if (s_prop_map[i].id == prop_id) return s_prop_map[i].type; |
| 3095 | } |
| 3096 | return -1; // Property ID not found |
| 3097 | } |
| 3098 | |
| 3099 | // Returns the size of the properties section, without the |
| 3100 | // size of the content's length |
| 3101 | static size_t get_properties_length(struct mg_mqtt_prop *props, size_t count) { |
| 3102 | size_t i, size = 0; |
| 3103 | for (i = 0; i < count; i++) { |
| 3104 | size++; // identifier |
| 3105 | switch (mqtt_prop_type_by_id(props[i].id)) { |
| 3106 | case MQTT_PROP_TYPE_STRING_PAIR: |
| 3107 | size += (uint32_t) (props[i].val.len + props[i].key.len + |
| 3108 | 2 * sizeof(uint16_t)); |
| 3109 | break; |
| 3110 | case MQTT_PROP_TYPE_STRING: |
| 3111 | size += (uint32_t) (props[i].val.len + sizeof(uint16_t)); |
| 3112 | break; |
| 3113 | case MQTT_PROP_TYPE_BINARY_DATA: |
| 3114 | size += (uint32_t) (props[i].val.len + sizeof(uint16_t)); |
| 3115 | break; |
| 3116 | case MQTT_PROP_TYPE_VARIABLE_INT: |
| 3117 | size += compute_variable_length_size((uint32_t) props[i].iv); |
| 3118 | break; |
| 3119 | case MQTT_PROP_TYPE_INT: |
| 3120 | size += (uint32_t) sizeof(uint32_t); |
| 3121 | break; |
| 3122 | case MQTT_PROP_TYPE_SHORT: |
| 3123 | size += (uint32_t) sizeof(uint16_t); |
| 3124 | break; |
| 3125 | case MQTT_PROP_TYPE_BYTE: |
| 3126 | size += (uint32_t) sizeof(uint8_t); |
| 3127 | break; |
| 3128 | default: |
| 3129 | return size; // cannot parse further down |
| 3130 | } |
| 3131 | } |
| 3132 | |
| 3133 | return size; |
| 3134 | } |
| 3135 | |
| 3136 | // returns the entire size of the properties section, including the |
| 3137 | // size of the variable length of the content |
| 3138 | static size_t get_props_size(struct mg_mqtt_prop *props, size_t count) { |
| 3139 | size_t size = get_properties_length(props, count); |
| 3140 | size += compute_variable_length_size(size); |
| 3141 | return size; |
| 3142 | } |
| 3143 | |
| 3144 | static void mg_send_mqtt_properties(struct mg_connection *c, |
| 3145 | struct mg_mqtt_prop *props, size_t nprops) { |
| 3146 | size_t total_size = get_properties_length(props, nprops); |
| 3147 | uint8_t buf_v[4] = {0, 0, 0, 0}; |
| 3148 | uint8_t buf[4] = {0, 0, 0, 0}; |
| 3149 | int i, len = encode_variable_length(buf, total_size); |
| 3150 | |
| 3151 | mg_send(c, buf, (size_t) len); |
| 3152 | for (i = 0; i < (int) nprops; i++) { |
| 3153 | mg_send(c, &props[i].id, sizeof(props[i].id)); |
| 3154 | switch (mqtt_prop_type_by_id(props[i].id)) { |
| 3155 | case MQTT_PROP_TYPE_STRING_PAIR: |
| 3156 | mg_send_u16(c, mg_htons((uint16_t) props[i].key.len)); |
| 3157 | mg_send(c, props[i].key.ptr, props[i].key.len); |
| 3158 | mg_send_u16(c, mg_htons((uint16_t) props[i].val.len)); |
| 3159 | mg_send(c, props[i].val.ptr, props[i].val.len); |
| 3160 | break; |
| 3161 | case MQTT_PROP_TYPE_BYTE: |
| 3162 | mg_send(c, &props[i].iv, sizeof(uint8_t)); |
| 3163 | break; |
| 3164 | case MQTT_PROP_TYPE_SHORT: |
| 3165 | mg_send_u16(c, mg_htons((uint16_t) props[i].iv)); |
| 3166 | break; |
| 3167 | case MQTT_PROP_TYPE_INT: |
| 3168 | mg_send_u32(c, mg_htonl((uint32_t) props[i].iv)); |
| 3169 | break; |
| 3170 | case MQTT_PROP_TYPE_STRING: |
| 3171 | mg_send_u16(c, mg_htons((uint16_t) props[i].val.len)); |
| 3172 | mg_send(c, props[i].val.ptr, props[i].val.len); |
| 3173 | break; |
| 3174 | case MQTT_PROP_TYPE_BINARY_DATA: |
| 3175 | mg_send_u16(c, mg_htons((uint16_t) props[i].val.len)); |
| 3176 | mg_send(c, props[i].val.ptr, props[i].val.len); |
| 3177 | break; |
| 3178 | case MQTT_PROP_TYPE_VARIABLE_INT: |
| 3179 | len = encode_variable_length(buf_v, props[i].iv); |
| 3180 | mg_send(c, buf_v, (size_t) len); |
| 3181 | break; |
| 3182 | } |
| 3183 | } |
| 3184 | } |
| 3185 | |
| 3186 | size_t mg_mqtt_next_prop(struct mg_mqtt_message *msg, struct mg_mqtt_prop *prop, |
| 3187 | size_t ofs) { |
| 3188 | uint8_t *i = (uint8_t *) msg->dgram.ptr + msg->props_start + ofs; |
| 3189 | uint8_t *end = (uint8_t *) msg->dgram.ptr + msg->dgram.len; |
| 3190 | size_t new_pos = ofs, len; |
| 3191 | prop->id = i[0]; |
| 3192 | |
| 3193 | if (ofs >= msg->dgram.len || ofs >= msg->props_start + msg->props_size) |
| 3194 | return 0; |
| 3195 | i++, new_pos++; |
| 3196 | |
| 3197 | switch (mqtt_prop_type_by_id(prop->id)) { |
| 3198 | case MQTT_PROP_TYPE_STRING_PAIR: |
| 3199 | prop->key.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]); |
| 3200 | prop->key.ptr = (char *) i + 2; |
| 3201 | i += 2 + prop->key.len; |
| 3202 | prop->val.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]); |
| 3203 | prop->val.ptr = (char *) i + 2; |
| 3204 | new_pos += 2 * sizeof(uint16_t) + prop->val.len + prop->key.len; |
| 3205 | break; |
| 3206 | case MQTT_PROP_TYPE_BYTE: |
| 3207 | prop->iv = (uint8_t) i[0]; |
| 3208 | new_pos++; |
| 3209 | break; |
| 3210 | case MQTT_PROP_TYPE_SHORT: |
| 3211 | prop->iv = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]); |
| 3212 | new_pos += sizeof(uint16_t); |
| 3213 | break; |
| 3214 | case MQTT_PROP_TYPE_INT: |
| 3215 | prop->iv = ((uint32_t) i[0] << 24) | ((uint32_t) i[1] << 16) | |
| 3216 | ((uint32_t) i[2] << 8) | i[3]; |
| 3217 | new_pos += sizeof(uint32_t); |
| 3218 | break; |
| 3219 | case MQTT_PROP_TYPE_STRING: |
| 3220 | prop->val.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]); |
| 3221 | prop->val.ptr = (char *) i + 2; |
| 3222 | new_pos += 2 + prop->val.len; |
| 3223 | break; |
| 3224 | case MQTT_PROP_TYPE_BINARY_DATA: |
| 3225 | prop->val.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]); |
| 3226 | prop->val.ptr = (char *) i + 2; |
| 3227 | new_pos += 2 + prop->val.len; |
| 3228 | break; |
| 3229 | case MQTT_PROP_TYPE_VARIABLE_INT: |
| 3230 | len = decode_varint(i, (size_t) (end - i), (size_t *) &prop->iv); |
| 3231 | new_pos = (!len) ? 0 : new_pos + len; |
| 3232 | break; |
| 3233 | default: |
| 3234 | new_pos = 0; |
| 3235 | } |
| 3236 | |
| 3237 | return new_pos; |
| 3238 | } |
| 3239 | |
| 3240 | void mg_mqtt_login(struct mg_connection *c, const struct mg_mqtt_opts *opts) { |
| 3241 | char rnd[10], client_id[21]; |
| 3242 | struct mg_str cid = opts->client_id; |
| 3243 | size_t total_len = 7 + 1 + 2 + 2; |
| 3244 | uint8_t hdr[8] = {0, 4, 'M', 'Q', 'T', 'T', opts->version, 0}; |
| 3245 | |
| 3246 | if (cid.len == 0) { |
| 3247 | mg_random(rnd, sizeof(rnd)); |
| 3248 | mg_hex(rnd, sizeof(rnd), client_id); |
| 3249 | client_id[sizeof(client_id) - 1] = '\0'; |
| 3250 | cid = mg_str(client_id); |
| 3251 | } |
| 3252 | |
| 3253 | if (hdr[6] == 0) hdr[6] = 4; // If version is not set, use 4 (3.1.1) |
| 3254 | c->is_mqtt5 = hdr[6] == 5; // Set version 5 flag |
| 3255 | hdr[7] = (uint8_t) ((opts->qos & 3) << 3); // Connection flags |
| 3256 | if (opts->user.len > 0) { |
| 3257 | total_len += 2 + (uint32_t) opts->user.len; |
| 3258 | hdr[7] |= MQTT_HAS_USER_NAME; |
| 3259 | } |
| 3260 | if (opts->pass.len > 0) { |
| 3261 | total_len += 2 + (uint32_t) opts->pass.len; |
| 3262 | hdr[7] |= MQTT_HAS_PASSWORD; |
| 3263 | } |
| 3264 | if (opts->topic.len > 0 && opts->message.len > 0) { |
| 3265 | total_len += 4 + (uint32_t) opts->topic.len + (uint32_t) opts->message.len; |
| 3266 | hdr[7] |= MQTT_HAS_WILL; |
| 3267 | } |
| 3268 | if (opts->clean || cid.len == 0) hdr[7] |= MQTT_CLEAN_SESSION; |
| 3269 | if (opts->retain) hdr[7] |= MQTT_WILL_RETAIN; |
| 3270 | total_len += (uint32_t) cid.len; |
| 3271 | if (c->is_mqtt5) { |
| 3272 | total_len += get_props_size(opts->props, opts->num_props); |
| 3273 | if (hdr[7] & MQTT_HAS_WILL) |
| 3274 | total_len += get_props_size(opts->will_props, opts->num_will_props); |
| 3275 | } |
| 3276 | |
| 3277 | mg_mqtt_send_header(c, MQTT_CMD_CONNECT, 0, (uint32_t) total_len); |
| 3278 | mg_send(c, hdr, sizeof(hdr)); |
| 3279 | // keepalive == 0 means "do not disconnect us!" |
| 3280 | mg_send_u16(c, mg_htons((uint16_t) opts->keepalive)); |
| 3281 | |
| 3282 | if (c->is_mqtt5) mg_send_mqtt_properties(c, opts->props, opts->num_props); |
| 3283 | |
| 3284 | mg_send_u16(c, mg_htons((uint16_t) cid.len)); |
| 3285 | mg_send(c, cid.ptr, cid.len); |
| 3286 | |
| 3287 | if (hdr[7] & MQTT_HAS_WILL) { |
| 3288 | if (c->is_mqtt5) |
| 3289 | mg_send_mqtt_properties(c, opts->will_props, opts->num_will_props); |
| 3290 | |
| 3291 | mg_send_u16(c, mg_htons((uint16_t) opts->topic.len)); |
| 3292 | mg_send(c, opts->topic.ptr, opts->topic.len); |
| 3293 | mg_send_u16(c, mg_htons((uint16_t) opts->message.len)); |
| 3294 | mg_send(c, opts->message.ptr, opts->message.len); |
| 3295 | } |
| 3296 | if (opts->user.len > 0) { |
| 3297 | mg_send_u16(c, mg_htons((uint16_t) opts->user.len)); |
| 3298 | mg_send(c, opts->user.ptr, opts->user.len); |
| 3299 | } |
| 3300 | if (opts->pass.len > 0) { |
| 3301 | mg_send_u16(c, mg_htons((uint16_t) opts->pass.len)); |
| 3302 | mg_send(c, opts->pass.ptr, opts->pass.len); |
| 3303 | } |
| 3304 | } |
| 3305 | |
| 3306 | void mg_mqtt_pub(struct mg_connection *c, const struct mg_mqtt_opts *opts) { |
| 3307 | uint8_t flags = (uint8_t) (((opts->qos & 3) << 1) | (opts->retain ? 1 : 0)); |
| 3308 | size_t len = 2 + opts->topic.len + opts->message.len; |
| 3309 | MG_DEBUG(("%lu [%.*s] -> [%.*s]" , c->id, (int) opts->topic.len, |
| 3310 | (char *) opts->topic.ptr, (int) opts->message.len, |
| 3311 | (char *) opts->message.ptr)); |
| 3312 | if (opts->qos > 0) len += 2; |
| 3313 | if (c->is_mqtt5) len += get_props_size(opts->props, opts->num_props); |
| 3314 | |
| 3315 | mg_mqtt_send_header(c, MQTT_CMD_PUBLISH, flags, (uint32_t) len); |
| 3316 | mg_send_u16(c, mg_htons((uint16_t) opts->topic.len)); |
| 3317 | mg_send(c, opts->topic.ptr, opts->topic.len); |
| 3318 | if (opts->qos > 0) { |
| 3319 | if (++c->mgr->mqtt_id == 0) ++c->mgr->mqtt_id; |
| 3320 | mg_send_u16(c, mg_htons(c->mgr->mqtt_id)); |
| 3321 | } |
| 3322 | |
| 3323 | if (c->is_mqtt5) mg_send_mqtt_properties(c, opts->props, opts->num_props); |
| 3324 | |
| 3325 | mg_send(c, opts->message.ptr, opts->message.len); |
| 3326 | } |
| 3327 | |
| 3328 | void mg_mqtt_sub(struct mg_connection *c, const struct mg_mqtt_opts *opts) { |
| 3329 | uint8_t qos_ = opts->qos & 3; |
| 3330 | size_t plen = c->is_mqtt5 ? get_props_size(opts->props, opts->num_props) : 0; |
| 3331 | size_t len = 2 + opts->topic.len + 2 + 1 + plen; |
| 3332 | |
| 3333 | mg_mqtt_send_header(c, MQTT_CMD_SUBSCRIBE, 2, (uint32_t) len); |
| 3334 | if (++c->mgr->mqtt_id == 0) ++c->mgr->mqtt_id; |
| 3335 | mg_send_u16(c, mg_htons(c->mgr->mqtt_id)); |
| 3336 | if (c->is_mqtt5) mg_send_mqtt_properties(c, opts->props, opts->num_props); |
| 3337 | |
| 3338 | mg_send_u16(c, mg_htons((uint16_t) opts->topic.len)); |
| 3339 | mg_send(c, opts->topic.ptr, opts->topic.len); |
| 3340 | mg_send(c, &qos_, sizeof(qos_)); |
| 3341 | } |
| 3342 | |
| 3343 | int mg_mqtt_parse(const uint8_t *buf, size_t len, uint8_t version, |
| 3344 | struct mg_mqtt_message *m) { |
| 3345 | uint8_t lc = 0, *p, *end; |
| 3346 | uint32_t n = 0, len_len = 0; |
| 3347 | |
| 3348 | memset(m, 0, sizeof(*m)); |
| 3349 | m->dgram.ptr = (char *) buf; |
| 3350 | if (len < 2) return MQTT_INCOMPLETE; |
| 3351 | m->cmd = (uint8_t) (buf[0] >> 4); |
| 3352 | m->qos = (buf[0] >> 1) & 3; |
| 3353 | |
| 3354 | n = len_len = 0; |
| 3355 | p = (uint8_t *) buf + 1; |
| 3356 | while ((size_t) (p - buf) < len) { |
| 3357 | lc = *((uint8_t *) p++); |
| 3358 | n += (uint32_t) ((lc & 0x7f) << 7 * len_len); |
| 3359 | len_len++; |
| 3360 | if (!(lc & 0x80)) break; |
| 3361 | if (len_len >= 4) return MQTT_MALFORMED; |
| 3362 | } |
| 3363 | end = p + n; |
| 3364 | if ((lc & 0x80) || (end > buf + len)) return MQTT_INCOMPLETE; |
| 3365 | m->dgram.len = (size_t) (end - buf); |
| 3366 | |
| 3367 | switch (m->cmd) { |
| 3368 | case MQTT_CMD_CONNACK: |
| 3369 | if (end - p < 2) return MQTT_MALFORMED; |
| 3370 | m->ack = p[1]; |
| 3371 | break; |
| 3372 | case MQTT_CMD_PUBACK: |
| 3373 | case MQTT_CMD_PUBREC: |
| 3374 | case MQTT_CMD_PUBREL: |
| 3375 | case MQTT_CMD_PUBCOMP: |
| 3376 | case MQTT_CMD_SUBSCRIBE: |
| 3377 | case MQTT_CMD_SUBACK: |
| 3378 | case MQTT_CMD_UNSUBSCRIBE: |
| 3379 | case MQTT_CMD_UNSUBACK: |
| 3380 | if (p + 2 > end) return MQTT_MALFORMED; |
| 3381 | m->id = (uint16_t) ((((uint16_t) p[0]) << 8) | p[1]); |
| 3382 | p += 2; |
| 3383 | break; |
| 3384 | case MQTT_CMD_PUBLISH: { |
| 3385 | if (p + 2 > end) return MQTT_MALFORMED; |
| 3386 | m->topic.len = (uint16_t) ((((uint16_t) p[0]) << 8) | p[1]); |
| 3387 | m->topic.ptr = (char *) p + 2; |
| 3388 | p += 2 + m->topic.len; |
| 3389 | if (p > end) return MQTT_MALFORMED; |
| 3390 | if (m->qos > 0) { |
| 3391 | if (p + 2 > end) return MQTT_MALFORMED; |
| 3392 | m->id = (uint16_t) ((((uint16_t) p[0]) << 8) | p[1]); |
| 3393 | p += 2; |
| 3394 | } |
| 3395 | if (p > end) return MQTT_MALFORMED; |
| 3396 | if (version == 5 && p + 2 < end) { |
| 3397 | len_len = (uint32_t) decode_varint(p, (size_t) (end - p), &m->props_size); |
| 3398 | if (!len_len) return MQTT_MALFORMED; |
| 3399 | m->props_start = (size_t) (p + len_len - buf); |
| 3400 | p += len_len + m->props_size; |
| 3401 | } |
| 3402 | if (p > end) return MQTT_MALFORMED; |
| 3403 | m->data.ptr = (char *) p; |
| 3404 | m->data.len = (size_t) (end - p); |
| 3405 | break; |
| 3406 | } |
| 3407 | default: |
| 3408 | break; |
| 3409 | } |
| 3410 | return MQTT_OK; |
| 3411 | } |
| 3412 | |
| 3413 | static void mqtt_cb(struct mg_connection *c, int ev, void *ev_data, |
| 3414 | void *fn_data) { |
| 3415 | if (ev == MG_EV_READ) { |
| 3416 | for (;;) { |
| 3417 | uint8_t version = c->is_mqtt5 ? 5 : 4; |
| 3418 | struct mg_mqtt_message mm; |
| 3419 | int rc = mg_mqtt_parse(c->recv.buf, c->recv.len, version, &mm); |
| 3420 | if (rc == MQTT_MALFORMED) { |
| 3421 | MG_ERROR(("%lu MQTT malformed message" , c->id)); |
| 3422 | c->is_closing = 1; |
| 3423 | break; |
| 3424 | } else if (rc == MQTT_OK) { |
| 3425 | MG_VERBOSE(("%lu MQTT CMD %d len %d [%.*s]" , c->id, mm.cmd, |
| 3426 | (int) mm.dgram.len, (int) mm.data.len, mm.data.ptr)); |
| 3427 | switch (mm.cmd) { |
| 3428 | case MQTT_CMD_CONNACK: |
| 3429 | mg_call(c, MG_EV_MQTT_OPEN, &mm.ack); |
| 3430 | if (mm.ack == 0) { |
| 3431 | MG_DEBUG(("%lu Connected" , c->id)); |
| 3432 | } else { |
| 3433 | MG_ERROR(("%lu MQTT auth failed, code %d" , c->id, mm.ack)); |
| 3434 | c->is_closing = 1; |
| 3435 | } |
| 3436 | break; |
| 3437 | case MQTT_CMD_PUBLISH: { |
| 3438 | MG_DEBUG(("%lu [%.*s] -> [%.*s]" , c->id, (int) mm.topic.len, |
| 3439 | mm.topic.ptr, (int) mm.data.len, mm.data.ptr)); |
| 3440 | if (mm.qos > 0) { |
| 3441 | uint16_t id = mg_ntohs(mm.id); |
| 3442 | uint32_t remaining_len = sizeof(id); |
| 3443 | if (c->is_mqtt5) remaining_len += 2; // 3.4.2 |
| 3444 | |
| 3445 | mg_mqtt_send_header( |
| 3446 | c, mm.qos == 2 ? MQTT_CMD_PUBREC : MQTT_CMD_PUBACK, 0, |
| 3447 | remaining_len); |
| 3448 | mg_send(c, &id, sizeof(id)); |
| 3449 | |
| 3450 | if (c->is_mqtt5) { |
| 3451 | uint16_t zero = 0; |
| 3452 | mg_send(c, &zero, sizeof(zero)); |
| 3453 | } |
| 3454 | } |
| 3455 | mg_call(c, MG_EV_MQTT_MSG, &mm); // let the app handle qos stuff |
| 3456 | break; |
| 3457 | } |
| 3458 | case MQTT_CMD_PUBREC: { // MQTT5: 3.5.2-1 TODO(): variable header rc |
| 3459 | uint16_t id = mg_ntohs(mm.id); |
| 3460 | uint32_t remaining_len = sizeof(id); // MQTT5 3.6.2-1 |
| 3461 | mg_mqtt_send_header(c, MQTT_CMD_PUBREL, 2, remaining_len); |
| 3462 | mg_send(c, &id, sizeof(id)); // MQTT5 3.6.1-1, flags = 2 |
| 3463 | break; |
| 3464 | } |
| 3465 | case MQTT_CMD_PUBREL: { // MQTT5: 3.6.2-1 TODO(): variable header rc |
| 3466 | uint16_t id = mg_ntohs(mm.id); |
| 3467 | uint32_t remaining_len = sizeof(id); // MQTT5 3.7.2-1 |
| 3468 | mg_mqtt_send_header(c, MQTT_CMD_PUBCOMP, 0, remaining_len); |
| 3469 | mg_send(c, &id, sizeof(id)); |
| 3470 | break; |
| 3471 | } |
| 3472 | } |
| 3473 | mg_call(c, MG_EV_MQTT_CMD, &mm); |
| 3474 | mg_iobuf_del(&c->recv, 0, mm.dgram.len); |
| 3475 | } else { |
| 3476 | break; |
| 3477 | } |
| 3478 | } |
| 3479 | } |
| 3480 | (void) ev_data; |
| 3481 | (void) fn_data; |
| 3482 | } |
| 3483 | |
| 3484 | void mg_mqtt_ping(struct mg_connection *nc) { |
| 3485 | mg_mqtt_send_header(nc, MQTT_CMD_PINGREQ, 0, 0); |
| 3486 | } |
| 3487 | |
| 3488 | void mg_mqtt_pong(struct mg_connection *nc) { |
| 3489 | mg_mqtt_send_header(nc, MQTT_CMD_PINGRESP, 0, 0); |
| 3490 | } |
| 3491 | |
| 3492 | void mg_mqtt_disconnect(struct mg_connection *c, |
| 3493 | const struct mg_mqtt_opts *opts) { |
| 3494 | size_t len = 0; |
| 3495 | if (c->is_mqtt5) len = 1 + get_props_size(opts->props, opts->num_props); |
| 3496 | mg_mqtt_send_header(c, MQTT_CMD_DISCONNECT, 0, (uint32_t) len); |
| 3497 | |
| 3498 | if (c->is_mqtt5) { |
| 3499 | uint8_t zero = 0; |
| 3500 | mg_send(c, &zero, sizeof(zero)); // reason code |
| 3501 | mg_send_mqtt_properties(c, opts->props, opts->num_props); |
| 3502 | } |
| 3503 | } |
| 3504 | |
| 3505 | struct mg_connection *mg_mqtt_connect(struct mg_mgr *mgr, const char *url, |
| 3506 | const struct mg_mqtt_opts *opts, |
| 3507 | mg_event_handler_t fn, void *fn_data) { |
| 3508 | struct mg_connection *c = mg_connect(mgr, url, fn, fn_data); |
| 3509 | if (c != NULL) { |
| 3510 | struct mg_mqtt_opts empty; |
| 3511 | memset(&empty, 0, sizeof(empty)); |
| 3512 | mg_mqtt_login(c, opts == NULL ? &empty : opts); |
| 3513 | c->pfn = mqtt_cb; |
| 3514 | } |
| 3515 | return c; |
| 3516 | } |
| 3517 | |
| 3518 | struct mg_connection *mg_mqtt_listen(struct mg_mgr *mgr, const char *url, |
| 3519 | mg_event_handler_t fn, void *fn_data) { |
| 3520 | struct mg_connection *c = mg_listen(mgr, url, fn, fn_data); |
| 3521 | if (c != NULL) c->pfn = mqtt_cb, c->pfn_data = mgr; |
| 3522 | return c; |
| 3523 | } |
| 3524 | |
| 3525 | #ifdef MG_ENABLE_LINES |
| 3526 | #line 1 "src/net.c" |
| 3527 | #endif |
| 3528 | |
| 3529 | |
| 3530 | |
| 3531 | |
| 3532 | |
| 3533 | |
| 3534 | |
| 3535 | |
| 3536 | size_t mg_vprintf(struct mg_connection *c, const char *fmt, va_list *ap) { |
| 3537 | size_t old = c->send.len; |
| 3538 | mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, ap); |
| 3539 | return c->send.len - old; |
| 3540 | } |
| 3541 | |
| 3542 | size_t mg_printf(struct mg_connection *c, const char *fmt, ...) { |
| 3543 | size_t len = 0; |
| 3544 | va_list ap; |
| 3545 | va_start(ap, fmt); |
| 3546 | len = mg_vprintf(c, fmt, &ap); |
| 3547 | va_end(ap); |
| 3548 | return len; |
| 3549 | } |
| 3550 | |
| 3551 | static bool mg_atonl(struct mg_str str, struct mg_addr *addr) { |
| 3552 | uint32_t localhost = mg_htonl(0x7f000001); |
| 3553 | if (mg_vcasecmp(&str, "localhost" ) != 0) return false; |
| 3554 | memcpy(addr->ip, &localhost, sizeof(uint32_t)); |
| 3555 | addr->is_ip6 = false; |
| 3556 | return true; |
| 3557 | } |
| 3558 | |
| 3559 | static bool mg_atone(struct mg_str str, struct mg_addr *addr) { |
| 3560 | if (str.len > 0) return false; |
| 3561 | memset(addr->ip, 0, sizeof(addr->ip)); |
| 3562 | addr->is_ip6 = false; |
| 3563 | return true; |
| 3564 | } |
| 3565 | |
| 3566 | static bool mg_aton4(struct mg_str str, struct mg_addr *addr) { |
| 3567 | uint8_t data[4] = {0, 0, 0, 0}; |
| 3568 | size_t i, num_dots = 0; |
| 3569 | for (i = 0; i < str.len; i++) { |
| 3570 | if (str.ptr[i] >= '0' && str.ptr[i] <= '9') { |
| 3571 | int octet = data[num_dots] * 10 + (str.ptr[i] - '0'); |
| 3572 | if (octet > 255) return false; |
| 3573 | data[num_dots] = (uint8_t) octet; |
| 3574 | } else if (str.ptr[i] == '.') { |
| 3575 | if (num_dots >= 3 || i == 0 || str.ptr[i - 1] == '.') return false; |
| 3576 | num_dots++; |
| 3577 | } else { |
| 3578 | return false; |
| 3579 | } |
| 3580 | } |
| 3581 | if (num_dots != 3 || str.ptr[i - 1] == '.') return false; |
| 3582 | memcpy(&addr->ip, data, sizeof(data)); |
| 3583 | addr->is_ip6 = false; |
| 3584 | return true; |
| 3585 | } |
| 3586 | |
| 3587 | static bool mg_v4mapped(struct mg_str str, struct mg_addr *addr) { |
| 3588 | int i; |
| 3589 | uint32_t ipv4; |
| 3590 | if (str.len < 14) return false; |
| 3591 | if (str.ptr[0] != ':' || str.ptr[1] != ':' || str.ptr[6] != ':') return false; |
| 3592 | for (i = 2; i < 6; i++) { |
| 3593 | if (str.ptr[i] != 'f' && str.ptr[i] != 'F') return false; |
| 3594 | } |
| 3595 | //struct mg_str s = mg_str_n(&str.ptr[7], str.len - 7); |
| 3596 | if (!mg_aton4(mg_str_n(&str.ptr[7], str.len - 7), addr)) return false; |
| 3597 | memcpy(&ipv4, addr->ip, sizeof(ipv4)); |
| 3598 | memset(addr->ip, 0, sizeof(addr->ip)); |
| 3599 | addr->ip[10] = addr->ip[11] = 255; |
| 3600 | memcpy(&addr->ip[12], &ipv4, 4); |
| 3601 | addr->is_ip6 = true; |
| 3602 | return true; |
| 3603 | } |
| 3604 | |
| 3605 | static bool mg_aton6(struct mg_str str, struct mg_addr *addr) { |
| 3606 | size_t i, j = 0, n = 0, dc = 42; |
| 3607 | if (str.len > 2 && str.ptr[0] == '[') str.ptr++, str.len -= 2; |
| 3608 | if (mg_v4mapped(str, addr)) return true; |
| 3609 | for (i = 0; i < str.len; i++) { |
| 3610 | if ((str.ptr[i] >= '0' && str.ptr[i] <= '9') || |
| 3611 | (str.ptr[i] >= 'a' && str.ptr[i] <= 'f') || |
| 3612 | (str.ptr[i] >= 'A' && str.ptr[i] <= 'F')) { |
| 3613 | unsigned long val; |
| 3614 | if (i > j + 3) return false; |
| 3615 | // MG_DEBUG(("%zu %zu [%.*s]", i, j, (int) (i - j + 1), &str.ptr[j])); |
| 3616 | val = mg_unhexn(&str.ptr[j], i - j + 1); |
| 3617 | addr->ip[n] = (uint8_t) ((val >> 8) & 255); |
| 3618 | addr->ip[n + 1] = (uint8_t) (val & 255); |
| 3619 | } else if (str.ptr[i] == ':') { |
| 3620 | j = i + 1; |
| 3621 | if (i > 0 && str.ptr[i - 1] == ':') { |
| 3622 | dc = n; // Double colon |
| 3623 | if (i > 1 && str.ptr[i - 2] == ':') return false; |
| 3624 | } else if (i > 0) { |
| 3625 | n += 2; |
| 3626 | } |
| 3627 | if (n > 14) return false; |
| 3628 | addr->ip[n] = addr->ip[n + 1] = 0; // For trailing :: |
| 3629 | } else { |
| 3630 | return false; |
| 3631 | } |
| 3632 | } |
| 3633 | if (n < 14 && dc == 42) return false; |
| 3634 | if (n < 14) { |
| 3635 | memmove(&addr->ip[dc + (14 - n)], &addr->ip[dc], n - dc + 2); |
| 3636 | memset(&addr->ip[dc], 0, 14 - n); |
| 3637 | } |
| 3638 | |
| 3639 | addr->is_ip6 = true; |
| 3640 | return true; |
| 3641 | } |
| 3642 | |
| 3643 | bool mg_aton(struct mg_str str, struct mg_addr *addr) { |
| 3644 | // MG_INFO(("[%.*s]", (int) str.len, str.ptr)); |
| 3645 | return mg_atone(str, addr) || mg_atonl(str, addr) || mg_aton4(str, addr) || |
| 3646 | mg_aton6(str, addr); |
| 3647 | } |
| 3648 | |
| 3649 | struct mg_connection *mg_alloc_conn(struct mg_mgr *mgr) { |
| 3650 | struct mg_connection *c = |
| 3651 | (struct mg_connection *) calloc(1, sizeof(*c) + mgr->extraconnsize); |
| 3652 | if (c != NULL) { |
| 3653 | c->mgr = mgr; |
| 3654 | c->send.align = c->recv.align = MG_IO_SIZE; |
| 3655 | c->id = ++mgr->nextid; |
| 3656 | } |
| 3657 | return c; |
| 3658 | } |
| 3659 | |
| 3660 | void mg_close_conn(struct mg_connection *c) { |
| 3661 | mg_resolve_cancel(c); // Close any pending DNS query |
| 3662 | LIST_DELETE(struct mg_connection, &c->mgr->conns, c); |
| 3663 | if (c == c->mgr->dns4.c) c->mgr->dns4.c = NULL; |
| 3664 | if (c == c->mgr->dns6.c) c->mgr->dns6.c = NULL; |
| 3665 | // Order of operations is important. `MG_EV_CLOSE` event must be fired |
| 3666 | // before we deallocate received data, see #1331 |
| 3667 | mg_call(c, MG_EV_CLOSE, NULL); |
| 3668 | MG_DEBUG(("%lu %p closed" , c->id, c->fd)); |
| 3669 | |
| 3670 | mg_tls_free(c); |
| 3671 | mg_iobuf_free(&c->recv); |
| 3672 | mg_iobuf_free(&c->send); |
| 3673 | memset(c, 0, sizeof(*c)); |
| 3674 | free(c); |
| 3675 | } |
| 3676 | |
| 3677 | struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *url, |
| 3678 | mg_event_handler_t fn, void *fn_data) { |
| 3679 | struct mg_connection *c = NULL; |
| 3680 | if (url == NULL || url[0] == '\0') { |
| 3681 | MG_ERROR(("null url" )); |
| 3682 | } else if ((c = mg_alloc_conn(mgr)) == NULL) { |
| 3683 | MG_ERROR(("OOM" )); |
| 3684 | } else { |
| 3685 | LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c); |
| 3686 | c->is_udp = (strncmp(url, "udp:" , 4) == 0); |
| 3687 | c->fd = (void *) (size_t) MG_INVALID_SOCKET; |
| 3688 | c->fn = fn; |
| 3689 | c->is_client = true; |
| 3690 | c->fn_data = fn_data; |
| 3691 | MG_DEBUG(("%lu %p %s" , c->id, c->fd, url)); |
| 3692 | mg_call(c, MG_EV_OPEN, NULL); |
| 3693 | mg_resolve(c, url); |
| 3694 | } |
| 3695 | return c; |
| 3696 | } |
| 3697 | |
| 3698 | struct mg_connection *mg_listen(struct mg_mgr *mgr, const char *url, |
| 3699 | mg_event_handler_t fn, void *fn_data) { |
| 3700 | struct mg_connection *c = NULL; |
| 3701 | if ((c = mg_alloc_conn(mgr)) == NULL) { |
| 3702 | MG_ERROR(("OOM %s" , url)); |
| 3703 | } else if (!mg_open_listener(c, url)) { |
| 3704 | MG_ERROR(("Failed: %s, errno %d" , url, errno)); |
| 3705 | free(c); |
| 3706 | c = NULL; |
| 3707 | } else { |
| 3708 | c->is_listening = 1; |
| 3709 | c->is_udp = strncmp(url, "udp:" , 4) == 0; |
| 3710 | LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c); |
| 3711 | c->fn = fn; |
| 3712 | c->fn_data = fn_data; |
| 3713 | mg_call(c, MG_EV_OPEN, NULL); |
| 3714 | MG_DEBUG(("%lu %p %s" , c->id, c->fd, url)); |
| 3715 | } |
| 3716 | return c; |
| 3717 | } |
| 3718 | |
| 3719 | struct mg_connection *mg_wrapfd(struct mg_mgr *mgr, int fd, |
| 3720 | mg_event_handler_t fn, void *fn_data) { |
| 3721 | struct mg_connection *c = mg_alloc_conn(mgr); |
| 3722 | if (c != NULL) { |
| 3723 | c->fd = (void *) (size_t) fd; |
| 3724 | c->fn = fn; |
| 3725 | c->fn_data = fn_data; |
| 3726 | MG_EPOLL_ADD(c); |
| 3727 | mg_call(c, MG_EV_OPEN, NULL); |
| 3728 | LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c); |
| 3729 | } |
| 3730 | return c; |
| 3731 | } |
| 3732 | |
| 3733 | struct mg_timer *mg_timer_add(struct mg_mgr *mgr, uint64_t milliseconds, |
| 3734 | unsigned flags, void (*fn)(void *), void *arg) { |
| 3735 | struct mg_timer *t = (struct mg_timer *) calloc(1, sizeof(*t)); |
| 3736 | if (t != NULL) { |
| 3737 | mg_timer_init(&mgr->timers, t, milliseconds, flags, fn, arg); |
| 3738 | t->id = mgr->timerid++; |
| 3739 | } |
| 3740 | return t; |
| 3741 | } |
| 3742 | |
| 3743 | void mg_mgr_free(struct mg_mgr *mgr) { |
| 3744 | struct mg_connection *c; |
| 3745 | struct mg_timer *tmp, *t = mgr->timers; |
| 3746 | while (t != NULL) tmp = t->next, free(t), t = tmp; |
| 3747 | mgr->timers = NULL; // Important. Next call to poll won't touch timers |
| 3748 | for (c = mgr->conns; c != NULL; c = c->next) c->is_closing = 1; |
| 3749 | mg_mgr_poll(mgr, 0); |
| 3750 | #if MG_ENABLE_FREERTOS_TCP |
| 3751 | FreeRTOS_DeleteSocketSet(mgr->ss); |
| 3752 | #endif |
| 3753 | MG_DEBUG(("All connections closed" )); |
| 3754 | #if MG_ENABLE_EPOLL |
| 3755 | if (mgr->epoll_fd >= 0) close(mgr->epoll_fd), mgr->epoll_fd = -1; |
| 3756 | #endif |
| 3757 | } |
| 3758 | |
| 3759 | void mg_mgr_init(struct mg_mgr *mgr) { |
| 3760 | memset(mgr, 0, sizeof(*mgr)); |
| 3761 | #if MG_ENABLE_EPOLL |
| 3762 | if ((mgr->epoll_fd = epoll_create1(0)) < 0) MG_ERROR(("epoll: %d" , errno)); |
| 3763 | #else |
| 3764 | mgr->epoll_fd = -1; |
| 3765 | #endif |
| 3766 | #if MG_ARCH == MG_ARCH_WIN32 && MG_ENABLE_WINSOCK |
| 3767 | // clang-format off |
| 3768 | { WSADATA data; WSAStartup(MAKEWORD(2, 2), &data); } |
| 3769 | // clang-format on |
| 3770 | #elif MG_ENABLE_FREERTOS_TCP |
| 3771 | mgr->ss = FreeRTOS_CreateSocketSet(); |
| 3772 | #elif defined(__unix) || defined(__unix__) || defined(__APPLE__) |
| 3773 | // Ignore SIGPIPE signal, so if client cancels the request, it |
| 3774 | // won't kill the whole process. |
| 3775 | signal(SIGPIPE, SIG_IGN); |
| 3776 | #endif |
| 3777 | mgr->dnstimeout = 3000; |
| 3778 | mgr->dns4.url = "udp://8.8.8.8:53" ; |
| 3779 | mgr->dns6.url = "udp://[2001:4860:4860::8888]:53" ; |
| 3780 | } |
| 3781 | |
| 3782 | #ifdef MG_ENABLE_LINES |
| 3783 | #line 1 "src/printf.c" |
| 3784 | #endif |
| 3785 | |
| 3786 | |
| 3787 | |
| 3788 | |
| 3789 | size_t mg_queue_vprintf(struct mg_queue *q, const char *fmt, va_list *ap) { |
| 3790 | size_t len = mg_snprintf(NULL, 0, fmt, ap); |
| 3791 | char *buf; |
| 3792 | if (len == 0 || mg_queue_book(q, &buf, len + 1) < len + 1) { |
| 3793 | len = 0; // Nah. Not enough space |
| 3794 | } else { |
| 3795 | len = mg_vsnprintf((char *) buf, len + 1, fmt, ap); |
| 3796 | mg_queue_add(q, len); |
| 3797 | } |
| 3798 | return len; |
| 3799 | } |
| 3800 | |
| 3801 | size_t mg_queue_printf(struct mg_queue *q, const char *fmt, ...) { |
| 3802 | va_list ap; |
| 3803 | size_t len; |
| 3804 | va_start(ap, fmt); |
| 3805 | len = mg_queue_vprintf(q, fmt, &ap); |
| 3806 | va_end(ap); |
| 3807 | return len; |
| 3808 | } |
| 3809 | |
| 3810 | static void mg_pfn_iobuf_private(char ch, void *param, bool expand) { |
| 3811 | struct mg_iobuf *io = (struct mg_iobuf *) param; |
| 3812 | if (expand && io->len + 2 > io->size) mg_iobuf_resize(io, io->len + 2); |
| 3813 | if (io->len + 2 <= io->size) { |
| 3814 | io->buf[io->len++] = (uint8_t) ch; |
| 3815 | io->buf[io->len] = 0; |
| 3816 | } else if (io->len < io->size) { |
| 3817 | io->buf[io->len++] = 0; // Guarantee to 0-terminate |
| 3818 | } |
| 3819 | } |
| 3820 | |
| 3821 | static void mg_putchar_iobuf_static(char ch, void *param) { |
| 3822 | mg_pfn_iobuf_private(ch, param, false); |
| 3823 | } |
| 3824 | |
| 3825 | void mg_pfn_iobuf(char ch, void *param) { |
| 3826 | mg_pfn_iobuf_private(ch, param, true); |
| 3827 | } |
| 3828 | |
| 3829 | size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list *ap) { |
| 3830 | struct mg_iobuf io = {(uint8_t *) buf, len, 0, 0}; |
| 3831 | size_t n = mg_vxprintf(mg_putchar_iobuf_static, &io, fmt, ap); |
| 3832 | if (n < len) buf[n] = '\0'; |
| 3833 | return n; |
| 3834 | } |
| 3835 | |
| 3836 | size_t mg_snprintf(char *buf, size_t len, const char *fmt, ...) { |
| 3837 | va_list ap; |
| 3838 | size_t n; |
| 3839 | va_start(ap, fmt); |
| 3840 | n = mg_vsnprintf(buf, len, fmt, &ap); |
| 3841 | va_end(ap); |
| 3842 | return n; |
| 3843 | } |
| 3844 | |
| 3845 | char *mg_vmprintf(const char *fmt, va_list *ap) { |
| 3846 | struct mg_iobuf io = {0, 0, 0, 256}; |
| 3847 | mg_vxprintf(mg_pfn_iobuf, &io, fmt, ap); |
| 3848 | return (char *) io.buf; |
| 3849 | } |
| 3850 | |
| 3851 | char *mg_mprintf(const char *fmt, ...) { |
| 3852 | char *s; |
| 3853 | va_list ap; |
| 3854 | va_start(ap, fmt); |
| 3855 | s = mg_vmprintf(fmt, &ap); |
| 3856 | va_end(ap); |
| 3857 | return s; |
| 3858 | } |
| 3859 | |
| 3860 | void mg_pfn_stdout(char c, void *param) { |
| 3861 | putchar(c); |
| 3862 | (void) param; |
| 3863 | } |
| 3864 | |
| 3865 | static size_t print_ip4(void (*out)(char, void *), void *arg, uint8_t *p) { |
| 3866 | return mg_xprintf(out, arg, "%d.%d.%d.%d" , p[0], p[1], p[2], p[3]); |
| 3867 | } |
| 3868 | |
| 3869 | static size_t print_ip6(void (*out)(char, void *), void *arg, uint16_t *p) { |
| 3870 | return mg_xprintf(out, arg, "[%x:%x:%x:%x:%x:%x:%x:%x]" , mg_ntohs(p[0]), |
| 3871 | mg_ntohs(p[1]), mg_ntohs(p[2]), mg_ntohs(p[3]), |
| 3872 | mg_ntohs(p[4]), mg_ntohs(p[5]), mg_ntohs(p[6]), |
| 3873 | mg_ntohs(p[7])); |
| 3874 | } |
| 3875 | |
| 3876 | size_t mg_print_ip4(void (*out)(char, void *), void *arg, va_list *ap) { |
| 3877 | uint8_t *p = va_arg(*ap, uint8_t *); |
| 3878 | return print_ip4(out, arg, p); |
| 3879 | } |
| 3880 | |
| 3881 | size_t mg_print_ip6(void (*out)(char, void *), void *arg, va_list *ap) { |
| 3882 | uint16_t *p = va_arg(*ap, uint16_t *); |
| 3883 | return print_ip6(out, arg, p); |
| 3884 | } |
| 3885 | |
| 3886 | size_t mg_print_ip(void (*out)(char, void *), void *arg, va_list *ap) { |
| 3887 | struct mg_addr *addr = va_arg(*ap, struct mg_addr *); |
| 3888 | if (addr->is_ip6) return print_ip6(out, arg, (uint16_t *) addr->ip); |
| 3889 | return print_ip4(out, arg, (uint8_t *) &addr->ip); |
| 3890 | } |
| 3891 | |
| 3892 | size_t mg_print_ip_port(void (*out)(char, void *), void *arg, va_list *ap) { |
| 3893 | struct mg_addr *a = va_arg(*ap, struct mg_addr *); |
| 3894 | return mg_xprintf(out, arg, "%M:%hu" , mg_print_ip, a, mg_ntohs(a->port)); |
| 3895 | } |
| 3896 | |
| 3897 | size_t mg_print_mac(void (*out)(char, void *), void *arg, va_list *ap) { |
| 3898 | uint8_t *p = va_arg(*ap, uint8_t *); |
| 3899 | return mg_xprintf(out, arg, "%02x:%02x:%02x:%02x:%02x:%02x" , p[0], p[1], p[2], |
| 3900 | p[3], p[4], p[5]); |
| 3901 | } |
| 3902 | |
| 3903 | static char mg_esc(int c, bool esc) { |
| 3904 | const char *p, *esc1 = "\b\f\n\r\t\\\"" , *esc2 = "bfnrt\\\"" ; |
| 3905 | for (p = esc ? esc1 : esc2; *p != '\0'; p++) { |
| 3906 | if (*p == c) return esc ? esc2[p - esc1] : esc1[p - esc2]; |
| 3907 | } |
| 3908 | return 0; |
| 3909 | } |
| 3910 | |
| 3911 | static char mg_escape(int c) { |
| 3912 | return mg_esc(c, true); |
| 3913 | } |
| 3914 | |
| 3915 | static size_t qcpy(void (*out)(char, void *), void *ptr, char *buf, |
| 3916 | size_t len) { |
| 3917 | size_t i = 0, = 0; |
| 3918 | for (i = 0; i < len && buf[i] != '\0'; i++) { |
| 3919 | char c = mg_escape(buf[i]); |
| 3920 | if (c) { |
| 3921 | out('\\', ptr), out(c, ptr), extra++; |
| 3922 | } else { |
| 3923 | out(buf[i], ptr); |
| 3924 | } |
| 3925 | } |
| 3926 | return i + extra; |
| 3927 | } |
| 3928 | |
| 3929 | static size_t bcpy(void (*out)(char, void *), void *arg, uint8_t *buf, |
| 3930 | size_t len) { |
| 3931 | size_t i, j, n = 0; |
| 3932 | const char *t = |
| 3933 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" ; |
| 3934 | for (i = 0; i < len; i += 3) { |
| 3935 | uint8_t c1 = buf[i], c2 = i + 1 < len ? buf[i + 1] : 0, |
| 3936 | c3 = i + 2 < len ? buf[i + 2] : 0; |
| 3937 | char tmp[4] = {t[c1 >> 2], t[(c1 & 3) << 4 | (c2 >> 4)], '=', '='}; |
| 3938 | if (i + 1 < len) tmp[2] = t[(c2 & 15) << 2 | (c3 >> 6)]; |
| 3939 | if (i + 2 < len) tmp[3] = t[c3 & 63]; |
| 3940 | for (j = 0; j < sizeof(tmp) && tmp[j] != '\0'; j++) out(tmp[j], arg); |
| 3941 | n += j; |
| 3942 | } |
| 3943 | return n; |
| 3944 | } |
| 3945 | |
| 3946 | size_t mg_print_hex(void (*out)(char, void *), void *arg, va_list *ap) { |
| 3947 | size_t bl = (size_t) va_arg(*ap, int); |
| 3948 | uint8_t *p = va_arg(*ap, uint8_t *); |
| 3949 | const char *hex = "0123456789abcdef" ; |
| 3950 | size_t j; |
| 3951 | for (j = 0; j < bl; j++) { |
| 3952 | out(hex[(p[j] >> 4) & 0x0F], arg); |
| 3953 | out(hex[p[j] & 0x0F], arg); |
| 3954 | } |
| 3955 | return 2 * bl; |
| 3956 | } |
| 3957 | size_t mg_print_base64(void (*out)(char, void *), void *arg, va_list *ap) { |
| 3958 | size_t len = (size_t) va_arg(*ap, int); |
| 3959 | uint8_t *buf = va_arg(*ap, uint8_t *); |
| 3960 | return bcpy(out, arg, buf, len); |
| 3961 | } |
| 3962 | |
| 3963 | size_t mg_print_esc(void (*out)(char, void *), void *arg, va_list *ap) { |
| 3964 | size_t len = (size_t) va_arg(*ap, int); |
| 3965 | char *p = va_arg(*ap, char *); |
| 3966 | if (len == 0) len = p == NULL ? 0 : strlen(p); |
| 3967 | return qcpy(out, arg, p, len); |
| 3968 | } |
| 3969 | |
| 3970 | #ifdef MG_ENABLE_LINES |
| 3971 | #line 1 "src/queue.c" |
| 3972 | #endif |
| 3973 | |
| 3974 | |
| 3975 | |
| 3976 | #if defined(__GNUC__) || defined(__clang__) |
| 3977 | #define MG_MEMORY_BARRIER() __sync_synchronize() |
| 3978 | #elif defined(_MSC_VER) && _MSC_VER >= 1700 |
| 3979 | #define MG_MEMORY_BARRIER() MemoryBarrier() |
| 3980 | #elif !defined(MG_MEMORY_BARRIER) |
| 3981 | #define MG_MEMORY_BARRIER() |
| 3982 | #endif |
| 3983 | |
| 3984 | // Every message in a queue is prepended by a 32-bit message length (ML). |
| 3985 | // If ML is 0, then it is the end, and reader must wrap to the beginning. |
| 3986 | // |
| 3987 | // Queue when q->tail <= q->head: |
| 3988 | // |----- free -----| ML | message1 | ML | message2 | ----- free ------| |
| 3989 | // ^ ^ ^ ^ |
| 3990 | // buf tail head len |
| 3991 | // |
| 3992 | // Queue when q->tail > q->head: |
| 3993 | // | ML | message2 |----- free ------| ML | message1 | 0 |---- free ----| |
| 3994 | // ^ ^ ^ ^ |
| 3995 | // buf head tail len |
| 3996 | |
| 3997 | void mg_queue_init(struct mg_queue *q, char *buf, size_t size) { |
| 3998 | q->size = size; |
| 3999 | q->buf = buf; |
| 4000 | q->head = q->tail = 0; |
| 4001 | } |
| 4002 | |
| 4003 | static size_t mg_queue_read_len(struct mg_queue *q) { |
| 4004 | uint32_t n = 0; |
| 4005 | MG_MEMORY_BARRIER(); |
| 4006 | memcpy(&n, q->buf + q->tail, sizeof(n)); |
| 4007 | assert(q->tail + n + sizeof(n) <= q->size); |
| 4008 | return n; |
| 4009 | } |
| 4010 | |
| 4011 | static void mg_queue_write_len(struct mg_queue *q, size_t len) { |
| 4012 | uint32_t n = (uint32_t) len; |
| 4013 | memcpy(q->buf + q->head, &n, sizeof(n)); |
| 4014 | MG_MEMORY_BARRIER(); |
| 4015 | } |
| 4016 | |
| 4017 | size_t mg_queue_book(struct mg_queue *q, char **buf, size_t len) { |
| 4018 | size_t space = 0, hs = sizeof(uint32_t) * 2; // *2 is for the 0 marker |
| 4019 | if (q->head >= q->tail && q->head + len + hs <= q->size) { |
| 4020 | space = q->size - q->head - hs; // There is enough space |
| 4021 | } else if (q->head >= q->tail && q->tail > hs) { |
| 4022 | mg_queue_write_len(q, 0); // Not enough space ahead |
| 4023 | q->head = 0; // Wrap head to the beginning |
| 4024 | } |
| 4025 | if (q->head + hs + len < q->tail) space = q->tail - q->head - hs; |
| 4026 | if (buf != NULL) *buf = q->buf + q->head + sizeof(uint32_t); |
| 4027 | return space; |
| 4028 | } |
| 4029 | |
| 4030 | size_t mg_queue_next(struct mg_queue *q, char **buf) { |
| 4031 | size_t len = 0; |
| 4032 | if (q->tail != q->head) { |
| 4033 | len = mg_queue_read_len(q); |
| 4034 | if (len == 0) { // Zero (head wrapped) ? |
| 4035 | q->tail = 0; // Reset tail to the start |
| 4036 | if (q->head > q->tail) len = mg_queue_read_len(q); // Read again |
| 4037 | } |
| 4038 | } |
| 4039 | if (buf != NULL) *buf = q->buf + q->tail + sizeof(uint32_t); |
| 4040 | assert(q->tail + len <= q->size); |
| 4041 | return len; |
| 4042 | } |
| 4043 | |
| 4044 | void mg_queue_add(struct mg_queue *q, size_t len) { |
| 4045 | assert(len > 0); |
| 4046 | mg_queue_write_len(q, len); |
| 4047 | assert(q->head + sizeof(uint32_t) * 2 + len <= q->size); |
| 4048 | q->head += len + sizeof(uint32_t); |
| 4049 | } |
| 4050 | |
| 4051 | void mg_queue_del(struct mg_queue *q, size_t len) { |
| 4052 | q->tail += len + sizeof(uint32_t); |
| 4053 | assert(q->tail + sizeof(uint32_t) <= q->size); |
| 4054 | } |
| 4055 | |
| 4056 | #ifdef MG_ENABLE_LINES |
| 4057 | #line 1 "src/rpc.c" |
| 4058 | #endif |
| 4059 | |
| 4060 | |
| 4061 | |
| 4062 | void mg_rpc_add(struct mg_rpc **head, struct mg_str method, |
| 4063 | void (*fn)(struct mg_rpc_req *), void *fn_data) { |
| 4064 | struct mg_rpc *rpc = (struct mg_rpc *) calloc(1, sizeof(*rpc)); |
| 4065 | if (rpc != NULL) { |
| 4066 | rpc->method = mg_strdup(method), rpc->fn = fn, rpc->fn_data = fn_data; |
| 4067 | rpc->next = *head, *head = rpc; |
| 4068 | } |
| 4069 | } |
| 4070 | |
| 4071 | void mg_rpc_del(struct mg_rpc **head, void (*fn)(struct mg_rpc_req *)) { |
| 4072 | struct mg_rpc *r; |
| 4073 | while ((r = *head) != NULL) { |
| 4074 | if (r->fn == fn || fn == NULL) { |
| 4075 | *head = r->next; |
| 4076 | free((void *) r->method.ptr); |
| 4077 | free(r); |
| 4078 | } else { |
| 4079 | head = &(*head)->next; |
| 4080 | } |
| 4081 | } |
| 4082 | } |
| 4083 | |
| 4084 | static void mg_rpc_call(struct mg_rpc_req *r, struct mg_str method) { |
| 4085 | struct mg_rpc *h = r->head == NULL ? NULL : *r->head; |
| 4086 | while (h != NULL && !mg_match(method, h->method, NULL)) h = h->next; |
| 4087 | if (h != NULL) { |
| 4088 | r->rpc = h; |
| 4089 | h->fn(r); |
| 4090 | } else { |
| 4091 | mg_rpc_err(r, -32601, "\"%.*s not found\"" , (int) method.len, method.ptr); |
| 4092 | } |
| 4093 | } |
| 4094 | |
| 4095 | void mg_rpc_process(struct mg_rpc_req *r) { |
| 4096 | int len, off = mg_json_get(r->frame, "$.method" , &len); |
| 4097 | if (off > 0 && r->frame.ptr[off] == '"') { |
| 4098 | struct mg_str method = mg_str_n(&r->frame.ptr[off + 1], (size_t) len - 2); |
| 4099 | mg_rpc_call(r, method); |
| 4100 | } else if ((off = mg_json_get(r->frame, "$.result" , &len)) > 0 || |
| 4101 | (off = mg_json_get(r->frame, "$.error" , &len)) > 0) { |
| 4102 | mg_rpc_call(r, mg_str("" )); // JSON response! call "" method handler |
| 4103 | } else { |
| 4104 | mg_rpc_err(r, -32700, "%m" , mg_print_esc, (int) r->frame.len, |
| 4105 | r->frame.ptr); // Invalid |
| 4106 | } |
| 4107 | } |
| 4108 | |
| 4109 | void mg_rpc_vok(struct mg_rpc_req *r, const char *fmt, va_list *ap) { |
| 4110 | int len, off = mg_json_get(r->frame, "$.id" , &len); |
| 4111 | if (off > 0) { |
| 4112 | mg_xprintf(r->pfn, r->pfn_data, "{%m:%.*s,%m:" , mg_print_esc, 0, "id" , len, |
| 4113 | &r->frame.ptr[off], mg_print_esc, 0, "result" ); |
| 4114 | mg_vxprintf(r->pfn, r->pfn_data, fmt == NULL ? "null" : fmt, ap); |
| 4115 | mg_xprintf(r->pfn, r->pfn_data, "}" ); |
| 4116 | } |
| 4117 | } |
| 4118 | |
| 4119 | void mg_rpc_ok(struct mg_rpc_req *r, const char *fmt, ...) { |
| 4120 | va_list ap; |
| 4121 | va_start(ap, fmt); |
| 4122 | mg_rpc_vok(r, fmt, &ap); |
| 4123 | va_end(ap); |
| 4124 | } |
| 4125 | |
| 4126 | void mg_rpc_verr(struct mg_rpc_req *r, int code, const char *fmt, va_list *ap) { |
| 4127 | int len, off = mg_json_get(r->frame, "$.id" , &len); |
| 4128 | mg_xprintf(r->pfn, r->pfn_data, "{" ); |
| 4129 | if (off > 0) { |
| 4130 | mg_xprintf(r->pfn, r->pfn_data, "%m:%.*s," , mg_print_esc, 0, "id" , len, |
| 4131 | &r->frame.ptr[off]); |
| 4132 | } |
| 4133 | mg_xprintf(r->pfn, r->pfn_data, "%m:{%m:%d,%m:" , mg_print_esc, 0, "error" , |
| 4134 | mg_print_esc, 0, "code" , code, mg_print_esc, 0, "message" ); |
| 4135 | mg_vxprintf(r->pfn, r->pfn_data, fmt == NULL ? "null" : fmt, ap); |
| 4136 | mg_xprintf(r->pfn, r->pfn_data, "}}" ); |
| 4137 | } |
| 4138 | |
| 4139 | void mg_rpc_err(struct mg_rpc_req *r, int code, const char *fmt, ...) { |
| 4140 | va_list ap; |
| 4141 | va_start(ap, fmt); |
| 4142 | mg_rpc_verr(r, code, fmt, &ap); |
| 4143 | va_end(ap); |
| 4144 | } |
| 4145 | |
| 4146 | static size_t print_methods(mg_pfn_t pfn, void *pfn_data, va_list *ap) { |
| 4147 | struct mg_rpc *h, **head = (struct mg_rpc **) va_arg(*ap, void **); |
| 4148 | size_t len = 0; |
| 4149 | for (h = *head; h != NULL; h = h->next) { |
| 4150 | if (h->method.len == 0) continue; // Ignore response handler |
| 4151 | len += mg_xprintf(pfn, pfn_data, "%s%m" , h == *head ? "" : "," , |
| 4152 | mg_print_esc, (int) h->method.len, h->method.ptr); |
| 4153 | } |
| 4154 | return len; |
| 4155 | } |
| 4156 | |
| 4157 | void mg_rpc_list(struct mg_rpc_req *r) { |
| 4158 | mg_rpc_ok(r, "[%M]" , print_methods, r->head); |
| 4159 | } |
| 4160 | |
| 4161 | #ifdef MG_ENABLE_LINES |
| 4162 | #line 1 "src/sha1.c" |
| 4163 | #endif |
| 4164 | /* Copyright(c) By Steve Reid <steve@edmweb.com> */ |
| 4165 | /* 100% Public Domain */ |
| 4166 | |
| 4167 | |
| 4168 | |
| 4169 | union char64long16 { |
| 4170 | unsigned char c[64]; |
| 4171 | uint32_t l[16]; |
| 4172 | }; |
| 4173 | |
| 4174 | #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) |
| 4175 | |
| 4176 | static uint32_t blk0(union char64long16 *block, int i) { |
| 4177 | if (MG_BIG_ENDIAN) { |
| 4178 | } else { |
| 4179 | block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | |
| 4180 | (rol(block->l[i], 8) & 0x00FF00FF); |
| 4181 | } |
| 4182 | return block->l[i]; |
| 4183 | } |
| 4184 | |
| 4185 | /* Avoid redefine warning (ARM /usr/include/sys/ucontext.h define R0~R4) */ |
| 4186 | #undef blk |
| 4187 | #undef R0 |
| 4188 | #undef R1 |
| 4189 | #undef R2 |
| 4190 | #undef R3 |
| 4191 | #undef R4 |
| 4192 | |
| 4193 | #define blk(i) \ |
| 4194 | (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ block->l[(i + 8) & 15] ^ \ |
| 4195 | block->l[(i + 2) & 15] ^ block->l[i & 15], \ |
| 4196 | 1)) |
| 4197 | #define R0(v, w, x, y, z, i) \ |
| 4198 | z += ((w & (x ^ y)) ^ y) + blk0(block, i) + 0x5A827999 + rol(v, 5); \ |
| 4199 | w = rol(w, 30); |
| 4200 | #define R1(v, w, x, y, z, i) \ |
| 4201 | z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \ |
| 4202 | w = rol(w, 30); |
| 4203 | #define R2(v, w, x, y, z, i) \ |
| 4204 | z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); \ |
| 4205 | w = rol(w, 30); |
| 4206 | #define R3(v, w, x, y, z, i) \ |
| 4207 | z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \ |
| 4208 | w = rol(w, 30); |
| 4209 | #define R4(v, w, x, y, z, i) \ |
| 4210 | z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \ |
| 4211 | w = rol(w, 30); |
| 4212 | |
| 4213 | static void mg_sha1_transform(uint32_t state[5], |
| 4214 | const unsigned char *buffer) { |
| 4215 | uint32_t a, b, c, d, e; |
| 4216 | union char64long16 block[1]; |
| 4217 | |
| 4218 | memcpy(block, buffer, 64); |
| 4219 | a = state[0]; |
| 4220 | b = state[1]; |
| 4221 | c = state[2]; |
| 4222 | d = state[3]; |
| 4223 | e = state[4]; |
| 4224 | R0(a, b, c, d, e, 0); |
| 4225 | R0(e, a, b, c, d, 1); |
| 4226 | R0(d, e, a, b, c, 2); |
| 4227 | R0(c, d, e, a, b, 3); |
| 4228 | R0(b, c, d, e, a, 4); |
| 4229 | R0(a, b, c, d, e, 5); |
| 4230 | R0(e, a, b, c, d, 6); |
| 4231 | R0(d, e, a, b, c, 7); |
| 4232 | R0(c, d, e, a, b, 8); |
| 4233 | R0(b, c, d, e, a, 9); |
| 4234 | R0(a, b, c, d, e, 10); |
| 4235 | R0(e, a, b, c, d, 11); |
| 4236 | R0(d, e, a, b, c, 12); |
| 4237 | R0(c, d, e, a, b, 13); |
| 4238 | R0(b, c, d, e, a, 14); |
| 4239 | R0(a, b, c, d, e, 15); |
| 4240 | R1(e, a, b, c, d, 16); |
| 4241 | R1(d, e, a, b, c, 17); |
| 4242 | R1(c, d, e, a, b, 18); |
| 4243 | R1(b, c, d, e, a, 19); |
| 4244 | R2(a, b, c, d, e, 20); |
| 4245 | R2(e, a, b, c, d, 21); |
| 4246 | R2(d, e, a, b, c, 22); |
| 4247 | R2(c, d, e, a, b, 23); |
| 4248 | R2(b, c, d, e, a, 24); |
| 4249 | R2(a, b, c, d, e, 25); |
| 4250 | R2(e, a, b, c, d, 26); |
| 4251 | R2(d, e, a, b, c, 27); |
| 4252 | R2(c, d, e, a, b, 28); |
| 4253 | R2(b, c, d, e, a, 29); |
| 4254 | R2(a, b, c, d, e, 30); |
| 4255 | R2(e, a, b, c, d, 31); |
| 4256 | R2(d, e, a, b, c, 32); |
| 4257 | R2(c, d, e, a, b, 33); |
| 4258 | R2(b, c, d, e, a, 34); |
| 4259 | R2(a, b, c, d, e, 35); |
| 4260 | R2(e, a, b, c, d, 36); |
| 4261 | R2(d, e, a, b, c, 37); |
| 4262 | R2(c, d, e, a, b, 38); |
| 4263 | R2(b, c, d, e, a, 39); |
| 4264 | R3(a, b, c, d, e, 40); |
| 4265 | R3(e, a, b, c, d, 41); |
| 4266 | R3(d, e, a, b, c, 42); |
| 4267 | R3(c, d, e, a, b, 43); |
| 4268 | R3(b, c, d, e, a, 44); |
| 4269 | R3(a, b, c, d, e, 45); |
| 4270 | R3(e, a, b, c, d, 46); |
| 4271 | R3(d, e, a, b, c, 47); |
| 4272 | R3(c, d, e, a, b, 48); |
| 4273 | R3(b, c, d, e, a, 49); |
| 4274 | R3(a, b, c, d, e, 50); |
| 4275 | R3(e, a, b, c, d, 51); |
| 4276 | R3(d, e, a, b, c, 52); |
| 4277 | R3(c, d, e, a, b, 53); |
| 4278 | R3(b, c, d, e, a, 54); |
| 4279 | R3(a, b, c, d, e, 55); |
| 4280 | R3(e, a, b, c, d, 56); |
| 4281 | R3(d, e, a, b, c, 57); |
| 4282 | R3(c, d, e, a, b, 58); |
| 4283 | R3(b, c, d, e, a, 59); |
| 4284 | R4(a, b, c, d, e, 60); |
| 4285 | R4(e, a, b, c, d, 61); |
| 4286 | R4(d, e, a, b, c, 62); |
| 4287 | R4(c, d, e, a, b, 63); |
| 4288 | R4(b, c, d, e, a, 64); |
| 4289 | R4(a, b, c, d, e, 65); |
| 4290 | R4(e, a, b, c, d, 66); |
| 4291 | R4(d, e, a, b, c, 67); |
| 4292 | R4(c, d, e, a, b, 68); |
| 4293 | R4(b, c, d, e, a, 69); |
| 4294 | R4(a, b, c, d, e, 70); |
| 4295 | R4(e, a, b, c, d, 71); |
| 4296 | R4(d, e, a, b, c, 72); |
| 4297 | R4(c, d, e, a, b, 73); |
| 4298 | R4(b, c, d, e, a, 74); |
| 4299 | R4(a, b, c, d, e, 75); |
| 4300 | R4(e, a, b, c, d, 76); |
| 4301 | R4(d, e, a, b, c, 77); |
| 4302 | R4(c, d, e, a, b, 78); |
| 4303 | R4(b, c, d, e, a, 79); |
| 4304 | state[0] += a; |
| 4305 | state[1] += b; |
| 4306 | state[2] += c; |
| 4307 | state[3] += d; |
| 4308 | state[4] += e; |
| 4309 | /* Erase working structures. The order of operations is important, |
| 4310 | * used to ensure that compiler doesn't optimize those out. */ |
| 4311 | memset(block, 0, sizeof(block)); |
| 4312 | a = b = c = d = e = 0; |
| 4313 | (void) a; |
| 4314 | (void) b; |
| 4315 | (void) c; |
| 4316 | (void) d; |
| 4317 | (void) e; |
| 4318 | } |
| 4319 | |
| 4320 | void mg_sha1_init(mg_sha1_ctx *context) { |
| 4321 | context->state[0] = 0x67452301; |
| 4322 | context->state[1] = 0xEFCDAB89; |
| 4323 | context->state[2] = 0x98BADCFE; |
| 4324 | context->state[3] = 0x10325476; |
| 4325 | context->state[4] = 0xC3D2E1F0; |
| 4326 | context->count[0] = context->count[1] = 0; |
| 4327 | } |
| 4328 | |
| 4329 | void mg_sha1_update(mg_sha1_ctx *context, const unsigned char *data, |
| 4330 | size_t len) { |
| 4331 | size_t i, j; |
| 4332 | |
| 4333 | j = context->count[0]; |
| 4334 | if ((context->count[0] += (uint32_t) len << 3) < j) context->count[1]++; |
| 4335 | context->count[1] += (uint32_t) (len >> 29); |
| 4336 | j = (j >> 3) & 63; |
| 4337 | if ((j + len) > 63) { |
| 4338 | memcpy(&context->buffer[j], data, (i = 64 - j)); |
| 4339 | mg_sha1_transform(context->state, context->buffer); |
| 4340 | for (; i + 63 < len; i += 64) { |
| 4341 | mg_sha1_transform(context->state, &data[i]); |
| 4342 | } |
| 4343 | j = 0; |
| 4344 | } else |
| 4345 | i = 0; |
| 4346 | memcpy(&context->buffer[j], &data[i], len - i); |
| 4347 | } |
| 4348 | |
| 4349 | void mg_sha1_final(unsigned char digest[20], mg_sha1_ctx *context) { |
| 4350 | unsigned i; |
| 4351 | unsigned char finalcount[8], c; |
| 4352 | |
| 4353 | for (i = 0; i < 8; i++) { |
| 4354 | finalcount[i] = (unsigned char) ((context->count[(i >= 4 ? 0 : 1)] >> |
| 4355 | ((3 - (i & 3)) * 8)) & |
| 4356 | 255); |
| 4357 | } |
| 4358 | c = 0200; |
| 4359 | mg_sha1_update(context, &c, 1); |
| 4360 | while ((context->count[0] & 504) != 448) { |
| 4361 | c = 0000; |
| 4362 | mg_sha1_update(context, &c, 1); |
| 4363 | } |
| 4364 | mg_sha1_update(context, finalcount, 8); |
| 4365 | for (i = 0; i < 20; i++) { |
| 4366 | digest[i] = |
| 4367 | (unsigned char) ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255); |
| 4368 | } |
| 4369 | memset(context, '\0', sizeof(*context)); |
| 4370 | memset(&finalcount, '\0', sizeof(finalcount)); |
| 4371 | } |
| 4372 | |
| 4373 | #ifdef MG_ENABLE_LINES |
| 4374 | #line 1 "src/sntp.c" |
| 4375 | #endif |
| 4376 | |
| 4377 | |
| 4378 | |
| 4379 | |
| 4380 | |
| 4381 | |
| 4382 | #define SNTP_TIME_OFFSET 2208988800U // (1970 - 1900) in seconds |
| 4383 | #define SNTP_MAX_FRAC 4294967295.0 // 2 ** 32 - 1 |
| 4384 | |
| 4385 | static int64_t gettimestamp(const uint32_t *data) { |
| 4386 | uint32_t sec = mg_ntohl(data[0]), frac = mg_ntohl(data[1]); |
| 4387 | if (sec) sec -= SNTP_TIME_OFFSET; |
| 4388 | return ((int64_t) sec) * 1000 + (int64_t) (frac / SNTP_MAX_FRAC * 1000.0); |
| 4389 | } |
| 4390 | |
| 4391 | int64_t mg_sntp_parse(const unsigned char *buf, size_t len) { |
| 4392 | int64_t res = -1; |
| 4393 | int mode = len > 0 ? buf[0] & 7 : 0; |
| 4394 | int version = len > 0 ? (buf[0] >> 3) & 7 : 0; |
| 4395 | if (len < 48) { |
| 4396 | MG_ERROR(("%s" , "corrupt packet" )); |
| 4397 | } else if (mode != 4 && mode != 5) { |
| 4398 | MG_ERROR(("%s" , "not a server reply" )); |
| 4399 | } else if (buf[1] == 0) { |
| 4400 | MG_ERROR(("%s" , "server sent a kiss of death" )); |
| 4401 | } else if (version == 4 || version == 3) { |
| 4402 | // int64_t ref = gettimestamp((uint32_t *) &buf[16]); |
| 4403 | int64_t t0 = gettimestamp((uint32_t *) &buf[24]); |
| 4404 | int64_t t1 = gettimestamp((uint32_t *) &buf[32]); |
| 4405 | int64_t t2 = gettimestamp((uint32_t *) &buf[40]); |
| 4406 | int64_t t3 = (int64_t) mg_millis(); |
| 4407 | int64_t delta = (t3 - t0) - (t2 - t1); |
| 4408 | MG_VERBOSE(("%lld %lld %lld %lld delta:%lld" , t0, t1, t2, t3, delta)); |
| 4409 | res = t2 + delta / 2; |
| 4410 | } else { |
| 4411 | MG_ERROR(("unexpected version: %d" , version)); |
| 4412 | } |
| 4413 | return res; |
| 4414 | } |
| 4415 | |
| 4416 | static void sntp_cb(struct mg_connection *c, int ev, void *evd, void *fnd) { |
| 4417 | if (ev == MG_EV_READ) { |
| 4418 | int64_t milliseconds = mg_sntp_parse(c->recv.buf, c->recv.len); |
| 4419 | if (milliseconds > 0) { |
| 4420 | MG_INFO(("%lu got time: %lld ms from epoch" , c->id, milliseconds)); |
| 4421 | mg_call(c, MG_EV_SNTP_TIME, (uint64_t *) &milliseconds); |
| 4422 | MG_VERBOSE(("%u.%u" , (unsigned) (milliseconds / 1000), |
| 4423 | (unsigned) (milliseconds % 1000))); |
| 4424 | } |
| 4425 | mg_iobuf_del(&c->recv, 0, c->recv.len); // Free receive buffer |
| 4426 | } else if (ev == MG_EV_CONNECT) { |
| 4427 | mg_sntp_request(c); |
| 4428 | } else if (ev == MG_EV_CLOSE) { |
| 4429 | } |
| 4430 | (void) fnd; |
| 4431 | (void) evd; |
| 4432 | } |
| 4433 | |
| 4434 | void mg_sntp_request(struct mg_connection *c) { |
| 4435 | if (c->is_resolving) { |
| 4436 | MG_ERROR(("%lu wait until resolved" , c->id)); |
| 4437 | } else { |
| 4438 | int64_t now = (int64_t) mg_millis(); // Use int64_t, for vc98 |
| 4439 | uint8_t buf[48] = {0}; |
| 4440 | uint32_t *t = (uint32_t *) &buf[40]; |
| 4441 | double frac = ((double) (now % 1000)) / 1000.0 * SNTP_MAX_FRAC; |
| 4442 | buf[0] = (0 << 6) | (4 << 3) | 3; |
| 4443 | t[0] = mg_htonl((uint32_t) (now / 1000) + SNTP_TIME_OFFSET); |
| 4444 | t[1] = mg_htonl((uint32_t) frac); |
| 4445 | mg_send(c, buf, sizeof(buf)); |
| 4446 | } |
| 4447 | } |
| 4448 | |
| 4449 | struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, const char *url, |
| 4450 | mg_event_handler_t fn, void *fnd) { |
| 4451 | struct mg_connection *c = NULL; |
| 4452 | if (url == NULL) url = "udp://time.google.com:123" ; |
| 4453 | if ((c = mg_connect(mgr, url, fn, fnd)) != NULL) c->pfn = sntp_cb; |
| 4454 | return c; |
| 4455 | } |
| 4456 | |
| 4457 | #ifdef MG_ENABLE_LINES |
| 4458 | #line 1 "src/sock.c" |
| 4459 | #endif |
| 4460 | |
| 4461 | |
| 4462 | |
| 4463 | |
| 4464 | |
| 4465 | |
| 4466 | |
| 4467 | |
| 4468 | |
| 4469 | |
| 4470 | |
| 4471 | #if MG_ENABLE_SOCKET |
| 4472 | |
| 4473 | #ifndef closesocket |
| 4474 | #define closesocket(x) close(x) |
| 4475 | #endif |
| 4476 | |
| 4477 | #define FD(c_) ((MG_SOCKET_TYPE) (size_t) (c_)->fd) |
| 4478 | #define S2PTR(s_) ((void *) (size_t) (s_)) |
| 4479 | |
| 4480 | #ifndef MSG_NONBLOCKING |
| 4481 | #define MSG_NONBLOCKING 0 |
| 4482 | #endif |
| 4483 | |
| 4484 | #ifndef AF_INET6 |
| 4485 | #define AF_INET6 10 |
| 4486 | #endif |
| 4487 | |
| 4488 | #ifndef MG_SOCK_ERR |
| 4489 | #define MG_SOCK_ERR(errcode) ((errcode) < 0 ? errno : 0) |
| 4490 | #endif |
| 4491 | |
| 4492 | #ifndef MG_SOCK_INTR |
| 4493 | #define MG_SOCK_INTR(fd) (fd == MG_INVALID_SOCKET && MG_SOCK_ERR(-1) == EINTR) |
| 4494 | #endif |
| 4495 | |
| 4496 | #ifndef MG_SOCK_PENDING |
| 4497 | #define MG_SOCK_PENDING(errcode) \ |
| 4498 | (((errcode) < 0) && (errno == EINPROGRESS || errno == EWOULDBLOCK)) |
| 4499 | #endif |
| 4500 | |
| 4501 | #ifndef MG_SOCK_RESET |
| 4502 | #define MG_SOCK_RESET(errcode) \ |
| 4503 | (((errcode) < 0) && (errno == EPIPE || errno == ECONNRESET)) |
| 4504 | #endif |
| 4505 | |
| 4506 | union usa { |
| 4507 | struct sockaddr sa; |
| 4508 | struct sockaddr_in sin; |
| 4509 | #if MG_ENABLE_IPV6 |
| 4510 | struct sockaddr_in6 sin6; |
| 4511 | #endif |
| 4512 | }; |
| 4513 | |
| 4514 | static socklen_t tousa(struct mg_addr *a, union usa *usa) { |
| 4515 | socklen_t len = sizeof(usa->sin); |
| 4516 | memset(usa, 0, sizeof(*usa)); |
| 4517 | usa->sin.sin_family = AF_INET; |
| 4518 | usa->sin.sin_port = a->port; |
| 4519 | memcpy(&usa->sin.sin_addr, a->ip, sizeof(uint32_t)); |
| 4520 | #if MG_ENABLE_IPV6 |
| 4521 | if (a->is_ip6) { |
| 4522 | usa->sin.sin_family = AF_INET6; |
| 4523 | usa->sin6.sin6_port = a->port; |
| 4524 | memcpy(&usa->sin6.sin6_addr, a->ip, sizeof(a->ip)); |
| 4525 | len = sizeof(usa->sin6); |
| 4526 | } |
| 4527 | #endif |
| 4528 | return len; |
| 4529 | } |
| 4530 | |
| 4531 | static void tomgaddr(union usa *usa, struct mg_addr *a, bool is_ip6) { |
| 4532 | a->is_ip6 = is_ip6; |
| 4533 | a->port = usa->sin.sin_port; |
| 4534 | memcpy(&a->ip, &usa->sin.sin_addr, sizeof(uint32_t)); |
| 4535 | #if MG_ENABLE_IPV6 |
| 4536 | if (is_ip6) { |
| 4537 | memcpy(a->ip, &usa->sin6.sin6_addr, sizeof(a->ip)); |
| 4538 | a->port = usa->sin6.sin6_port; |
| 4539 | } |
| 4540 | #endif |
| 4541 | } |
| 4542 | |
| 4543 | static void setlocaddr(MG_SOCKET_TYPE fd, struct mg_addr *addr) { |
| 4544 | union usa usa; |
| 4545 | socklen_t n = sizeof(usa); |
| 4546 | if (getsockname(fd, &usa.sa, &n) == 0) { |
| 4547 | tomgaddr(&usa, addr, n != sizeof(usa.sin)); |
| 4548 | } |
| 4549 | } |
| 4550 | |
| 4551 | static void iolog(struct mg_connection *c, char *buf, long n, bool r) { |
| 4552 | if (n == MG_IO_WAIT) { |
| 4553 | // Do nothing |
| 4554 | } else if (n <= 0) { |
| 4555 | c->is_closing = 1; // Termination. Don't call mg_error(): #1529 |
| 4556 | } else if (n > 0) { |
| 4557 | if (c->is_hexdumping) { |
| 4558 | union usa usa; |
| 4559 | socklen_t slen = sizeof(usa.sin); |
| 4560 | if (getsockname(FD(c), &usa.sa, &slen) < 0) (void) 0; // Ignore result |
| 4561 | MG_INFO(("\n-- %lu %M %s %M %ld" , c->id, mg_print_ip_port, &c->loc, |
| 4562 | r ? "<-" : "->" , mg_print_ip_port, &c->rem, n)); |
| 4563 | |
| 4564 | mg_hexdump(buf, (size_t) n); |
| 4565 | } |
| 4566 | if (r) { |
| 4567 | c->recv.len += (size_t) n; |
| 4568 | mg_call(c, MG_EV_READ, &n); |
| 4569 | } else { |
| 4570 | mg_iobuf_del(&c->send, 0, (size_t) n); |
| 4571 | // if (c->send.len == 0) mg_iobuf_resize(&c->send, 0); |
| 4572 | if (c->send.len == 0) { |
| 4573 | MG_EPOLL_MOD(c, 0); |
| 4574 | } |
| 4575 | mg_call(c, MG_EV_WRITE, &n); |
| 4576 | } |
| 4577 | } |
| 4578 | } |
| 4579 | |
| 4580 | long mg_io_send(struct mg_connection *c, const void *buf, size_t len) { |
| 4581 | long n; |
| 4582 | if (c->is_udp) { |
| 4583 | union usa usa; |
| 4584 | socklen_t slen = tousa(&c->rem, &usa); |
| 4585 | n = sendto(FD(c), (char *) buf, len, 0, &usa.sa, slen); |
| 4586 | if (n > 0) setlocaddr(FD(c), &c->loc); |
| 4587 | } else { |
| 4588 | n = send(FD(c), (char *) buf, len, MSG_NONBLOCKING); |
| 4589 | } |
| 4590 | if (MG_SOCK_PENDING(n)) return MG_IO_WAIT; |
| 4591 | if (MG_SOCK_RESET(n)) return MG_IO_RESET; |
| 4592 | if (n <= 0) return MG_IO_ERR; |
| 4593 | return n; |
| 4594 | } |
| 4595 | |
| 4596 | bool mg_send(struct mg_connection *c, const void *buf, size_t len) { |
| 4597 | if (c->is_udp) { |
| 4598 | long n = mg_io_send(c, buf, len); |
| 4599 | MG_DEBUG(("%lu %p %d:%d %ld err %d" , c->id, c->fd, (int) c->send.len, |
| 4600 | (int) c->recv.len, n, MG_SOCK_ERR(n))); |
| 4601 | iolog(c, (char *) buf, n, false); |
| 4602 | return n > 0; |
| 4603 | } else { |
| 4604 | return mg_iobuf_add(&c->send, c->send.len, buf, len); |
| 4605 | } |
| 4606 | } |
| 4607 | |
| 4608 | static void mg_set_non_blocking_mode(MG_SOCKET_TYPE fd) { |
| 4609 | #if defined(MG_CUSTOM_NONBLOCK) |
| 4610 | MG_CUSTOM_NONBLOCK(fd); |
| 4611 | #elif MG_ARCH == MG_ARCH_WIN32 && MG_ENABLE_WINSOCK |
| 4612 | unsigned long on = 1; |
| 4613 | ioctlsocket(fd, FIONBIO, &on); |
| 4614 | #elif MG_ENABLE_RL |
| 4615 | unsigned long on = 1; |
| 4616 | ioctlsocket(fd, FIONBIO, &on); |
| 4617 | #elif MG_ENABLE_FREERTOS_TCP |
| 4618 | const BaseType_t off = 0; |
| 4619 | if (setsockopt(fd, 0, FREERTOS_SO_RCVTIMEO, &off, sizeof(off)) != 0) (void) 0; |
| 4620 | if (setsockopt(fd, 0, FREERTOS_SO_SNDTIMEO, &off, sizeof(off)) != 0) (void) 0; |
| 4621 | #elif MG_ENABLE_LWIP |
| 4622 | lwip_fcntl(fd, F_SETFL, O_NONBLOCK); |
| 4623 | #elif MG_ARCH == MG_ARCH_AZURERTOS |
| 4624 | fcntl(fd, F_SETFL, O_NONBLOCK); |
| 4625 | #elif MG_ARCH == MG_ARCH_TIRTOS |
| 4626 | int val = 0; |
| 4627 | setsockopt(fd, SOL_SOCKET, SO_BLOCKING, &val, sizeof(val)); |
| 4628 | // SPRU524J section 3.3.3 page 63, SO_SNDLOWAT |
| 4629 | int sz = sizeof(val); |
| 4630 | getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &val, &sz); |
| 4631 | val /= 2; // set send low-water mark at half send buffer size |
| 4632 | setsockopt(fd, SOL_SOCKET, SO_SNDLOWAT, &val, sizeof(val)); |
| 4633 | #else |
| 4634 | fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); // Non-blocking mode |
| 4635 | fcntl(fd, F_SETFD, FD_CLOEXEC); // Set close-on-exec |
| 4636 | #endif |
| 4637 | } |
| 4638 | |
| 4639 | bool mg_open_listener(struct mg_connection *c, const char *url) { |
| 4640 | MG_SOCKET_TYPE fd = MG_INVALID_SOCKET; |
| 4641 | bool success = false; |
| 4642 | c->loc.port = mg_htons(mg_url_port(url)); |
| 4643 | if (!mg_aton(mg_url_host(url), &c->loc)) { |
| 4644 | MG_ERROR(("invalid listening URL: %s" , url)); |
| 4645 | } else { |
| 4646 | union usa usa; |
| 4647 | socklen_t slen = tousa(&c->loc, &usa); |
| 4648 | int rc, on = 1, af = c->loc.is_ip6 ? AF_INET6 : AF_INET; |
| 4649 | int type = strncmp(url, "udp:" , 4) == 0 ? SOCK_DGRAM : SOCK_STREAM; |
| 4650 | int proto = type == SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP; |
| 4651 | (void) on; |
| 4652 | |
| 4653 | if ((fd = socket(af, type, proto)) == MG_INVALID_SOCKET) { |
| 4654 | MG_ERROR(("socket: %d" , MG_SOCK_ERR(-1))); |
| 4655 | #if defined(SO_EXCLUSIVEADDRUSE) |
| 4656 | } else if ((rc = setsockopt(fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, |
| 4657 | (char *) &on, sizeof(on))) != 0) { |
| 4658 | // "Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE" |
| 4659 | MG_ERROR(("setsockopt(SO_EXCLUSIVEADDRUSE): %d %d" , on, MG_SOCK_ERR(rc))); |
| 4660 | #elif defined(SO_REUSEADDR) && (!defined(LWIP_SOCKET) || SO_REUSE) |
| 4661 | } else if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, |
| 4662 | sizeof(on))) != 0) { |
| 4663 | // 1. SO_REUSEADDR semantics on UNIX and Windows is different. On |
| 4664 | // Windows, SO_REUSEADDR allows to bind a socket to a port without error |
| 4665 | // even if the port is already open by another program. This is not the |
| 4666 | // behavior SO_REUSEADDR was designed for, and leads to hard-to-track |
| 4667 | // failure scenarios. |
| 4668 | // |
| 4669 | // 2. For LWIP, SO_REUSEADDR should be explicitly enabled by defining |
| 4670 | // SO_REUSE = 1 in lwipopts.h, otherwise the code below will compile but |
| 4671 | // won't work! (setsockopt will return EINVAL) |
| 4672 | MG_ERROR(("setsockopt(SO_REUSEADDR): %d" , MG_SOCK_ERR(rc))); |
| 4673 | #endif |
| 4674 | #if defined(IPV6_V6ONLY) |
| 4675 | } else if (c->loc.is_ip6 && |
| 4676 | (rc = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &on, |
| 4677 | sizeof(on))) != 0) { |
| 4678 | // See #2089. Allow to bind v4 and v6 sockets on the same port |
| 4679 | MG_ERROR(("setsockopt(IPV6_V6ONLY): %d" , MG_SOCK_ERR(rc))); |
| 4680 | #endif |
| 4681 | } else if ((rc = bind(fd, &usa.sa, slen)) != 0) { |
| 4682 | MG_ERROR(("bind: %d" , MG_SOCK_ERR(rc))); |
| 4683 | } else if ((type == SOCK_STREAM && |
| 4684 | (rc = listen(fd, MG_SOCK_LISTEN_BACKLOG_SIZE)) != 0)) { |
| 4685 | // NOTE(lsm): FreeRTOS uses backlog value as a connection limit |
| 4686 | // In case port was set to 0, get the real port number |
| 4687 | MG_ERROR(("listen: %d" , MG_SOCK_ERR(rc))); |
| 4688 | } else { |
| 4689 | setlocaddr(fd, &c->loc); |
| 4690 | mg_set_non_blocking_mode(fd); |
| 4691 | c->fd = S2PTR(fd); |
| 4692 | MG_EPOLL_ADD(c); |
| 4693 | success = true; |
| 4694 | } |
| 4695 | } |
| 4696 | if (success == false && fd != MG_INVALID_SOCKET) closesocket(fd); |
| 4697 | return success; |
| 4698 | } |
| 4699 | |
| 4700 | long mg_io_recv(struct mg_connection *c, void *buf, size_t len) { |
| 4701 | long n = 0; |
| 4702 | if (c->is_udp) { |
| 4703 | union usa usa; |
| 4704 | socklen_t slen = tousa(&c->rem, &usa); |
| 4705 | n = recvfrom(FD(c), (char *) buf, len, 0, &usa.sa, &slen); |
| 4706 | if (n > 0) tomgaddr(&usa, &c->rem, slen != sizeof(usa.sin)); |
| 4707 | } else { |
| 4708 | n = recv(FD(c), (char *) buf, len, MSG_NONBLOCKING); |
| 4709 | } |
| 4710 | if (MG_SOCK_PENDING(n)) return MG_IO_WAIT; |
| 4711 | if (MG_SOCK_RESET(n)) return MG_IO_RESET; |
| 4712 | if (n <= 0) return MG_IO_ERR; |
| 4713 | return n; |
| 4714 | } |
| 4715 | |
| 4716 | // NOTE(lsm): do only one iteration of reads, cause some systems |
| 4717 | // (e.g. FreeRTOS stack) return 0 instead of -1/EWOULDBLOCK when no data |
| 4718 | static void read_conn(struct mg_connection *c) { |
| 4719 | long n = -1; |
| 4720 | if (c->recv.len >= MG_MAX_RECV_SIZE) { |
| 4721 | mg_error(c, "max_recv_buf_size reached" ); |
| 4722 | } else if (c->recv.size <= c->recv.len && |
| 4723 | !mg_iobuf_resize(&c->recv, c->recv.size + MG_IO_SIZE)) { |
| 4724 | mg_error(c, "oom" ); |
| 4725 | } else { |
| 4726 | char *buf = (char *) &c->recv.buf[c->recv.len]; |
| 4727 | size_t len = c->recv.size - c->recv.len; |
| 4728 | n = c->is_tls ? mg_tls_recv(c, buf, len) : mg_io_recv(c, buf, len); |
| 4729 | MG_DEBUG(("%lu %p snd %ld/%ld rcv %ld/%ld n=%ld err=%d" , c->id, c->fd, |
| 4730 | (long) c->send.len, (long) c->send.size, (long) c->recv.len, |
| 4731 | (long) c->recv.size, n, MG_SOCK_ERR(n))); |
| 4732 | iolog(c, buf, n, true); |
| 4733 | } |
| 4734 | } |
| 4735 | |
| 4736 | static void write_conn(struct mg_connection *c) { |
| 4737 | char *buf = (char *) c->send.buf; |
| 4738 | size_t len = c->send.len; |
| 4739 | long n = c->is_tls ? mg_tls_send(c, buf, len) : mg_io_send(c, buf, len); |
| 4740 | MG_DEBUG(("%lu %p snd %ld/%ld rcv %ld/%ld n=%ld err=%d" , c->id, c->fd, |
| 4741 | (long) c->send.len, (long) c->send.size, (long) c->recv.len, |
| 4742 | (long) c->recv.size, n, MG_SOCK_ERR(n))); |
| 4743 | iolog(c, buf, n, false); |
| 4744 | } |
| 4745 | |
| 4746 | static void close_conn(struct mg_connection *c) { |
| 4747 | if (FD(c) != MG_INVALID_SOCKET) { |
| 4748 | #if MG_ENABLE_EPOLL |
| 4749 | epoll_ctl(c->mgr->epoll_fd, EPOLL_CTL_DEL, FD(c), NULL); |
| 4750 | #endif |
| 4751 | closesocket(FD(c)); |
| 4752 | #if MG_ENABLE_FREERTOS_TCP |
| 4753 | FreeRTOS_FD_CLR(c->fd, c->mgr->ss, eSELECT_ALL); |
| 4754 | #endif |
| 4755 | } |
| 4756 | mg_close_conn(c); |
| 4757 | } |
| 4758 | |
| 4759 | static void connect_conn(struct mg_connection *c) { |
| 4760 | union usa usa; |
| 4761 | socklen_t n = sizeof(usa); |
| 4762 | // Use getpeername() to test whether we have connected |
| 4763 | if (getpeername(FD(c), &usa.sa, &n) == 0) { |
| 4764 | c->is_connecting = 0; |
| 4765 | mg_call(c, MG_EV_CONNECT, NULL); |
| 4766 | MG_EPOLL_MOD(c, 0); |
| 4767 | if (c->is_tls_hs) mg_tls_handshake(c); |
| 4768 | } else { |
| 4769 | mg_error(c, "socket error" ); |
| 4770 | } |
| 4771 | } |
| 4772 | |
| 4773 | static void setsockopts(struct mg_connection *c) { |
| 4774 | #if MG_ENABLE_FREERTOS_TCP || MG_ARCH == MG_ARCH_AZURERTOS || \ |
| 4775 | MG_ARCH == MG_ARCH_TIRTOS |
| 4776 | (void) c; |
| 4777 | #else |
| 4778 | int on = 1; |
| 4779 | #if !defined(SOL_TCP) |
| 4780 | #define SOL_TCP IPPROTO_TCP |
| 4781 | #endif |
| 4782 | if (setsockopt(FD(c), SOL_TCP, TCP_NODELAY, (char *) &on, sizeof(on)) != 0) |
| 4783 | (void) 0; |
| 4784 | if (setsockopt(FD(c), SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on)) != |
| 4785 | 0) |
| 4786 | (void) 0; |
| 4787 | #endif |
| 4788 | } |
| 4789 | |
| 4790 | void mg_connect_resolved(struct mg_connection *c) { |
| 4791 | int type = c->is_udp ? SOCK_DGRAM : SOCK_STREAM; |
| 4792 | int rc, af = c->rem.is_ip6 ? AF_INET6 : AF_INET; // c->rem has resolved IP |
| 4793 | c->fd = S2PTR(socket(af, type, 0)); // Create outbound socket |
| 4794 | c->is_resolving = 0; // Clear resolving flag |
| 4795 | if (FD(c) == MG_INVALID_SOCKET) { |
| 4796 | mg_error(c, "socket(): %d" , MG_SOCK_ERR(-1)); |
| 4797 | } else if (c->is_udp) { |
| 4798 | MG_EPOLL_ADD(c); |
| 4799 | #if MG_ARCH == MG_ARCH_TIRTOS |
| 4800 | union usa usa; // TI-RTOS NDK requires binding to receive on UDP sockets |
| 4801 | socklen_t slen = tousa(&c->loc, &usa); |
| 4802 | if ((rc = bind(c->fd, &usa.sa, slen)) != 0) |
| 4803 | MG_ERROR(("bind: %d" , MG_SOCK_ERR(rc))); |
| 4804 | #endif |
| 4805 | mg_call(c, MG_EV_RESOLVE, NULL); |
| 4806 | mg_call(c, MG_EV_CONNECT, NULL); |
| 4807 | } else { |
| 4808 | union usa usa; |
| 4809 | socklen_t slen = tousa(&c->rem, &usa); |
| 4810 | mg_set_non_blocking_mode(FD(c)); |
| 4811 | setsockopts(c); |
| 4812 | MG_EPOLL_ADD(c); |
| 4813 | mg_call(c, MG_EV_RESOLVE, NULL); |
| 4814 | rc = connect(FD(c), &usa.sa, slen); // Attempt to connect |
| 4815 | if (rc == 0) { // Success |
| 4816 | mg_call(c, MG_EV_CONNECT, NULL); // Send MG_EV_CONNECT to the user |
| 4817 | } else if (MG_SOCK_PENDING(rc)) { // Need to wait for TCP handshake |
| 4818 | MG_DEBUG(("%lu %p -> %M pend" , c->id, c->fd, mg_print_ip_port, &c->rem)); |
| 4819 | c->is_connecting = 1; |
| 4820 | } else { |
| 4821 | mg_error(c, "connect: %d" , MG_SOCK_ERR(rc)); |
| 4822 | } |
| 4823 | } |
| 4824 | } |
| 4825 | |
| 4826 | static MG_SOCKET_TYPE raccept(MG_SOCKET_TYPE sock, union usa *usa, |
| 4827 | socklen_t *len) { |
| 4828 | MG_SOCKET_TYPE fd = MG_INVALID_SOCKET; |
| 4829 | do { |
| 4830 | memset(usa, 0, sizeof(*usa)); |
| 4831 | fd = accept(sock, &usa->sa, len); |
| 4832 | } while (MG_SOCK_INTR(fd)); |
| 4833 | return fd; |
| 4834 | } |
| 4835 | |
| 4836 | static void accept_conn(struct mg_mgr *mgr, struct mg_connection *lsn) { |
| 4837 | struct mg_connection *c = NULL; |
| 4838 | union usa usa; |
| 4839 | socklen_t sa_len = sizeof(usa); |
| 4840 | MG_SOCKET_TYPE fd = raccept(FD(lsn), &usa, &sa_len); |
| 4841 | if (fd == MG_INVALID_SOCKET) { |
| 4842 | #if MG_ARCH == MG_ARCH_AZURERTOS |
| 4843 | // AzureRTOS, in non-block socket mode can mark listening socket readable |
| 4844 | // even it is not. See comment for 'select' func implementation in |
| 4845 | // nx_bsd.c That's not an error, just should try later |
| 4846 | if (errno != EAGAIN) |
| 4847 | #endif |
| 4848 | MG_ERROR(("%lu accept failed, errno %d" , lsn->id, MG_SOCK_ERR(-1))); |
| 4849 | #if (MG_ARCH != MG_ARCH_WIN32) && !MG_ENABLE_FREERTOS_TCP && \ |
| 4850 | (MG_ARCH != MG_ARCH_TIRTOS) && !MG_ENABLE_POLL && !MG_ENABLE_EPOLL |
| 4851 | } else if ((long) fd >= FD_SETSIZE) { |
| 4852 | MG_ERROR(("%ld > %ld" , (long) fd, (long) FD_SETSIZE)); |
| 4853 | closesocket(fd); |
| 4854 | #endif |
| 4855 | } else if ((c = mg_alloc_conn(mgr)) == NULL) { |
| 4856 | MG_ERROR(("%lu OOM" , lsn->id)); |
| 4857 | closesocket(fd); |
| 4858 | } else { |
| 4859 | tomgaddr(&usa, &c->rem, sa_len != sizeof(usa.sin)); |
| 4860 | LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c); |
| 4861 | c->fd = S2PTR(fd); |
| 4862 | MG_EPOLL_ADD(c); |
| 4863 | mg_set_non_blocking_mode(FD(c)); |
| 4864 | setsockopts(c); |
| 4865 | c->is_accepted = 1; |
| 4866 | c->is_hexdumping = lsn->is_hexdumping; |
| 4867 | c->loc = lsn->loc; |
| 4868 | c->pfn = lsn->pfn; |
| 4869 | c->pfn_data = lsn->pfn_data; |
| 4870 | c->fn = lsn->fn; |
| 4871 | c->fn_data = lsn->fn_data; |
| 4872 | MG_DEBUG(("%lu %p accepted %M -> %M" , c->id, c->fd, mg_print_ip_port, |
| 4873 | &c->rem, mg_print_ip_port, &c->loc)); |
| 4874 | mg_call(c, MG_EV_OPEN, NULL); |
| 4875 | mg_call(c, MG_EV_ACCEPT, NULL); |
| 4876 | } |
| 4877 | } |
| 4878 | |
| 4879 | static bool mg_socketpair(MG_SOCKET_TYPE sp[2], union usa usa[2], bool udp) { |
| 4880 | MG_SOCKET_TYPE sock; |
| 4881 | socklen_t n = sizeof(usa[0].sin); |
| 4882 | bool success = false; |
| 4883 | |
| 4884 | sock = sp[0] = sp[1] = MG_INVALID_SOCKET; |
| 4885 | (void) memset(&usa[0], 0, sizeof(usa[0])); |
| 4886 | usa[0].sin.sin_family = AF_INET; |
| 4887 | *(uint32_t *) &usa->sin.sin_addr = mg_htonl(0x7f000001U); // 127.0.0.1 |
| 4888 | usa[1] = usa[0]; |
| 4889 | |
| 4890 | if (udp && (sp[0] = socket(AF_INET, SOCK_DGRAM, 0)) != MG_INVALID_SOCKET && |
| 4891 | (sp[1] = socket(AF_INET, SOCK_DGRAM, 0)) != MG_INVALID_SOCKET && |
| 4892 | bind(sp[0], &usa[0].sa, n) == 0 && bind(sp[1], &usa[1].sa, n) == 0 && |
| 4893 | getsockname(sp[0], &usa[0].sa, &n) == 0 && |
| 4894 | getsockname(sp[1], &usa[1].sa, &n) == 0 && |
| 4895 | connect(sp[0], &usa[1].sa, n) == 0 && |
| 4896 | connect(sp[1], &usa[0].sa, n) == 0) { |
| 4897 | success = true; |
| 4898 | } else if (!udp && |
| 4899 | (sock = socket(AF_INET, SOCK_STREAM, 0)) != MG_INVALID_SOCKET && |
| 4900 | bind(sock, &usa[0].sa, n) == 0 && |
| 4901 | listen(sock, MG_SOCK_LISTEN_BACKLOG_SIZE) == 0 && |
| 4902 | getsockname(sock, &usa[0].sa, &n) == 0 && |
| 4903 | (sp[0] = socket(AF_INET, SOCK_STREAM, 0)) != MG_INVALID_SOCKET && |
| 4904 | connect(sp[0], &usa[0].sa, n) == 0 && |
| 4905 | (sp[1] = raccept(sock, &usa[1], &n)) != MG_INVALID_SOCKET) { |
| 4906 | success = true; |
| 4907 | } |
| 4908 | if (success) { |
| 4909 | mg_set_non_blocking_mode(sp[1]); |
| 4910 | } else { |
| 4911 | if (sp[0] != MG_INVALID_SOCKET) closesocket(sp[0]); |
| 4912 | if (sp[1] != MG_INVALID_SOCKET) closesocket(sp[1]); |
| 4913 | sp[0] = sp[1] = MG_INVALID_SOCKET; |
| 4914 | } |
| 4915 | if (sock != MG_INVALID_SOCKET) closesocket(sock); |
| 4916 | return success; |
| 4917 | } |
| 4918 | |
| 4919 | int mg_mkpipe(struct mg_mgr *mgr, mg_event_handler_t fn, void *fn_data, |
| 4920 | bool udp) { |
| 4921 | union usa usa[2]; |
| 4922 | MG_SOCKET_TYPE sp[2] = {MG_INVALID_SOCKET, MG_INVALID_SOCKET}; |
| 4923 | struct mg_connection *c = NULL; |
| 4924 | if (!mg_socketpair(sp, usa, udp)) { |
| 4925 | MG_ERROR(("Cannot create socket pair" )); |
| 4926 | } else if ((c = mg_wrapfd(mgr, (int) sp[1], fn, fn_data)) == NULL) { |
| 4927 | closesocket(sp[0]); |
| 4928 | closesocket(sp[1]); |
| 4929 | sp[0] = sp[1] = MG_INVALID_SOCKET; |
| 4930 | } else { |
| 4931 | tomgaddr(&usa[0], &c->rem, false); |
| 4932 | MG_DEBUG(("%lu %p pipe %lu" , c->id, c->fd, (unsigned long) sp[0])); |
| 4933 | } |
| 4934 | return (int) sp[0]; |
| 4935 | } |
| 4936 | |
| 4937 | static bool can_read(const struct mg_connection *c) { |
| 4938 | return c->is_full == false; |
| 4939 | } |
| 4940 | |
| 4941 | static bool can_write(const struct mg_connection *c) { |
| 4942 | return c->is_connecting || (c->send.len > 0 && c->is_tls_hs == 0); |
| 4943 | } |
| 4944 | |
| 4945 | static bool skip_iotest(const struct mg_connection *c) { |
| 4946 | return (c->is_closing || c->is_resolving || FD(c) == MG_INVALID_SOCKET) || |
| 4947 | (can_read(c) == false && can_write(c) == false); |
| 4948 | } |
| 4949 | |
| 4950 | static void mg_iotest(struct mg_mgr *mgr, int ms) { |
| 4951 | #if MG_ENABLE_FREERTOS_TCP |
| 4952 | struct mg_connection *c; |
| 4953 | for (c = mgr->conns; c != NULL; c = c->next) { |
| 4954 | c->is_readable = c->is_writable = 0; |
| 4955 | if (skip_iotest(c)) continue; |
| 4956 | if (can_read(c)) |
| 4957 | FreeRTOS_FD_SET(c->fd, mgr->ss, eSELECT_READ | eSELECT_EXCEPT); |
| 4958 | if (can_write(c)) FreeRTOS_FD_SET(c->fd, mgr->ss, eSELECT_WRITE); |
| 4959 | } |
| 4960 | FreeRTOS_select(mgr->ss, pdMS_TO_TICKS(ms)); |
| 4961 | for (c = mgr->conns; c != NULL; c = c->next) { |
| 4962 | EventBits_t bits = FreeRTOS_FD_ISSET(c->fd, mgr->ss); |
| 4963 | c->is_readable = bits & (eSELECT_READ | eSELECT_EXCEPT) ? 1U : 0; |
| 4964 | c->is_writable = bits & eSELECT_WRITE ? 1U : 0; |
| 4965 | if (c->fd != MG_INVALID_SOCKET) |
| 4966 | FreeRTOS_FD_CLR(c->fd, mgr->ss, |
| 4967 | eSELECT_READ | eSELECT_EXCEPT | eSELECT_WRITE); |
| 4968 | } |
| 4969 | #elif MG_ENABLE_EPOLL |
| 4970 | size_t max = 1; |
| 4971 | for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) { |
| 4972 | c->is_readable = c->is_writable = 0; |
| 4973 | if (mg_tls_pending(c) > 0) ms = 1, c->is_readable = 1; |
| 4974 | if (can_write(c)) MG_EPOLL_MOD(c, 1); |
| 4975 | max++; |
| 4976 | } |
| 4977 | struct epoll_event *evs = (struct epoll_event *) alloca(max * sizeof(evs[0])); |
| 4978 | int n = epoll_wait(mgr->epoll_fd, evs, (int) max, ms); |
| 4979 | for (int i = 0; i < n; i++) { |
| 4980 | struct mg_connection *c = (struct mg_connection *) evs[i].data.ptr; |
| 4981 | if (evs[i].events & EPOLLERR) { |
| 4982 | mg_error(c, "socket error" ); |
| 4983 | } else if (c->is_readable == 0) { |
| 4984 | bool rd = evs[i].events & (EPOLLIN | EPOLLHUP); |
| 4985 | bool wr = evs[i].events & EPOLLOUT; |
| 4986 | c->is_readable = can_read(c) && rd ? 1U : 0; |
| 4987 | c->is_writable = can_write(c) && wr ? 1U : 0; |
| 4988 | } |
| 4989 | } |
| 4990 | (void) skip_iotest; |
| 4991 | #elif MG_ENABLE_POLL |
| 4992 | nfds_t n = 0; |
| 4993 | for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) n++; |
| 4994 | struct pollfd *fds = (struct pollfd *) alloca(n * sizeof(fds[0])); |
| 4995 | memset(fds, 0, n * sizeof(fds[0])); |
| 4996 | n = 0; |
| 4997 | for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) { |
| 4998 | c->is_readable = c->is_writable = 0; |
| 4999 | if (skip_iotest(c)) { |
| 5000 | // Socket not valid, ignore |
| 5001 | } else if (mg_tls_pending(c) > 0) { |
| 5002 | ms = 1; // Don't wait if TLS is ready |
| 5003 | } else { |
| 5004 | fds[n].fd = FD(c); |
| 5005 | if (can_read(c)) fds[n].events |= POLLIN; |
| 5006 | if (can_write(c)) fds[n].events |= POLLOUT; |
| 5007 | n++; |
| 5008 | } |
| 5009 | } |
| 5010 | |
| 5011 | // MG_INFO(("poll n=%d ms=%d", (int) n, ms)); |
| 5012 | if (poll(fds, n, ms) < 0) { |
| 5013 | #if MG_ARCH == MG_ARCH_WIN32 |
| 5014 | if (n == 0) Sleep(ms); // On Windows, poll fails if no sockets |
| 5015 | #endif |
| 5016 | memset(fds, 0, n * sizeof(fds[0])); |
| 5017 | } |
| 5018 | n = 0; |
| 5019 | for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) { |
| 5020 | if (skip_iotest(c)) { |
| 5021 | // Socket not valid, ignore |
| 5022 | } else if (mg_tls_pending(c) > 0) { |
| 5023 | c->is_readable = 1; |
| 5024 | } else { |
| 5025 | if (fds[n].revents & POLLERR) { |
| 5026 | mg_error(c, "socket error" ); |
| 5027 | } else { |
| 5028 | c->is_readable = |
| 5029 | (unsigned) (fds[n].revents & (POLLIN | POLLHUP) ? 1 : 0); |
| 5030 | c->is_writable = (unsigned) (fds[n].revents & POLLOUT ? 1 : 0); |
| 5031 | } |
| 5032 | n++; |
| 5033 | } |
| 5034 | } |
| 5035 | #else |
| 5036 | struct timeval tv = {ms / 1000, (ms % 1000) * 1000}, tv_zero = {0, 0}, *tvp; |
| 5037 | struct mg_connection *c; |
| 5038 | fd_set rset, wset, eset; |
| 5039 | MG_SOCKET_TYPE maxfd = 0; |
| 5040 | int rc; |
| 5041 | |
| 5042 | FD_ZERO(&rset); |
| 5043 | FD_ZERO(&wset); |
| 5044 | FD_ZERO(&eset); |
| 5045 | tvp = ms < 0 ? NULL : &tv; |
| 5046 | for (c = mgr->conns; c != NULL; c = c->next) { |
| 5047 | c->is_readable = c->is_writable = 0; |
| 5048 | if (skip_iotest(c)) continue; |
| 5049 | FD_SET(FD(c), &eset); |
| 5050 | if (can_read(c)) FD_SET(FD(c), &rset); |
| 5051 | if (can_write(c)) FD_SET(FD(c), &wset); |
| 5052 | if (mg_tls_pending(c) > 0) tvp = &tv_zero; |
| 5053 | if (FD(c) > maxfd) maxfd = FD(c); |
| 5054 | } |
| 5055 | |
| 5056 | if ((rc = select((int) maxfd + 1, &rset, &wset, &eset, tvp)) < 0) { |
| 5057 | #if MG_ARCH == MG_ARCH_WIN32 |
| 5058 | if (maxfd == 0) Sleep(ms); // On Windows, select fails if no sockets |
| 5059 | #else |
| 5060 | MG_ERROR(("select: %d %d" , rc, MG_SOCK_ERR(rc))); |
| 5061 | #endif |
| 5062 | FD_ZERO(&rset); |
| 5063 | FD_ZERO(&wset); |
| 5064 | FD_ZERO(&eset); |
| 5065 | } |
| 5066 | |
| 5067 | for (c = mgr->conns; c != NULL; c = c->next) { |
| 5068 | if (FD(c) != MG_INVALID_SOCKET && FD_ISSET(FD(c), &eset)) { |
| 5069 | mg_error(c, "socket error" ); |
| 5070 | } else { |
| 5071 | c->is_readable = FD(c) != MG_INVALID_SOCKET && FD_ISSET(FD(c), &rset); |
| 5072 | c->is_writable = FD(c) != MG_INVALID_SOCKET && FD_ISSET(FD(c), &wset); |
| 5073 | if (mg_tls_pending(c) > 0) c->is_readable = 1; |
| 5074 | } |
| 5075 | } |
| 5076 | #endif |
| 5077 | } |
| 5078 | |
| 5079 | void mg_mgr_poll(struct mg_mgr *mgr, int ms) { |
| 5080 | struct mg_connection *c, *tmp; |
| 5081 | uint64_t now; |
| 5082 | |
| 5083 | mg_iotest(mgr, ms); |
| 5084 | now = mg_millis(); |
| 5085 | mg_timer_poll(&mgr->timers, now); |
| 5086 | |
| 5087 | for (c = mgr->conns; c != NULL; c = tmp) { |
| 5088 | bool is_resp = c->is_resp; |
| 5089 | tmp = c->next; |
| 5090 | mg_call(c, MG_EV_POLL, &now); |
| 5091 | if (is_resp && !c->is_resp) { |
| 5092 | long n = 0; |
| 5093 | mg_call(c, MG_EV_READ, &n); |
| 5094 | } |
| 5095 | MG_VERBOSE(("%lu %c%c %c%c%c%c%c" , c->id, c->is_readable ? 'r' : '-', |
| 5096 | c->is_writable ? 'w' : '-', c->is_tls ? 'T' : 't', |
| 5097 | c->is_connecting ? 'C' : 'c', c->is_tls_hs ? 'H' : 'h', |
| 5098 | c->is_resolving ? 'R' : 'r', c->is_closing ? 'C' : 'c')); |
| 5099 | if (c->is_resolving || c->is_closing) { |
| 5100 | // Do nothing |
| 5101 | } else if (c->is_listening && c->is_udp == 0) { |
| 5102 | if (c->is_readable) accept_conn(mgr, c); |
| 5103 | } else if (c->is_connecting) { |
| 5104 | if (c->is_readable || c->is_writable) connect_conn(c); |
| 5105 | } else if (c->is_tls_hs) { |
| 5106 | if ((c->is_readable || c->is_writable)) mg_tls_handshake(c); |
| 5107 | } else { |
| 5108 | if (c->is_readable) read_conn(c); |
| 5109 | if (c->is_writable) write_conn(c); |
| 5110 | } |
| 5111 | |
| 5112 | if (c->is_draining && c->send.len == 0) c->is_closing = 1; |
| 5113 | if (c->is_closing) close_conn(c); |
| 5114 | } |
| 5115 | } |
| 5116 | #endif |
| 5117 | |
| 5118 | #ifdef MG_ENABLE_LINES |
| 5119 | #line 1 "src/ssi.c" |
| 5120 | #endif |
| 5121 | |
| 5122 | |
| 5123 | |
| 5124 | |
| 5125 | #ifndef MG_MAX_SSI_DEPTH |
| 5126 | #define MG_MAX_SSI_DEPTH 5 |
| 5127 | #endif |
| 5128 | |
| 5129 | #ifndef MG_SSI_BUFSIZ |
| 5130 | #define MG_SSI_BUFSIZ 1024 |
| 5131 | #endif |
| 5132 | |
| 5133 | #if MG_ENABLE_SSI |
| 5134 | static char *mg_ssi(const char *path, const char *root, int depth) { |
| 5135 | struct mg_iobuf b = {NULL, 0, 0, MG_IO_SIZE}; |
| 5136 | FILE *fp = fopen(path, "rb" ); |
| 5137 | if (fp != NULL) { |
| 5138 | char buf[MG_SSI_BUFSIZ], arg[sizeof(buf)]; |
| 5139 | int ch, intag = 0; |
| 5140 | size_t len = 0; |
| 5141 | buf[0] = arg[0] = '\0'; |
| 5142 | while ((ch = fgetc(fp)) != EOF) { |
| 5143 | if (intag && ch == '>' && buf[len - 1] == '-' && buf[len - 2] == '-') { |
| 5144 | buf[len++] = (char) (ch & 0xff); |
| 5145 | buf[len] = '\0'; |
| 5146 | if (sscanf(buf, "<!--#include file=\"%[^\"]" , arg)) { |
| 5147 | char tmp[MG_PATH_MAX + MG_SSI_BUFSIZ + 10], |
| 5148 | *p = (char *) path + strlen(path), *data; |
| 5149 | while (p > path && p[-1] != MG_DIRSEP && p[-1] != '/') p--; |
| 5150 | mg_snprintf(tmp, sizeof(tmp), "%.*s%s" , (int) (p - path), path, arg); |
| 5151 | if (depth < MG_MAX_SSI_DEPTH && |
| 5152 | (data = mg_ssi(tmp, root, depth + 1)) != NULL) { |
| 5153 | mg_iobuf_add(&b, b.len, data, strlen(data)); |
| 5154 | free(data); |
| 5155 | } else { |
| 5156 | MG_ERROR(("%s: file=%s error or too deep" , path, arg)); |
| 5157 | } |
| 5158 | } else if (sscanf(buf, "<!--#include virtual=\"%[^\"]" , arg)) { |
| 5159 | char tmp[MG_PATH_MAX + MG_SSI_BUFSIZ + 10], *data; |
| 5160 | mg_snprintf(tmp, sizeof(tmp), "%s%s" , root, arg); |
| 5161 | if (depth < MG_MAX_SSI_DEPTH && |
| 5162 | (data = mg_ssi(tmp, root, depth + 1)) != NULL) { |
| 5163 | mg_iobuf_add(&b, b.len, data, strlen(data)); |
| 5164 | free(data); |
| 5165 | } else { |
| 5166 | MG_ERROR(("%s: virtual=%s error or too deep" , path, arg)); |
| 5167 | } |
| 5168 | } else { |
| 5169 | // Unknown SSI tag |
| 5170 | MG_ERROR(("Unknown SSI tag: %.*s" , (int) len, buf)); |
| 5171 | mg_iobuf_add(&b, b.len, buf, len); |
| 5172 | } |
| 5173 | intag = 0; |
| 5174 | len = 0; |
| 5175 | } else if (ch == '<') { |
| 5176 | intag = 1; |
| 5177 | if (len > 0) mg_iobuf_add(&b, b.len, buf, len); |
| 5178 | len = 0; |
| 5179 | buf[len++] = (char) (ch & 0xff); |
| 5180 | } else if (intag) { |
| 5181 | if (len == 5 && strncmp(buf, "<!--#" , 5) != 0) { |
| 5182 | intag = 0; |
| 5183 | } else if (len >= sizeof(buf) - 2) { |
| 5184 | MG_ERROR(("%s: SSI tag is too large" , path)); |
| 5185 | len = 0; |
| 5186 | } |
| 5187 | buf[len++] = (char) (ch & 0xff); |
| 5188 | } else { |
| 5189 | buf[len++] = (char) (ch & 0xff); |
| 5190 | if (len >= sizeof(buf)) { |
| 5191 | mg_iobuf_add(&b, b.len, buf, len); |
| 5192 | len = 0; |
| 5193 | } |
| 5194 | } |
| 5195 | } |
| 5196 | if (len > 0) mg_iobuf_add(&b, b.len, buf, len); |
| 5197 | if (b.len > 0) mg_iobuf_add(&b, b.len, "" , 1); // nul-terminate |
| 5198 | fclose(fp); |
| 5199 | } |
| 5200 | (void) depth; |
| 5201 | (void) root; |
| 5202 | return (char *) b.buf; |
| 5203 | } |
| 5204 | |
| 5205 | void mg_http_serve_ssi(struct mg_connection *c, const char *root, |
| 5206 | const char *fullpath) { |
| 5207 | const char *headers = "Content-Type: text/html; charset=utf-8\r\n" ; |
| 5208 | char *data = mg_ssi(fullpath, root, 0); |
| 5209 | mg_http_reply(c, 200, headers, "%s" , data == NULL ? "" : data); |
| 5210 | free(data); |
| 5211 | } |
| 5212 | #else |
| 5213 | void mg_http_serve_ssi(struct mg_connection *c, const char *root, |
| 5214 | const char *fullpath) { |
| 5215 | mg_http_reply(c, 501, NULL, "SSI not enabled" ); |
| 5216 | (void) root, (void) fullpath; |
| 5217 | } |
| 5218 | #endif |
| 5219 | |
| 5220 | #ifdef MG_ENABLE_LINES |
| 5221 | #line 1 "src/str.c" |
| 5222 | #endif |
| 5223 | |
| 5224 | |
| 5225 | struct mg_str mg_str_s(const char *s) { |
| 5226 | struct mg_str str = {s, s == NULL ? 0 : strlen(s)}; |
| 5227 | return str; |
| 5228 | } |
| 5229 | |
| 5230 | struct mg_str mg_str_n(const char *s, size_t n) { |
| 5231 | struct mg_str str = {s, n}; |
| 5232 | return str; |
| 5233 | } |
| 5234 | |
| 5235 | int mg_lower(const char *s) { |
| 5236 | int c = *s; |
| 5237 | if (c >= 'A' && c <= 'Z') c += 'a' - 'A'; |
| 5238 | return c; |
| 5239 | } |
| 5240 | |
| 5241 | int mg_ncasecmp(const char *s1, const char *s2, size_t len) { |
| 5242 | int diff = 0; |
| 5243 | if (len > 0) do { |
| 5244 | diff = mg_lower(s1++) - mg_lower(s2++); |
| 5245 | } while (diff == 0 && s1[-1] != '\0' && --len > 0); |
| 5246 | return diff; |
| 5247 | } |
| 5248 | |
| 5249 | int mg_casecmp(const char *s1, const char *s2) { |
| 5250 | return mg_ncasecmp(s1, s2, (size_t) ~0); |
| 5251 | } |
| 5252 | |
| 5253 | int mg_vcmp(const struct mg_str *s1, const char *s2) { |
| 5254 | size_t n2 = strlen(s2), n1 = s1->len; |
| 5255 | int r = strncmp(s1->ptr, s2, (n1 < n2) ? n1 : n2); |
| 5256 | if (r == 0) return (int) (n1 - n2); |
| 5257 | return r; |
| 5258 | } |
| 5259 | |
| 5260 | int mg_vcasecmp(const struct mg_str *str1, const char *str2) { |
| 5261 | size_t n2 = strlen(str2), n1 = str1->len; |
| 5262 | int r = mg_ncasecmp(str1->ptr, str2, (n1 < n2) ? n1 : n2); |
| 5263 | if (r == 0) return (int) (n1 - n2); |
| 5264 | return r; |
| 5265 | } |
| 5266 | |
| 5267 | struct mg_str mg_strdup(const struct mg_str s) { |
| 5268 | struct mg_str r = {NULL, 0}; |
| 5269 | if (s.len > 0 && s.ptr != NULL) { |
| 5270 | char *sc = (char *) calloc(1, s.len + 1); |
| 5271 | if (sc != NULL) { |
| 5272 | memcpy(sc, s.ptr, s.len); |
| 5273 | sc[s.len] = '\0'; |
| 5274 | r.ptr = sc; |
| 5275 | r.len = s.len; |
| 5276 | } |
| 5277 | } |
| 5278 | return r; |
| 5279 | } |
| 5280 | |
| 5281 | int mg_strcmp(const struct mg_str str1, const struct mg_str str2) { |
| 5282 | size_t i = 0; |
| 5283 | while (i < str1.len && i < str2.len) { |
| 5284 | int c1 = str1.ptr[i]; |
| 5285 | int c2 = str2.ptr[i]; |
| 5286 | if (c1 < c2) return -1; |
| 5287 | if (c1 > c2) return 1; |
| 5288 | i++; |
| 5289 | } |
| 5290 | if (i < str1.len) return 1; |
| 5291 | if (i < str2.len) return -1; |
| 5292 | return 0; |
| 5293 | } |
| 5294 | |
| 5295 | const char *mg_strstr(const struct mg_str haystack, |
| 5296 | const struct mg_str needle) { |
| 5297 | size_t i; |
| 5298 | if (needle.len > haystack.len) return NULL; |
| 5299 | if (needle.len == 0) return haystack.ptr; |
| 5300 | for (i = 0; i <= haystack.len - needle.len; i++) { |
| 5301 | if (memcmp(haystack.ptr + i, needle.ptr, needle.len) == 0) { |
| 5302 | return haystack.ptr + i; |
| 5303 | } |
| 5304 | } |
| 5305 | return NULL; |
| 5306 | } |
| 5307 | |
| 5308 | static bool is_space(int c) { |
| 5309 | return c == ' ' || c == '\r' || c == '\n' || c == '\t'; |
| 5310 | } |
| 5311 | |
| 5312 | struct mg_str mg_strstrip(struct mg_str s) { |
| 5313 | while (s.len > 0 && is_space((int) *s.ptr)) s.ptr++, s.len--; |
| 5314 | while (s.len > 0 && is_space((int) *(s.ptr + s.len - 1))) s.len--; |
| 5315 | return s; |
| 5316 | } |
| 5317 | |
| 5318 | bool mg_match(struct mg_str s, struct mg_str p, struct mg_str *caps) { |
| 5319 | size_t i = 0, j = 0, ni = 0, nj = 0; |
| 5320 | if (caps) caps->ptr = NULL, caps->len = 0; |
| 5321 | while (i < p.len || j < s.len) { |
| 5322 | if (i < p.len && j < s.len && (p.ptr[i] == '?' || s.ptr[j] == p.ptr[i])) { |
| 5323 | if (caps == NULL) { |
| 5324 | } else if (p.ptr[i] == '?') { |
| 5325 | caps->ptr = &s.ptr[j], caps->len = 1; // Finalize `?` cap |
| 5326 | caps++, caps->ptr = NULL, caps->len = 0; // Init next cap |
| 5327 | } else if (caps->ptr != NULL && caps->len == 0) { |
| 5328 | caps->len = (size_t) (&s.ptr[j] - caps->ptr); // Finalize current cap |
| 5329 | caps++, caps->len = 0, caps->ptr = NULL; // Init next cap |
| 5330 | } |
| 5331 | i++, j++; |
| 5332 | } else if (i < p.len && (p.ptr[i] == '*' || p.ptr[i] == '#')) { |
| 5333 | if (caps && !caps->ptr) caps->len = 0, caps->ptr = &s.ptr[j]; // Init cap |
| 5334 | ni = i++, nj = j + 1; |
| 5335 | } else if (nj > 0 && nj <= s.len && (p.ptr[ni] == '#' || s.ptr[j] != '/')) { |
| 5336 | i = ni, j = nj; |
| 5337 | if (caps && caps->ptr == NULL && caps->len == 0) { |
| 5338 | caps--, caps->len = 0; // Restart previous cap |
| 5339 | } |
| 5340 | } else { |
| 5341 | return false; |
| 5342 | } |
| 5343 | } |
| 5344 | if (caps && caps->ptr && caps->len == 0) { |
| 5345 | caps->len = (size_t) (&s.ptr[j] - caps->ptr); |
| 5346 | } |
| 5347 | return true; |
| 5348 | } |
| 5349 | |
| 5350 | bool mg_globmatch(const char *s1, size_t n1, const char *s2, size_t n2) { |
| 5351 | return mg_match(mg_str_n(s2, n2), mg_str_n(s1, n1), NULL); |
| 5352 | } |
| 5353 | |
| 5354 | static size_t mg_nce(const char *s, size_t n, size_t ofs, size_t *koff, |
| 5355 | size_t *klen, size_t *voff, size_t *vlen, char delim) { |
| 5356 | size_t kvlen, kl; |
| 5357 | for (kvlen = 0; ofs + kvlen < n && s[ofs + kvlen] != delim;) kvlen++; |
| 5358 | for (kl = 0; kl < kvlen && s[ofs + kl] != '=';) kl++; |
| 5359 | if (koff != NULL) *koff = ofs; |
| 5360 | if (klen != NULL) *klen = kl; |
| 5361 | if (voff != NULL) *voff = kl < kvlen ? ofs + kl + 1 : 0; |
| 5362 | if (vlen != NULL) *vlen = kl < kvlen ? kvlen - kl - 1 : 0; |
| 5363 | ofs += kvlen + 1; |
| 5364 | return ofs > n ? n : ofs; |
| 5365 | } |
| 5366 | |
| 5367 | bool mg_split(struct mg_str *s, struct mg_str *k, struct mg_str *v, char sep) { |
| 5368 | size_t koff = 0, klen = 0, voff = 0, vlen = 0, off = 0; |
| 5369 | if (s->ptr == NULL || s->len == 0) return 0; |
| 5370 | off = mg_nce(s->ptr, s->len, 0, &koff, &klen, &voff, &vlen, sep); |
| 5371 | if (k != NULL) *k = mg_str_n(s->ptr + koff, klen); |
| 5372 | if (v != NULL) *v = mg_str_n(s->ptr + voff, vlen); |
| 5373 | *s = mg_str_n(s->ptr + off, s->len - off); |
| 5374 | return off > 0; |
| 5375 | } |
| 5376 | |
| 5377 | bool mg_commalist(struct mg_str *s, struct mg_str *k, struct mg_str *v) { |
| 5378 | return mg_split(s, k, v, ','); |
| 5379 | } |
| 5380 | |
| 5381 | char *mg_hex(const void *buf, size_t len, char *to) { |
| 5382 | const unsigned char *p = (const unsigned char *) buf; |
| 5383 | const char *hex = "0123456789abcdef" ; |
| 5384 | size_t i = 0; |
| 5385 | for (; len--; p++) { |
| 5386 | to[i++] = hex[p[0] >> 4]; |
| 5387 | to[i++] = hex[p[0] & 0x0f]; |
| 5388 | } |
| 5389 | to[i] = '\0'; |
| 5390 | return to; |
| 5391 | } |
| 5392 | |
| 5393 | static unsigned char mg_unhex_nimble(unsigned char c) { |
| 5394 | return (c >= '0' && c <= '9') ? (unsigned char) (c - '0') |
| 5395 | : (c >= 'A' && c <= 'F') ? (unsigned char) (c - '7') |
| 5396 | : (unsigned char) (c - 'W'); |
| 5397 | } |
| 5398 | |
| 5399 | unsigned long mg_unhexn(const char *s, size_t len) { |
| 5400 | unsigned long i = 0, v = 0; |
| 5401 | for (i = 0; i < len; i++) v <<= 4, v |= mg_unhex_nimble(((uint8_t *) s)[i]); |
| 5402 | return v; |
| 5403 | } |
| 5404 | |
| 5405 | void mg_unhex(const char *buf, size_t len, unsigned char *to) { |
| 5406 | size_t i; |
| 5407 | for (i = 0; i < len; i += 2) { |
| 5408 | to[i >> 1] = (unsigned char) mg_unhexn(&buf[i], 2); |
| 5409 | } |
| 5410 | } |
| 5411 | |
| 5412 | char *mg_remove_double_dots(char *s) { |
| 5413 | char *saved = s, *p = s; |
| 5414 | while (*s != '\0') { |
| 5415 | *p++ = *s++; |
| 5416 | if (s[-1] == '/' || s[-1] == '\\') { |
| 5417 | while (s[0] != '\0') { |
| 5418 | if (s[0] == '/' || s[0] == '\\') { |
| 5419 | s++; |
| 5420 | } else if (s[0] == '.' && s[1] == '.' && |
| 5421 | (s[2] == '/' || s[2] == '\\')) { |
| 5422 | s += 2; |
| 5423 | } else { |
| 5424 | break; |
| 5425 | } |
| 5426 | } |
| 5427 | } |
| 5428 | } |
| 5429 | *p = '\0'; |
| 5430 | return saved; |
| 5431 | } |
| 5432 | |
| 5433 | #ifdef MG_ENABLE_LINES |
| 5434 | #line 1 "src/timer.c" |
| 5435 | #endif |
| 5436 | |
| 5437 | |
| 5438 | |
| 5439 | #define MG_TIMER_CALLED 4 |
| 5440 | |
| 5441 | void mg_timer_init(struct mg_timer **head, struct mg_timer *t, uint64_t ms, |
| 5442 | unsigned flags, void (*fn)(void *), void *arg) { |
| 5443 | t->id = 0, t->period_ms = ms, t->expire = 0; |
| 5444 | t->flags = flags, t->fn = fn, t->arg = arg, t->next = *head; |
| 5445 | *head = t; |
| 5446 | } |
| 5447 | |
| 5448 | void mg_timer_free(struct mg_timer **head, struct mg_timer *t) { |
| 5449 | while (*head && *head != t) head = &(*head)->next; |
| 5450 | if (*head) *head = t->next; |
| 5451 | } |
| 5452 | |
| 5453 | // t: expiration time, prd: period, now: current time. Return true if expired |
| 5454 | bool mg_timer_expired(uint64_t *t, uint64_t prd, uint64_t now) { |
| 5455 | if (now + prd < *t) *t = 0; // Time wrapped? Reset timer |
| 5456 | if (*t == 0) *t = now + prd; // Firt poll? Set expiration |
| 5457 | if (*t > now) return false; // Not expired yet, return |
| 5458 | *t = (now - *t) > prd ? now + prd : *t + prd; // Next expiration time |
| 5459 | return true; // Expired, return true |
| 5460 | } |
| 5461 | |
| 5462 | void mg_timer_poll(struct mg_timer **head, uint64_t now_ms) { |
| 5463 | struct mg_timer *t, *tmp; |
| 5464 | for (t = *head; t != NULL; t = tmp) { |
| 5465 | bool once = t->expire == 0 && (t->flags & MG_TIMER_RUN_NOW) && |
| 5466 | !(t->flags & MG_TIMER_CALLED); // Handle MG_TIMER_NOW only once |
| 5467 | bool expired = mg_timer_expired(&t->expire, t->period_ms, now_ms); |
| 5468 | tmp = t->next; |
| 5469 | if (!once && !expired) continue; |
| 5470 | if ((t->flags & MG_TIMER_REPEAT) || !(t->flags & MG_TIMER_CALLED)) { |
| 5471 | t->fn(t->arg); |
| 5472 | } |
| 5473 | t->flags |= MG_TIMER_CALLED; |
| 5474 | } |
| 5475 | } |
| 5476 | |
| 5477 | #ifdef MG_ENABLE_LINES |
| 5478 | #line 1 "src/tls_dummy.c" |
| 5479 | #endif |
| 5480 | |
| 5481 | |
| 5482 | #if !MG_ENABLE_MBEDTLS && !MG_ENABLE_OPENSSL && !MG_ENABLE_CUSTOM_TLS |
| 5483 | void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) { |
| 5484 | (void) opts; |
| 5485 | mg_error(c, "TLS is not enabled" ); |
| 5486 | } |
| 5487 | void mg_tls_handshake(struct mg_connection *c) { |
| 5488 | (void) c; |
| 5489 | } |
| 5490 | void mg_tls_free(struct mg_connection *c) { |
| 5491 | (void) c; |
| 5492 | } |
| 5493 | long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) { |
| 5494 | return c == NULL || buf == NULL || len == 0 ? 0 : -1; |
| 5495 | } |
| 5496 | long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) { |
| 5497 | return c == NULL || buf == NULL || len == 0 ? 0 : -1; |
| 5498 | } |
| 5499 | size_t mg_tls_pending(struct mg_connection *c) { |
| 5500 | (void) c; |
| 5501 | return 0; |
| 5502 | } |
| 5503 | #endif |
| 5504 | |
| 5505 | #ifdef MG_ENABLE_LINES |
| 5506 | #line 1 "src/tls_mbed.c" |
| 5507 | #endif |
| 5508 | |
| 5509 | |
| 5510 | |
| 5511 | |
| 5512 | #if MG_ENABLE_MBEDTLS |
| 5513 | |
| 5514 | #if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= 0x03000000 |
| 5515 | #define MGRNG , rng_get, NULL |
| 5516 | #else |
| 5517 | #define MGRNG |
| 5518 | #endif |
| 5519 | |
| 5520 | void mg_tls_free(struct mg_connection *c) { |
| 5521 | struct mg_tls *tls = (struct mg_tls *) c->tls; |
| 5522 | if (tls != NULL) { |
| 5523 | free(tls->cafile); |
| 5524 | mbedtls_ssl_free(&tls->ssl); |
| 5525 | mbedtls_pk_free(&tls->pk); |
| 5526 | mbedtls_x509_crt_free(&tls->ca); |
| 5527 | mbedtls_x509_crt_free(&tls->cert); |
| 5528 | mbedtls_ssl_config_free(&tls->conf); |
| 5529 | free(tls); |
| 5530 | c->tls = NULL; |
| 5531 | } |
| 5532 | } |
| 5533 | |
| 5534 | static int mg_net_send(void *ctx, const unsigned char *buf, size_t len) { |
| 5535 | long n = mg_io_send((struct mg_connection *) ctx, buf, len); |
| 5536 | MG_VERBOSE(("%lu n=%ld e=%d" , ((struct mg_connection *) ctx)->id, n, errno)); |
| 5537 | if (n == MG_IO_WAIT) return MBEDTLS_ERR_SSL_WANT_WRITE; |
| 5538 | if (n == MG_IO_RESET) return MBEDTLS_ERR_NET_CONN_RESET; |
| 5539 | if (n == MG_IO_ERR) return MBEDTLS_ERR_NET_SEND_FAILED; |
| 5540 | return (int) n; |
| 5541 | } |
| 5542 | |
| 5543 | static int mg_net_recv(void *ctx, unsigned char *buf, size_t len) { |
| 5544 | long n = mg_io_recv((struct mg_connection *) ctx, buf, len); |
| 5545 | MG_VERBOSE(("%lu n=%ld" , ((struct mg_connection *) ctx)->id, n)); |
| 5546 | if (n == MG_IO_WAIT) return MBEDTLS_ERR_SSL_WANT_WRITE; |
| 5547 | if (n == MG_IO_RESET) return MBEDTLS_ERR_NET_CONN_RESET; |
| 5548 | if (n == MG_IO_ERR) return MBEDTLS_ERR_NET_RECV_FAILED; |
| 5549 | return (int) n; |
| 5550 | } |
| 5551 | |
| 5552 | void mg_tls_handshake(struct mg_connection *c) { |
| 5553 | struct mg_tls *tls = (struct mg_tls *) c->tls; |
| 5554 | int rc = mbedtls_ssl_handshake(&tls->ssl); |
| 5555 | if (rc == 0) { // Success |
| 5556 | MG_DEBUG(("%lu success" , c->id)); |
| 5557 | c->is_tls_hs = 0; |
| 5558 | mg_call(c, MG_EV_TLS_HS, NULL); |
| 5559 | } else if (rc == MBEDTLS_ERR_SSL_WANT_READ || |
| 5560 | rc == MBEDTLS_ERR_SSL_WANT_WRITE) { // Still pending |
| 5561 | MG_VERBOSE(("%lu pending, %d%d %d (-%#x)" , c->id, c->is_connecting, |
| 5562 | c->is_tls_hs, rc, -rc)); |
| 5563 | } else { |
| 5564 | mg_error(c, "TLS handshake: -%#x" , -rc); // Error |
| 5565 | } |
| 5566 | } |
| 5567 | |
| 5568 | static int mbed_rng(void *ctx, unsigned char *buf, size_t len) { |
| 5569 | mg_random(buf, len); |
| 5570 | (void) ctx; |
| 5571 | return 0; |
| 5572 | } |
| 5573 | |
| 5574 | static void debug_cb(void *c, int lev, const char *s, int n, const char *s2) { |
| 5575 | n = (int) strlen(s2) - 1; |
| 5576 | MG_INFO(("%lu %d %.*s" , ((struct mg_connection *) c)->id, lev, n, s2)); |
| 5577 | (void) s; |
| 5578 | } |
| 5579 | |
| 5580 | #if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= 0x03000000 |
| 5581 | static int rng_get(void *p_rng, unsigned char *buf, size_t len) { |
| 5582 | (void) p_rng; |
| 5583 | mg_random(buf, len); |
| 5584 | return 0; |
| 5585 | } |
| 5586 | #endif |
| 5587 | |
| 5588 | static struct mg_str mg_loadfile(struct mg_fs *fs, const char *path) { |
| 5589 | size_t n = 0; |
| 5590 | if (path[0] == '-') return mg_str(path); |
| 5591 | char *p = mg_file_read(fs, path, &n); |
| 5592 | return mg_str_n(p, n); |
| 5593 | } |
| 5594 | |
| 5595 | void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) { |
| 5596 | struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs; |
| 5597 | struct mg_tls *tls = (struct mg_tls *) calloc(1, sizeof(*tls)); |
| 5598 | int rc = 0; |
| 5599 | c->tls = tls; |
| 5600 | if (c->tls == NULL) { |
| 5601 | mg_error(c, "TLS OOM" ); |
| 5602 | goto fail; |
| 5603 | } |
| 5604 | MG_DEBUG(("%lu Setting TLS" , c->id)); |
| 5605 | mbedtls_ssl_init(&tls->ssl); |
| 5606 | mbedtls_ssl_config_init(&tls->conf); |
| 5607 | mbedtls_x509_crt_init(&tls->ca); |
| 5608 | mbedtls_x509_crt_init(&tls->cert); |
| 5609 | mbedtls_pk_init(&tls->pk); |
| 5610 | mbedtls_ssl_conf_dbg(&tls->conf, debug_cb, c); |
| 5611 | #if defined(MG_MBEDTLS_DEBUG_LEVEL) |
| 5612 | mbedtls_debug_set_threshold(MG_MBEDTLS_DEBUG_LEVEL); |
| 5613 | #endif |
| 5614 | if ((rc = mbedtls_ssl_config_defaults( |
| 5615 | &tls->conf, |
| 5616 | c->is_client ? MBEDTLS_SSL_IS_CLIENT : MBEDTLS_SSL_IS_SERVER, |
| 5617 | MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT)) != 0) { |
| 5618 | mg_error(c, "tls defaults %#x" , -rc); |
| 5619 | goto fail; |
| 5620 | } |
| 5621 | mbedtls_ssl_conf_rng(&tls->conf, mbed_rng, c); |
| 5622 | if (opts->ca == NULL || strcmp(opts->ca, "*" ) == 0) { |
| 5623 | mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_NONE); |
| 5624 | } else if (opts->ca != NULL && opts->ca[0] != '\0') { |
| 5625 | #if defined(MBEDTLS_X509_CA_CHAIN_ON_DISK) |
| 5626 | tls->cafile = strdup(opts->ca); |
| 5627 | rc = mbedtls_ssl_conf_ca_chain_file(&tls->conf, tls->cafile, NULL); |
| 5628 | if (rc != 0) { |
| 5629 | mg_error(c, "parse on-disk chain(%s) err %#x" , tls->cafile, -rc); |
| 5630 | goto fail; |
| 5631 | } |
| 5632 | #else |
| 5633 | struct mg_str s = mg_loadfile(fs, opts->ca); |
| 5634 | rc = mbedtls_x509_crt_parse(&tls->ca, (uint8_t *) s.ptr, s.len + 1); |
| 5635 | if (opts->ca[0] != '-') free((char *) s.ptr); |
| 5636 | if (rc != 0) { |
| 5637 | mg_error(c, "parse(%s) err %#x" , opts->ca, -rc); |
| 5638 | goto fail; |
| 5639 | } |
| 5640 | mbedtls_ssl_conf_ca_chain(&tls->conf, &tls->ca, NULL); |
| 5641 | #endif |
| 5642 | if (opts->srvname.len > 0) { |
| 5643 | char *x = mg_mprintf("%.*s" , (int) opts->srvname.len, opts->srvname.ptr); |
| 5644 | mbedtls_ssl_set_hostname(&tls->ssl, x); |
| 5645 | free(x); |
| 5646 | } |
| 5647 | mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_REQUIRED); |
| 5648 | } |
| 5649 | if (opts->cert != NULL && opts->cert[0] != '\0') { |
| 5650 | struct mg_str s = mg_loadfile(fs, opts->cert); |
| 5651 | const char *key = opts->certkey == NULL ? opts->cert : opts->certkey; |
| 5652 | rc = mbedtls_x509_crt_parse(&tls->cert, (uint8_t *) s.ptr, s.len + 1); |
| 5653 | if (opts->cert[0] != '-') free((char *) s.ptr); |
| 5654 | if (rc != 0) { |
| 5655 | mg_error(c, "parse(%s) err %#x" , opts->cert, -rc); |
| 5656 | goto fail; |
| 5657 | } |
| 5658 | s = mg_loadfile(fs, key); |
| 5659 | rc = mbedtls_pk_parse_key(&tls->pk, (uint8_t *) s.ptr, s.len + 1, NULL, |
| 5660 | 0 MGRNG); |
| 5661 | if (key[0] != '-') free((char *) s.ptr); |
| 5662 | if (rc != 0) { |
| 5663 | mg_error(c, "tls key(%s) %#x" , key, -rc); |
| 5664 | goto fail; |
| 5665 | } |
| 5666 | rc = mbedtls_ssl_conf_own_cert(&tls->conf, &tls->cert, &tls->pk); |
| 5667 | if (rc != 0) { |
| 5668 | mg_error(c, "own cert %#x" , -rc); |
| 5669 | goto fail; |
| 5670 | } |
| 5671 | } |
| 5672 | if ((rc = mbedtls_ssl_setup(&tls->ssl, &tls->conf)) != 0) { |
| 5673 | mg_error(c, "setup err %#x" , -rc); |
| 5674 | goto fail; |
| 5675 | } |
| 5676 | c->tls = tls; |
| 5677 | c->is_tls = 1; |
| 5678 | c->is_tls_hs = 1; |
| 5679 | mbedtls_ssl_set_bio(&tls->ssl, c, mg_net_send, mg_net_recv, 0); |
| 5680 | if (c->is_client && c->is_resolving == 0 && c->is_connecting == 0) { |
| 5681 | mg_tls_handshake(c); |
| 5682 | } |
| 5683 | return; |
| 5684 | fail: |
| 5685 | mg_tls_free(c); |
| 5686 | } |
| 5687 | |
| 5688 | size_t mg_tls_pending(struct mg_connection *c) { |
| 5689 | struct mg_tls *tls = (struct mg_tls *) c->tls; |
| 5690 | return tls == NULL ? 0 : mbedtls_ssl_get_bytes_avail(&tls->ssl); |
| 5691 | } |
| 5692 | |
| 5693 | long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) { |
| 5694 | struct mg_tls *tls = (struct mg_tls *) c->tls; |
| 5695 | long n = mbedtls_ssl_read(&tls->ssl, (unsigned char *) buf, len); |
| 5696 | if (n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE) |
| 5697 | return MG_IO_WAIT; |
| 5698 | if (n <= 0) return MG_IO_ERR; |
| 5699 | return n; |
| 5700 | } |
| 5701 | |
| 5702 | long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) { |
| 5703 | struct mg_tls *tls = (struct mg_tls *) c->tls; |
| 5704 | long n = mbedtls_ssl_write(&tls->ssl, (unsigned char *) buf, len); |
| 5705 | if (n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE) |
| 5706 | return MG_IO_WAIT; |
| 5707 | if (n <= 0) return MG_IO_ERR; |
| 5708 | return n; |
| 5709 | } |
| 5710 | #endif |
| 5711 | |
| 5712 | #ifdef MG_ENABLE_LINES |
| 5713 | #line 1 "src/tls_openssl.c" |
| 5714 | #endif |
| 5715 | |
| 5716 | |
| 5717 | |
| 5718 | #if MG_ENABLE_OPENSSL |
| 5719 | static int mg_tls_err(struct mg_tls *tls, int res) { |
| 5720 | int err = SSL_get_error(tls->ssl, res); |
| 5721 | // We've just fetched the last error from the queue. |
| 5722 | // Now we need to clear the error queue. If we do not, then the following |
| 5723 | // can happen (actually reported): |
| 5724 | // - A new connection is accept()-ed with cert error (e.g. self-signed cert) |
| 5725 | // - Since all accept()-ed connections share listener's context, |
| 5726 | // - *ALL* SSL accepted connection report read error on the next poll cycle. |
| 5727 | // Thus a single errored connection can close all the rest, unrelated ones. |
| 5728 | // Clearing the error keeps the shared SSL_CTX in an OK state. |
| 5729 | |
| 5730 | if (err != 0) ERR_print_errors_fp(stderr); |
| 5731 | ERR_clear_error(); |
| 5732 | if (err == SSL_ERROR_WANT_READ) return 0; |
| 5733 | if (err == SSL_ERROR_WANT_WRITE) return 0; |
| 5734 | return err; |
| 5735 | } |
| 5736 | |
| 5737 | void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) { |
| 5738 | struct mg_tls *tls = (struct mg_tls *) calloc(1, sizeof(*tls)); |
| 5739 | const char *id = "mongoose" ; |
| 5740 | static unsigned char s_initialised = 0; |
| 5741 | int rc; |
| 5742 | |
| 5743 | if (tls == NULL) { |
| 5744 | mg_error(c, "TLS OOM" ); |
| 5745 | goto fail; |
| 5746 | } |
| 5747 | |
| 5748 | if (!s_initialised) { |
| 5749 | SSL_library_init(); |
| 5750 | s_initialised++; |
| 5751 | } |
| 5752 | MG_DEBUG(("%lu Setting TLS, CA: %s, cert: %s, key: %s" , c->id, |
| 5753 | opts->ca == NULL ? "null" : opts->ca, |
| 5754 | opts->cert == NULL ? "null" : opts->cert, |
| 5755 | opts->certkey == NULL ? "null" : opts->certkey)); |
| 5756 | tls->ctx = c->is_client ? SSL_CTX_new(SSLv23_client_method()) |
| 5757 | : SSL_CTX_new(SSLv23_server_method()); |
| 5758 | if ((tls->ssl = SSL_new(tls->ctx)) == NULL) { |
| 5759 | mg_error(c, "SSL_new" ); |
| 5760 | goto fail; |
| 5761 | } |
| 5762 | SSL_set_session_id_context(tls->ssl, (const uint8_t *) id, |
| 5763 | (unsigned) strlen(id)); |
| 5764 | // Disable deprecated protocols |
| 5765 | SSL_set_options(tls->ssl, SSL_OP_NO_SSLv2); |
| 5766 | SSL_set_options(tls->ssl, SSL_OP_NO_SSLv3); |
| 5767 | SSL_set_options(tls->ssl, SSL_OP_NO_TLSv1); |
| 5768 | SSL_set_options(tls->ssl, SSL_OP_NO_TLSv1_1); |
| 5769 | #ifdef MG_ENABLE_OPENSSL_NO_COMPRESSION |
| 5770 | SSL_set_options(tls->ssl, SSL_OP_NO_COMPRESSION); |
| 5771 | #endif |
| 5772 | #ifdef MG_ENABLE_OPENSSL_CIPHER_SERVER_PREFERENCE |
| 5773 | SSL_set_options(tls->ssl, SSL_OP_CIPHER_SERVER_PREFERENCE); |
| 5774 | #endif |
| 5775 | |
| 5776 | if (opts->ca != NULL && opts->ca[0] != '\0') { |
| 5777 | SSL_set_verify(tls->ssl, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, |
| 5778 | NULL); |
| 5779 | if ((rc = SSL_CTX_load_verify_locations(tls->ctx, opts->ca, NULL)) != 1) { |
| 5780 | mg_error(c, "load('%s') %d err %d" , opts->ca, rc, mg_tls_err(tls, rc)); |
| 5781 | goto fail; |
| 5782 | } |
| 5783 | } |
| 5784 | if (opts->cert != NULL && opts->cert[0] != '\0') { |
| 5785 | const char *key = opts->certkey; |
| 5786 | if (key == NULL) key = opts->cert; |
| 5787 | if ((rc = SSL_use_certificate_file(tls->ssl, opts->cert, 1)) != 1) { |
| 5788 | mg_error(c, "Invalid SSL cert, err %d" , mg_tls_err(tls, rc)); |
| 5789 | goto fail; |
| 5790 | } else if ((rc = SSL_use_PrivateKey_file(tls->ssl, key, 1)) != 1) { |
| 5791 | mg_error(c, "Invalid SSL key, err %d" , mg_tls_err(tls, rc)); |
| 5792 | goto fail; |
| 5793 | #if OPENSSL_VERSION_NUMBER > 0x10100000L |
| 5794 | } else if ((rc = SSL_use_certificate_chain_file(tls->ssl, opts->cert)) != |
| 5795 | 1) { |
| 5796 | mg_error(c, "Invalid chain, err %d" , mg_tls_err(tls, rc)); |
| 5797 | goto fail; |
| 5798 | #endif |
| 5799 | } else { |
| 5800 | SSL_set_mode(tls->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); |
| 5801 | #if OPENSSL_VERSION_NUMBER > 0x10002000L |
| 5802 | SSL_set_ecdh_auto(tls->ssl, 1); |
| 5803 | #endif |
| 5804 | } |
| 5805 | } |
| 5806 | if (opts->ciphers != NULL) SSL_set_cipher_list(tls->ssl, opts->ciphers); |
| 5807 | #if OPENSSL_VERSION_NUMBER >= 0x10100000L |
| 5808 | if (opts->srvname.len > 0) { |
| 5809 | char *s = mg_mprintf("%.*s" , (int) opts->srvname.len, opts->srvname.ptr); |
| 5810 | SSL_set1_host(tls->ssl, s); |
| 5811 | SSL_set_tlsext_host_name(tls->ssl, s); |
| 5812 | free(s); |
| 5813 | } |
| 5814 | #endif |
| 5815 | c->tls = tls; |
| 5816 | c->is_tls = 1; |
| 5817 | c->is_tls_hs = 1; |
| 5818 | if (c->is_client && c->is_resolving == 0 && c->is_connecting == 0) { |
| 5819 | mg_tls_handshake(c); |
| 5820 | } |
| 5821 | MG_DEBUG(("%lu SSL %s OK" , c->id, c->is_accepted ? "accept" : "client" )); |
| 5822 | return; |
| 5823 | fail: |
| 5824 | c->is_closing = 1; |
| 5825 | free(tls); |
| 5826 | } |
| 5827 | |
| 5828 | void mg_tls_handshake(struct mg_connection *c) { |
| 5829 | struct mg_tls *tls = (struct mg_tls *) c->tls; |
| 5830 | int rc; |
| 5831 | SSL_set_fd(tls->ssl, (int) (size_t) c->fd); |
| 5832 | rc = c->is_client ? SSL_connect(tls->ssl) : SSL_accept(tls->ssl); |
| 5833 | if (rc == 1) { |
| 5834 | MG_DEBUG(("%lu success" , c->id)); |
| 5835 | c->is_tls_hs = 0; |
| 5836 | mg_call(c, MG_EV_TLS_HS, NULL); |
| 5837 | } else { |
| 5838 | int code = mg_tls_err(tls, rc); |
| 5839 | if (code != 0) mg_error(c, "tls hs: rc %d, err %d" , rc, code); |
| 5840 | } |
| 5841 | } |
| 5842 | |
| 5843 | void mg_tls_free(struct mg_connection *c) { |
| 5844 | struct mg_tls *tls = (struct mg_tls *) c->tls; |
| 5845 | if (tls == NULL) return; |
| 5846 | SSL_free(tls->ssl); |
| 5847 | SSL_CTX_free(tls->ctx); |
| 5848 | free(tls); |
| 5849 | c->tls = NULL; |
| 5850 | } |
| 5851 | |
| 5852 | size_t mg_tls_pending(struct mg_connection *c) { |
| 5853 | struct mg_tls *tls = (struct mg_tls *) c->tls; |
| 5854 | return tls == NULL ? 0 : (size_t) SSL_pending(tls->ssl); |
| 5855 | } |
| 5856 | |
| 5857 | long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) { |
| 5858 | struct mg_tls *tls = (struct mg_tls *) c->tls; |
| 5859 | int n = SSL_read(tls->ssl, buf, (int) len); |
| 5860 | if (n < 0 && mg_tls_err(tls, n) == 0) return MG_IO_WAIT; |
| 5861 | if (n <= 0) return MG_IO_ERR; |
| 5862 | return n; |
| 5863 | } |
| 5864 | |
| 5865 | long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) { |
| 5866 | struct mg_tls *tls = (struct mg_tls *) c->tls; |
| 5867 | int n = SSL_write(tls->ssl, buf, (int) len); |
| 5868 | if (n < 0 && mg_tls_err(tls, n) == 0) return MG_IO_WAIT; |
| 5869 | if (n <= 0) return MG_IO_ERR; |
| 5870 | return n; |
| 5871 | } |
| 5872 | #endif |
| 5873 | |
| 5874 | #ifdef MG_ENABLE_LINES |
| 5875 | #line 1 "src/url.c" |
| 5876 | #endif |
| 5877 | |
| 5878 | |
| 5879 | struct url { |
| 5880 | size_t key, user, pass, host, port, uri, end; |
| 5881 | }; |
| 5882 | |
| 5883 | int mg_url_is_ssl(const char *url) { |
| 5884 | return strncmp(url, "wss:" , 4) == 0 || strncmp(url, "https:" , 6) == 0 || |
| 5885 | strncmp(url, "mqtts:" , 6) == 0 || strncmp(url, "ssl:" , 4) == 0 || |
| 5886 | strncmp(url, "tls:" , 4) == 0; |
| 5887 | } |
| 5888 | |
| 5889 | static struct url urlparse(const char *url) { |
| 5890 | size_t i; |
| 5891 | struct url u; |
| 5892 | memset(&u, 0, sizeof(u)); |
| 5893 | for (i = 0; url[i] != '\0'; i++) { |
| 5894 | if (url[i] == '/' && i > 0 && u.host == 0 && url[i - 1] == '/') { |
| 5895 | u.host = i + 1; |
| 5896 | u.port = 0; |
| 5897 | } else if (url[i] == ']') { |
| 5898 | u.port = 0; // IPv6 URLs, like http://[::1]/bar |
| 5899 | } else if (url[i] == ':' && u.port == 0 && u.uri == 0) { |
| 5900 | u.port = i + 1; |
| 5901 | } else if (url[i] == '@' && u.user == 0 && u.pass == 0 && u.uri == 0) { |
| 5902 | u.user = u.host; |
| 5903 | u.pass = u.port; |
| 5904 | u.host = i + 1; |
| 5905 | u.port = 0; |
| 5906 | } else if (url[i] == '/' && u.host && u.uri == 0) { |
| 5907 | u.uri = i; |
| 5908 | } |
| 5909 | } |
| 5910 | u.end = i; |
| 5911 | #if 0 |
| 5912 | printf("[%s] %d %d %d %d %d\n" , url, u.user, u.pass, u.host, u.port, u.uri); |
| 5913 | #endif |
| 5914 | return u; |
| 5915 | } |
| 5916 | |
| 5917 | struct mg_str mg_url_host(const char *url) { |
| 5918 | struct url u = urlparse(url); |
| 5919 | size_t n = u.port ? u.port - u.host - 1 |
| 5920 | : u.uri ? u.uri - u.host |
| 5921 | : u.end - u.host; |
| 5922 | struct mg_str s = mg_str_n(url + u.host, n); |
| 5923 | return s; |
| 5924 | } |
| 5925 | |
| 5926 | const char *mg_url_uri(const char *url) { |
| 5927 | struct url u = urlparse(url); |
| 5928 | return u.uri ? url + u.uri : "/" ; |
| 5929 | } |
| 5930 | |
| 5931 | unsigned short mg_url_port(const char *url) { |
| 5932 | struct url u = urlparse(url); |
| 5933 | unsigned short port = 0; |
| 5934 | if (strncmp(url, "http:" , 5) == 0 || strncmp(url, "ws:" , 3) == 0) port = 80; |
| 5935 | if (strncmp(url, "wss:" , 4) == 0 || strncmp(url, "https:" , 6) == 0) |
| 5936 | port = 443; |
| 5937 | if (strncmp(url, "mqtt:" , 5) == 0) port = 1883; |
| 5938 | if (strncmp(url, "mqtts:" , 6) == 0) port = 8883; |
| 5939 | if (u.port) port = (unsigned short) atoi(url + u.port); |
| 5940 | return port; |
| 5941 | } |
| 5942 | |
| 5943 | struct mg_str mg_url_user(const char *url) { |
| 5944 | struct url u = urlparse(url); |
| 5945 | struct mg_str s = mg_str("" ); |
| 5946 | if (u.user && (u.pass || u.host)) { |
| 5947 | size_t n = u.pass ? u.pass - u.user - 1 : u.host - u.user - 1; |
| 5948 | s = mg_str_n(url + u.user, n); |
| 5949 | } |
| 5950 | return s; |
| 5951 | } |
| 5952 | |
| 5953 | struct mg_str mg_url_pass(const char *url) { |
| 5954 | struct url u = urlparse(url); |
| 5955 | struct mg_str s = mg_str_n("" , 0UL); |
| 5956 | if (u.pass && u.host) { |
| 5957 | size_t n = u.host - u.pass - 1; |
| 5958 | s = mg_str_n(url + u.pass, n); |
| 5959 | } |
| 5960 | return s; |
| 5961 | } |
| 5962 | |
| 5963 | #ifdef MG_ENABLE_LINES |
| 5964 | #line 1 "src/util.c" |
| 5965 | #endif |
| 5966 | |
| 5967 | |
| 5968 | #if MG_ENABLE_CUSTOM_RANDOM |
| 5969 | #else |
| 5970 | void mg_random(void *buf, size_t len) { |
| 5971 | bool done = false; |
| 5972 | unsigned char *p = (unsigned char *) buf; |
| 5973 | #if MG_ARCH == MG_ARCH_ESP32 |
| 5974 | while (len--) *p++ = (unsigned char) (esp_random() & 255); |
| 5975 | done = true; |
| 5976 | #elif MG_ARCH == MG_ARCH_WIN32 |
| 5977 | #elif MG_ARCH == MG_ARCH_UNIX |
| 5978 | FILE *fp = fopen("/dev/urandom" , "rb" ); |
| 5979 | if (fp != NULL) { |
| 5980 | if (fread(buf, 1, len, fp) == len) done = true; |
| 5981 | fclose(fp); |
| 5982 | } |
| 5983 | #endif |
| 5984 | // If everything above did not work, fallback to a pseudo random generator |
| 5985 | while (!done && len--) *p++ = (unsigned char) (rand() & 255); |
| 5986 | } |
| 5987 | #endif |
| 5988 | |
| 5989 | char *mg_random_str(char *buf, size_t len) { |
| 5990 | size_t i; |
| 5991 | mg_random(buf, len); |
| 5992 | for (i = 0; i < len; i++) { |
| 5993 | uint8_t c = ((uint8_t *) buf)[i] % 62U; |
| 5994 | buf[i] = i == len - 1 ? (char) '\0' // 0-terminate last byte |
| 5995 | : c < 26 ? (char) ('a' + c) // lowercase |
| 5996 | : c < 52 ? (char) ('A' + c - 26) // uppercase |
| 5997 | : (char) ('0' + c - 52); // numeric |
| 5998 | } |
| 5999 | return buf; |
| 6000 | } |
| 6001 | |
| 6002 | uint32_t mg_ntohl(uint32_t net) { |
| 6003 | uint8_t data[4] = {0, 0, 0, 0}; |
| 6004 | memcpy(&data, &net, sizeof(data)); |
| 6005 | return (((uint32_t) data[3]) << 0) | (((uint32_t) data[2]) << 8) | |
| 6006 | (((uint32_t) data[1]) << 16) | (((uint32_t) data[0]) << 24); |
| 6007 | } |
| 6008 | |
| 6009 | uint16_t mg_ntohs(uint16_t net) { |
| 6010 | uint8_t data[2] = {0, 0}; |
| 6011 | memcpy(&data, &net, sizeof(data)); |
| 6012 | return (uint16_t) ((uint16_t) data[1] | (((uint16_t) data[0]) << 8)); |
| 6013 | } |
| 6014 | |
| 6015 | uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len) { |
| 6016 | static const uint32_t crclut[16] = { |
| 6017 | // table for polynomial 0xEDB88320 (reflected) |
| 6018 | 0x00000000, 0x1DB71064, 0x3B6E20C8, 0x26D930AC, 0x76DC4190, 0x6B6B51F4, |
| 6019 | 0x4DB26158, 0x5005713C, 0xEDB88320, 0xF00F9344, 0xD6D6A3E8, 0xCB61B38C, |
| 6020 | 0x9B64C2B0, 0x86D3D2D4, 0xA00AE278, 0xBDBDF21C}; |
| 6021 | crc = ~crc; |
| 6022 | while (len--) { |
| 6023 | uint8_t byte = *(uint8_t *)buf++; |
| 6024 | crc = crclut[(crc ^ byte) & 0x0F] ^ (crc >> 4); |
| 6025 | crc = crclut[(crc ^ (byte >> 4)) & 0x0F] ^ (crc >> 4); |
| 6026 | } |
| 6027 | return ~crc; |
| 6028 | } |
| 6029 | |
| 6030 | static int isbyte(int n) { |
| 6031 | return n >= 0 && n <= 255; |
| 6032 | } |
| 6033 | |
| 6034 | static int parse_net(const char *spec, uint32_t *net, uint32_t *mask) { |
| 6035 | int n, a, b, c, d, slash = 32, len = 0; |
| 6036 | if ((sscanf(spec, "%d.%d.%d.%d/%d%n" , &a, &b, &c, &d, &slash, &n) == 5 || |
| 6037 | sscanf(spec, "%d.%d.%d.%d%n" , &a, &b, &c, &d, &n) == 4) && |
| 6038 | isbyte(a) && isbyte(b) && isbyte(c) && isbyte(d) && slash >= 0 && |
| 6039 | slash < 33) { |
| 6040 | len = n; |
| 6041 | *net = ((uint32_t) a << 24) | ((uint32_t) b << 16) | ((uint32_t) c << 8) | |
| 6042 | (uint32_t) d; |
| 6043 | *mask = slash ? (uint32_t) (0xffffffffU << (32 - slash)) : (uint32_t) 0; |
| 6044 | } |
| 6045 | return len; |
| 6046 | } |
| 6047 | |
| 6048 | int mg_check_ip_acl(struct mg_str acl, uint32_t remote_ip) { |
| 6049 | struct mg_str k, v; |
| 6050 | int allowed = acl.len == 0 ? '+' : '-'; // If any ACL is set, deny by default |
| 6051 | while (mg_commalist(&acl, &k, &v)) { |
| 6052 | uint32_t net, mask; |
| 6053 | if (k.ptr[0] != '+' && k.ptr[0] != '-') return -1; |
| 6054 | if (parse_net(&k.ptr[1], &net, &mask) == 0) return -2; |
| 6055 | if ((mg_ntohl(remote_ip) & mask) == net) allowed = k.ptr[0]; |
| 6056 | } |
| 6057 | return allowed == '+'; |
| 6058 | } |
| 6059 | |
| 6060 | #if MG_ENABLE_CUSTOM_MILLIS |
| 6061 | #else |
| 6062 | uint64_t mg_millis(void) { |
| 6063 | #if MG_ARCH == MG_ARCH_WIN32 |
| 6064 | return GetTickCount(); |
| 6065 | #elif MG_ARCH == MG_ARCH_RP2040 |
| 6066 | return time_us_64() / 1000; |
| 6067 | #elif MG_ARCH == MG_ARCH_ESP32 |
| 6068 | return esp_timer_get_time() / 1000; |
| 6069 | #elif MG_ARCH == MG_ARCH_ESP8266 || MG_ARCH == MG_ARCH_FREERTOS |
| 6070 | return xTaskGetTickCount() * portTICK_PERIOD_MS; |
| 6071 | #elif MG_ARCH == MG_ARCH_AZURERTOS |
| 6072 | return tx_time_get() * (1000 /* MS per SEC */ / TX_TIMER_TICKS_PER_SECOND); |
| 6073 | #elif MG_ARCH == MG_ARCH_TIRTOS |
| 6074 | return (uint64_t) Clock_getTicks(); |
| 6075 | #elif MG_ARCH == MG_ARCH_ZEPHYR |
| 6076 | return (uint64_t) k_uptime_get(); |
| 6077 | #elif MG_ARCH == MG_ARCH_CMSIS_RTOS1 |
| 6078 | return (uint64_t)rt_time_get(); |
| 6079 | #elif MG_ARCH == MG_ARCH_CMSIS_RTOS2 |
| 6080 | return (uint64_t)((osKernelGetTickCount() * 1000) / osKernelGetTickFreq()); |
| 6081 | #elif MG_ARCH == MG_ARCH_RTTHREAD |
| 6082 | return (uint64_t) ((rt_tick_get() * 1000) / RT_TICK_PER_SECOND); |
| 6083 | #elif MG_ARCH == MG_ARCH_UNIX && defined(__APPLE__) |
| 6084 | // Apple CLOCK_MONOTONIC_RAW is equivalent to CLOCK_BOOTTIME on linux |
| 6085 | // Apple CLOCK_UPTIME_RAW is equivalent to CLOCK_MONOTONIC_RAW on linux |
| 6086 | return clock_gettime_nsec_np(CLOCK_UPTIME_RAW) / 1000000; |
| 6087 | #elif MG_ARCH == MG_ARCH_UNIX |
| 6088 | struct timespec ts = {0, 0}; |
| 6089 | // See #1615 - prefer monotonic clock |
| 6090 | #if defined(CLOCK_MONOTONIC_RAW) |
| 6091 | // Raw hardware-based time that is not subject to NTP adjustment |
| 6092 | clock_gettime(CLOCK_MONOTONIC_RAW, &ts); |
| 6093 | #elif defined(CLOCK_MONOTONIC) |
| 6094 | // Affected by the incremental adjustments performed by adjtime and NTP |
| 6095 | clock_gettime(CLOCK_MONOTONIC, &ts); |
| 6096 | #else |
| 6097 | // Affected by discontinuous jumps in the system time and by the incremental |
| 6098 | // adjustments performed by adjtime and NTP |
| 6099 | clock_gettime(CLOCK_REALTIME, &ts); |
| 6100 | #endif |
| 6101 | return ((uint64_t) ts.tv_sec * 1000 + (uint64_t) ts.tv_nsec / 1000000); |
| 6102 | #elif defined(ARDUINO) |
| 6103 | return (uint64_t) millis(); |
| 6104 | #else |
| 6105 | return (uint64_t) (time(NULL) * 1000); |
| 6106 | #endif |
| 6107 | } |
| 6108 | #endif |
| 6109 | |
| 6110 | #ifdef MG_ENABLE_LINES |
| 6111 | #line 1 "src/ws.c" |
| 6112 | #endif |
| 6113 | |
| 6114 | |
| 6115 | |
| 6116 | |
| 6117 | |
| 6118 | |
| 6119 | |
| 6120 | |
| 6121 | |
| 6122 | |
| 6123 | |
| 6124 | struct ws_msg { |
| 6125 | uint8_t flags; |
| 6126 | size_t ; |
| 6127 | size_t data_len; |
| 6128 | }; |
| 6129 | |
| 6130 | size_t mg_ws_vprintf(struct mg_connection *c, int op, const char *fmt, |
| 6131 | va_list *ap) { |
| 6132 | size_t len = c->send.len; |
| 6133 | size_t n = mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, ap); |
| 6134 | mg_ws_wrap(c, c->send.len - len, op); |
| 6135 | return n; |
| 6136 | } |
| 6137 | |
| 6138 | size_t mg_ws_printf(struct mg_connection *c, int op, const char *fmt, ...) { |
| 6139 | size_t len = 0; |
| 6140 | va_list ap; |
| 6141 | va_start(ap, fmt); |
| 6142 | len = mg_ws_vprintf(c, op, fmt, &ap); |
| 6143 | va_end(ap); |
| 6144 | return len; |
| 6145 | } |
| 6146 | |
| 6147 | static void ws_handshake(struct mg_connection *c, const struct mg_str *wskey, |
| 6148 | const struct mg_str *wsproto, const char *fmt, |
| 6149 | va_list *ap) { |
| 6150 | const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" ; |
| 6151 | unsigned char sha[20], b64_sha[30]; |
| 6152 | |
| 6153 | mg_sha1_ctx sha_ctx; |
| 6154 | mg_sha1_init(&sha_ctx); |
| 6155 | mg_sha1_update(&sha_ctx, (unsigned char *) wskey->ptr, wskey->len); |
| 6156 | mg_sha1_update(&sha_ctx, (unsigned char *) magic, 36); |
| 6157 | mg_sha1_final(sha, &sha_ctx); |
| 6158 | mg_base64_encode(sha, sizeof(sha), (char *) b64_sha); |
| 6159 | mg_xprintf(mg_pfn_iobuf, &c->send, |
| 6160 | "HTTP/1.1 101 Switching Protocols\r\n" |
| 6161 | "Upgrade: websocket\r\n" |
| 6162 | "Connection: Upgrade\r\n" |
| 6163 | "Sec-WebSocket-Accept: %s\r\n" , |
| 6164 | b64_sha); |
| 6165 | if (fmt != NULL) mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, ap); |
| 6166 | if (wsproto != NULL) { |
| 6167 | mg_printf(c, "Sec-WebSocket-Protocol: %.*s\r\n" , (int) wsproto->len, |
| 6168 | wsproto->ptr); |
| 6169 | } |
| 6170 | mg_send(c, "\r\n" , 2); |
| 6171 | } |
| 6172 | |
| 6173 | static uint32_t be32(const uint8_t *p) { |
| 6174 | return (((uint32_t) p[3]) << 0) | (((uint32_t) p[2]) << 8) | |
| 6175 | (((uint32_t) p[1]) << 16) | (((uint32_t) p[0]) << 24); |
| 6176 | } |
| 6177 | |
| 6178 | static size_t ws_process(uint8_t *buf, size_t len, struct ws_msg *msg) { |
| 6179 | size_t i, n = 0, mask_len = 0; |
| 6180 | memset(msg, 0, sizeof(*msg)); |
| 6181 | if (len >= 2) { |
| 6182 | n = buf[1] & 0x7f; // Frame length |
| 6183 | mask_len = buf[1] & 128 ? 4 : 0; // last bit is a mask bit |
| 6184 | msg->flags = buf[0]; |
| 6185 | if (n < 126 && len >= mask_len) { |
| 6186 | msg->data_len = n; |
| 6187 | msg->header_len = 2 + mask_len; |
| 6188 | } else if (n == 126 && len >= 4 + mask_len) { |
| 6189 | msg->header_len = 4 + mask_len; |
| 6190 | msg->data_len = (((size_t) buf[2]) << 8) | buf[3]; |
| 6191 | } else if (len >= 10 + mask_len) { |
| 6192 | msg->header_len = 10 + mask_len; |
| 6193 | msg->data_len = |
| 6194 | (size_t) (((uint64_t) be32(buf + 2) << 32) + be32(buf + 6)); |
| 6195 | } |
| 6196 | } |
| 6197 | // Sanity check, and integer overflow protection for the boundary check below |
| 6198 | // data_len should not be larger than 1 Gb |
| 6199 | if (msg->data_len > 1024 * 1024 * 1024) return 0; |
| 6200 | if (msg->header_len + msg->data_len > len) return 0; |
| 6201 | if (mask_len > 0) { |
| 6202 | uint8_t *p = buf + msg->header_len, *m = p - mask_len; |
| 6203 | for (i = 0; i < msg->data_len; i++) p[i] ^= m[i & 3]; |
| 6204 | } |
| 6205 | return msg->header_len + msg->data_len; |
| 6206 | } |
| 6207 | |
| 6208 | static size_t mkhdr(size_t len, int op, bool is_client, uint8_t *buf) { |
| 6209 | size_t n = 0; |
| 6210 | buf[0] = (uint8_t) (op | 128); |
| 6211 | if (len < 126) { |
| 6212 | buf[1] = (unsigned char) len; |
| 6213 | n = 2; |
| 6214 | } else if (len < 65536) { |
| 6215 | uint16_t tmp = mg_htons((uint16_t) len); |
| 6216 | buf[1] = 126; |
| 6217 | memcpy(&buf[2], &tmp, sizeof(tmp)); |
| 6218 | n = 4; |
| 6219 | } else { |
| 6220 | uint32_t tmp; |
| 6221 | buf[1] = 127; |
| 6222 | tmp = mg_htonl((uint32_t) (((uint64_t) len) >> 32)); |
| 6223 | memcpy(&buf[2], &tmp, sizeof(tmp)); |
| 6224 | tmp = mg_htonl((uint32_t) (len & 0xffffffffU)); |
| 6225 | memcpy(&buf[6], &tmp, sizeof(tmp)); |
| 6226 | n = 10; |
| 6227 | } |
| 6228 | if (is_client) { |
| 6229 | buf[1] |= 1 << 7; // Set masking flag |
| 6230 | mg_random(&buf[n], 4); |
| 6231 | n += 4; |
| 6232 | } |
| 6233 | return n; |
| 6234 | } |
| 6235 | |
| 6236 | static void mg_ws_mask(struct mg_connection *c, size_t len) { |
| 6237 | if (c->is_client && c->send.buf != NULL) { |
| 6238 | size_t i; |
| 6239 | uint8_t *p = c->send.buf + c->send.len - len, *mask = p - 4; |
| 6240 | for (i = 0; i < len; i++) p[i] ^= mask[i & 3]; |
| 6241 | } |
| 6242 | } |
| 6243 | |
| 6244 | size_t mg_ws_send(struct mg_connection *c, const void *buf, size_t len, |
| 6245 | int op) { |
| 6246 | uint8_t [14]; |
| 6247 | size_t = mkhdr(len, op, c->is_client, header); |
| 6248 | mg_send(c, header, header_len); |
| 6249 | MG_VERBOSE(("WS out: %d [%.*s]" , (int) len, (int) len, buf)); |
| 6250 | mg_send(c, buf, len); |
| 6251 | mg_ws_mask(c, len); |
| 6252 | return header_len + len; |
| 6253 | } |
| 6254 | |
| 6255 | static bool mg_ws_client_handshake(struct mg_connection *c) { |
| 6256 | int n = mg_http_get_request_len(c->recv.buf, c->recv.len); |
| 6257 | if (n < 0) { |
| 6258 | mg_error(c, "not http" ); // Some just, not an HTTP request |
| 6259 | } else if (n > 0) { |
| 6260 | if (n < 15 || memcmp(c->recv.buf + 9, "101" , 3) != 0) { |
| 6261 | mg_error(c, "handshake error" ); |
| 6262 | } else { |
| 6263 | struct mg_http_message hm; |
| 6264 | mg_http_parse((char *) c->recv.buf, c->recv.len, &hm); |
| 6265 | c->is_websocket = 1; |
| 6266 | mg_call(c, MG_EV_WS_OPEN, &hm); |
| 6267 | } |
| 6268 | mg_iobuf_del(&c->recv, 0, (size_t) n); |
| 6269 | } else { |
| 6270 | return true; // Request is not yet received, quit event handler |
| 6271 | } |
| 6272 | return false; // Continue event handler |
| 6273 | } |
| 6274 | |
| 6275 | static void mg_ws_cb(struct mg_connection *c, int ev, void *ev_data, |
| 6276 | void *fn_data) { |
| 6277 | struct ws_msg msg; |
| 6278 | size_t ofs = (size_t) c->pfn_data; |
| 6279 | |
| 6280 | // assert(ofs < c->recv.len); |
| 6281 | if (ev == MG_EV_READ) { |
| 6282 | if (c->is_client && !c->is_websocket && mg_ws_client_handshake(c)) return; |
| 6283 | |
| 6284 | while (ws_process(c->recv.buf + ofs, c->recv.len - ofs, &msg) > 0) { |
| 6285 | char *s = (char *) c->recv.buf + ofs + msg.header_len; |
| 6286 | struct mg_ws_message m = {{s, msg.data_len}, msg.flags}; |
| 6287 | size_t len = msg.header_len + msg.data_len; |
| 6288 | uint8_t final = msg.flags & 128, op = msg.flags & 15; |
| 6289 | // MG_VERBOSE ("fin %d op %d len %d [%.*s]", final, op, |
| 6290 | // (int) m.data.len, (int) m.data.len, m.data.ptr)); |
| 6291 | switch (op) { |
| 6292 | case WEBSOCKET_OP_CONTINUE: |
| 6293 | mg_call(c, MG_EV_WS_CTL, &m); |
| 6294 | break; |
| 6295 | case WEBSOCKET_OP_PING: |
| 6296 | MG_DEBUG(("%s" , "WS PONG" )); |
| 6297 | mg_ws_send(c, s, msg.data_len, WEBSOCKET_OP_PONG); |
| 6298 | mg_call(c, MG_EV_WS_CTL, &m); |
| 6299 | break; |
| 6300 | case WEBSOCKET_OP_PONG: |
| 6301 | mg_call(c, MG_EV_WS_CTL, &m); |
| 6302 | break; |
| 6303 | case WEBSOCKET_OP_TEXT: |
| 6304 | case WEBSOCKET_OP_BINARY: |
| 6305 | if (final) mg_call(c, MG_EV_WS_MSG, &m); |
| 6306 | break; |
| 6307 | case WEBSOCKET_OP_CLOSE: |
| 6308 | MG_DEBUG(("%lu WS CLOSE" , c->id)); |
| 6309 | mg_call(c, MG_EV_WS_CTL, &m); |
| 6310 | // Echo the payload of the received CLOSE message back to the sender |
| 6311 | mg_ws_send(c, m.data.ptr, m.data.len, WEBSOCKET_OP_CLOSE); |
| 6312 | c->is_draining = 1; |
| 6313 | break; |
| 6314 | default: |
| 6315 | // Per RFC6455, close conn when an unknown op is recvd |
| 6316 | mg_error(c, "unknown WS op %d" , op); |
| 6317 | break; |
| 6318 | } |
| 6319 | |
| 6320 | // Handle fragmented frames: strip header, keep in c->recv |
| 6321 | if (final == 0 || op == 0) { |
| 6322 | if (op) ofs++, len--, msg.header_len--; // First frame |
| 6323 | mg_iobuf_del(&c->recv, ofs, msg.header_len); // Strip header |
| 6324 | len -= msg.header_len; |
| 6325 | ofs += len; |
| 6326 | c->pfn_data = (void *) ofs; |
| 6327 | // MG_INFO(("FRAG %d [%.*s]", (int) ofs, (int) ofs, c->recv.buf)); |
| 6328 | } |
| 6329 | // Remove non-fragmented frame |
| 6330 | if (final && op) mg_iobuf_del(&c->recv, ofs, len); |
| 6331 | // Last chunk of the fragmented frame |
| 6332 | if (final && !op) { |
| 6333 | m.flags = c->recv.buf[0]; |
| 6334 | m.data = mg_str_n((char *) &c->recv.buf[1], (size_t) (ofs - 1)); |
| 6335 | mg_call(c, MG_EV_WS_MSG, &m); |
| 6336 | mg_iobuf_del(&c->recv, 0, ofs); |
| 6337 | ofs = 0; |
| 6338 | c->pfn_data = NULL; |
| 6339 | } |
| 6340 | } |
| 6341 | } |
| 6342 | (void) fn_data; |
| 6343 | (void) ev_data; |
| 6344 | } |
| 6345 | |
| 6346 | struct mg_connection *mg_ws_connect(struct mg_mgr *mgr, const char *url, |
| 6347 | mg_event_handler_t fn, void *fn_data, |
| 6348 | const char *fmt, ...) { |
| 6349 | struct mg_connection *c = mg_connect(mgr, url, fn, fn_data); |
| 6350 | if (c != NULL) { |
| 6351 | char nonce[16], key[30]; |
| 6352 | struct mg_str host = mg_url_host(url); |
| 6353 | mg_random(nonce, sizeof(nonce)); |
| 6354 | mg_base64_encode((unsigned char *) nonce, sizeof(nonce), key); |
| 6355 | mg_xprintf(mg_pfn_iobuf, &c->send, |
| 6356 | "GET %s HTTP/1.1\r\n" |
| 6357 | "Upgrade: websocket\r\n" |
| 6358 | "Host: %.*s\r\n" |
| 6359 | "Connection: Upgrade\r\n" |
| 6360 | "Sec-WebSocket-Version: 13\r\n" |
| 6361 | "Sec-WebSocket-Key: %s\r\n" , |
| 6362 | mg_url_uri(url), (int) host.len, host.ptr, key); |
| 6363 | if (fmt != NULL) { |
| 6364 | va_list ap; |
| 6365 | va_start(ap, fmt); |
| 6366 | mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, &ap); |
| 6367 | va_end(ap); |
| 6368 | } |
| 6369 | mg_xprintf(mg_pfn_iobuf, &c->send, "\r\n" ); |
| 6370 | c->pfn = mg_ws_cb; |
| 6371 | c->pfn_data = NULL; |
| 6372 | } |
| 6373 | return c; |
| 6374 | } |
| 6375 | |
| 6376 | void mg_ws_upgrade(struct mg_connection *c, struct mg_http_message *hm, |
| 6377 | const char *fmt, ...) { |
| 6378 | struct mg_str *wskey = mg_http_get_header(hm, "Sec-WebSocket-Key" ); |
| 6379 | c->pfn = mg_ws_cb; |
| 6380 | c->pfn_data = NULL; |
| 6381 | if (wskey == NULL) { |
| 6382 | mg_http_reply(c, 426, "" , "WS upgrade expected\n" ); |
| 6383 | c->is_draining = 1; |
| 6384 | } else { |
| 6385 | struct mg_str *wsproto = mg_http_get_header(hm, "Sec-WebSocket-Protocol" ); |
| 6386 | va_list ap; |
| 6387 | va_start(ap, fmt); |
| 6388 | ws_handshake(c, wskey, wsproto, fmt, &ap); |
| 6389 | va_end(ap); |
| 6390 | c->is_websocket = 1; |
| 6391 | c->is_resp = 0; |
| 6392 | mg_call(c, MG_EV_WS_OPEN, hm); |
| 6393 | } |
| 6394 | } |
| 6395 | |
| 6396 | size_t mg_ws_wrap(struct mg_connection *c, size_t len, int op) { |
| 6397 | uint8_t [14], *p; |
| 6398 | size_t = mkhdr(len, op, c->is_client, header); |
| 6399 | |
| 6400 | // NOTE: order of operations is important! |
| 6401 | mg_iobuf_add(&c->send, c->send.len, NULL, header_len); |
| 6402 | p = &c->send.buf[c->send.len - len]; // p points to data |
| 6403 | memmove(p, p - header_len, len); // Shift data |
| 6404 | memcpy(p - header_len, header, header_len); // Prepend header |
| 6405 | mg_ws_mask(c, len); // Mask data |
| 6406 | |
| 6407 | return c->send.len; |
| 6408 | } |
| 6409 | |
| 6410 | #ifdef MG_ENABLE_LINES |
| 6411 | #line 1 "src/tcpip/driver_nxpimxrt1020.c" |
| 6412 | #endif |
| 6413 | |
| 6414 | |
| 6415 | /* |
| 6416 | * Todo |
| 6417 | * This driver doesn't support 10M line autoconfiguration yet. |
| 6418 | * Packets aren't sent if the link negociated 10M line. |
| 6419 | * todo: MAC back auto reconfiguration. |
| 6420 | */ |
| 6421 | |
| 6422 | #if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_IMXRT1020) |
| 6423 | struct imx_rt1020_enet { |
| 6424 | volatile uint32_t RESERVED0, EIR, EIMR, RESERVED1, RDAR, TDAR, RESERVED2[3], ECR, RESERVED3[6], MMFR, MSCR, RESERVED4[7], MIBC, RESERVED5[7], RCR, RESERVED6[15], TCR, RESERVED7[7], PALR, PAUR, OPD, TXIC0, TXIC1, TXIC2, RESERVED8, RXIC0, RXIC1, RXIC2, RESERVED9[3], IAUR, IALR, GAUR, GALR, RESERVED10[7], TFWR, RESERVED11[14], RDSR, TDSR, MRBR[2], RSFL, RSEM, RAEM, RAFL, TSEM, TAEM, TAFL, TIPG, FTRL, RESERVED12[3], TACC, RACC, RESERVED13[15], RMON_T_PACKETS, RMON_T_BC_PKT, RMON_T_MC_PKT, RMON_T_CRC_ALIGN, RMON_T_UNDERSIZE, RMON_T_OVERSIZE, RMON_T_FRAG, RMON_T_JAB, RMON_T_COL, RMON_T_P64, RMON_T_P65TO127, RMON_T_P128TO255, RMON_T_P256TO511, RMON_T_P512TO1023, RMON_T_P1024TO2048, RMON_T_GTE2048, RMON_T_OCTETS, IEEE_T_DROP, IEEE_T_FRAME_OK, IEEE_T_1COL, IEEE_T_MCOL, IEEE_T_DEF, IEEE_T_LCOL, IEEE_T_EXCOL, IEEE_T_MACERR, IEEE_T_CSERR, IEEE_T_SQE, IEEE_T_FDXFC, IEEE_T_OCTETS_OK, RESERVED14[3], RMON_R_PACKETS, RMON_R_BC_PKT, RMON_R_MC_PKT, RMON_R_CRC_ALIGN, RMON_R_UNDERSIZE, RMON_R_OVERSIZE, RMON_R_FRAG, RMON_R_JAB, RESERVED15, RMON_R_P64, RMON_R_P65TO127, RMON_R_P128TO255, RMON_R_P256TO511, RMON_R_P512TO1023, RMON_R_P1024TO2047, RMON_R_GTE2048, RMON_R_OCTETS, IEEE_R_DROP, IEEE_R_FRAME_OK, IEEE_R_CRC, IEEE_R_ALIGN, IEEE_R_MACERR, IEEE_R_FDXFC, IEEE_R_OCTETS_OK, RESERVED16[71], ATCR, ATVR, ATOFF, ATPER, ATCOR, ATINC, ATSTMP, RESERVED17[122], TGSR, TCSR0, TCCR0, TCSR1, TCCR1, TCSR2, TCCR2, TCSR3; |
| 6425 | }; |
| 6426 | |
| 6427 | #undef ENET |
| 6428 | #define ENET ((struct imx_rt1020_enet *) (uintptr_t) 0x402D8000u) |
| 6429 | |
| 6430 | #undef BIT |
| 6431 | #define BIT(x) ((uint32_t) 1 << (x)) |
| 6432 | |
| 6433 | #define ENET_RXBUFF_SIZE 1536 // 1522 Buffer must be 64bits aligned |
| 6434 | #define ENET_TXBUFF_SIZE 1536 // 1522 hence set to 0x600 (1536) |
| 6435 | #define ENET_RXBD_NUM (4) |
| 6436 | #define ENET_TXBD_NUM (4) |
| 6437 | |
| 6438 | const uint32_t EIMR_RX_ERR = 0x2400000; // Intr mask RXF+EBERR |
| 6439 | |
| 6440 | void ETH_IRQHandler(void); |
| 6441 | static bool mg_tcpip_driver_imxrt1020_init(struct mg_tcpip_if *ifp); |
| 6442 | static void wait_phy_complete(void); |
| 6443 | static struct mg_tcpip_if *s_ifp; // MIP interface |
| 6444 | |
| 6445 | static size_t mg_tcpip_driver_imxrt1020_tx(const void *, size_t , struct mg_tcpip_if *); |
| 6446 | static bool mg_tcpip_driver_imxrt1020_up(struct mg_tcpip_if *ifp); |
| 6447 | |
| 6448 | enum { IMXRT1020_PHY_ADDR = 0x02, IMXRT1020_PHY_BCR = 0, IMXRT1020_PHY_BSR = 1 }; // PHY constants |
| 6449 | |
| 6450 | void delay(uint32_t); |
| 6451 | void delay (uint32_t di) { |
| 6452 | volatile int dno = 0; // Prevent optimization |
| 6453 | for (uint32_t i = 0; i < di; i++) |
| 6454 | for (int j=0; j<20; j++) // PLLx20 (500 MHz/24MHz) |
| 6455 | dno++; |
| 6456 | } |
| 6457 | |
| 6458 | static void wait_phy_complete(void) { |
| 6459 | delay(0x00010000); |
| 6460 | const uint32_t delay_max = 0x00100000; |
| 6461 | uint32_t delay_cnt = 0; |
| 6462 | while (!(ENET->EIR & BIT(23)) && (delay_cnt < delay_max)) |
| 6463 | {delay_cnt++;} |
| 6464 | ENET->EIR |= BIT(23); // MII interrupt clear |
| 6465 | } |
| 6466 | |
| 6467 | static uint32_t imxrt1020_eth_read_phy(uint8_t addr, uint8_t reg) { |
| 6468 | ENET->EIR |= BIT(23); // MII interrupt clear |
| 6469 | uint32_t mask_phy_adr_reg = 0x1f; // 0b00011111: Ensure we write 5 bits (Phy address & register) |
| 6470 | uint32_t phy_transaction = 0x00; |
| 6471 | phy_transaction = (0x1 << 30) \ |
| 6472 | | (0x2 << 28) \ |
| 6473 | | ((uint32_t)(addr & mask_phy_adr_reg) << 23) \ |
| 6474 | | ((uint32_t)(reg & mask_phy_adr_reg) << 18) \ |
| 6475 | | (0x2 << 16); |
| 6476 | |
| 6477 | ENET->MMFR = phy_transaction; |
| 6478 | wait_phy_complete(); |
| 6479 | |
| 6480 | return (ENET->MMFR & 0x0000ffff); |
| 6481 | } |
| 6482 | |
| 6483 | static void imxrt1020_eth_write_phy(uint8_t addr, uint8_t reg, uint32_t val) { |
| 6484 | ENET->EIR |= BIT(23); // MII interrupt clear |
| 6485 | uint8_t mask_phy_adr_reg = 0x1f; // 0b00011111: Ensure we write 5 bits (Phy address & register) |
| 6486 | uint32_t mask_phy_data = 0x0000ffff; // Ensure we write 16 bits (data) |
| 6487 | addr &= mask_phy_adr_reg; |
| 6488 | reg &= mask_phy_adr_reg; |
| 6489 | val &= mask_phy_data; |
| 6490 | uint32_t phy_transaction = 0x00; |
| 6491 | phy_transaction = (uint32_t)(0x1 << 30) \ |
| 6492 | | (uint32_t)(0x1 << 28) \ |
| 6493 | | (uint32_t)(addr << 23) \ |
| 6494 | | (uint32_t)(reg << 18) \ |
| 6495 | | (uint32_t)(0x2 << 16) \ |
| 6496 | | (uint32_t)(val); |
| 6497 | ENET->MMFR = phy_transaction; |
| 6498 | wait_phy_complete(); |
| 6499 | } |
| 6500 | |
| 6501 | // FEC RX/TX descriptors (Enhanced descriptor not enabled) |
| 6502 | // Descriptor buffer structure, little endian |
| 6503 | |
| 6504 | typedef struct enet_bd_struct_def |
| 6505 | { |
| 6506 | uint16_t length; // Data length |
| 6507 | uint16_t control; // Control and status |
| 6508 | uint32_t *buffer; // Data ptr |
| 6509 | } enet_bd_struct_t; |
| 6510 | |
| 6511 | // Descriptor and buffer globals, in non-cached area, 64 bits aligned. |
| 6512 | |
| 6513 | __attribute__((section("NonCacheable,\"aw\",%nobits @" ))) enet_bd_struct_t rx_buffer_descriptor[(ENET_RXBD_NUM)] __attribute__((aligned((64U)))); |
| 6514 | __attribute__((section("NonCacheable,\"aw\",%nobits @" ))) enet_bd_struct_t tx_buffer_descriptor[(ENET_TXBD_NUM)] __attribute__((aligned((64U)))); |
| 6515 | |
| 6516 | uint8_t rx_data_buffer[(ENET_RXBD_NUM)][((unsigned int)(((ENET_RXBUFF_SIZE)) + (((64U))-1U)) & (unsigned int)(~(unsigned int)(((64U))-1U)))] __attribute__((aligned((64U)))); |
| 6517 | uint8_t tx_data_buffer[(ENET_TXBD_NUM)][((unsigned int)(((ENET_TXBUFF_SIZE)) + (((64U))-1U)) & (unsigned int)(~(unsigned int)(((64U))-1U)))] __attribute__((aligned((64U)))); |
| 6518 | |
| 6519 | // Initialise driver imx_rt1020 |
| 6520 | |
| 6521 | // static bool mg_tcpip_driver_imxrt1020_init(uint8_t *mac, void *data) { // VO |
| 6522 | static bool mg_tcpip_driver_imxrt1020_init(struct mg_tcpip_if *ifp) { |
| 6523 | |
| 6524 | struct mg_tcpip_driver_imxrt1020_data *d = (struct mg_tcpip_driver_imxrt1020_data *) ifp->driver_data; |
| 6525 | s_ifp = ifp; |
| 6526 | |
| 6527 | // ENET Reset, wait complete |
| 6528 | ENET->ECR |= BIT(0); |
| 6529 | while((ENET->ECR & BIT(0)) != 0) {} |
| 6530 | |
| 6531 | // Re-latches the pin strapping pin values |
| 6532 | ENET->ECR |= BIT(0); |
| 6533 | while((ENET->ECR & BIT(0)) != 0) {} |
| 6534 | |
| 6535 | // Setup MII/RMII MDC clock divider (<= 2.5MHz). |
| 6536 | ENET->MSCR = 0x130; // HOLDTIME 2 clk, Preamble enable, MDC MII_Speed Div 0x30 |
| 6537 | imxrt1020_eth_write_phy(IMXRT1020_PHY_ADDR, IMXRT1020_PHY_BCR, 0x8000); // PHY W @0x00 D=0x8000 Soft reset |
| 6538 | while (imxrt1020_eth_read_phy(IMXRT1020_PHY_ADDR, IMXRT1020_PHY_BSR) & BIT(15)) {delay(0x5000);} // Wait finished poll 10ms |
| 6539 | |
| 6540 | // PHY: Start Link |
| 6541 | { |
| 6542 | imxrt1020_eth_write_phy(IMXRT1020_PHY_ADDR, IMXRT1020_PHY_BCR, 0x1200); // PHY W @0x00 D=0x1200 Autonego enable + start |
| 6543 | imxrt1020_eth_write_phy(IMXRT1020_PHY_ADDR, 0x1f, 0x8180); // PHY W @0x1f D=0x8180 Ref clock 50 MHz at XI input |
| 6544 | |
| 6545 | uint32_t bcr = imxrt1020_eth_read_phy(IMXRT1020_PHY_ADDR, IMXRT1020_PHY_BCR); |
| 6546 | bcr &= ~BIT(10); // Isolation -> Normal |
| 6547 | imxrt1020_eth_write_phy(IMXRT1020_PHY_ADDR, IMXRT1020_PHY_BCR, bcr); |
| 6548 | } |
| 6549 | |
| 6550 | // Disable ENET |
| 6551 | ENET->ECR = 0x0; // Disable before configuration |
| 6552 | |
| 6553 | // Configure ENET |
| 6554 | ENET->RCR = 0x05ee0104; // #CRCFWD=0 (CRC kept in frame) + RMII + MII Enable |
| 6555 | |
| 6556 | ENET->TCR = BIT(8) | BIT(2); // Addins (MAC address from PAUR+PALR) + Full duplex enable |
| 6557 | //ENET->TFWR = BIT(8); // Store And Forward Enable, 64 bytes (minimize tx latency) |
| 6558 | |
| 6559 | // Configure descriptors and buffers |
| 6560 | // RX |
| 6561 | for (int i = 0; i < ENET_RXBD_NUM; i++) { |
| 6562 | // Wrap last descriptor buffer ptr |
| 6563 | rx_buffer_descriptor[i].control = (BIT(15) | ((i<(ENET_RXBD_NUM-1))?0:BIT(13))); // E+(W*) |
| 6564 | rx_buffer_descriptor[i].buffer = (uint32_t *)rx_data_buffer[i]; |
| 6565 | } |
| 6566 | |
| 6567 | // TX |
| 6568 | for (int i = 0; i < ENET_TXBD_NUM; i++) { |
| 6569 | // Wrap last descriptor buffer ptr |
| 6570 | tx_buffer_descriptor[i].control = ((i<(ENET_RXBD_NUM-1))?0:BIT(13)) | BIT(10); // (W*)+TC |
| 6571 | tx_buffer_descriptor[i].buffer = (uint32_t *)tx_data_buffer[i]; |
| 6572 | } |
| 6573 | |
| 6574 | // Continue ENET configuration |
| 6575 | ENET->RDSR = (uint32_t)(uintptr_t)rx_buffer_descriptor; |
| 6576 | ENET->TDSR = (uint32_t)(uintptr_t)tx_buffer_descriptor; |
| 6577 | ENET->MRBR[0] = ENET_RXBUFF_SIZE; // Same size for RX/TX buffers |
| 6578 | |
| 6579 | // MAC address filtering (bytes in reversed order) |
| 6580 | ENET->PAUR = ((uint32_t) ifp->mac[4] << 24U) | (uint32_t) ifp->mac[5] << 16U; |
| 6581 | ENET->PALR = (uint32_t) (ifp->mac[0] << 24U) | ((uint32_t) ifp->mac[1] << 16U) | |
| 6582 | ((uint32_t) ifp->mac[2] << 8U) | ifp->mac[3]; |
| 6583 | |
| 6584 | // Init Hash tables (mac filtering) |
| 6585 | ENET->IAUR = 0; // Unicast |
| 6586 | ENET->IALR = 0; |
| 6587 | ENET->GAUR = 0; // Multicast |
| 6588 | ENET->GALR = 0; |
| 6589 | |
| 6590 | // Set ENET Online |
| 6591 | ENET->ECR |= BIT(8); // ENET Set Little-endian + (FEC buffer desc.) |
| 6592 | ENET->ECR |= BIT(1); // Enable |
| 6593 | |
| 6594 | // Set interrupt mask |
| 6595 | ENET->EIMR = EIMR_RX_ERR; |
| 6596 | |
| 6597 | // RX Descriptor activation |
| 6598 | ENET->RDAR = BIT(24); // Activate Receive Descriptor |
| 6599 | return true; |
| 6600 | } |
| 6601 | |
| 6602 | // Transmit frame |
| 6603 | static uint32_t s_rt1020_txno; |
| 6604 | |
| 6605 | static size_t mg_tcpip_driver_imxrt1020_tx(const void *buf, size_t len, struct mg_tcpip_if *ifp) { |
| 6606 | |
| 6607 | if (len > sizeof(tx_data_buffer[ENET_TXBD_NUM])) { |
| 6608 | // MG_ERROR(("Frame too big, %ld", (long) len)); |
| 6609 | len = 0; // Frame is too big |
| 6610 | } else if ((tx_buffer_descriptor[s_rt1020_txno].control & BIT(15))) { |
| 6611 | MG_ERROR(("No free descriptors" )); |
| 6612 | // printf("D0 %lx SR %lx\n", (long) s_txdesc[0][0], (long) ETH->DMASR); |
| 6613 | len = 0; // All descriptors are busy, fail |
| 6614 | } else { |
| 6615 | memcpy(tx_data_buffer[s_rt1020_txno], buf, len); // Copy data |
| 6616 | tx_buffer_descriptor[s_rt1020_txno].length = (uint16_t) len; // Set data len |
| 6617 | tx_buffer_descriptor[s_rt1020_txno].control |= (uint16_t)(BIT(10)); // TC (transmit CRC) |
| 6618 | // tx_buffer_descriptor[s_rt1020_txno].control &= (uint16_t)(BIT(14) | BIT(12)); // Own doesn't affect HW |
| 6619 | tx_buffer_descriptor[s_rt1020_txno].control |= (uint16_t)(BIT(15) | BIT(11)); // R+L (ready+last) |
| 6620 | ENET->TDAR = BIT(24); // Descriptor updated. Hand over to DMA. |
| 6621 | // INFO |
| 6622 | // Relevant Descriptor bits: 15(R) Ready |
| 6623 | // 11(L) last in frame |
| 6624 | // 10(TC) transmis CRC |
| 6625 | // __DSB(); // ARM errata 838869 Cortex-M4, M4F, M7, M7F: "store immediate overlapping |
| 6626 | // exception" return might vector to incorrect interrupt. |
| 6627 | if (++s_rt1020_txno >= ENET_TXBD_NUM) s_rt1020_txno = 0; |
| 6628 | } |
| 6629 | (void) ifp; |
| 6630 | return len; |
| 6631 | } |
| 6632 | |
| 6633 | // IRQ (RX) |
| 6634 | static uint32_t s_rt1020_rxno; |
| 6635 | |
| 6636 | void ENET_IRQHandler(void) { |
| 6637 | ENET->EIMR = 0; // Mask interrupts. |
| 6638 | uint32_t eir = ENET->EIR; // Read EIR |
| 6639 | ENET->EIR = 0xffffffff; // Clear interrupts |
| 6640 | |
| 6641 | if (eir & EIMR_RX_ERR) // Global mask used |
| 6642 | { |
| 6643 | if (rx_buffer_descriptor[s_rt1020_rxno].control & BIT(15)) { |
| 6644 | ENET->EIMR = EIMR_RX_ERR; // Enable interrupts |
| 6645 | return; // Empty? -> exit. |
| 6646 | } |
| 6647 | // Read inframes |
| 6648 | else { // Frame received, loop |
| 6649 | for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever |
| 6650 | if (rx_buffer_descriptor[s_rt1020_rxno].control & BIT(15)) break; // exit when done |
| 6651 | // Process if CRC OK and frame not truncated |
| 6652 | if (!(rx_buffer_descriptor[s_rt1020_rxno].control & (BIT(2) | BIT(0)))) { |
| 6653 | uint32_t len = (rx_buffer_descriptor[s_rt1020_rxno].length); |
| 6654 | mg_tcpip_qwrite(rx_buffer_descriptor[s_rt1020_rxno].buffer, len > 4 ? len - 4 : len, s_ifp); |
| 6655 | } |
| 6656 | rx_buffer_descriptor[s_rt1020_rxno].control |= BIT(15); // Inform DMA RX is empty |
| 6657 | if (++s_rt1020_rxno >= ENET_RXBD_NUM) s_rt1020_rxno = 0; |
| 6658 | } |
| 6659 | } |
| 6660 | } |
| 6661 | ENET->EIMR = EIMR_RX_ERR; // Enable interrupts |
| 6662 | } |
| 6663 | |
| 6664 | // Up/down status |
| 6665 | static bool mg_tcpip_driver_imxrt1020_up(struct mg_tcpip_if *ifp) { |
| 6666 | uint32_t bsr = imxrt1020_eth_read_phy(IMXRT1020_PHY_ADDR, IMXRT1020_PHY_BSR); |
| 6667 | (void) ifp; |
| 6668 | return bsr & BIT(2) ? 1 : 0; |
| 6669 | } |
| 6670 | |
| 6671 | // API |
| 6672 | struct mg_tcpip_driver mg_tcpip_driver_imxrt1020 = { |
| 6673 | mg_tcpip_driver_imxrt1020_init, mg_tcpip_driver_imxrt1020_tx, NULL, |
| 6674 | mg_tcpip_driver_imxrt1020_up}; |
| 6675 | |
| 6676 | #endif |
| 6677 | |
| 6678 | #ifdef MG_ENABLE_LINES |
| 6679 | #line 1 "src/tcpip/driver_stm32.c" |
| 6680 | #endif |
| 6681 | |
| 6682 | |
| 6683 | #if MG_ENABLE_TCPIP && MG_ENABLE_DRIVER_STM32 |
| 6684 | struct stm32_eth { |
| 6685 | volatile uint32_t MACCR, MACFFR, MACHTHR, MACHTLR, MACMIIAR, MACMIIDR, MACFCR, |
| 6686 | MACVLANTR, RESERVED0[2], MACRWUFFR, MACPMTCSR, RESERVED1, MACDBGR, MACSR, |
| 6687 | MACIMR, MACA0HR, MACA0LR, MACA1HR, MACA1LR, MACA2HR, MACA2LR, MACA3HR, |
| 6688 | MACA3LR, RESERVED2[40], MMCCR, MMCRIR, MMCTIR, MMCRIMR, MMCTIMR, |
| 6689 | RESERVED3[14], MMCTGFSCCR, MMCTGFMSCCR, RESERVED4[5], MMCTGFCR, |
| 6690 | RESERVED5[10], MMCRFCECR, MMCRFAECR, RESERVED6[10], MMCRGUFCR, |
| 6691 | RESERVED7[334], PTPTSCR, PTPSSIR, PTPTSHR, PTPTSLR, PTPTSHUR, PTPTSLUR, |
| 6692 | PTPTSAR, PTPTTHR, PTPTTLR, RESERVED8, PTPTSSR, PTPPPSCR, RESERVED9[564], |
| 6693 | DMABMR, DMATPDR, DMARPDR, DMARDLAR, DMATDLAR, DMASR, DMAOMR, DMAIER, |
| 6694 | DMAMFBOCR, DMARSWTR, RESERVED10[8], DMACHTDR, DMACHRDR, DMACHTBAR, |
| 6695 | DMACHRBAR; |
| 6696 | }; |
| 6697 | #undef ETH |
| 6698 | #define ETH ((struct stm32_eth *) (uintptr_t) 0x40028000) |
| 6699 | |
| 6700 | #undef DSB |
| 6701 | #if defined(__CC_ARM) |
| 6702 | #define DSB() __dsb(0xF) |
| 6703 | #elif defined(__ARMCC_VERSION) |
| 6704 | #define DSB() __builtin_arm_dsb(0xF) |
| 6705 | #elif defined(__GNUC__) && defined(__arm__) && defined(__thumb__) |
| 6706 | #define DSB() asm("DSB 0xF") |
| 6707 | #elif defined(__ICCARM__) |
| 6708 | #define DSB() __iar_builtin_DSB() |
| 6709 | #else |
| 6710 | #define DSB() |
| 6711 | #endif |
| 6712 | |
| 6713 | #undef BIT |
| 6714 | #define BIT(x) ((uint32_t) 1 << (x)) |
| 6715 | #define ETH_PKT_SIZE 1540 // Max frame size |
| 6716 | #define ETH_DESC_CNT 4 // Descriptors count |
| 6717 | #define ETH_DS 4 // Descriptor size (words) |
| 6718 | |
| 6719 | static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors |
| 6720 | static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors |
| 6721 | static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers |
| 6722 | static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers |
| 6723 | static uint8_t s_txno; // Current TX descriptor |
| 6724 | static uint8_t s_rxno; // Current RX descriptor |
| 6725 | |
| 6726 | static struct mg_tcpip_if *s_ifp; // MIP interface |
| 6727 | enum { PHY_ADDR = 0, PHY_BCR = 0, PHY_BSR = 1, PHY_CSCR = 31 }; |
| 6728 | |
| 6729 | static uint32_t eth_read_phy(uint8_t addr, uint8_t reg) { |
| 6730 | ETH->MACMIIAR &= (7 << 2); |
| 6731 | ETH->MACMIIAR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6); |
| 6732 | ETH->MACMIIAR |= BIT(0); |
| 6733 | while (ETH->MACMIIAR & BIT(0)) (void) 0; |
| 6734 | return ETH->MACMIIDR; |
| 6735 | } |
| 6736 | |
| 6737 | static void eth_write_phy(uint8_t addr, uint8_t reg, uint32_t val) { |
| 6738 | ETH->MACMIIDR = val; |
| 6739 | ETH->MACMIIAR &= (7 << 2); |
| 6740 | ETH->MACMIIAR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6) | BIT(1); |
| 6741 | ETH->MACMIIAR |= BIT(0); |
| 6742 | while (ETH->MACMIIAR & BIT(0)) (void) 0; |
| 6743 | } |
| 6744 | |
| 6745 | static uint32_t get_hclk(void) { |
| 6746 | struct rcc { |
| 6747 | volatile uint32_t CR, PLLCFGR, CFGR; |
| 6748 | } *rcc = (struct rcc *) 0x40023800; |
| 6749 | uint32_t clk = 0, hsi = 16000000 /* 16 MHz */, hse = 8000000 /* 8MHz */; |
| 6750 | |
| 6751 | if (rcc->CFGR & (1 << 2)) { |
| 6752 | clk = hse; |
| 6753 | } else if (rcc->CFGR & (1 << 3)) { |
| 6754 | uint32_t vco, m, n, p; |
| 6755 | m = (rcc->PLLCFGR & (0x3f << 0)) >> 0; |
| 6756 | n = (rcc->PLLCFGR & (0x1ff << 6)) >> 6; |
| 6757 | p = (((rcc->PLLCFGR & (3 << 16)) >> 16) + 1) * 2; |
| 6758 | clk = (rcc->PLLCFGR & (1 << 22)) ? hse : hsi; |
| 6759 | vco = (uint32_t) ((uint64_t) clk * n / m); |
| 6760 | clk = vco / p; |
| 6761 | } else { |
| 6762 | clk = hsi; |
| 6763 | } |
| 6764 | uint32_t hpre = (rcc->CFGR & (15 << 4)) >> 4; |
| 6765 | if (hpre < 8) return clk; |
| 6766 | |
| 6767 | uint8_t ahbptab[8] = {1, 2, 3, 4, 6, 7, 8, 9}; // log2(div) |
| 6768 | return ((uint32_t) clk) >> ahbptab[hpre - 8]; |
| 6769 | } |
| 6770 | |
| 6771 | // Guess CR from HCLK. MDC clock is generated from HCLK (AHB); as per 802.3, |
| 6772 | // it must not exceed 2.5MHz As the AHB clock can be (and usually is) derived |
| 6773 | // from the HSI (internal RC), and it can go above specs, the datasheets |
| 6774 | // specify a range of frequencies and activate one of a series of dividers to |
| 6775 | // keep the MDC clock safely below 2.5MHz. We guess a divider setting based on |
| 6776 | // HCLK with a +5% drift. If the user uses a different clock from our |
| 6777 | // defaults, needs to set the macros on top Valid for STM32F74xxx/75xxx |
| 6778 | // (38.8.1) and STM32F42xxx/43xxx (33.8.1) (both 4.5% worst case drift) |
| 6779 | static int guess_mdc_cr(void) { |
| 6780 | uint8_t crs[] = {2, 3, 0, 1, 4, 5}; // ETH->MACMIIAR::CR values |
| 6781 | uint8_t div[] = {16, 26, 42, 62, 102, 124}; // Respective HCLK dividers |
| 6782 | uint32_t hclk = get_hclk(); // Guess system HCLK |
| 6783 | int result = -1; // Invalid CR value |
| 6784 | if (hclk < 25000000) { |
| 6785 | MG_ERROR(("HCLK too low" )); |
| 6786 | } else { |
| 6787 | for (int i = 0; i < 6; i++) { |
| 6788 | if (hclk / div[i] <= 2375000UL /* 2.5MHz - 5% */) { |
| 6789 | result = crs[i]; |
| 6790 | break; |
| 6791 | } |
| 6792 | } |
| 6793 | if (result < 0) MG_ERROR(("HCLK too high" )); |
| 6794 | } |
| 6795 | MG_DEBUG(("HCLK: %u, CR: %d" , hclk, result)); |
| 6796 | return result; |
| 6797 | } |
| 6798 | |
| 6799 | static bool mg_tcpip_driver_stm32_init(struct mg_tcpip_if *ifp) { |
| 6800 | struct mg_tcpip_driver_stm32_data *d = |
| 6801 | (struct mg_tcpip_driver_stm32_data *) ifp->driver_data; |
| 6802 | s_ifp = ifp; |
| 6803 | |
| 6804 | // Init RX descriptors |
| 6805 | for (int i = 0; i < ETH_DESC_CNT; i++) { |
| 6806 | s_rxdesc[i][0] = BIT(31); // Own |
| 6807 | s_rxdesc[i][1] = sizeof(s_rxbuf[i]) | BIT(14); // 2nd address chained |
| 6808 | s_rxdesc[i][2] = (uint32_t) (uintptr_t) s_rxbuf[i]; // Point to data buffer |
| 6809 | s_rxdesc[i][3] = |
| 6810 | (uint32_t) (uintptr_t) s_rxdesc[(i + 1) % ETH_DESC_CNT]; // Chain |
| 6811 | } |
| 6812 | |
| 6813 | // Init TX descriptors |
| 6814 | for (int i = 0; i < ETH_DESC_CNT; i++) { |
| 6815 | s_txdesc[i][2] = (uint32_t) (uintptr_t) s_txbuf[i]; // Buf pointer |
| 6816 | s_txdesc[i][3] = |
| 6817 | (uint32_t) (uintptr_t) s_txdesc[(i + 1) % ETH_DESC_CNT]; // Chain |
| 6818 | } |
| 6819 | |
| 6820 | ETH->DMABMR |= BIT(0); // Software reset |
| 6821 | while ((ETH->DMABMR & BIT(0)) != 0) (void) 0; // Wait until done |
| 6822 | |
| 6823 | // Set MDC clock divider. If user told us the value, use it. Otherwise, guess |
| 6824 | int cr = (d == NULL || d->mdc_cr < 0) ? guess_mdc_cr() : d->mdc_cr; |
| 6825 | ETH->MACMIIAR = ((uint32_t) cr & 7) << 2; |
| 6826 | |
| 6827 | // NOTE(cpq): we do not use extended descriptor bit 7, and do not use |
| 6828 | // hardware checksum. Therefore, descriptor size is 4, not 8 |
| 6829 | // ETH->DMABMR = BIT(13) | BIT(16) | BIT(22) | BIT(23) | BIT(25); |
| 6830 | ETH->MACIMR = BIT(3) | BIT(9); // Mask timestamp & PMT IT |
| 6831 | ETH->MACFCR = BIT(7); // Disable zero quarta pause |
| 6832 | // ETH->MACFFR = BIT(31); // Receive all |
| 6833 | eth_write_phy(PHY_ADDR, PHY_BCR, BIT(15)); // Reset PHY |
| 6834 | eth_write_phy(PHY_ADDR, PHY_BCR, BIT(12)); // Set autonegotiation |
| 6835 | ETH->DMARDLAR = (uint32_t) (uintptr_t) s_rxdesc; // RX descriptors |
| 6836 | ETH->DMATDLAR = (uint32_t) (uintptr_t) s_txdesc; // RX descriptors |
| 6837 | ETH->DMAIER = BIT(6) | BIT(16); // RIE, NISE |
| 6838 | ETH->MACCR = BIT(2) | BIT(3) | BIT(11) | BIT(14); // RE, TE, Duplex, Fast |
| 6839 | ETH->DMAOMR = BIT(1) | BIT(13) | BIT(21) | BIT(25); // SR, ST, TSF, RSF |
| 6840 | |
| 6841 | // MAC address filtering |
| 6842 | ETH->MACA0HR = ((uint32_t) ifp->mac[5] << 8U) | ifp->mac[4]; |
| 6843 | ETH->MACA0LR = (uint32_t) (ifp->mac[3] << 24) | |
| 6844 | ((uint32_t) ifp->mac[2] << 16) | |
| 6845 | ((uint32_t) ifp->mac[1] << 8) | ifp->mac[0]; |
| 6846 | return true; |
| 6847 | } |
| 6848 | |
| 6849 | static size_t mg_tcpip_driver_stm32_tx(const void *buf, size_t len, |
| 6850 | struct mg_tcpip_if *ifp) { |
| 6851 | if (len > sizeof(s_txbuf[s_txno])) { |
| 6852 | MG_ERROR(("Frame too big, %ld" , (long) len)); |
| 6853 | len = 0; // Frame is too big |
| 6854 | } else if ((s_txdesc[s_txno][0] & BIT(31))) { |
| 6855 | ifp->nerr++; |
| 6856 | MG_ERROR(("No free descriptors" )); |
| 6857 | // printf("D0 %lx SR %lx\n", (long) s_txdesc[0][0], (long) ETH->DMASR); |
| 6858 | len = 0; // All descriptors are busy, fail |
| 6859 | } else { |
| 6860 | memcpy(s_txbuf[s_txno], buf, len); // Copy data |
| 6861 | s_txdesc[s_txno][1] = (uint32_t) len; // Set data len |
| 6862 | s_txdesc[s_txno][0] = BIT(20) | BIT(28) | BIT(29); // Chain,FS,LS |
| 6863 | s_txdesc[s_txno][0] |= BIT(31); // Set OWN bit - let DMA take over |
| 6864 | if (++s_txno >= ETH_DESC_CNT) s_txno = 0; |
| 6865 | } |
| 6866 | DSB(); // ensure descriptors have been written |
| 6867 | ETH->DMASR = BIT(2) | BIT(5); // Clear any prior TBUS/TUS |
| 6868 | ETH->DMATPDR = 0; // and resume |
| 6869 | return len; |
| 6870 | } |
| 6871 | |
| 6872 | static bool mg_tcpip_driver_stm32_up(struct mg_tcpip_if *ifp) { |
| 6873 | uint32_t bsr = eth_read_phy(PHY_ADDR, PHY_BSR); |
| 6874 | bool up = bsr & BIT(2) ? 1 : 0; |
| 6875 | if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up |
| 6876 | uint32_t scsr = eth_read_phy(PHY_ADDR, PHY_CSCR); |
| 6877 | uint32_t maccr = ETH->MACCR | BIT(14) | BIT(11); // 100M, Full-duplex |
| 6878 | if ((scsr & BIT(3)) == 0) maccr &= ~BIT(14); // 10M |
| 6879 | if ((scsr & BIT(4)) == 0) maccr &= ~BIT(11); // Half-duplex |
| 6880 | ETH->MACCR = maccr; // IRQ handler does not fiddle with this register |
| 6881 | MG_DEBUG(("Link is %uM %s-duplex" , maccr & BIT(14) ? 100 : 10, |
| 6882 | maccr & BIT(11) ? "full" : "half" )); |
| 6883 | } |
| 6884 | return up; |
| 6885 | } |
| 6886 | |
| 6887 | void ETH_IRQHandler(void); |
| 6888 | void ETH_IRQHandler(void) { |
| 6889 | if (ETH->DMASR & BIT(6)) { // Frame received, loop |
| 6890 | ETH->DMASR = BIT(16) | BIT(6); // Clear flag |
| 6891 | for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever |
| 6892 | if (s_rxdesc[s_rxno][0] & BIT(31)) break; // exit when done |
| 6893 | if (((s_rxdesc[s_rxno][0] & (BIT(8) | BIT(9))) == (BIT(8) | BIT(9))) && |
| 6894 | !(s_rxdesc[s_rxno][0] & BIT(15))) { // skip partial/errored frames |
| 6895 | uint32_t len = ((s_rxdesc[s_rxno][0] >> 16) & (BIT(14) - 1)); |
| 6896 | // printf("%lx %lu %lx %.8lx\n", s_rxno, len, s_rxdesc[s_rxno][0], |
| 6897 | // ETH->DMASR); |
| 6898 | mg_tcpip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp); |
| 6899 | } |
| 6900 | s_rxdesc[s_rxno][0] = BIT(31); |
| 6901 | if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0; |
| 6902 | } |
| 6903 | } |
| 6904 | ETH->DMASR = BIT(7); // Clear possible RBUS while processing |
| 6905 | ETH->DMARPDR = 0; // and resume RX |
| 6906 | } |
| 6907 | |
| 6908 | struct mg_tcpip_driver mg_tcpip_driver_stm32 = {mg_tcpip_driver_stm32_init, |
| 6909 | mg_tcpip_driver_stm32_tx, NULL, |
| 6910 | mg_tcpip_driver_stm32_up}; |
| 6911 | #endif |
| 6912 | |
| 6913 | #ifdef MG_ENABLE_LINES |
| 6914 | #line 1 "src/tcpip/driver_stm32h.c" |
| 6915 | #endif |
| 6916 | |
| 6917 | |
| 6918 | #if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_STM32H) && \ |
| 6919 | MG_ENABLE_DRIVER_STM32H |
| 6920 | struct stm32h_eth { |
| 6921 | volatile uint32_t MACCR, MACECR, MACPFR, MACWTR, MACHT0R, MACHT1R, |
| 6922 | RESERVED1[14], MACVTR, RESERVED2, MACVHTR, RESERVED3, MACVIR, MACIVIR, |
| 6923 | RESERVED4[2], MACTFCR, RESERVED5[7], MACRFCR, RESERVED6[7], MACISR, |
| 6924 | MACIER, MACRXTXSR, RESERVED7, MACPCSR, MACRWKPFR, RESERVED8[2], MACLCSR, |
| 6925 | MACLTCR, MACLETR, MAC1USTCR, RESERVED9[12], MACVR, MACDR, RESERVED10, |
| 6926 | MACHWF0R, MACHWF1R, MACHWF2R, RESERVED11[54], MACMDIOAR, MACMDIODR, |
| 6927 | RESERVED12[2], MACARPAR, RESERVED13[59], MACA0HR, MACA0LR, MACA1HR, |
| 6928 | MACA1LR, MACA2HR, MACA2LR, MACA3HR, MACA3LR, RESERVED14[248], MMCCR, |
| 6929 | MMCRIR, MMCTIR, MMCRIMR, MMCTIMR, RESERVED15[14], MMCTSCGPR, MMCTMCGPR, |
| 6930 | RESERVED16[5], MMCTPCGR, RESERVED17[10], MMCRCRCEPR, MMCRAEPR, |
| 6931 | RESERVED18[10], MMCRUPGR, RESERVED19[9], MMCTLPIMSTR, MMCTLPITCR, |
| 6932 | MMCRLPIMSTR, MMCRLPITCR, RESERVED20[65], MACL3L4C0R, MACL4A0R, |
| 6933 | RESERVED21[2], MACL3A0R0R, MACL3A1R0R, MACL3A2R0R, MACL3A3R0R, |
| 6934 | RESERVED22[4], MACL3L4C1R, MACL4A1R, RESERVED23[2], MACL3A0R1R, |
| 6935 | MACL3A1R1R, MACL3A2R1R, MACL3A3R1R, RESERVED24[108], MACTSCR, MACSSIR, |
| 6936 | MACSTSR, MACSTNR, MACSTSUR, MACSTNUR, MACTSAR, RESERVED25, MACTSSR, |
| 6937 | RESERVED26[3], MACTTSSNR, MACTTSSSR, RESERVED27[2], MACACR, RESERVED28, |
| 6938 | MACATSNR, MACATSSR, MACTSIACR, MACTSEACR, MACTSICNR, MACTSECNR, |
| 6939 | RESERVED29[4], MACPPSCR, RESERVED30[3], MACPPSTTSR, MACPPSTTNR, MACPPSIR, |
| 6940 | MACPPSWR, RESERVED31[12], MACPOCR, MACSPI0R, MACSPI1R, MACSPI2R, MACLMIR, |
| 6941 | RESERVED32[11], MTLOMR, RESERVED33[7], MTLISR, RESERVED34[55], MTLTQOMR, |
| 6942 | MTLTQUR, MTLTQDR, RESERVED35[8], MTLQICSR, MTLRQOMR, MTLRQMPOCR, MTLRQDR, |
| 6943 | RESERVED36[177], DMAMR, DMASBMR, DMAISR, DMADSR, RESERVED37[60], DMACCR, |
| 6944 | DMACTCR, DMACRCR, RESERVED38[2], DMACTDLAR, RESERVED39, DMACRDLAR, |
| 6945 | DMACTDTPR, RESERVED40, DMACRDTPR, DMACTDRLR, DMACRDRLR, DMACIER, |
| 6946 | DMACRIWTR, DMACSFCSR, RESERVED41, DMACCATDR, RESERVED42, DMACCARDR, |
| 6947 | RESERVED43, DMACCATBR, RESERVED44, DMACCARBR, DMACSR, RESERVED45[2], |
| 6948 | DMACMFCR; |
| 6949 | }; |
| 6950 | #undef ETH |
| 6951 | #define ETH \ |
| 6952 | ((struct stm32h_eth *) (uintptr_t) (0x40000000UL + 0x00020000UL + 0x8000UL)) |
| 6953 | |
| 6954 | #undef BIT |
| 6955 | #define BIT(x) ((uint32_t) 1 << (x)) |
| 6956 | #define ETH_PKT_SIZE 1540 // Max frame size |
| 6957 | #define ETH_DESC_CNT 4 // Descriptors count |
| 6958 | #define ETH_DS 4 // Descriptor size (words) |
| 6959 | |
| 6960 | static volatile uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors |
| 6961 | static volatile uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors |
| 6962 | static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers |
| 6963 | static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers |
| 6964 | static struct mg_tcpip_if *s_ifp; // MIP interface |
| 6965 | enum { |
| 6966 | PHY_ADDR = 0, |
| 6967 | PHY_BCR = 0, |
| 6968 | PHY_BSR = 1, |
| 6969 | PHY_CSCR = 31 |
| 6970 | }; // PHY constants |
| 6971 | |
| 6972 | static uint32_t eth_read_phy(uint8_t addr, uint8_t reg) { |
| 6973 | ETH->MACMDIOAR &= (0xF << 8); |
| 6974 | ETH->MACMDIOAR |= ((uint32_t) addr << 21) | ((uint32_t) reg << 16) | 3 << 2; |
| 6975 | ETH->MACMDIOAR |= BIT(0); |
| 6976 | while (ETH->MACMDIOAR & BIT(0)) (void) 0; |
| 6977 | return ETH->MACMDIODR; |
| 6978 | } |
| 6979 | |
| 6980 | static void eth_write_phy(uint8_t addr, uint8_t reg, uint32_t val) { |
| 6981 | ETH->MACMDIODR = val; |
| 6982 | ETH->MACMDIOAR &= (0xF << 8); |
| 6983 | ETH->MACMDIOAR |= ((uint32_t) addr << 21) | ((uint32_t) reg << 16) | 1 << 2; |
| 6984 | ETH->MACMDIOAR |= BIT(0); |
| 6985 | while (ETH->MACMDIOAR & BIT(0)) (void) 0; |
| 6986 | } |
| 6987 | |
| 6988 | static uint32_t get_hclk(void) { |
| 6989 | struct rcc { |
| 6990 | volatile uint32_t CR, HSICFGR, CRRCR, CSICFGR, CFGR, RESERVED1, D1CFGR, |
| 6991 | D2CFGR, D3CFGR, RESERVED2, PLLCKSELR, PLLCFGR, PLL1DIVR, PLL1FRACR, |
| 6992 | PLL2DIVR, PLL2FRACR, PLL3DIVR, PLL3FRACR, RESERVED3, D1CCIPR, D2CCIP1R, |
| 6993 | D2CCIP2R, D3CCIPR, RESERVED4, CIER, CIFR, CICR, RESERVED5, BDCR, CSR, |
| 6994 | RESERVED6, AHB3RSTR, AHB1RSTR, AHB2RSTR, AHB4RSTR, APB3RSTR, APB1LRSTR, |
| 6995 | APB1HRSTR, APB2RSTR, APB4RSTR, GCR, RESERVED8, D3AMR, RESERVED11[9], |
| 6996 | RSR, AHB3ENR, AHB1ENR, AHB2ENR, AHB4ENR, APB3ENR, APB1LENR, APB1HENR, |
| 6997 | APB2ENR, APB4ENR, RESERVED12, AHB3LPENR, AHB1LPENR, AHB2LPENR, |
| 6998 | AHB4LPENR, APB3LPENR, APB1LLPENR, APB1HLPENR, APB2LPENR, APB4LPENR, |
| 6999 | RESERVED13[4]; |
| 7000 | } *rcc = ((struct rcc *) (0x40000000 + 0x18020000 + 0x4400)); |
| 7001 | uint32_t clk = 0, hsi = 64000000 /* 64 MHz */, hse = 8000000 /* 8MHz */, |
| 7002 | csi = 4000000 /* 4MHz */; |
| 7003 | unsigned int sel = (rcc->CFGR & (7 << 3)) >> 3; |
| 7004 | |
| 7005 | if (sel == 1) { |
| 7006 | clk = csi; |
| 7007 | } else if (sel == 2) { |
| 7008 | clk = hse; |
| 7009 | } else if (sel == 3) { |
| 7010 | uint32_t vco, m, n, p; |
| 7011 | unsigned int src = (rcc->PLLCKSELR & (3 << 0)) >> 0; |
| 7012 | m = ((rcc->PLLCKSELR & (0x3F << 4)) >> 4); |
| 7013 | n = ((rcc->PLL1DIVR & (0x1FF << 0)) >> 0) + 1 + |
| 7014 | ((rcc->PLLCFGR & BIT(0)) ? 1 : 0); // round-up in fractional mode |
| 7015 | p = ((rcc->PLL1DIVR & (0x7F << 9)) >> 9) + 1; |
| 7016 | if (src == 1) { |
| 7017 | clk = csi; |
| 7018 | } else if (src == 2) { |
| 7019 | clk = hse; |
| 7020 | } else { |
| 7021 | clk = hsi; |
| 7022 | clk >>= ((rcc->CR & 3) >> 3); |
| 7023 | } |
| 7024 | vco = (uint32_t) ((uint64_t) clk * n / m); |
| 7025 | clk = vco / p; |
| 7026 | } else { |
| 7027 | clk = hsi; |
| 7028 | clk >>= ((rcc->CR & 3) >> 3); |
| 7029 | } |
| 7030 | const uint8_t cptab[12] = {1, 2, 3, 4, 6, 7, 8, 9}; // log2(div) |
| 7031 | uint32_t d1cpre = (rcc->D1CFGR & (0x0F << 8)) >> 8; |
| 7032 | if (d1cpre >= 8) clk >>= cptab[d1cpre - 8]; |
| 7033 | MG_DEBUG(("D1 CLK: %u" , clk)); |
| 7034 | uint32_t hpre = (rcc->D1CFGR & (0x0F << 0)) >> 0; |
| 7035 | if (hpre < 8) return clk; |
| 7036 | return ((uint32_t) clk) >> cptab[hpre - 8]; |
| 7037 | } |
| 7038 | |
| 7039 | // Guess CR from AHB1 clock. MDC clock is generated from the ETH peripheral |
| 7040 | // clock (AHB1); as per 802.3, it must not exceed 2. As the AHB clock can |
| 7041 | // be derived from HSI or CSI (internal RC) clocks, and those can go above |
| 7042 | // specs, the datasheets specify a range of frequencies and activate one of a |
| 7043 | // series of dividers to keep the MDC clock safely below 2.5MHz. We guess a |
| 7044 | // divider setting based on HCLK with some drift. If the user uses a different |
| 7045 | // clock from our defaults, needs to set the macros on top. Valid for |
| 7046 | // STM32H74xxx/75xxx (58.11.4)(4.5% worst case drift)(CSI clock has a 7.5 % |
| 7047 | // worst case drift @ max temp) |
| 7048 | static int guess_mdc_cr(void) { |
| 7049 | const uint8_t crs[] = {2, 3, 0, 1, 4, 5}; // ETH->MACMDIOAR::CR values |
| 7050 | const uint8_t div[] = {16, 26, 42, 62, 102, 124}; // Respective HCLK dividers |
| 7051 | uint32_t hclk = get_hclk(); // Guess system HCLK |
| 7052 | int result = -1; // Invalid CR value |
| 7053 | for (int i = 0; i < 6; i++) { |
| 7054 | if (hclk / div[i] <= 2375000UL /* 2.5MHz - 5% */) { |
| 7055 | result = crs[i]; |
| 7056 | break; |
| 7057 | } |
| 7058 | } |
| 7059 | if (result < 0) MG_ERROR(("HCLK too high" )); |
| 7060 | MG_DEBUG(("HCLK: %u, CR: %d" , hclk, result)); |
| 7061 | return result; |
| 7062 | } |
| 7063 | |
| 7064 | static bool mg_tcpip_driver_stm32h_init(struct mg_tcpip_if *ifp) { |
| 7065 | struct mg_tcpip_driver_stm32h_data *d = |
| 7066 | (struct mg_tcpip_driver_stm32h_data *) ifp->driver_data; |
| 7067 | s_ifp = ifp; |
| 7068 | |
| 7069 | // Init RX descriptors |
| 7070 | for (int i = 0; i < ETH_DESC_CNT; i++) { |
| 7071 | s_rxdesc[i][0] = (uint32_t) (uintptr_t) s_rxbuf[i]; // Point to data buffer |
| 7072 | s_rxdesc[i][3] = BIT(31) | BIT(30) | BIT(24); // OWN, IOC, BUF1V |
| 7073 | } |
| 7074 | |
| 7075 | // Init TX descriptors |
| 7076 | for (int i = 0; i < ETH_DESC_CNT; i++) { |
| 7077 | s_txdesc[i][0] = (uint32_t) (uintptr_t) s_txbuf[i]; // Buf pointer |
| 7078 | } |
| 7079 | |
| 7080 | ETH->DMAMR |= BIT(0); // Software reset |
| 7081 | while ((ETH->DMAMR & BIT(0)) != 0) (void) 0; // Wait until done |
| 7082 | |
| 7083 | // Set MDC clock divider. If user told us the value, use it. Otherwise, guess |
| 7084 | int cr = (d == NULL || d->mdc_cr < 0) ? guess_mdc_cr() : d->mdc_cr; |
| 7085 | ETH->MACMDIOAR = ((uint32_t) cr & 0xF) << 8; |
| 7086 | |
| 7087 | // NOTE(scaprile): We do not use timing facilities so the DMA engine does not |
| 7088 | // re-write buffer address |
| 7089 | ETH->DMAMR = 0 << 16; // use interrupt mode 0 (58.8.1) (reset value) |
| 7090 | ETH->DMASBMR |= BIT(12); // AAL NOTE(scaprile): is this actually needed |
| 7091 | ETH->MACIER = 0; // Do not enable additional irq sources (reset value) |
| 7092 | ETH->MACTFCR = BIT(7); // Disable zero-quanta pause |
| 7093 | // ETH->MACPFR = BIT(31); // Receive all |
| 7094 | eth_write_phy(PHY_ADDR, PHY_BCR, BIT(15)); // Reset PHY |
| 7095 | eth_write_phy(PHY_ADDR, PHY_BCR, BIT(12)); // Set autonegotiation |
| 7096 | ETH->DMACRDLAR = |
| 7097 | (uint32_t) (uintptr_t) s_rxdesc; // RX descriptors start address |
| 7098 | ETH->DMACRDRLR = ETH_DESC_CNT - 1; // ring length |
| 7099 | ETH->DMACRDTPR = |
| 7100 | (uint32_t) (uintptr_t) &s_rxdesc[ETH_DESC_CNT - |
| 7101 | 1]; // last valid descriptor address |
| 7102 | ETH->DMACTDLAR = |
| 7103 | (uint32_t) (uintptr_t) s_txdesc; // TX descriptors start address |
| 7104 | ETH->DMACTDRLR = ETH_DESC_CNT - 1; // ring length |
| 7105 | ETH->DMACTDTPR = |
| 7106 | (uint32_t) (uintptr_t) s_txdesc; // first available descriptor address |
| 7107 | ETH->DMACCR = 0; // DSL = 0 (contiguous descriptor table) (reset value) |
| 7108 | ETH->DMACIER = BIT(6) | BIT(15); // RIE, NIE |
| 7109 | ETH->MACCR = BIT(0) | BIT(1) | BIT(13) | BIT(14) | |
| 7110 | BIT(15); // RE, TE, Duplex, Fast, Reserved |
| 7111 | ETH->MTLTQOMR |= BIT(1); // TSF |
| 7112 | ETH->MTLRQOMR |= BIT(5); // RSF |
| 7113 | ETH->DMACTCR |= BIT(0); // ST |
| 7114 | ETH->DMACRCR |= BIT(0); // SR |
| 7115 | |
| 7116 | // MAC address filtering |
| 7117 | ETH->MACA0HR = ((uint32_t) ifp->mac[5] << 8U) | ifp->mac[4]; |
| 7118 | ETH->MACA0LR = (uint32_t) (ifp->mac[3] << 24) | |
| 7119 | ((uint32_t) ifp->mac[2] << 16) | |
| 7120 | ((uint32_t) ifp->mac[1] << 8) | ifp->mac[0]; |
| 7121 | return true; |
| 7122 | } |
| 7123 | |
| 7124 | static uint32_t s_txno; |
| 7125 | static size_t mg_tcpip_driver_stm32h_tx(const void *buf, size_t len, |
| 7126 | struct mg_tcpip_if *ifp) { |
| 7127 | if (len > sizeof(s_txbuf[s_txno])) { |
| 7128 | MG_ERROR(("Frame too big, %ld" , (long) len)); |
| 7129 | len = 0; // Frame is too big |
| 7130 | } else if ((s_txdesc[s_txno][3] & BIT(31))) { |
| 7131 | MG_ERROR(("No free descriptors: %u %08X %08X %08X" , s_txno, |
| 7132 | s_txdesc[s_txno][3], ETH->DMACSR, ETH->DMACTCR)); |
| 7133 | for (int i = 0; i < ETH_DESC_CNT; i++) MG_ERROR(("%08X" , s_txdesc[i][3])); |
| 7134 | len = 0; // All descriptors are busy, fail |
| 7135 | } else { |
| 7136 | memcpy(s_txbuf[s_txno], buf, len); // Copy data |
| 7137 | s_txdesc[s_txno][2] = (uint32_t) len; // Set data len |
| 7138 | s_txdesc[s_txno][3] = BIT(28) | BIT(29); // FD, LD |
| 7139 | s_txdesc[s_txno][3] |= BIT(31); // Set OWN bit - let DMA take over |
| 7140 | if (++s_txno >= ETH_DESC_CNT) s_txno = 0; |
| 7141 | } |
| 7142 | ETH->DMACSR |= BIT(2) | BIT(1); // Clear any prior TBU, TPS |
| 7143 | ETH->DMACTDTPR = (uint32_t) (uintptr_t) &s_txdesc[s_txno]; // and resume |
| 7144 | return len; |
| 7145 | (void) ifp; |
| 7146 | } |
| 7147 | |
| 7148 | static bool mg_tcpip_driver_stm32h_up(struct mg_tcpip_if *ifp) { |
| 7149 | uint32_t bsr = eth_read_phy(PHY_ADDR, PHY_BSR); |
| 7150 | bool up = bsr & BIT(2) ? 1 : 0; |
| 7151 | if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up |
| 7152 | uint32_t scsr = eth_read_phy(PHY_ADDR, PHY_CSCR); |
| 7153 | uint32_t maccr = ETH->MACCR | BIT(14) | BIT(13); // 100M, Full-duplex |
| 7154 | if ((scsr & BIT(3)) == 0) maccr &= ~BIT(14); // 10M |
| 7155 | if ((scsr & BIT(4)) == 0) maccr &= ~BIT(13); // Half-duplex |
| 7156 | ETH->MACCR = maccr; // IRQ handler does not fiddle with this register |
| 7157 | MG_DEBUG(("Link is %uM %s-duplex" , maccr & BIT(14) ? 100 : 10, |
| 7158 | maccr & BIT(13) ? "full" : "half" )); |
| 7159 | } |
| 7160 | return up; |
| 7161 | } |
| 7162 | |
| 7163 | void ETH_IRQHandler(void); |
| 7164 | static uint32_t s_rxno; |
| 7165 | void ETH_IRQHandler(void) { |
| 7166 | if (ETH->DMACSR & BIT(6)) { // Frame received, loop |
| 7167 | ETH->DMACSR = BIT(15) | BIT(6); // Clear flag |
| 7168 | for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever |
| 7169 | if (s_rxdesc[s_rxno][3] & BIT(31)) break; // exit when done |
| 7170 | if (((s_rxdesc[s_rxno][3] & (BIT(28) | BIT(29))) == |
| 7171 | (BIT(28) | BIT(29))) && |
| 7172 | !(s_rxdesc[s_rxno][3] & BIT(15))) { // skip partial/errored frames |
| 7173 | uint32_t len = s_rxdesc[s_rxno][3] & (BIT(15) - 1); |
| 7174 | // MG_DEBUG(("%lx %lu %lx %08lx", s_rxno, len, s_rxdesc[s_rxno][3], |
| 7175 | // ETH->DMACSR)); |
| 7176 | mg_tcpip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp); |
| 7177 | } |
| 7178 | s_rxdesc[s_rxno][3] = BIT(31) | BIT(30) | BIT(24); // OWN, IOC, BUF1V |
| 7179 | if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0; |
| 7180 | } |
| 7181 | } |
| 7182 | ETH->DMACSR = BIT(7) | BIT(8); // Clear possible RBU RPS while processing |
| 7183 | ETH->DMACRDTPR = |
| 7184 | (uint32_t) (uintptr_t) &s_rxdesc[ETH_DESC_CNT - 1]; // and resume RX |
| 7185 | } |
| 7186 | |
| 7187 | struct mg_tcpip_driver mg_tcpip_driver_stm32h = { |
| 7188 | mg_tcpip_driver_stm32h_init, mg_tcpip_driver_stm32h_tx, NULL, |
| 7189 | mg_tcpip_driver_stm32h_up}; |
| 7190 | #endif |
| 7191 | |
| 7192 | #ifdef MG_ENABLE_LINES |
| 7193 | #line 1 "src/tcpip/driver_tm4c.c" |
| 7194 | #endif |
| 7195 | |
| 7196 | |
| 7197 | #if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_TM4C) && MG_ENABLE_DRIVER_TM4C |
| 7198 | struct tm4c_emac { |
| 7199 | volatile uint32_t EMACCFG, EMACFRAMEFLTR, EMACHASHTBLH, EMACHASHTBLL, |
| 7200 | EMACMIIADDR, EMACMIIDATA, EMACFLOWCTL, EMACVLANTG, RESERVED0, EMACSTATUS, |
| 7201 | EMACRWUFF, EMACPMTCTLSTAT, RESERVED1[2], EMACRIS, EMACIM, EMACADDR0H, |
| 7202 | EMACADDR0L, EMACADDR1H, EMACADDR1L, EMACADDR2H, EMACADDR2L, EMACADDR3H, |
| 7203 | EMACADDR3L, RESERVED2[31], EMACWDOGTO, RESERVED3[8], EMACMMCCTRL, |
| 7204 | EMACMMCRXRIS, EMACMMCTXRIS, EMACMMCRXIM, EMACMMCTXIM, RESERVED4, |
| 7205 | EMACTXCNTGB, RESERVED5[12], EMACTXCNTSCOL, EMACTXCNTMCOL, RESERVED6[4], |
| 7206 | EMACTXOCTCNTG, RESERVED7[6], EMACRXCNTGB, RESERVED8[4], EMACRXCNTCRCERR, |
| 7207 | EMACRXCNTALGNERR, RESERVED9[10], EMACRXCNTGUNI, RESERVED10[239], |
| 7208 | EMACVLNINCREP, EMACVLANHASH, RESERVED11[93], EMACTIMSTCTRL, EMACSUBSECINC, |
| 7209 | EMACTIMSEC, EMACTIMNANO, EMACTIMSECU, EMACTIMNANOU, EMACTIMADD, |
| 7210 | EMACTARGSEC, EMACTARGNANO, EMACHWORDSEC, EMACTIMSTAT, EMACPPSCTRL, |
| 7211 | RESERVED12[12], EMACPPS0INTVL, EMACPPS0WIDTH, RESERVED13[294], |
| 7212 | EMACDMABUSMOD, EMACTXPOLLD, EMACRXPOLLD, EMACRXDLADDR, EMACTXDLADDR, |
| 7213 | EMACDMARIS, EMACDMAOPMODE, EMACDMAIM, EMACMFBOC, EMACRXINTWDT, |
| 7214 | RESERVED14[8], EMACHOSTXDESC, EMACHOSRXDESC, EMACHOSTXBA, EMACHOSRXBA, |
| 7215 | RESERVED15[218], EMACPP, EMACPC, EMACCC, RESERVED16, EMACEPHYRIS, |
| 7216 | EMACEPHYIM, EMACEPHYIMSC; |
| 7217 | }; |
| 7218 | #undef EMAC |
| 7219 | #define EMAC ((struct tm4c_emac *) (uintptr_t) 0x400EC000) |
| 7220 | |
| 7221 | #undef BIT |
| 7222 | #define BIT(x) ((uint32_t) 1 << (x)) |
| 7223 | #define ETH_PKT_SIZE 1540 // Max frame size |
| 7224 | #define ETH_DESC_CNT 4 // Descriptors count |
| 7225 | #define ETH_DS 4 // Descriptor size (words) |
| 7226 | |
| 7227 | static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors |
| 7228 | static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors |
| 7229 | static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers |
| 7230 | static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers |
| 7231 | static struct mg_tcpip_if *s_ifp; // MIP interface |
| 7232 | enum { |
| 7233 | EPHY_ADDR = 0, |
| 7234 | EPHYBMCR = 0, |
| 7235 | EPHYBMSR = 1, |
| 7236 | EPHYSTS = 16 |
| 7237 | }; // PHY constants |
| 7238 | |
| 7239 | static inline void tm4cspin(volatile uint32_t count) { |
| 7240 | while (count--) (void) 0; |
| 7241 | } |
| 7242 | |
| 7243 | static uint32_t emac_read_phy(uint8_t addr, uint8_t reg) { |
| 7244 | EMAC->EMACMIIADDR &= (0xf << 2); |
| 7245 | EMAC->EMACMIIADDR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6); |
| 7246 | EMAC->EMACMIIADDR |= BIT(0); |
| 7247 | while (EMAC->EMACMIIADDR & BIT(0)) tm4cspin(1); |
| 7248 | return EMAC->EMACMIIDATA; |
| 7249 | } |
| 7250 | |
| 7251 | static void emac_write_phy(uint8_t addr, uint8_t reg, uint32_t val) { |
| 7252 | EMAC->EMACMIIDATA = val; |
| 7253 | EMAC->EMACMIIADDR &= (0xf << 2); |
| 7254 | EMAC->EMACMIIADDR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6) | BIT(1); |
| 7255 | EMAC->EMACMIIADDR |= BIT(0); |
| 7256 | while (EMAC->EMACMIIADDR & BIT(0)) tm4cspin(1); |
| 7257 | } |
| 7258 | |
| 7259 | static uint32_t get_sysclk(void) { |
| 7260 | struct sysctl { |
| 7261 | volatile uint32_t DONTCARE0[44], RSCLKCFG, DONTCARE1[43], PLLFREQ0, |
| 7262 | PLLFREQ1; |
| 7263 | } *sysctl = (struct sysctl *) 0x400FE000; |
| 7264 | uint32_t clk = 0, piosc = 16000000 /* 16 MHz */, mosc = 25000000 /* 25MHz */; |
| 7265 | if (sysctl->RSCLKCFG & (1 << 28)) { // USEPLL |
| 7266 | uint32_t fin, vco, mdiv, n, q, psysdiv; |
| 7267 | uint32_t pllsrc = (sysctl->RSCLKCFG & (0xf << 24)) >> 24; |
| 7268 | if (pllsrc == 0) { |
| 7269 | clk = piosc; |
| 7270 | } else if (pllsrc == 3) { |
| 7271 | clk = mosc; |
| 7272 | } else { |
| 7273 | MG_ERROR(("Unsupported clock source" )); |
| 7274 | } |
| 7275 | q = (sysctl->PLLFREQ1 & (0x1f << 8)) >> 8; |
| 7276 | n = (sysctl->PLLFREQ1 & (0x1f << 0)) >> 0; |
| 7277 | fin = clk / ((q + 1) * (n + 1)); |
| 7278 | mdiv = (sysctl->PLLFREQ0 & (0x3ff << 0)) >> |
| 7279 | 0; // mint + (mfrac / 1024); MFRAC not supported |
| 7280 | psysdiv = (sysctl->RSCLKCFG & (0x3f << 0)) >> 0; |
| 7281 | vco = (uint32_t) ((uint64_t) fin * mdiv); |
| 7282 | return vco / (psysdiv + 1); |
| 7283 | } |
| 7284 | uint32_t oscsrc = (sysctl->RSCLKCFG & (0xf << 20)) >> 20; |
| 7285 | if (oscsrc == 0) { |
| 7286 | clk = piosc; |
| 7287 | } else if (oscsrc == 3) { |
| 7288 | clk = mosc; |
| 7289 | } else { |
| 7290 | MG_ERROR(("Unsupported clock source" )); |
| 7291 | } |
| 7292 | uint32_t osysdiv = (sysctl->RSCLKCFG & (0xf << 16)) >> 16; |
| 7293 | return clk / (osysdiv + 1); |
| 7294 | } |
| 7295 | |
| 7296 | // Guess CR from SYSCLK. MDC clock is generated from SYSCLK (AHB); as per |
| 7297 | // 802.3, it must not exceed 2.5MHz (also 20.4.2.6) As the AHB clock can be |
| 7298 | // derived from the PIOSC (internal RC), and it can go above specs, the |
| 7299 | // datasheets specify a range of frequencies and activate one of a series of |
| 7300 | // dividers to keep the MDC clock safely below 2.5MHz. We guess a divider |
| 7301 | // setting based on SYSCLK with a +5% drift. If the user uses a different clock |
| 7302 | // from our defaults, needs to set the macros on top Valid for TM4C129x (20.7) |
| 7303 | // (4.5% worst case drift) |
| 7304 | // The PHY receives the main oscillator (MOSC) (20.3.1) |
| 7305 | static int guess_mdc_cr(void) { |
| 7306 | uint8_t crs[] = {2, 3, 0, 1}; // EMAC->MACMIIAR::CR values |
| 7307 | uint8_t div[] = {16, 26, 42, 62}; // Respective HCLK dividers |
| 7308 | uint32_t sysclk = get_sysclk(); // Guess system SYSCLK |
| 7309 | int result = -1; // Invalid CR value |
| 7310 | if (sysclk < 25000000) { |
| 7311 | MG_ERROR(("SYSCLK too low" )); |
| 7312 | } else { |
| 7313 | for (int i = 0; i < 4; i++) { |
| 7314 | if (sysclk / div[i] <= 2375000UL /* 2.5MHz - 5% */) { |
| 7315 | result = crs[i]; |
| 7316 | break; |
| 7317 | } |
| 7318 | } |
| 7319 | if (result < 0) MG_ERROR(("SYSCLK too high" )); |
| 7320 | } |
| 7321 | MG_DEBUG(("SYSCLK: %u, CR: %d" , sysclk, result)); |
| 7322 | return result; |
| 7323 | } |
| 7324 | |
| 7325 | static bool mg_tcpip_driver_tm4c_init(struct mg_tcpip_if *ifp) { |
| 7326 | struct mg_tcpip_driver_tm4c_data *d = |
| 7327 | (struct mg_tcpip_driver_tm4c_data *) ifp->driver_data; |
| 7328 | s_ifp = ifp; |
| 7329 | |
| 7330 | // Init RX descriptors |
| 7331 | for (int i = 0; i < ETH_DESC_CNT; i++) { |
| 7332 | s_rxdesc[i][0] = BIT(31); // Own |
| 7333 | s_rxdesc[i][1] = sizeof(s_rxbuf[i]) | BIT(14); // 2nd address chained |
| 7334 | s_rxdesc[i][2] = (uint32_t) (uintptr_t) s_rxbuf[i]; // Point to data buffer |
| 7335 | s_rxdesc[i][3] = |
| 7336 | (uint32_t) (uintptr_t) s_rxdesc[(i + 1) % ETH_DESC_CNT]; // Chain |
| 7337 | // MG_DEBUG(("%d %p", i, s_rxdesc[i])); |
| 7338 | } |
| 7339 | |
| 7340 | // Init TX descriptors |
| 7341 | for (int i = 0; i < ETH_DESC_CNT; i++) { |
| 7342 | s_txdesc[i][2] = (uint32_t) (uintptr_t) s_txbuf[i]; // Buf pointer |
| 7343 | s_txdesc[i][3] = |
| 7344 | (uint32_t) (uintptr_t) s_txdesc[(i + 1) % ETH_DESC_CNT]; // Chain |
| 7345 | } |
| 7346 | |
| 7347 | EMAC->EMACDMABUSMOD |= BIT(0); // Software reset |
| 7348 | while ((EMAC->EMACDMABUSMOD & BIT(0)) != 0) tm4cspin(1); // Wait until done |
| 7349 | |
| 7350 | // Set MDC clock divider. If user told us the value, use it. Otherwise, guess |
| 7351 | int cr = (d == NULL || d->mdc_cr < 0) ? guess_mdc_cr() : d->mdc_cr; |
| 7352 | EMAC->EMACMIIADDR = ((uint32_t) cr & 0xf) << 2; |
| 7353 | |
| 7354 | // NOTE(cpq): we do not use extended descriptor bit 7, and do not use |
| 7355 | // hardware checksum. Therefore, descriptor size is 4, not 8 |
| 7356 | // EMAC->EMACDMABUSMOD = BIT(13) | BIT(16) | BIT(22) | BIT(23) | BIT(25); |
| 7357 | EMAC->EMACIM = BIT(3) | BIT(9); // Mask timestamp & PMT IT |
| 7358 | EMAC->EMACFLOWCTL = BIT(7); // Disable zero-quanta pause |
| 7359 | // EMAC->EMACFRAMEFLTR = BIT(31); // Receive all |
| 7360 | // EMAC->EMACPC defaults to internal PHY (EPHY) in MMI mode |
| 7361 | emac_write_phy(EPHY_ADDR, EPHYBMCR, BIT(15)); // Reset internal PHY (EPHY) |
| 7362 | emac_write_phy(EPHY_ADDR, EPHYBMCR, BIT(12)); // Set autonegotiation |
| 7363 | EMAC->EMACRXDLADDR = (uint32_t) (uintptr_t) s_rxdesc; // RX descriptors |
| 7364 | EMAC->EMACTXDLADDR = (uint32_t) (uintptr_t) s_txdesc; // TX descriptors |
| 7365 | EMAC->EMACDMAIM = BIT(6) | BIT(16); // RIE, NIE |
| 7366 | EMAC->EMACCFG = BIT(2) | BIT(3) | BIT(11) | BIT(14); // RE, TE, Duplex, Fast |
| 7367 | EMAC->EMACDMAOPMODE = |
| 7368 | BIT(1) | BIT(13) | BIT(21) | BIT(25); // SR, ST, TSF, RSF |
| 7369 | EMAC->EMACADDR0H = ((uint32_t) ifp->mac[5] << 8U) | ifp->mac[4]; |
| 7370 | EMAC->EMACADDR0L = (uint32_t) (ifp->mac[3] << 24) | |
| 7371 | ((uint32_t) ifp->mac[2] << 16) | |
| 7372 | ((uint32_t) ifp->mac[1] << 8) | ifp->mac[0]; |
| 7373 | // NOTE(scaprile) There are 3 additional slots for filtering, disabled by |
| 7374 | // default. This also applies to the STM32 driver (at least for F7) |
| 7375 | return true; |
| 7376 | } |
| 7377 | |
| 7378 | static uint32_t s_txno; |
| 7379 | static size_t mg_tcpip_driver_tm4c_tx(const void *buf, size_t len, |
| 7380 | struct mg_tcpip_if *ifp) { |
| 7381 | if (len > sizeof(s_txbuf[s_txno])) { |
| 7382 | MG_ERROR(("Frame too big, %ld" , (long) len)); |
| 7383 | len = 0; // fail |
| 7384 | } else if ((s_txdesc[s_txno][0] & BIT(31))) { |
| 7385 | MG_ERROR(("No descriptors available" )); |
| 7386 | // printf("D0 %lx SR %lx\n", (long) s_txdesc[0][0], (long) |
| 7387 | // EMAC->EMACDMARIS); |
| 7388 | len = 0; // fail |
| 7389 | } else { |
| 7390 | memcpy(s_txbuf[s_txno], buf, len); // Copy data |
| 7391 | s_txdesc[s_txno][1] = (uint32_t) len; // Set data len |
| 7392 | s_txdesc[s_txno][0] = |
| 7393 | BIT(20) | BIT(28) | BIT(29) | BIT(30); // Chain,FS,LS,IC |
| 7394 | s_txdesc[s_txno][0] |= BIT(31); // Set OWN bit - let DMA take over |
| 7395 | if (++s_txno >= ETH_DESC_CNT) s_txno = 0; |
| 7396 | } |
| 7397 | EMAC->EMACDMARIS = BIT(2) | BIT(5); // Clear any prior TU/UNF |
| 7398 | EMAC->EMACTXPOLLD = 0; // and resume |
| 7399 | return len; |
| 7400 | (void) ifp; |
| 7401 | } |
| 7402 | |
| 7403 | static bool mg_tcpip_driver_tm4c_up(struct mg_tcpip_if *ifp) { |
| 7404 | uint32_t bmsr = emac_read_phy(EPHY_ADDR, EPHYBMSR); |
| 7405 | bool up = (bmsr & BIT(2)) ? 1 : 0; |
| 7406 | if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up |
| 7407 | uint32_t sts = emac_read_phy(EPHY_ADDR, EPHYSTS); |
| 7408 | uint32_t emaccfg = EMAC->EMACCFG | BIT(14) | BIT(11); // 100M, Full-duplex |
| 7409 | if (sts & BIT(1)) emaccfg &= ~BIT(14); // 10M |
| 7410 | if ((sts & BIT(2)) == 0) emaccfg &= ~BIT(11); // Half-duplex |
| 7411 | EMAC->EMACCFG = emaccfg; // IRQ handler does not fiddle with this register |
| 7412 | MG_DEBUG(("Link is %uM %s-duplex" , emaccfg & BIT(14) ? 100 : 10, |
| 7413 | emaccfg & BIT(11) ? "full" : "half" )); |
| 7414 | } |
| 7415 | return up; |
| 7416 | } |
| 7417 | |
| 7418 | void EMAC0_IRQHandler(void); |
| 7419 | static uint32_t s_rxno; |
| 7420 | void EMAC0_IRQHandler(void) { |
| 7421 | if (EMAC->EMACDMARIS & BIT(6)) { // Frame received, loop |
| 7422 | EMAC->EMACDMARIS = BIT(16) | BIT(6); // Clear flag |
| 7423 | for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever |
| 7424 | if (s_rxdesc[s_rxno][0] & BIT(31)) break; // exit when done |
| 7425 | if (((s_rxdesc[s_rxno][0] & (BIT(8) | BIT(9))) == (BIT(8) | BIT(9))) && |
| 7426 | !(s_rxdesc[s_rxno][0] & BIT(15))) { // skip partial/errored frames |
| 7427 | uint32_t len = ((s_rxdesc[s_rxno][0] >> 16) & (BIT(14) - 1)); |
| 7428 | // printf("%lx %lu %lx %.8lx\n", s_rxno, len, s_rxdesc[s_rxno][0], |
| 7429 | // EMAC->EMACDMARIS); |
| 7430 | mg_tcpip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp); |
| 7431 | } |
| 7432 | s_rxdesc[s_rxno][0] = BIT(31); |
| 7433 | if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0; |
| 7434 | } |
| 7435 | } |
| 7436 | EMAC->EMACDMARIS = BIT(7); // Clear possible RU while processing |
| 7437 | EMAC->EMACRXPOLLD = 0; // and resume RX |
| 7438 | } |
| 7439 | |
| 7440 | struct mg_tcpip_driver mg_tcpip_driver_tm4c = {mg_tcpip_driver_tm4c_init, |
| 7441 | mg_tcpip_driver_tm4c_tx, NULL, |
| 7442 | mg_tcpip_driver_tm4c_up}; |
| 7443 | #endif |
| 7444 | |
| 7445 | #ifdef MG_ENABLE_LINES |
| 7446 | #line 1 "src/tcpip/driver_w5500.c" |
| 7447 | #endif |
| 7448 | |
| 7449 | |
| 7450 | #if MG_ENABLE_TCPIP |
| 7451 | |
| 7452 | enum { W5500_CR = 0, W5500_S0 = 1, W5500_TX0 = 2, W5500_RX0 = 3 }; |
| 7453 | |
| 7454 | static void w5500_txn(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, bool wr, |
| 7455 | void *buf, size_t len) { |
| 7456 | uint8_t *p = (uint8_t *) buf; |
| 7457 | uint8_t cmd[] = {(uint8_t) (addr >> 8), (uint8_t) (addr & 255), |
| 7458 | (uint8_t) ((block << 3) | (wr ? 4 : 0))}; |
| 7459 | s->begin(s->spi); |
| 7460 | for (size_t i = 0; i < sizeof(cmd); i++) s->txn(s->spi, cmd[i]); |
| 7461 | for (size_t i = 0; i < len; i++) { |
| 7462 | uint8_t r = s->txn(s->spi, p[i]); |
| 7463 | if (!wr) p[i] = r; |
| 7464 | } |
| 7465 | s->end(s->spi); |
| 7466 | } |
| 7467 | |
| 7468 | // clang-format off |
| 7469 | 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); } |
| 7470 | 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); } |
| 7471 | 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)); } |
| 7472 | 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); } |
| 7473 | 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; } |
| 7474 | 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]); } |
| 7475 | // clang-format on |
| 7476 | |
| 7477 | static size_t w5500_rx(void *buf, size_t buflen, struct mg_tcpip_if *ifp) { |
| 7478 | struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) ifp->driver_data; |
| 7479 | uint16_t r = 0, n = 0, len = (uint16_t) buflen, n2; // Read recv len |
| 7480 | while ((n2 = w5500_r2(s, W5500_S0, 0x26)) > n) n = n2; // Until it is stable |
| 7481 | // printf("RSR: %d\n", (int) n); |
| 7482 | if (n > 0) { |
| 7483 | uint16_t ptr = w5500_r2(s, W5500_S0, 0x28); // Get read pointer |
| 7484 | n = w5500_r2(s, W5500_RX0, ptr); // Read frame length |
| 7485 | if (n <= len + 2 && n > 1) { |
| 7486 | r = (uint16_t) (n - 2); |
| 7487 | w5500_rn(s, W5500_RX0, (uint16_t) (ptr + 2), buf, r); |
| 7488 | } |
| 7489 | w5500_w2(s, W5500_S0, 0x28, (uint16_t) (ptr + n)); // Advance read pointer |
| 7490 | w5500_w1(s, W5500_S0, 1, 0x40); // Sock0 CR -> RECV |
| 7491 | // printf(" RX_RD: tot=%u n=%u r=%u\n", n2, n, r); |
| 7492 | } |
| 7493 | return r; |
| 7494 | } |
| 7495 | |
| 7496 | static size_t w5500_tx(const void *buf, size_t buflen, struct mg_tcpip_if *ifp) { |
| 7497 | struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) ifp->driver_data; |
| 7498 | uint16_t n = 0, len = (uint16_t) buflen; |
| 7499 | while (n < len) n = w5500_r2(s, W5500_S0, 0x20); // Wait for space |
| 7500 | uint16_t ptr = w5500_r2(s, W5500_S0, 0x24); // Get write pointer |
| 7501 | w5500_wn(s, W5500_TX0, ptr, (void *) buf, len); // Write data |
| 7502 | w5500_w2(s, W5500_S0, 0x24, (uint16_t) (ptr + len)); // Advance write pointer |
| 7503 | w5500_w1(s, W5500_S0, 1, 0x20); // Sock0 CR -> SEND |
| 7504 | for (int i = 0; i < 40; i++) { |
| 7505 | uint8_t ir = w5500_r1(s, W5500_S0, 2); // Read S0 IR |
| 7506 | if (ir == 0) continue; |
| 7507 | // printf("IR %d, len=%d, free=%d, ptr %d\n", ir, (int) len, (int) n, ptr); |
| 7508 | w5500_w1(s, W5500_S0, 2, ir); // Write S0 IR: clear it! |
| 7509 | if (ir & 8) len = 0; // Timeout. Report error |
| 7510 | if (ir & (16 | 8)) break; // Stop on SEND_OK or timeout |
| 7511 | } |
| 7512 | return len; |
| 7513 | } |
| 7514 | |
| 7515 | static bool w5500_init(struct mg_tcpip_if *ifp) { |
| 7516 | struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) ifp->driver_data; |
| 7517 | s->end(s->spi); |
| 7518 | w5500_w1(s, W5500_CR, 0, 0x80); // Reset chip: CR -> 0x80 |
| 7519 | w5500_w1(s, W5500_CR, 0x2e, 0); // CR PHYCFGR -> reset |
| 7520 | w5500_w1(s, W5500_CR, 0x2e, 0xf8); // CR PHYCFGR -> set |
| 7521 | // w5500_wn(s, W5500_CR, 9, s->mac, 6); // Set source MAC |
| 7522 | w5500_w1(s, W5500_S0, 0x1e, 16); // Sock0 RX buf size |
| 7523 | w5500_w1(s, W5500_S0, 0x1f, 16); // Sock0 TX buf size |
| 7524 | w5500_w1(s, W5500_S0, 0, 4); // Sock0 MR -> MACRAW |
| 7525 | w5500_w1(s, W5500_S0, 1, 1); // Sock0 CR -> OPEN |
| 7526 | return w5500_r1(s, W5500_S0, 3) == 0x42; // Sock0 SR == MACRAW |
| 7527 | } |
| 7528 | |
| 7529 | static bool w5500_up(struct mg_tcpip_if *ifp) { |
| 7530 | struct mg_tcpip_spi *spi = (struct mg_tcpip_spi *) ifp->driver_data; |
| 7531 | uint8_t phycfgr = w5500_r1(spi, W5500_CR, 0x2e); |
| 7532 | return phycfgr & 1; // Bit 0 of PHYCFGR is LNK (0 - down, 1 - up) |
| 7533 | } |
| 7534 | |
| 7535 | struct mg_tcpip_driver mg_tcpip_driver_w5500 = {w5500_init, w5500_tx, w5500_rx, w5500_up}; |
| 7536 | #endif |
| 7537 | |
| 7538 | #ifdef MG_ENABLE_LINES |
| 7539 | #line 1 "src/tcpip/tcpip.c" |
| 7540 | #endif |
| 7541 | |
| 7542 | |
| 7543 | #if MG_ENABLE_TCPIP |
| 7544 | |
| 7545 | #define MG_EPHEMERAL_PORT_BASE 32768 |
| 7546 | #define PDIFF(a, b) ((size_t) (((char *) (b)) - ((char *) (a)))) |
| 7547 | |
| 7548 | #ifndef MIP_TCP_KEEPALIVE_MS |
| 7549 | #define MIP_TCP_KEEPALIVE_MS 45000 // TCP keep-alive period, ms |
| 7550 | #endif |
| 7551 | |
| 7552 | #define MIP_TCP_ACK_MS 150 // Timeout for ACKing |
| 7553 | |
| 7554 | struct connstate { |
| 7555 | uint32_t seq, ack; // TCP seq/ack counters |
| 7556 | uint64_t timer; // TCP keep-alive / ACK timer |
| 7557 | uint8_t mac[6]; // Peer MAC address |
| 7558 | uint8_t ttype; // Timer type. 0: ack, 1: keep-alive |
| 7559 | #define MIP_TTYPE_KEEPALIVE 0 // Connection is idle for long, send keepalive |
| 7560 | #define MIP_TTYPE_ACK 1 // Peer sent us data, we have to ack it soon |
| 7561 | uint8_t tmiss; // Number of keep-alive misses |
| 7562 | struct mg_iobuf raw; // For TLS only. Incoming raw data |
| 7563 | }; |
| 7564 | |
| 7565 | #pragma pack(push, 1) |
| 7566 | |
| 7567 | struct lcp { |
| 7568 | uint8_t addr, ctrl, proto[2], code, id, len[2]; |
| 7569 | }; |
| 7570 | |
| 7571 | struct eth { |
| 7572 | uint8_t dst[6]; // Destination MAC address |
| 7573 | uint8_t src[6]; // Source MAC address |
| 7574 | uint16_t type; // Ethernet type |
| 7575 | }; |
| 7576 | |
| 7577 | struct ip { |
| 7578 | uint8_t ver; // Version |
| 7579 | uint8_t tos; // Unused |
| 7580 | uint16_t len; // Length |
| 7581 | uint16_t id; // Unused |
| 7582 | uint16_t frag; // Fragmentation |
| 7583 | uint8_t ttl; // Time to live |
| 7584 | uint8_t proto; // Upper level protocol |
| 7585 | uint16_t csum; // Checksum |
| 7586 | uint32_t src; // Source IP |
| 7587 | uint32_t dst; // Destination IP |
| 7588 | }; |
| 7589 | |
| 7590 | struct ip6 { |
| 7591 | uint8_t ver; // Version |
| 7592 | uint8_t opts[3]; // Options |
| 7593 | uint16_t len; // Length |
| 7594 | uint8_t proto; // Upper level protocol |
| 7595 | uint8_t ttl; // Time to live |
| 7596 | uint8_t src[16]; // Source IP |
| 7597 | uint8_t dst[16]; // Destination IP |
| 7598 | }; |
| 7599 | |
| 7600 | struct icmp { |
| 7601 | uint8_t type; |
| 7602 | uint8_t code; |
| 7603 | uint16_t csum; |
| 7604 | }; |
| 7605 | |
| 7606 | struct arp { |
| 7607 | uint16_t fmt; // Format of hardware address |
| 7608 | uint16_t pro; // Format of protocol address |
| 7609 | uint8_t hlen; // Length of hardware address |
| 7610 | uint8_t plen; // Length of protocol address |
| 7611 | uint16_t op; // Operation |
| 7612 | uint8_t sha[6]; // Sender hardware address |
| 7613 | uint32_t spa; // Sender protocol address |
| 7614 | uint8_t tha[6]; // Target hardware address |
| 7615 | uint32_t tpa; // Target protocol address |
| 7616 | }; |
| 7617 | |
| 7618 | struct tcp { |
| 7619 | uint16_t sport; // Source port |
| 7620 | uint16_t dport; // Destination port |
| 7621 | uint32_t seq; // Sequence number |
| 7622 | uint32_t ack; // Acknowledgement number |
| 7623 | uint8_t off; // Data offset |
| 7624 | uint8_t flags; // TCP flags |
| 7625 | #define TH_FIN 0x01 |
| 7626 | #define TH_SYN 0x02 |
| 7627 | #define TH_RST 0x04 |
| 7628 | #define TH_PUSH 0x08 |
| 7629 | #define TH_ACK 0x10 |
| 7630 | #define TH_URG 0x20 |
| 7631 | #define TH_ECE 0x40 |
| 7632 | #define TH_CWR 0x80 |
| 7633 | uint16_t win; // Window |
| 7634 | uint16_t csum; // Checksum |
| 7635 | uint16_t urp; // Urgent pointer |
| 7636 | }; |
| 7637 | |
| 7638 | struct udp { |
| 7639 | uint16_t sport; // Source port |
| 7640 | uint16_t dport; // Destination port |
| 7641 | uint16_t len; // UDP length |
| 7642 | uint16_t csum; // UDP checksum |
| 7643 | }; |
| 7644 | |
| 7645 | struct dhcp { |
| 7646 | uint8_t op, htype, hlen, hops; |
| 7647 | uint32_t xid; |
| 7648 | uint16_t secs, flags; |
| 7649 | uint32_t ciaddr, yiaddr, siaddr, giaddr; |
| 7650 | uint8_t hwaddr[208]; |
| 7651 | uint32_t magic; |
| 7652 | uint8_t options[32]; |
| 7653 | }; |
| 7654 | |
| 7655 | #pragma pack(pop) |
| 7656 | |
| 7657 | struct pkt { |
| 7658 | struct mg_str raw; // Raw packet data |
| 7659 | struct mg_str pay; // Payload data |
| 7660 | struct eth *eth; |
| 7661 | struct llc *llc; |
| 7662 | struct arp *arp; |
| 7663 | struct ip *ip; |
| 7664 | struct ip6 *ip6; |
| 7665 | struct icmp *icmp; |
| 7666 | struct tcp *tcp; |
| 7667 | struct udp *udp; |
| 7668 | struct dhcp *dhcp; |
| 7669 | }; |
| 7670 | |
| 7671 | static void mkpay(struct pkt *pkt, void *p) { |
| 7672 | pkt->pay = |
| 7673 | mg_str_n((char *) p, (size_t) (&pkt->raw.ptr[pkt->raw.len] - (char *) p)); |
| 7674 | } |
| 7675 | |
| 7676 | static uint32_t csumup(uint32_t sum, const void *buf, size_t len) { |
| 7677 | const uint8_t *p = (const uint8_t *) buf; |
| 7678 | for (size_t i = 0; i < len; i++) sum += i & 1 ? p[i] : (uint32_t) (p[i] << 8); |
| 7679 | return sum; |
| 7680 | } |
| 7681 | |
| 7682 | static uint16_t csumfin(uint32_t sum) { |
| 7683 | while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16); |
| 7684 | return mg_htons(~sum & 0xffff); |
| 7685 | } |
| 7686 | |
| 7687 | static uint16_t ipcsum(const void *buf, size_t len) { |
| 7688 | uint32_t sum = csumup(0, buf, len); |
| 7689 | return csumfin(sum); |
| 7690 | } |
| 7691 | |
| 7692 | static size_t ether_output(struct mg_tcpip_if *ifp, size_t len) { |
| 7693 | // size_t min = 64; // Pad short frames to 64 bytes (minimum Ethernet size) |
| 7694 | // if (len < min) memset(ifp->tx.ptr + len, 0, min - len), len = min; |
| 7695 | // mg_hexdump(ifp->tx.ptr, len); |
| 7696 | size_t n = ifp->driver->tx(ifp->tx.ptr, len, ifp); |
| 7697 | if (n == len) ifp->nsent++; |
| 7698 | return n; |
| 7699 | } |
| 7700 | |
| 7701 | static void arp_ask(struct mg_tcpip_if *ifp, uint32_t ip) { |
| 7702 | struct eth *eth = (struct eth *) ifp->tx.ptr; |
| 7703 | struct arp *arp = (struct arp *) (eth + 1); |
| 7704 | memset(eth->dst, 255, sizeof(eth->dst)); |
| 7705 | memcpy(eth->src, ifp->mac, sizeof(eth->src)); |
| 7706 | eth->type = mg_htons(0x806); |
| 7707 | memset(arp, 0, sizeof(*arp)); |
| 7708 | arp->fmt = mg_htons(1), arp->pro = mg_htons(0x800), arp->hlen = 6, |
| 7709 | arp->plen = 4; |
| 7710 | arp->op = mg_htons(1), arp->tpa = ip, arp->spa = ifp->ip; |
| 7711 | memcpy(arp->sha, ifp->mac, sizeof(arp->sha)); |
| 7712 | ether_output(ifp, PDIFF(eth, arp + 1)); |
| 7713 | } |
| 7714 | |
| 7715 | static void onstatechange(struct mg_tcpip_if *ifp) { |
| 7716 | if (ifp->state == MG_TCPIP_STATE_READY) { |
| 7717 | MG_INFO(("READY, IP: %M" , mg_print_ip4, &ifp->ip)); |
| 7718 | MG_INFO((" GW: %M" , mg_print_ip4, &ifp->gw)); |
| 7719 | MG_INFO((" MAC: %M" , mg_print_mac, &ifp->mac)); |
| 7720 | arp_ask(ifp, ifp->gw); |
| 7721 | } else if (ifp->state == MG_TCPIP_STATE_UP) { |
| 7722 | MG_ERROR(("Link up" )); |
| 7723 | srand((unsigned int) mg_millis()); |
| 7724 | } else if (ifp->state == MG_TCPIP_STATE_DOWN) { |
| 7725 | MG_ERROR(("Link down" )); |
| 7726 | } |
| 7727 | } |
| 7728 | |
| 7729 | static struct ip *tx_ip(struct mg_tcpip_if *ifp, uint8_t *mac_dst, |
| 7730 | uint8_t proto, uint32_t ip_src, uint32_t ip_dst, |
| 7731 | size_t plen) { |
| 7732 | struct eth *eth = (struct eth *) ifp->tx.ptr; |
| 7733 | struct ip *ip = (struct ip *) (eth + 1); |
| 7734 | memcpy(eth->dst, mac_dst, sizeof(eth->dst)); |
| 7735 | memcpy(eth->src, ifp->mac, sizeof(eth->src)); // Use our MAC |
| 7736 | eth->type = mg_htons(0x800); |
| 7737 | memset(ip, 0, sizeof(*ip)); |
| 7738 | ip->ver = 0x45; // Version 4, header length 5 words |
| 7739 | ip->frag = 0x40; // Don't fragment |
| 7740 | ip->len = mg_htons((uint16_t) (sizeof(*ip) + plen)); |
| 7741 | ip->ttl = 64; |
| 7742 | ip->proto = proto; |
| 7743 | ip->src = ip_src; |
| 7744 | ip->dst = ip_dst; |
| 7745 | ip->csum = ipcsum(ip, sizeof(*ip)); |
| 7746 | return ip; |
| 7747 | } |
| 7748 | |
| 7749 | static void tx_udp(struct mg_tcpip_if *ifp, uint8_t *mac_dst, uint32_t ip_src, |
| 7750 | uint16_t sport, uint32_t ip_dst, uint16_t dport, |
| 7751 | const void *buf, size_t len) { |
| 7752 | struct ip *ip = |
| 7753 | tx_ip(ifp, mac_dst, 17, ip_src, ip_dst, len + sizeof(struct udp)); |
| 7754 | struct udp *udp = (struct udp *) (ip + 1); |
| 7755 | // MG_DEBUG(("UDP XX LEN %d %d", (int) len, (int) ifp->tx.len)); |
| 7756 | udp->sport = sport; |
| 7757 | udp->dport = dport; |
| 7758 | udp->len = mg_htons((uint16_t) (sizeof(*udp) + len)); |
| 7759 | udp->csum = 0; |
| 7760 | uint32_t cs = csumup(0, udp, sizeof(*udp)); |
| 7761 | cs = csumup(cs, buf, len); |
| 7762 | cs = csumup(cs, &ip->src, sizeof(ip->src)); |
| 7763 | cs = csumup(cs, &ip->dst, sizeof(ip->dst)); |
| 7764 | cs += (uint32_t) (ip->proto + sizeof(*udp) + len); |
| 7765 | udp->csum = csumfin(cs); |
| 7766 | memmove(udp + 1, buf, len); |
| 7767 | // MG_DEBUG(("UDP LEN %d %d", (int) len, (int) ifp->frame_len)); |
| 7768 | ether_output(ifp, sizeof(struct eth) + sizeof(*ip) + sizeof(*udp) + len); |
| 7769 | } |
| 7770 | |
| 7771 | static void tx_dhcp(struct mg_tcpip_if *ifp, uint8_t *mac_dst, uint32_t ip_src, |
| 7772 | uint32_t ip_dst, uint8_t *opts, size_t optslen, |
| 7773 | bool ciaddr) { |
| 7774 | // https://datatracker.ietf.org/doc/html/rfc2132#section-9.6 |
| 7775 | struct dhcp dhcp = {1, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, {0}, 0, {0}}; |
| 7776 | dhcp.magic = mg_htonl(0x63825363); |
| 7777 | memcpy(&dhcp.hwaddr, ifp->mac, sizeof(ifp->mac)); |
| 7778 | memcpy(&dhcp.xid, ifp->mac + 2, sizeof(dhcp.xid)); |
| 7779 | memcpy(&dhcp.options, opts, optslen); |
| 7780 | if (ciaddr) dhcp.ciaddr = ip_src; |
| 7781 | tx_udp(ifp, mac_dst, ip_src, mg_htons(68), ip_dst, mg_htons(67), &dhcp, |
| 7782 | sizeof(dhcp)); |
| 7783 | } |
| 7784 | |
| 7785 | static const uint8_t broadcast[] = {255, 255, 255, 255, 255, 255}; |
| 7786 | |
| 7787 | // RFC-2131 #4.3.6, #4.4.1 |
| 7788 | static void tx_dhcp_request_sel(struct mg_tcpip_if *ifp, uint32_t ip_req, |
| 7789 | uint32_t ip_srv) { |
| 7790 | uint8_t opts[] = { |
| 7791 | 53, 1, 3, // Type: DHCP request |
| 7792 | 55, 2, 1, 3, // GW and mask |
| 7793 | 12, 3, 'm', 'i', 'p', // Host name: "mip" |
| 7794 | 54, 4, 0, 0, 0, 0, // DHCP server ID |
| 7795 | 50, 4, 0, 0, 0, 0, // Requested IP |
| 7796 | 255 // End of options |
| 7797 | }; |
| 7798 | memcpy(opts + 14, &ip_srv, sizeof(ip_srv)); |
| 7799 | memcpy(opts + 20, &ip_req, sizeof(ip_req)); |
| 7800 | tx_dhcp(ifp, (uint8_t *) broadcast, 0, 0xffffffff, opts, sizeof(opts), false); |
| 7801 | MG_DEBUG(("DHCP req sent" )); |
| 7802 | } |
| 7803 | |
| 7804 | // RFC-2131 #4.3.6, #4.4.5 (renewing: unicast, rebinding: bcast) |
| 7805 | static void tx_dhcp_request_re(struct mg_tcpip_if *ifp, uint8_t *mac_dst, |
| 7806 | uint32_t ip_src, uint32_t ip_dst) { |
| 7807 | uint8_t opts[] = { |
| 7808 | 53, 1, 3, // Type: DHCP request |
| 7809 | 255 // End of options |
| 7810 | }; |
| 7811 | tx_dhcp(ifp, mac_dst, ip_src, ip_dst, opts, sizeof(opts), true); |
| 7812 | MG_DEBUG(("DHCP req sent" )); |
| 7813 | } |
| 7814 | |
| 7815 | static void tx_dhcp_discover(struct mg_tcpip_if *ifp) { |
| 7816 | uint8_t opts[] = { |
| 7817 | 53, 1, 1, // Type: DHCP discover |
| 7818 | 55, 2, 1, 3, // Parameters: ip, mask |
| 7819 | 255 // End of options |
| 7820 | }; |
| 7821 | tx_dhcp(ifp, (uint8_t *) broadcast, 0, 0xffffffff, opts, sizeof(opts), false); |
| 7822 | MG_DEBUG(("DHCP discover sent. Our MAC: %M" , mg_print_mac, ifp->mac)); |
| 7823 | } |
| 7824 | |
| 7825 | static struct mg_connection *getpeer(struct mg_mgr *mgr, struct pkt *pkt, |
| 7826 | bool lsn) { |
| 7827 | struct mg_connection *c = NULL; |
| 7828 | for (c = mgr->conns; c != NULL; c = c->next) { |
| 7829 | if (c->is_udp && pkt->udp && c->loc.port == pkt->udp->dport) break; |
| 7830 | if (!c->is_udp && pkt->tcp && c->loc.port == pkt->tcp->dport && |
| 7831 | lsn == c->is_listening && (lsn || c->rem.port == pkt->tcp->sport)) |
| 7832 | break; |
| 7833 | } |
| 7834 | return c; |
| 7835 | } |
| 7836 | |
| 7837 | static void rx_arp(struct mg_tcpip_if *ifp, struct pkt *pkt) { |
| 7838 | if (pkt->arp->op == mg_htons(1) && pkt->arp->tpa == ifp->ip) { |
| 7839 | // ARP request. Make a response, then send |
| 7840 | // MG_DEBUG(("ARP op %d %M: %M", mg_ntohs(pkt->arp->op), mg_print_ip4, |
| 7841 | // &pkt->arp->spa, mg_print_ip4, &pkt->arp->tpa)); |
| 7842 | struct eth *eth = (struct eth *) ifp->tx.ptr; |
| 7843 | struct arp *arp = (struct arp *) (eth + 1); |
| 7844 | memcpy(eth->dst, pkt->eth->src, sizeof(eth->dst)); |
| 7845 | memcpy(eth->src, ifp->mac, sizeof(eth->src)); |
| 7846 | eth->type = mg_htons(0x806); |
| 7847 | *arp = *pkt->arp; |
| 7848 | arp->op = mg_htons(2); |
| 7849 | memcpy(arp->tha, pkt->arp->sha, sizeof(pkt->arp->tha)); |
| 7850 | memcpy(arp->sha, ifp->mac, sizeof(pkt->arp->sha)); |
| 7851 | arp->tpa = pkt->arp->spa; |
| 7852 | arp->spa = ifp->ip; |
| 7853 | MG_DEBUG(("ARP: tell %M we're %M" , mg_print_ip4, &arp->tpa, mg_print_ip4, |
| 7854 | &ifp->ip)); |
| 7855 | ether_output(ifp, PDIFF(eth, arp + 1)); |
| 7856 | } else if (pkt->arp->op == mg_htons(2)) { |
| 7857 | if (memcmp(pkt->arp->tha, ifp->mac, sizeof(pkt->arp->tha)) != 0) return; |
| 7858 | if (pkt->arp->spa == ifp->gw) { |
| 7859 | // Got response for the GW ARP request. Set ifp->gwmac |
| 7860 | memcpy(ifp->gwmac, pkt->arp->sha, sizeof(ifp->gwmac)); |
| 7861 | } else { |
| 7862 | struct mg_connection *c = getpeer(ifp->mgr, pkt, false); |
| 7863 | if (c != NULL && c->is_arplooking) { |
| 7864 | struct connstate *s = (struct connstate *) (c + 1); |
| 7865 | memcpy(s->mac, pkt->arp->sha, sizeof(s->mac)); |
| 7866 | MG_DEBUG(("%lu ARP resolved %M -> %M" , c->id, mg_print_ip4, c->rem.ip, |
| 7867 | mg_print_mac, s->mac)); |
| 7868 | c->is_arplooking = 0; |
| 7869 | } |
| 7870 | } |
| 7871 | } |
| 7872 | } |
| 7873 | |
| 7874 | static void rx_icmp(struct mg_tcpip_if *ifp, struct pkt *pkt) { |
| 7875 | // MG_DEBUG(("ICMP %d", (int) len)); |
| 7876 | if (pkt->icmp->type == 8 && pkt->ip != NULL && pkt->ip->dst == ifp->ip) { |
| 7877 | size_t hlen = sizeof(struct eth) + sizeof(struct ip) + sizeof(struct icmp); |
| 7878 | size_t space = ifp->tx.len - hlen, plen = pkt->pay.len; |
| 7879 | if (plen > space) plen = space; |
| 7880 | struct ip *ip = tx_ip(ifp, pkt->eth->src, 1, ifp->ip, pkt->ip->src, |
| 7881 | sizeof(struct icmp) + plen); |
| 7882 | struct icmp *icmp = (struct icmp *) (ip + 1); |
| 7883 | memset(icmp, 0, sizeof(*icmp)); // Set csum to 0 |
| 7884 | memcpy(icmp + 1, pkt->pay.ptr, plen); // Copy RX payload to TX |
| 7885 | icmp->csum = ipcsum(icmp, sizeof(*icmp) + plen); |
| 7886 | ether_output(ifp, hlen + plen); |
| 7887 | } |
| 7888 | } |
| 7889 | |
| 7890 | static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) { |
| 7891 | uint32_t ip = 0, gw = 0, mask = 0, lease = 0; |
| 7892 | uint8_t msgtype = 0, state = ifp->state; |
| 7893 | // perform size check first, then access fields |
| 7894 | uint8_t *p = pkt->dhcp->options, |
| 7895 | *end = (uint8_t *) &pkt->raw.ptr[pkt->raw.len]; |
| 7896 | if (end < (uint8_t *) (pkt->dhcp + 1)) return; |
| 7897 | if (memcmp(&pkt->dhcp->xid, ifp->mac + 2, sizeof(pkt->dhcp->xid))) return; |
| 7898 | while (p + 1 < end && p[0] != 255) { // Parse options RFC-1533 #9 |
| 7899 | if (p[0] == 1 && p[1] == sizeof(ifp->mask) && p + 6 < end) { // Mask |
| 7900 | memcpy(&mask, p + 2, sizeof(mask)); |
| 7901 | } else if (p[0] == 3 && p[1] == sizeof(ifp->gw) && p + 6 < end) { // GW |
| 7902 | memcpy(&gw, p + 2, sizeof(gw)); |
| 7903 | ip = pkt->dhcp->yiaddr; |
| 7904 | } else if (p[0] == 51 && p[1] == 4 && p + 6 < end) { // Lease |
| 7905 | memcpy(&lease, p + 2, sizeof(lease)); |
| 7906 | lease = mg_ntohl(lease); |
| 7907 | } else if (p[0] == 53 && p[1] == 1 && p + 6 < end) { // Msg Type |
| 7908 | msgtype = p[2]; |
| 7909 | } |
| 7910 | p += p[1] + 2; |
| 7911 | } |
| 7912 | // Process message type, RFC-1533 (9.4); RFC-2131 (3.1, 4) |
| 7913 | if (msgtype == 6 && ifp->ip == ip) { // DHCPNACK, release IP |
| 7914 | ifp->state = MG_TCPIP_STATE_UP, ifp->ip = 0; |
| 7915 | } else if (msgtype == 2 && ifp->state == MG_TCPIP_STATE_UP && ip && gw && |
| 7916 | lease) { // DHCPOFFER |
| 7917 | tx_dhcp_request_sel(ifp, ip, pkt->dhcp->siaddr); // select IP, (4.4.1) |
| 7918 | ifp->state = MG_TCPIP_STATE_REQ; // REQUESTING state |
| 7919 | } else if (msgtype == 5) { // DHCPACK |
| 7920 | if (ifp->state == MG_TCPIP_STATE_REQ && ip && gw && lease) { // got an IP |
| 7921 | ifp->lease_expire = ifp->now + lease * 1000; |
| 7922 | MG_INFO(("Lease: %u sec (%lld)" , lease, ifp->lease_expire / 1000)); |
| 7923 | // assume DHCP server = router until ARP resolves |
| 7924 | memcpy(ifp->gwmac, pkt->eth->src, sizeof(ifp->gwmac)); |
| 7925 | ifp->ip = ip, ifp->gw = gw, ifp->mask = mask; |
| 7926 | ifp->state = MG_TCPIP_STATE_READY; // BOUND state |
| 7927 | uint64_t rand; |
| 7928 | mg_random(&rand, sizeof(rand)); |
| 7929 | srand((unsigned int) (rand + mg_millis())); |
| 7930 | } else if (ifp->state == MG_TCPIP_STATE_READY && ifp->ip == ip) { // renew |
| 7931 | ifp->lease_expire = ifp->now + lease * 1000; |
| 7932 | MG_INFO(("Lease: %u sec (%lld)" , lease, ifp->lease_expire / 1000)); |
| 7933 | } // TODO(): accept provided T1/T2 and store server IP for renewal (4.4) |
| 7934 | } |
| 7935 | if (ifp->state != state) onstatechange(ifp); |
| 7936 | } |
| 7937 | |
| 7938 | // Simple DHCP server that assigns a next IP address: ifp->ip + 1 |
| 7939 | static void rx_dhcp_server(struct mg_tcpip_if *ifp, struct pkt *pkt) { |
| 7940 | uint8_t op = 0, *p = pkt->dhcp->options, |
| 7941 | *end = (uint8_t *) &pkt->raw.ptr[pkt->raw.len]; |
| 7942 | if (end < (uint8_t *) (pkt->dhcp + 1)) return; |
| 7943 | // struct dhcp *req = pkt->dhcp; |
| 7944 | struct dhcp res = {2, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, {0}, 0, {0}}; |
| 7945 | res.yiaddr = ifp->ip; |
| 7946 | ((uint8_t *) (&res.yiaddr))[3]++; // Offer our IP + 1 |
| 7947 | while (p + 1 < end && p[0] != 255) { // Parse options |
| 7948 | if (p[0] == 53 && p[1] == 1 && p + 2 < end) { // Message type |
| 7949 | op = p[2]; |
| 7950 | } |
| 7951 | p += p[1] + 2; |
| 7952 | } |
| 7953 | if (op == 1 || op == 3) { // DHCP Discover or DHCP Request |
| 7954 | uint8_t msg = op == 1 ? 2 : 5; // Message type: DHCP OFFER or DHCP ACK |
| 7955 | uint8_t opts[] = { |
| 7956 | 53, 1, msg, // Message type |
| 7957 | 1, 4, 0, 0, 0, 0, // Subnet mask |
| 7958 | 54, 4, 0, 0, 0, 0, // Server ID |
| 7959 | 12, 3, 'm', 'i', 'p', // Host name: "mip" |
| 7960 | 51, 4, 255, 255, 255, 255, // Lease time |
| 7961 | 255 // End of options |
| 7962 | }; |
| 7963 | memcpy(&res.hwaddr, pkt->dhcp->hwaddr, 6); |
| 7964 | memcpy(opts + 5, &ifp->mask, sizeof(ifp->mask)); |
| 7965 | memcpy(opts + 11, &ifp->ip, sizeof(ifp->ip)); |
| 7966 | memcpy(&res.options, opts, sizeof(opts)); |
| 7967 | res.magic = pkt->dhcp->magic; |
| 7968 | res.xid = pkt->dhcp->xid; |
| 7969 | // memcpy(ifp->gwmac, pkt->eth->src, sizeof(ifp->gwmac)); |
| 7970 | tx_udp(ifp, pkt->eth->src, ifp->ip, mg_htons(67), |
| 7971 | op == 1 ? ~0U : res.yiaddr, mg_htons(68), &res, sizeof(res)); |
| 7972 | } |
| 7973 | } |
| 7974 | |
| 7975 | static void rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) { |
| 7976 | struct mg_connection *c = getpeer(ifp->mgr, pkt, true); |
| 7977 | if (c == NULL) { |
| 7978 | // No UDP listener on this port. Should send ICMP, but keep silent. |
| 7979 | } else { |
| 7980 | c->rem.port = pkt->udp->sport; |
| 7981 | memcpy(c->rem.ip, &pkt->ip->src, sizeof(uint32_t)); |
| 7982 | struct connstate *s = (struct connstate *) (c + 1); |
| 7983 | memcpy(s->mac, pkt->eth->src, sizeof(s->mac)); |
| 7984 | if (c->recv.len >= MG_MAX_RECV_SIZE) { |
| 7985 | mg_error(c, "max_recv_buf_size reached" ); |
| 7986 | } else if (c->recv.size - c->recv.len < pkt->pay.len && |
| 7987 | !mg_iobuf_resize(&c->recv, c->recv.len + pkt->pay.len)) { |
| 7988 | mg_error(c, "oom" ); |
| 7989 | } else { |
| 7990 | memcpy(&c->recv.buf[c->recv.len], pkt->pay.ptr, pkt->pay.len); |
| 7991 | c->recv.len += pkt->pay.len; |
| 7992 | mg_call(c, MG_EV_READ, &pkt->pay.len); |
| 7993 | } |
| 7994 | } |
| 7995 | } |
| 7996 | |
| 7997 | static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *dst_mac, uint32_t dst_ip, |
| 7998 | uint8_t flags, uint16_t sport, uint16_t dport, |
| 7999 | uint32_t seq, uint32_t ack, const void *buf, size_t len) { |
| 8000 | struct ip *ip = |
| 8001 | tx_ip(ifp, dst_mac, 6, ifp->ip, dst_ip, sizeof(struct tcp) + len); |
| 8002 | struct tcp *tcp = (struct tcp *) (ip + 1); |
| 8003 | memset(tcp, 0, sizeof(*tcp)); |
| 8004 | if (buf != NULL && len) memmove(tcp + 1, buf, len); |
| 8005 | tcp->sport = sport; |
| 8006 | tcp->dport = dport; |
| 8007 | tcp->seq = seq; |
| 8008 | tcp->ack = ack; |
| 8009 | tcp->flags = flags; |
| 8010 | tcp->win = mg_htons(8192); |
| 8011 | tcp->off = (uint8_t) (sizeof(*tcp) / 4 << 4); |
| 8012 | uint32_t cs = 0; |
| 8013 | uint16_t n = (uint16_t) (sizeof(*tcp) + len); |
| 8014 | uint8_t pseudo[] = {0, ip->proto, (uint8_t) (n >> 8), (uint8_t) (n & 255)}; |
| 8015 | cs = csumup(cs, tcp, n); |
| 8016 | cs = csumup(cs, &ip->src, sizeof(ip->src)); |
| 8017 | cs = csumup(cs, &ip->dst, sizeof(ip->dst)); |
| 8018 | cs = csumup(cs, pseudo, sizeof(pseudo)); |
| 8019 | tcp->csum = csumfin(cs); |
| 8020 | MG_DEBUG(("TCP %M:%hu -> %M:%hu fl %x len %u" , mg_print_ip4, &ip->src, |
| 8021 | mg_ntohs(tcp->sport), mg_print_ip4, &ip->dst, mg_ntohs(tcp->dport), |
| 8022 | tcp->flags, (int) len)); |
| 8023 | return ether_output(ifp, PDIFF(ifp->tx.ptr, tcp + 1) + len); |
| 8024 | } |
| 8025 | |
| 8026 | static size_t tx_tcp_pkt(struct mg_tcpip_if *ifp, struct pkt *pkt, |
| 8027 | uint8_t flags, uint32_t seq, const void *buf, |
| 8028 | size_t len) { |
| 8029 | uint32_t delta = (pkt->tcp->flags & (TH_SYN | TH_FIN)) ? 1 : 0; |
| 8030 | return tx_tcp(ifp, pkt->eth->src, pkt->ip->src, flags, pkt->tcp->dport, |
| 8031 | pkt->tcp->sport, seq, mg_htonl(mg_ntohl(pkt->tcp->seq) + delta), |
| 8032 | buf, len); |
| 8033 | } |
| 8034 | |
| 8035 | static void settmout(struct mg_connection *c, uint8_t type) { |
| 8036 | struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv; |
| 8037 | struct connstate *s = (struct connstate *) (c + 1); |
| 8038 | unsigned n = type == MIP_TTYPE_ACK ? MIP_TCP_ACK_MS : MIP_TCP_KEEPALIVE_MS; |
| 8039 | s->timer = ifp->now + n; |
| 8040 | s->ttype = type; |
| 8041 | MG_VERBOSE(("%lu %d -> %llx" , c->id, type, s->timer)); |
| 8042 | } |
| 8043 | |
| 8044 | static struct mg_connection *accept_conn(struct mg_connection *lsn, |
| 8045 | struct pkt *pkt) { |
| 8046 | struct mg_connection *c = mg_alloc_conn(lsn->mgr); |
| 8047 | if (c == NULL) { |
| 8048 | MG_ERROR(("OOM" )); |
| 8049 | return NULL; |
| 8050 | } |
| 8051 | struct connstate *s = (struct connstate *) (c + 1); |
| 8052 | s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq); |
| 8053 | memcpy(s->mac, pkt->eth->src, sizeof(s->mac)); |
| 8054 | settmout(c, MIP_TTYPE_KEEPALIVE); |
| 8055 | memcpy(c->rem.ip, &pkt->ip->src, sizeof(uint32_t)); |
| 8056 | c->rem.port = pkt->tcp->sport; |
| 8057 | MG_DEBUG(("%lu accepted %M" , c->id, mg_print_ip_port, &c->rem)); |
| 8058 | LIST_ADD_HEAD(struct mg_connection, &lsn->mgr->conns, c); |
| 8059 | c->is_accepted = 1; |
| 8060 | c->is_hexdumping = lsn->is_hexdumping; |
| 8061 | c->pfn = lsn->pfn; |
| 8062 | c->loc = lsn->loc; |
| 8063 | c->pfn_data = lsn->pfn_data; |
| 8064 | c->fn = lsn->fn; |
| 8065 | c->fn_data = lsn->fn_data; |
| 8066 | mg_call(c, MG_EV_OPEN, NULL); |
| 8067 | mg_call(c, MG_EV_ACCEPT, NULL); |
| 8068 | return c; |
| 8069 | } |
| 8070 | |
| 8071 | long mg_io_send(struct mg_connection *c, const void *buf, size_t len) { |
| 8072 | struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv; |
| 8073 | struct connstate *s = (struct connstate *) (c + 1); |
| 8074 | uint32_t rem_ip; |
| 8075 | memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t)); |
| 8076 | if (c->is_udp) { |
| 8077 | size_t max_headers_len = 14 + 24 /* max IP */ + 8 /* UDP */; |
| 8078 | if (len + max_headers_len > ifp->tx.len) { |
| 8079 | len = ifp->tx.len - max_headers_len; |
| 8080 | } |
| 8081 | tx_udp(ifp, s->mac, ifp->ip, c->loc.port, rem_ip, c->rem.port, buf, len); |
| 8082 | } else { |
| 8083 | size_t max_headers_len = 14 + 24 /* max IP */ + 60 /* max TCP */; |
| 8084 | if (len + max_headers_len > ifp->tx.len) |
| 8085 | len = ifp->tx.len - max_headers_len; |
| 8086 | if (tx_tcp(ifp, s->mac, rem_ip, TH_PUSH | TH_ACK, c->loc.port, c->rem.port, |
| 8087 | mg_htonl(s->seq), mg_htonl(s->ack), buf, len) > 0) { |
| 8088 | s->seq += (uint32_t) len; |
| 8089 | if (s->ttype == MIP_TTYPE_ACK) settmout(c, MIP_TTYPE_KEEPALIVE); |
| 8090 | } else { |
| 8091 | return MG_IO_ERR; |
| 8092 | } |
| 8093 | } |
| 8094 | return (long) len; |
| 8095 | } |
| 8096 | |
| 8097 | long mg_io_recv(struct mg_connection *c, void *buf, size_t len) { |
| 8098 | struct connstate *s = (struct connstate *) (c + 1); |
| 8099 | if (s->raw.len == 0) return MG_IO_WAIT; |
| 8100 | if (len > s->raw.len) len = s->raw.len; |
| 8101 | memcpy(buf, s->raw.buf, len); |
| 8102 | mg_iobuf_del(&s->raw, 0, len); |
| 8103 | MG_DEBUG(("%lu" , len)); |
| 8104 | return (long) len; |
| 8105 | } |
| 8106 | |
| 8107 | static void read_conn(struct mg_connection *c, struct pkt *pkt) { |
| 8108 | struct connstate *s = (struct connstate *) (c + 1); |
| 8109 | struct mg_iobuf *io = c->is_tls ? &s->raw : &c->recv; |
| 8110 | uint32_t seq = mg_ntohl(pkt->tcp->seq); |
| 8111 | s->raw.align = c->recv.align; |
| 8112 | if (pkt->tcp->flags & TH_FIN) { |
| 8113 | s->ack = mg_htonl(pkt->tcp->seq) + 1, s->seq = mg_htonl(pkt->tcp->ack); |
| 8114 | c->is_closing = 1; |
| 8115 | } else if (pkt->pay.len == 0) { |
| 8116 | // TODO(cpq): handle this peer's ACK |
| 8117 | } else if (seq != s->ack) { |
| 8118 | uint32_t ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len); |
| 8119 | if (s->ack == ack) { |
| 8120 | MG_VERBOSE(("ignoring duplicate pkt" )); |
| 8121 | } else { |
| 8122 | // TODO(cpq): peer sent us SEQ which we don't expect. Retransmit |
| 8123 | // rather than close this connection |
| 8124 | mg_error(c, "SEQ != ACK: %x %x %x" , seq, s->ack, ack); |
| 8125 | } |
| 8126 | } else if (io->size - io->len < pkt->pay.len && |
| 8127 | !mg_iobuf_resize(io, io->len + pkt->pay.len)) { |
| 8128 | mg_error(c, "oom" ); |
| 8129 | } else { |
| 8130 | // Copy TCP payload into the IO buffer. If the connection is plain text, |
| 8131 | // we copy to c->recv. If the connection is TLS, this data is encrypted, |
| 8132 | // therefore we copy that encrypted data to the s->raw iobuffer instead, |
| 8133 | // and then call mg_tls_recv() to decrypt it. NOTE: mg_tls_recv() will |
| 8134 | // call back mg_io_recv() which grabs raw data from s->raw |
| 8135 | memcpy(&io->buf[io->len], pkt->pay.ptr, pkt->pay.len); |
| 8136 | io->len += pkt->pay.len; |
| 8137 | |
| 8138 | MG_DEBUG(("%lu SEQ %x -> %x" , c->id, mg_htonl(pkt->tcp->seq), s->ack)); |
| 8139 | // Advance ACK counter |
| 8140 | s->ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len); |
| 8141 | #if 0 |
| 8142 | // Send ACK immediately |
| 8143 | MG_DEBUG((" imm ACK" , c->id, mg_htonl(pkt->tcp->seq), s->ack)); |
| 8144 | tx_tcp((struct mg_tcpip_if *) c->mgr->priv, c->rem.ip, TH_ACK, c->loc.port, |
| 8145 | c->rem.port, mg_htonl(s->seq), mg_htonl(s->ack), "" , 0); |
| 8146 | #else |
| 8147 | // if not already running, setup a timer to send an ACK later |
| 8148 | if (s->ttype != MIP_TTYPE_ACK) settmout(c, MIP_TTYPE_ACK); |
| 8149 | #endif |
| 8150 | |
| 8151 | if (c->is_tls) { |
| 8152 | // TLS connection. Make room for decrypted data in c->recv |
| 8153 | io = &c->recv; |
| 8154 | if (io->size - io->len < pkt->pay.len && |
| 8155 | !mg_iobuf_resize(io, io->len + pkt->pay.len)) { |
| 8156 | mg_error(c, "oom" ); |
| 8157 | } else { |
| 8158 | // Decrypt data directly into c->recv |
| 8159 | long n = mg_tls_recv(c, &io->buf[io->len], io->size - io->len); |
| 8160 | if (n == MG_IO_ERR) { |
| 8161 | mg_error(c, "TLS recv error" ); |
| 8162 | } else if (n > 0) { |
| 8163 | // Decrypted successfully - trigger MG_EV_READ |
| 8164 | io->len += (size_t) n; |
| 8165 | mg_call(c, MG_EV_READ, &n); |
| 8166 | } |
| 8167 | } |
| 8168 | } else { |
| 8169 | // Plain text connection, data is already in c->recv, trigger |
| 8170 | // MG_EV_READ |
| 8171 | mg_call(c, MG_EV_READ, &pkt->pay.len); |
| 8172 | } |
| 8173 | } |
| 8174 | } |
| 8175 | |
| 8176 | static void rx_tcp(struct mg_tcpip_if *ifp, struct pkt *pkt) { |
| 8177 | struct mg_connection *c = getpeer(ifp->mgr, pkt, false); |
| 8178 | struct connstate *s = c == NULL ? NULL : (struct connstate *) (c + 1); |
| 8179 | #if 0 |
| 8180 | MG_INFO(("%lu %hhu %d" , c ? c->id : 0, pkt->tcp->flags, (int) pkt->pay.len)); |
| 8181 | #endif |
| 8182 | if (c != NULL && c->is_connecting && pkt->tcp->flags & (TH_SYN | TH_ACK)) { |
| 8183 | s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq) + 1; |
| 8184 | tx_tcp_pkt(ifp, pkt, TH_ACK, pkt->tcp->ack, NULL, 0); |
| 8185 | c->is_connecting = 0; // Client connected |
| 8186 | settmout(c, MIP_TTYPE_KEEPALIVE); |
| 8187 | mg_call(c, MG_EV_CONNECT, NULL); // Let user know |
| 8188 | } else if (c != NULL && c->is_connecting) { |
| 8189 | tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0); |
| 8190 | } else if (c != NULL && pkt->tcp->flags & TH_RST) { |
| 8191 | mg_error(c, "peer RST" ); // RFC-1122 4.2.2.13 |
| 8192 | } else if (c != NULL) { |
| 8193 | #if 0 |
| 8194 | MG_DEBUG(("%lu %d %M:%hu -> %M:%hu" , c->id, (int) pkt->raw.len, |
| 8195 | mg_print_ip4, &pkt->ip->src, mg_ntohs(pkt->tcp->sport), |
| 8196 | mg_print_ip4, &pkt->ip->dst, mg_ntohs(pkt->tcp->dport))); |
| 8197 | mg_hexdump(pkt->pay.buf, pkt->pay.len); |
| 8198 | #endif |
| 8199 | s->tmiss = 0; // Reset missed keep-alive counter |
| 8200 | if (s->ttype == MIP_TTYPE_KEEPALIVE) // Advance keep-alive timer |
| 8201 | settmout(c, |
| 8202 | MIP_TTYPE_KEEPALIVE); // unless a former ACK timeout is pending |
| 8203 | read_conn(c, pkt); // Override timer with ACK timeout if needed |
| 8204 | } else if ((c = getpeer(ifp->mgr, pkt, true)) == NULL) { |
| 8205 | tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0); |
| 8206 | } else if (pkt->tcp->flags & TH_RST) { |
| 8207 | if (c->is_accepted) mg_error(c, "peer RST" ); // RFC-1122 4.2.2.13 |
| 8208 | // ignore RST if not connected |
| 8209 | } else if (pkt->tcp->flags & TH_SYN) { |
| 8210 | // Use peer's source port as ISN, in order to recognise the handshake |
| 8211 | uint32_t isn = mg_htonl((uint32_t) mg_ntohs(pkt->tcp->sport)); |
| 8212 | tx_tcp_pkt(ifp, pkt, TH_SYN | TH_ACK, isn, NULL, 0); |
| 8213 | } else if (pkt->tcp->flags & TH_FIN) { |
| 8214 | tx_tcp_pkt(ifp, pkt, TH_FIN | TH_ACK, pkt->tcp->ack, NULL, 0); |
| 8215 | } else if (mg_htonl(pkt->tcp->ack) == mg_htons(pkt->tcp->sport) + 1U) { |
| 8216 | accept_conn(c, pkt); |
| 8217 | } else if (!c->is_accepted) { // no peer |
| 8218 | tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0); |
| 8219 | } else { |
| 8220 | // MG_DEBUG(("dropped silently..")); |
| 8221 | } |
| 8222 | } |
| 8223 | |
| 8224 | static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) { |
| 8225 | if (pkt->ip->proto == 1) { |
| 8226 | pkt->icmp = (struct icmp *) (pkt->ip + 1); |
| 8227 | if (pkt->pay.len < sizeof(*pkt->icmp)) return; |
| 8228 | mkpay(pkt, pkt->icmp + 1); |
| 8229 | rx_icmp(ifp, pkt); |
| 8230 | } else if (pkt->ip->proto == 17) { |
| 8231 | pkt->udp = (struct udp *) (pkt->ip + 1); |
| 8232 | if (pkt->pay.len < sizeof(*pkt->udp)) return; |
| 8233 | mkpay(pkt, pkt->udp + 1); |
| 8234 | MG_DEBUG(("UDP %M:%hu -> %M:%hu len %u" , mg_print_ip4, &pkt->ip->src, |
| 8235 | mg_ntohs(pkt->udp->sport), mg_print_ip4, &pkt->ip->dst, |
| 8236 | mg_ntohs(pkt->udp->dport), (int) pkt->pay.len)); |
| 8237 | if (pkt->udp->dport == mg_htons(68)) { |
| 8238 | pkt->dhcp = (struct dhcp *) (pkt->udp + 1); |
| 8239 | mkpay(pkt, pkt->dhcp + 1); |
| 8240 | rx_dhcp_client(ifp, pkt); |
| 8241 | } else if (ifp->enable_dhcp_server && pkt->udp->dport == mg_htons(67)) { |
| 8242 | pkt->dhcp = (struct dhcp *) (pkt->udp + 1); |
| 8243 | mkpay(pkt, pkt->dhcp + 1); |
| 8244 | rx_dhcp_server(ifp, pkt); |
| 8245 | } else { |
| 8246 | rx_udp(ifp, pkt); |
| 8247 | } |
| 8248 | } else if (pkt->ip->proto == 6) { |
| 8249 | pkt->tcp = (struct tcp *) (pkt->ip + 1); |
| 8250 | if (pkt->pay.len < sizeof(*pkt->tcp)) return; |
| 8251 | mkpay(pkt, pkt->tcp + 1); |
| 8252 | uint16_t iplen = mg_ntohs(pkt->ip->len); |
| 8253 | uint16_t off = (uint16_t) (sizeof(*pkt->ip) + ((pkt->tcp->off >> 4) * 4U)); |
| 8254 | if (iplen >= off) pkt->pay.len = (size_t) (iplen - off); |
| 8255 | MG_DEBUG(("TCP %M:%hu -> %M:%hu len %u" , mg_print_ip4, &pkt->ip->src, |
| 8256 | mg_ntohs(pkt->tcp->sport), mg_print_ip4, &pkt->ip->dst, |
| 8257 | mg_ntohs(pkt->tcp->dport), (int) pkt->pay.len)); |
| 8258 | rx_tcp(ifp, pkt); |
| 8259 | } |
| 8260 | } |
| 8261 | |
| 8262 | static void rx_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt) { |
| 8263 | // MG_DEBUG(("IP %d", (int) len)); |
| 8264 | if (pkt->ip6->proto == 1 || pkt->ip6->proto == 58) { |
| 8265 | pkt->icmp = (struct icmp *) (pkt->ip6 + 1); |
| 8266 | if (pkt->pay.len < sizeof(*pkt->icmp)) return; |
| 8267 | mkpay(pkt, pkt->icmp + 1); |
| 8268 | rx_icmp(ifp, pkt); |
| 8269 | } else if (pkt->ip6->proto == 17) { |
| 8270 | pkt->udp = (struct udp *) (pkt->ip6 + 1); |
| 8271 | if (pkt->pay.len < sizeof(*pkt->udp)) return; |
| 8272 | // MG_DEBUG((" UDP %u %u -> %u", len, mg_htons(udp->sport), |
| 8273 | // mg_htons(udp->dport))); |
| 8274 | mkpay(pkt, pkt->udp + 1); |
| 8275 | } |
| 8276 | } |
| 8277 | |
| 8278 | static void mg_tcpip_rx(struct mg_tcpip_if *ifp, void *buf, size_t len) { |
| 8279 | struct pkt pkt; |
| 8280 | memset(&pkt, 0, sizeof(pkt)); |
| 8281 | pkt.raw.ptr = (char *) buf; |
| 8282 | pkt.raw.len = len; |
| 8283 | pkt.eth = (struct eth *) buf; |
| 8284 | if (pkt.raw.len < sizeof(*pkt.eth)) return; // Truncated - runt? |
| 8285 | if (ifp->enable_mac_check && |
| 8286 | memcmp(pkt.eth->dst, ifp->mac, sizeof(pkt.eth->dst)) != 0 && |
| 8287 | memcmp(pkt.eth->dst, broadcast, sizeof(pkt.eth->dst)) != 0) |
| 8288 | return; |
| 8289 | if (ifp->enable_crc32_check && len > 4) { |
| 8290 | len -= 4; // TODO(scaprile): check on bigendian |
| 8291 | uint32_t crc = mg_crc32(0, (const char *) buf, len); |
| 8292 | if (memcmp((void *) ((size_t) buf + len), &crc, sizeof(crc))) return; |
| 8293 | } |
| 8294 | if (pkt.eth->type == mg_htons(0x806)) { |
| 8295 | pkt.arp = (struct arp *) (pkt.eth + 1); |
| 8296 | if (sizeof(*pkt.eth) + sizeof(*pkt.arp) > pkt.raw.len) return; // Truncated |
| 8297 | rx_arp(ifp, &pkt); |
| 8298 | } else if (pkt.eth->type == mg_htons(0x86dd)) { |
| 8299 | pkt.ip6 = (struct ip6 *) (pkt.eth + 1); |
| 8300 | if (pkt.raw.len < sizeof(*pkt.eth) + sizeof(*pkt.ip6)) return; // Truncated |
| 8301 | if ((pkt.ip6->ver >> 4) != 0x6) return; // Not IP |
| 8302 | mkpay(&pkt, pkt.ip6 + 1); |
| 8303 | rx_ip6(ifp, &pkt); |
| 8304 | } else if (pkt.eth->type == mg_htons(0x800)) { |
| 8305 | pkt.ip = (struct ip *) (pkt.eth + 1); |
| 8306 | if (pkt.raw.len < sizeof(*pkt.eth) + sizeof(*pkt.ip)) return; // Truncated |
| 8307 | // Truncate frame to what IP header tells us |
| 8308 | if ((size_t) mg_ntohs(pkt.ip->len) + sizeof(struct eth) < pkt.raw.len) { |
| 8309 | pkt.raw.len = (size_t) mg_ntohs(pkt.ip->len) + sizeof(struct eth); |
| 8310 | } |
| 8311 | if (pkt.raw.len < sizeof(*pkt.eth) + sizeof(*pkt.ip)) return; // Truncated |
| 8312 | if ((pkt.ip->ver >> 4) != 4) return; // Not IP |
| 8313 | mkpay(&pkt, pkt.ip + 1); |
| 8314 | rx_ip(ifp, &pkt); |
| 8315 | } else { |
| 8316 | MG_DEBUG((" Unknown eth type %x" , mg_htons(pkt.eth->type))); |
| 8317 | mg_hexdump(buf, len >= 16 ? 16 : len); |
| 8318 | } |
| 8319 | } |
| 8320 | |
| 8321 | static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t uptime_ms) { |
| 8322 | if (ifp == NULL || ifp->driver == NULL) return; |
| 8323 | bool expired_1000ms = mg_timer_expired(&ifp->timer_1000ms, 1000, uptime_ms); |
| 8324 | ifp->now = uptime_ms; |
| 8325 | |
| 8326 | // Handle physical interface up/down status |
| 8327 | if (expired_1000ms && ifp->driver->up) { |
| 8328 | bool up = ifp->driver->up(ifp); |
| 8329 | bool current = ifp->state != MG_TCPIP_STATE_DOWN; |
| 8330 | if (up != current) { |
| 8331 | ifp->state = up == false ? MG_TCPIP_STATE_DOWN |
| 8332 | : ifp->enable_dhcp_client ? MG_TCPIP_STATE_UP |
| 8333 | : MG_TCPIP_STATE_READY; |
| 8334 | if (!up && ifp->enable_dhcp_client) ifp->ip = 0; |
| 8335 | onstatechange(ifp); |
| 8336 | } |
| 8337 | } |
| 8338 | if (ifp->state == MG_TCPIP_STATE_DOWN) return; |
| 8339 | |
| 8340 | // DHCP RFC-2131 (4.4) |
| 8341 | if (ifp->state == MG_TCPIP_STATE_UP && expired_1000ms) { |
| 8342 | tx_dhcp_discover(ifp); // INIT (4.4.1) |
| 8343 | } else if (expired_1000ms && ifp->state == MG_TCPIP_STATE_READY && |
| 8344 | ifp->lease_expire > 0) { // BOUND / RENEWING / REBINDING |
| 8345 | if (ifp->now >= ifp->lease_expire) { |
| 8346 | ifp->state = MG_TCPIP_STATE_UP, ifp->ip = 0; // expired, release IP |
| 8347 | onstatechange(ifp); |
| 8348 | } else if (ifp->now + 30 * 60 * 1000 > ifp->lease_expire && |
| 8349 | ((ifp->now / 1000) % 60) == 0) { |
| 8350 | // hack: 30 min before deadline, try to rebind (4.3.6) every min |
| 8351 | tx_dhcp_request_re(ifp, (uint8_t *) broadcast, ifp->ip, 0xffffffff); |
| 8352 | } // TODO(): Handle T1 (RENEWING) and T2 (REBINDING) (4.4.5) |
| 8353 | } |
| 8354 | |
| 8355 | // Read data from the network |
| 8356 | if (ifp->driver->rx != NULL) { // Polling driver. We must call it |
| 8357 | size_t len = |
| 8358 | ifp->driver->rx(ifp->recv_queue.buf, ifp->recv_queue.size, ifp); |
| 8359 | if (len > 0) mg_tcpip_rx(ifp, ifp->recv_queue.buf, len); |
| 8360 | } else { // Interrupt-based driver. Fills recv queue itself |
| 8361 | char *buf; |
| 8362 | size_t len = mg_queue_next(&ifp->recv_queue, &buf); |
| 8363 | if (len > 0) { |
| 8364 | mg_tcpip_rx(ifp, buf, len); |
| 8365 | mg_queue_del(&ifp->recv_queue, len); |
| 8366 | } |
| 8367 | } |
| 8368 | |
| 8369 | // Process timeouts |
| 8370 | for (struct mg_connection *c = ifp->mgr->conns; c != NULL; c = c->next) { |
| 8371 | if (c->is_udp || c->is_listening) continue; |
| 8372 | if (c->is_connecting || c->is_resolving) continue; |
| 8373 | struct connstate *s = (struct connstate *) (c + 1); |
| 8374 | uint32_t rem_ip; |
| 8375 | memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t)); |
| 8376 | if (uptime_ms > s->timer) { |
| 8377 | if (s->ttype == MIP_TTYPE_ACK) { |
| 8378 | MG_DEBUG(("%lu ack %x %x" , c->id, s->seq, s->ack)); |
| 8379 | tx_tcp(ifp, s->mac, rem_ip, TH_ACK, c->loc.port, c->rem.port, |
| 8380 | mg_htonl(s->seq), mg_htonl(s->ack), "" , 0); |
| 8381 | } else { |
| 8382 | if (s->tmiss++ > 2) { |
| 8383 | mg_error(c, "keepalive" ); |
| 8384 | } else { |
| 8385 | MG_DEBUG(("%lu keepalive" , c->id)); |
| 8386 | tx_tcp(ifp, s->mac, rem_ip, TH_ACK, c->loc.port, c->rem.port, |
| 8387 | mg_htonl(s->seq - 1), mg_htonl(s->ack), "" , 0); |
| 8388 | } |
| 8389 | } |
| 8390 | settmout(c, MIP_TTYPE_KEEPALIVE); |
| 8391 | } |
| 8392 | } |
| 8393 | } |
| 8394 | |
| 8395 | // This function executes in interrupt context, thus it should copy data |
| 8396 | // somewhere fast. Note that newlib's malloc is not thread safe, thus use |
| 8397 | // our lock-free queue with preallocated buffer to copy data and return asap |
| 8398 | void mg_tcpip_qwrite(void *buf, size_t len, struct mg_tcpip_if *ifp) { |
| 8399 | char *p; |
| 8400 | if (mg_queue_book(&ifp->recv_queue, &p, len) >= len) { |
| 8401 | memcpy(p, buf, len); |
| 8402 | mg_queue_add(&ifp->recv_queue, len); |
| 8403 | ifp->nrecv++; |
| 8404 | } else { |
| 8405 | ifp->ndrop++; |
| 8406 | } |
| 8407 | } |
| 8408 | |
| 8409 | void mg_tcpip_init(struct mg_mgr *mgr, struct mg_tcpip_if *ifp) { |
| 8410 | // If MAC address is not set, make a random one |
| 8411 | if (ifp->mac[0] == 0 && ifp->mac[1] == 0 && ifp->mac[2] == 0 && |
| 8412 | ifp->mac[3] == 0 && ifp->mac[4] == 0 && ifp->mac[5] == 0) { |
| 8413 | ifp->mac[0] = 0x02; // Locally administered, unicast |
| 8414 | mg_random(&ifp->mac[1], sizeof(ifp->mac) - 1); |
| 8415 | MG_INFO(("MAC not set. Generated random: %M" , mg_print_mac, ifp->mac)); |
| 8416 | } |
| 8417 | |
| 8418 | if (ifp->driver->init && !ifp->driver->init(ifp)) { |
| 8419 | MG_ERROR(("driver init failed" )); |
| 8420 | } else { |
| 8421 | size_t framesize = 1540; |
| 8422 | ifp->tx.ptr = (char *) calloc(1, framesize), ifp->tx.len = framesize; |
| 8423 | if (ifp->recv_queue.size == 0) |
| 8424 | ifp->recv_queue.size = ifp->driver->rx ? framesize : 8192; |
| 8425 | ifp->recv_queue.buf = (char *) calloc(1, ifp->recv_queue.size); |
| 8426 | ifp->timer_1000ms = mg_millis(); |
| 8427 | mgr->priv = ifp; |
| 8428 | ifp->mgr = mgr; |
| 8429 | mgr->extraconnsize = sizeof(struct connstate); |
| 8430 | if (ifp->ip == 0) ifp->enable_dhcp_client = true; |
| 8431 | memset(ifp->gwmac, 255, sizeof(ifp->gwmac)); // Set to broadcast |
| 8432 | mg_random(&ifp->eport, sizeof(ifp->eport)); // Random from 0 to 65535 |
| 8433 | ifp->eport |= MG_EPHEMERAL_PORT_BASE; // Random from |
| 8434 | // MG_EPHEMERAL_PORT_BASE to 65535 |
| 8435 | if (ifp->tx.ptr == NULL || ifp->recv_queue.buf == NULL) MG_ERROR(("OOM" )); |
| 8436 | } |
| 8437 | } |
| 8438 | |
| 8439 | void mg_tcpip_free(struct mg_tcpip_if *ifp) { |
| 8440 | free(ifp->recv_queue.buf); |
| 8441 | free((char *) ifp->tx.ptr); |
| 8442 | } |
| 8443 | |
| 8444 | int mg_mkpipe(struct mg_mgr *m, mg_event_handler_t fn, void *d, bool udp) { |
| 8445 | (void) m, (void) fn, (void) d, (void) udp; |
| 8446 | MG_ERROR(("Not implemented" )); |
| 8447 | return -1; |
| 8448 | } |
| 8449 | |
| 8450 | static void send_syn(struct mg_connection *c) { |
| 8451 | struct connstate *s = (struct connstate *) (c + 1); |
| 8452 | uint32_t isn = mg_htonl((uint32_t) mg_ntohs(c->loc.port)); |
| 8453 | struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv; |
| 8454 | uint32_t rem_ip; |
| 8455 | memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t)); |
| 8456 | tx_tcp(ifp, s->mac, rem_ip, TH_SYN, c->loc.port, c->rem.port, isn, 0, NULL, |
| 8457 | 0); |
| 8458 | } |
| 8459 | |
| 8460 | void mg_connect_resolved(struct mg_connection *c) { |
| 8461 | struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv; |
| 8462 | uint32_t rem_ip; |
| 8463 | memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t)); |
| 8464 | c->is_resolving = 0; |
| 8465 | if (ifp->eport < MG_EPHEMERAL_PORT_BASE) ifp->eport = MG_EPHEMERAL_PORT_BASE; |
| 8466 | memcpy(c->loc.ip, &ifp->ip, sizeof(uint32_t)); |
| 8467 | c->loc.port = mg_htons(ifp->eport++); |
| 8468 | MG_DEBUG(("%lu %M -> %M" , c->id, mg_print_ip_port, &c->loc, mg_print_ip_port, |
| 8469 | &c->rem)); |
| 8470 | mg_call(c, MG_EV_RESOLVE, NULL); |
| 8471 | if (((rem_ip & ifp->mask) == (ifp->ip & ifp->mask))) { |
| 8472 | // If we're in the same LAN, fire an ARP lookup. TODO(cpq): handle this! |
| 8473 | MG_DEBUG(("%lu ARP lookup..." , c->id)); |
| 8474 | arp_ask(ifp, rem_ip); |
| 8475 | c->is_arplooking = 1; |
| 8476 | } else if (rem_ip == (ifp->ip | ~ifp->mask)) { |
| 8477 | struct connstate *s = (struct connstate *) (c + 1); |
| 8478 | memset(s->mac, 0xFF, sizeof(s->mac)); // local broadcast |
| 8479 | } else if ((*((uint8_t *) &rem_ip) & 0xE0) == 0xE0) { |
| 8480 | struct connstate *s = (struct connstate *) (c + 1); // 224 to 239, E0 to EF |
| 8481 | uint8_t mcastp[3] = {0x01, 0x00, 0x5E}; // multicast group |
| 8482 | memcpy(s->mac, mcastp, 3); |
| 8483 | memcpy(s->mac + 3, ((uint8_t *) &rem_ip) + 1, 3); // 23 LSb |
| 8484 | s->mac[3] &= 0x7F; |
| 8485 | } else { |
| 8486 | struct connstate *s = (struct connstate *) (c + 1); |
| 8487 | memcpy(s->mac, ifp->gwmac, sizeof(ifp->gwmac)); |
| 8488 | if (c->is_udp) { |
| 8489 | mg_call(c, MG_EV_CONNECT, NULL); |
| 8490 | } else { |
| 8491 | send_syn(c); |
| 8492 | c->is_connecting = 1; |
| 8493 | } |
| 8494 | } |
| 8495 | } |
| 8496 | |
| 8497 | bool mg_open_listener(struct mg_connection *c, const char *url) { |
| 8498 | c->loc.port = mg_htons(mg_url_port(url)); |
| 8499 | return true; |
| 8500 | } |
| 8501 | |
| 8502 | static void write_conn(struct mg_connection *c) { |
| 8503 | long len = c->is_tls ? mg_tls_send(c, c->send.buf, c->send.len) |
| 8504 | : mg_io_send(c, c->send.buf, c->send.len); |
| 8505 | if (len > 0) { |
| 8506 | mg_iobuf_del(&c->send, 0, (size_t) len); |
| 8507 | mg_call(c, MG_EV_WRITE, &len); |
| 8508 | } |
| 8509 | } |
| 8510 | |
| 8511 | static void close_conn(struct mg_connection *c) { |
| 8512 | struct connstate *s = (struct connstate *) (c + 1); |
| 8513 | uint32_t rem_ip; |
| 8514 | memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t)); |
| 8515 | mg_iobuf_free(&s->raw); // For TLS connections, release raw data |
| 8516 | if (c->is_udp == false && c->is_listening == false) { // For TCP conns, |
| 8517 | struct mg_tcpip_if *ifp = |
| 8518 | (struct mg_tcpip_if *) c->mgr->priv; // send TCP FIN |
| 8519 | tx_tcp(ifp, s->mac, rem_ip, TH_FIN | TH_ACK, c->loc.port, c->rem.port, |
| 8520 | mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0); |
| 8521 | } |
| 8522 | mg_close_conn(c); |
| 8523 | } |
| 8524 | |
| 8525 | static bool can_write(struct mg_connection *c) { |
| 8526 | return c->is_connecting == 0 && c->is_resolving == 0 && c->send.len > 0 && |
| 8527 | c->is_tls_hs == 0 && c->is_arplooking == 0; |
| 8528 | } |
| 8529 | |
| 8530 | void mg_mgr_poll(struct mg_mgr *mgr, int ms) { |
| 8531 | struct mg_connection *c, *tmp; |
| 8532 | uint64_t now = mg_millis(); |
| 8533 | mg_tcpip_poll((struct mg_tcpip_if *) mgr->priv, now); |
| 8534 | mg_timer_poll(&mgr->timers, now); |
| 8535 | for (c = mgr->conns; c != NULL; c = tmp) { |
| 8536 | tmp = c->next; |
| 8537 | mg_call(c, MG_EV_POLL, &now); |
| 8538 | MG_VERBOSE(("%lu .. %c%c%c%c%c" , c->id, c->is_tls ? 'T' : 't', |
| 8539 | c->is_connecting ? 'C' : 'c', c->is_tls_hs ? 'H' : 'h', |
| 8540 | c->is_resolving ? 'R' : 'r', c->is_closing ? 'C' : 'c')); |
| 8541 | if (c->is_tls_hs) mg_tls_handshake(c); |
| 8542 | if (can_write(c)) write_conn(c); |
| 8543 | if (c->is_draining && c->send.len == 0) c->is_closing = 1; |
| 8544 | if (c->is_closing) close_conn(c); |
| 8545 | } |
| 8546 | (void) ms; |
| 8547 | } |
| 8548 | |
| 8549 | bool mg_send(struct mg_connection *c, const void *buf, size_t len) { |
| 8550 | struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv; |
| 8551 | bool res = false; |
| 8552 | uint32_t rem_ip; |
| 8553 | memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t)); |
| 8554 | if (ifp->ip == 0 || ifp->state != MG_TCPIP_STATE_READY) { |
| 8555 | mg_error(c, "net down" ); |
| 8556 | } else if (c->is_udp) { |
| 8557 | struct connstate *s = (struct connstate *) (c + 1); |
| 8558 | tx_udp(ifp, s->mac, ifp->ip, c->loc.port, rem_ip, c->rem.port, buf, len); |
| 8559 | res = true; |
| 8560 | } else { |
| 8561 | res = mg_iobuf_add(&c->send, c->send.len, buf, len); |
| 8562 | } |
| 8563 | return res; |
| 8564 | } |
| 8565 | #endif // MG_ENABLE_TCPIP |
| 8566 | |