mirror of
https://github.com/cesanta/mongoose.git
synced 2024-12-27 15:01:03 +08:00
Add IPv6 client test
This commit is contained in:
parent
fbab767b9f
commit
2e87783246
2
Makefile
2
Makefile
@ -71,7 +71,7 @@ vc98: Makefile mongoose.c mongoose.h test/unit_test.c
|
||||
$(VC98) wine cl mongoose.c test/unit_test.c $(VCFLAGS) ws2_32.lib /Fe$@.exe
|
||||
$(VC98) wine $@.exe
|
||||
|
||||
vc2017: CFLAGS += -DMG_ENABLE_IPV6=1
|
||||
#vc2017: VCFLAGS += -DMG_ENABLE_IPV6=1
|
||||
vc2017: Makefile mongoose.c mongoose.h test/unit_test.c
|
||||
$(VC2017) wine64 cl mongoose.c test/unit_test.c $(VCFLAGS) ws2_32.lib /Fe$@.exe
|
||||
$(VC2017) wine64 $@.exe
|
||||
|
308
mongoose.c
308
mongoose.c
@ -147,17 +147,21 @@ struct dns_data {
|
||||
uint16_t txnid;
|
||||
};
|
||||
|
||||
static void mg_dns_free(struct dns_data **head, struct dns_data *d) {
|
||||
LIST_DELETE(struct dns_data, head, d);
|
||||
static struct dns_data *s_reqs; // Active DNS requests
|
||||
|
||||
static void mg_sendnsreq(struct mg_connection *, struct mg_str *, int,
|
||||
struct mg_dns *, bool);
|
||||
|
||||
static void mg_dns_free(struct dns_data *d) {
|
||||
LIST_DELETE(struct dns_data, &s_reqs, d);
|
||||
free(d);
|
||||
}
|
||||
|
||||
void mg_resolve_cancel(struct mg_mgr *mgr, struct mg_connection *c) {
|
||||
struct dns_data *tmp, *d, **head;
|
||||
head = mgr->dnsc == NULL ? NULL : (struct dns_data **) &mgr->dnsc->pfn_data;
|
||||
for (d = head == NULL ? NULL : *head; d != NULL; d = tmp) {
|
||||
void mg_resolve_cancel(struct mg_connection *c) {
|
||||
struct dns_data *tmp, *d;
|
||||
for (d = s_reqs; d != NULL; d = tmp) {
|
||||
tmp = d->next;
|
||||
if (d->c == c) mg_dns_free(head, d);
|
||||
if (d->c == c) mg_dns_free(d);
|
||||
}
|
||||
}
|
||||
|
||||
@ -219,11 +223,16 @@ bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *dm) {
|
||||
atype = ((int) s[j - 8] << 8) | s[j - 7];
|
||||
aclass = ((int) s[j - 6] << 8) | s[j - 5];
|
||||
n = ((int) s[j] << 8) | s[j + 1];
|
||||
// LOG(LL_DEBUG,
|
||||
//("NAME %s, IP len %zu t: %hu, c: %hu", dm->name, n, atype, aclass));
|
||||
LOG(LL_DEBUG, ("%s %d %hu %hu", dm->name, (int) n, atype, aclass));
|
||||
if (&s[j] + 2 + n > e) break;
|
||||
if (n == 4 && atype == 1 && aclass == 1) {
|
||||
memcpy(&dm->ipaddr, &s[j + 2], 4);
|
||||
dm->addr.is_ip6 = false;
|
||||
memcpy(&dm->addr.ip, &s[j + 2], n);
|
||||
dm->resolved = true;
|
||||
break; // Return success
|
||||
} else if (n == 16 && atype == 28 && aclass == 1) {
|
||||
dm->addr.is_ip6 = true;
|
||||
memcpy(&dm->addr.ip6, &s[j + 2], n);
|
||||
dm->resolved = true;
|
||||
break; // Return success
|
||||
}
|
||||
@ -234,7 +243,7 @@ bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *dm) {
|
||||
|
||||
static void dns_cb(struct mg_connection *c, int ev, void *ev_data,
|
||||
void *fn_data) {
|
||||
struct dns_data *d, *tmp, **head = (struct dns_data **) &c->pfn_data;
|
||||
struct dns_data *d, *tmp;
|
||||
if (ev == MG_EV_POLL) {
|
||||
unsigned long now = *(unsigned long *) ev_data;
|
||||
for (d = (struct dns_data *) fn_data; d != NULL; d = tmp) {
|
||||
@ -250,37 +259,44 @@ static void dns_cb(struct mg_connection *c, int ev, void *ev_data,
|
||||
LOG(LL_ERROR, ("Unexpected DNS response:\n%s\n", s));
|
||||
free(s);
|
||||
} else {
|
||||
for (d = (struct dns_data *) c->pfn_data; d != NULL; d = tmp) {
|
||||
LOG(LL_DEBUG, ("%s %d", dm.name, dm.resolved));
|
||||
for (d = s_reqs; d != NULL; d = tmp) {
|
||||
tmp = d->next;
|
||||
// LOG(LL_INFO, ("d %p %hu %hu", d, d->txnid, dm.txnid));
|
||||
if (dm.txnid != d->txnid) continue;
|
||||
if (d->c->is_resolving) {
|
||||
d->c->is_resolving = 0;
|
||||
if (dm.resolved) {
|
||||
d->c->peer.ip = dm.ipaddr;
|
||||
dm.addr.port = d->c->peer.port; // Save port
|
||||
d->c->peer = dm.addr; // Copy resolved address
|
||||
mg_connect_resolved(d->c);
|
||||
#if MG_ENABLE_IPV6
|
||||
} else if (dm.addr.is_ip6 == false && dm.name[0] != '\0') {
|
||||
struct mg_str x = mg_str(dm.name);
|
||||
mg_sendnsreq(d->c, &x, c->mgr->dnstimeout, &c->mgr->dns6, true);
|
||||
#endif
|
||||
} else {
|
||||
mg_error(d->c, "%s DNS lookup failed", dm.name);
|
||||
}
|
||||
} else {
|
||||
LOG(LL_ERROR, ("%lu already resolved", d->c->id));
|
||||
}
|
||||
mg_dns_free(head, d);
|
||||
mg_dns_free(d);
|
||||
resolved = 1;
|
||||
}
|
||||
}
|
||||
if (!resolved) LOG(LL_ERROR, ("stray DNS reply"));
|
||||
c->recv.len = 0;
|
||||
} else if (ev == MG_EV_CLOSE) {
|
||||
for (d = *head; d != NULL; d = tmp) {
|
||||
for (d = s_reqs; d != NULL; d = tmp) {
|
||||
tmp = d->next;
|
||||
mg_dns_free(head, d);
|
||||
mg_dns_free(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mg_dns_send(struct mg_connection *c, const struct mg_str *name,
|
||||
uint16_t txnid) {
|
||||
uint16_t txnid, bool ipv6) {
|
||||
struct {
|
||||
struct mg_dns_header header;
|
||||
uint8_t data[256];
|
||||
@ -289,7 +305,7 @@ void mg_dns_send(struct mg_connection *c, const struct mg_str *name,
|
||||
memset(&pkt, 0, sizeof(pkt));
|
||||
pkt.header.transaction_id = mg_htons(txnid);
|
||||
pkt.header.flags = mg_htons(0x100);
|
||||
pkt.header.num_questions = mg_htons(2);
|
||||
pkt.header.num_questions = mg_htons(1);
|
||||
for (i = n = 0; i < sizeof(pkt.data) - 5; i++) {
|
||||
if (name->ptr[i] == '.' || i >= name->len) {
|
||||
pkt.data[n] = (uint8_t)(i - n);
|
||||
@ -300,8 +316,9 @@ void mg_dns_send(struct mg_connection *c, const struct mg_str *name,
|
||||
}
|
||||
memcpy(&pkt.data[n], "\x00\x00\x01\x00\x01", 5); // A query
|
||||
n += 5;
|
||||
memcpy(&pkt.data[n], "\xc0\x0c\x00\x1c\x00\x01", 6); // AAAA query
|
||||
n += 6;
|
||||
if (ipv6) pkt.data[n - 3] = 0x1c; // AAAA query
|
||||
// memcpy(&pkt.data[n], "\xc0\x0c\x00\x1c\x00\x01", 6); // AAAA query
|
||||
// n += 6;
|
||||
mg_send(c, &pkt, sizeof(pkt.header) + n);
|
||||
#if 0
|
||||
// Immediately after A query, send AAAA query. Whatever reply comes first,
|
||||
@ -312,39 +329,43 @@ void mg_dns_send(struct mg_connection *c, const struct mg_str *name,
|
||||
#endif
|
||||
}
|
||||
|
||||
void mg_resolve(struct mg_mgr *mgr, struct mg_connection *c,
|
||||
struct mg_str *name, int ms) {
|
||||
static void mg_sendnsreq(struct mg_connection *c, struct mg_str *name, int ms,
|
||||
struct mg_dns *dnsc, bool ipv6) {
|
||||
struct dns_data *d = NULL;
|
||||
if (dnsc->url == NULL) {
|
||||
mg_error(c, "DNS server URL is NULL. Call mg_mgr_init()");
|
||||
} else if (dnsc->c == NULL) {
|
||||
dnsc->c = mg_connect(c->mgr, dnsc->url, NULL, NULL);
|
||||
if (dnsc->c != NULL) {
|
||||
dnsc->c->pfn = dns_cb;
|
||||
snprintf(dnsc->c->label, sizeof(dnsc->c->label), "%s", "DNS");
|
||||
// dnsc->c->is_hexdumping = 1;
|
||||
}
|
||||
}
|
||||
if (dnsc->c == NULL) {
|
||||
mg_error(c, "resolver");
|
||||
} else if ((d = (struct dns_data *) calloc(1, sizeof(*d))) == NULL) {
|
||||
mg_error(c, "resolve OOM");
|
||||
} else {
|
||||
d->txnid = s_reqs ? s_reqs->txnid + 1 : 1;
|
||||
d->next = s_reqs;
|
||||
s_reqs = d;
|
||||
d->expire = mg_millis() + ms;
|
||||
d->c = c;
|
||||
c->is_resolving = 1;
|
||||
LOG(LL_DEBUG, ("%lu resolving %.*s, txnid %hu", c->id, (int) name->len,
|
||||
name->ptr, d->txnid));
|
||||
mg_dns_send(dnsc->c, name, d->txnid, ipv6);
|
||||
}
|
||||
}
|
||||
|
||||
void mg_resolve(struct mg_connection *c, struct mg_str *name, int ms) {
|
||||
if (mg_aton(*name, &c->peer)) {
|
||||
// name is an IP address, do not fire name resolution
|
||||
mg_connect_resolved(c);
|
||||
} else {
|
||||
// name is not an IP, send DNS resolution request
|
||||
if (mgr->dnsc == NULL) {
|
||||
const char *srv = mgr->dnsserver ? mgr->dnsserver : "udp://8.8.8.8:53";
|
||||
mgr->dnsc = mg_connect(mgr, srv, NULL, NULL);
|
||||
if (mgr->dnsc != NULL) {
|
||||
mgr->dnsc->pfn = dns_cb;
|
||||
// mgr->dnsc->is_hexdumping = 1;
|
||||
snprintf(mgr->dnsc->label, sizeof(mgr->dnsc->label), "%s", "RESOLVER");
|
||||
}
|
||||
}
|
||||
if (mgr->dnsc == NULL) {
|
||||
mg_error(c, "resolver");
|
||||
} else if ((d = (struct dns_data *) calloc(1, sizeof(*d))) == NULL) {
|
||||
mg_error(c, "resolve OOM");
|
||||
} else {
|
||||
struct dns_data **head = (struct dns_data **) &mgr->dnsc->pfn_data;
|
||||
d->txnid = *head ? (*head)->txnid + 1 : 1;
|
||||
d->next = *head;
|
||||
*head = d;
|
||||
d->expire = mg_millis() + ms;
|
||||
d->c = c;
|
||||
c->is_resolving = 1;
|
||||
LOG(LL_DEBUG, ("%lu resolving %.*s, txnid %hu", c->id, (int) name->len,
|
||||
name->ptr, d->txnid));
|
||||
mg_dns_send(mgr->dnsc, name, d->txnid);
|
||||
}
|
||||
mg_sendnsreq(c, name, ms, &c->mgr->dns4, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1929,9 +1950,18 @@ int mg_printf(struct mg_connection *c, const char *fmt, ...) {
|
||||
}
|
||||
|
||||
char *mg_straddr(struct mg_connection *c, char *buf, size_t len) {
|
||||
unsigned char *p = (unsigned char *) &c->peer.ip;
|
||||
snprintf(buf, len, "%d.%d.%d.%d:%hu", p[0], p[1], p[2], p[3],
|
||||
mg_ntohs(c->peer.port));
|
||||
if (c->peer.is_ip6) {
|
||||
uint16_t *p = (uint16_t *) c->peer.ip6;
|
||||
snprintf(buf, len, "[%x:%x:%x:%x:%x:%x:%x:%x]:%hu", mg_htons(p[0]),
|
||||
mg_htons(p[1]), mg_htons(p[2]), mg_htons(p[3]), mg_htons(p[4]),
|
||||
mg_htons(p[5]), mg_htons(p[6]), mg_htons(p[7]),
|
||||
mg_ntohs(c->peer.port));
|
||||
|
||||
} else {
|
||||
unsigned char *p = (unsigned char *) &c->peer.ip;
|
||||
snprintf(buf, len, "%d.%d.%d.%d:%hu", p[0], p[1], p[2], p[3],
|
||||
mg_ntohs(c->peer.port));
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
@ -1942,35 +1972,73 @@ char *mg_ntoa(const struct mg_addr *addr, char *buf, size_t len) {
|
||||
return buf;
|
||||
}
|
||||
|
||||
static bool mg_atonl(struct mg_str str, struct mg_addr *addr) {
|
||||
if (mg_casecmp(str.ptr, "localhost") != 0) return false;
|
||||
addr->ip = mg_htonl(0x7f000001);
|
||||
addr->is_ip6 = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool mg_aton4(struct mg_str str, struct mg_addr *addr) {
|
||||
uint8_t data[4] = {0, 0, 0, 0};
|
||||
size_t i, num_dots = 0;
|
||||
for (i = 0; i < str.len; i++) {
|
||||
if (str.ptr[i] >= '0' && str.ptr[i] <= '9') {
|
||||
int octet = data[num_dots] * 10 + (str.ptr[i] - '0');
|
||||
if (octet > 255) return false;
|
||||
data[num_dots] = octet;
|
||||
} else if (str.ptr[i] == '.') {
|
||||
if (num_dots >= 3 || i == 0 || str.ptr[i - 1] == '.') return false;
|
||||
num_dots++;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (num_dots != 3 || str.ptr[i - 1] == '.') return false;
|
||||
memcpy(&addr->ip, data, sizeof(data));
|
||||
addr->is_ip6 = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool mg_aton6(struct mg_str str, struct mg_addr *addr) {
|
||||
size_t i, j = 0, n = 0, dc = 42;
|
||||
for (i = 0; i < str.len; i++) {
|
||||
if ((str.ptr[i] >= '0' && str.ptr[i] <= '9') ||
|
||||
(str.ptr[i] >= 'a' && str.ptr[i] <= 'f') ||
|
||||
(str.ptr[i] >= 'A' && str.ptr[i] <= 'F')) {
|
||||
unsigned long val;
|
||||
if (i > j + 3) return false;
|
||||
// LOG(LL_DEBUG, ("%zu %zu [%.*s]", i, j, (int) (i - j + 1),
|
||||
// &str.ptr[j]));
|
||||
val = mg_unhexn(&str.ptr[j], i - j + 1);
|
||||
addr->ip6[n] = (uint8_t)((val >> 8) & 255);
|
||||
addr->ip6[n + 1] = (uint8_t)(val & 255);
|
||||
} else if (str.ptr[i] == ':') {
|
||||
j = i + 1;
|
||||
if (i > 0 && str.ptr[i - 1] == ':') {
|
||||
dc = n; // Double colon
|
||||
if (i > 1 && str.ptr[i - 2] == ':') return false;
|
||||
} else if (i > 0) {
|
||||
n += 2;
|
||||
}
|
||||
if (n > 14) return false;
|
||||
addr->ip6[n] = addr->ip6[n + 1] = 0; // For trailing ::
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (n < 14 && dc == 42) return false;
|
||||
if (n < 14) {
|
||||
memmove(&addr->ip6[dc + (14 - n)], &addr->ip6[dc], n - dc + 2);
|
||||
memset(&addr->ip6[dc], 0, 14 - n);
|
||||
}
|
||||
addr->is_ip6 = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mg_aton(struct mg_str str, struct mg_addr *addr) {
|
||||
// LOG(LL_INFO, ("[%.*s]", (int) str.len, str.ptr));
|
||||
if (!mg_casecmp(str.ptr, "localhost")) {
|
||||
addr->ip = mg_htonl(0x7f000001);
|
||||
return true;
|
||||
} else if (addr->is_ip6) {
|
||||
return false;
|
||||
} else {
|
||||
uint8_t data[4] = {0, 0, 0, 0};
|
||||
size_t i, num_octets = 0;
|
||||
// LOG(LL_DEBUG, ("[%.*s]", (int) str.len, str.ptr));
|
||||
for (i = 0; i < str.len; i++) {
|
||||
// LOG(LL_DEBUG,
|
||||
//(" %c %zu %hhu %zu", str.ptr[i], i, data[num_octets], num_octets));
|
||||
if (str.ptr[i] >= '0' && str.ptr[i] <= '9') {
|
||||
int octet = data[num_octets] * 10 + (str.ptr[i] - '0');
|
||||
if (octet > 255) return false;
|
||||
data[num_octets] = octet;
|
||||
} else if (str.ptr[i] == '.') {
|
||||
if (num_octets >= 3 || i == 0 || str.ptr[i - 1] == '.') return false;
|
||||
num_octets++;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (num_octets != 3 || str.ptr[i - 1] == '.') return false;
|
||||
memcpy(&addr->ip, data, sizeof(data));
|
||||
return true;
|
||||
}
|
||||
return mg_atonl(str, addr) || mg_aton4(str, addr) || mg_aton6(str, addr);
|
||||
}
|
||||
|
||||
void mg_mgr_free(struct mg_mgr *mgr) {
|
||||
@ -1996,6 +2064,8 @@ void mg_mgr_init(struct mg_mgr *mgr) {
|
||||
#endif
|
||||
memset(mgr, 0, sizeof(*mgr));
|
||||
mgr->dnstimeout = 3000;
|
||||
mgr->dns4.url = "udp://8.8.8.8:53";
|
||||
mgr->dns6.url = "udp://[2001:4860:4860::8888]:53";
|
||||
}
|
||||
|
||||
#ifdef MG_ENABLE_LINES
|
||||
@ -2381,6 +2451,13 @@ static union usa tousa(struct mg_addr *a) {
|
||||
usa.sin.sin_family = AF_INET;
|
||||
usa.sin.sin_port = a->port;
|
||||
*(uint32_t *) &usa.sin.sin_addr = a->ip;
|
||||
#if MG_ENABLE_IPV6
|
||||
if (a->is_ip6) {
|
||||
usa.sin.sin_family = AF_INET6;
|
||||
usa.sin6.sin6_port = a->port;
|
||||
memcpy(&usa.sin6.sin6_addr, a->ip6, sizeof(a->ip6));
|
||||
}
|
||||
#endif
|
||||
return usa;
|
||||
}
|
||||
|
||||
@ -2414,10 +2491,20 @@ static int mg_sock_recv(struct mg_connection *c, void *buf, int len,
|
||||
if (c->is_udp) {
|
||||
union usa usa;
|
||||
socklen_t slen = sizeof(usa.sin);
|
||||
#if MG_ENABLE_IPV6
|
||||
if (c->peer.is_ip6) slen = sizeof(usa.sin6);
|
||||
#endif
|
||||
n = recvfrom(FD(c), buf, len, 0, &usa.sa, &slen);
|
||||
if (n > 0) {
|
||||
c->peer.ip = *(uint32_t *) &usa.sin.sin_addr;
|
||||
c->peer.port = usa.sin.sin_port;
|
||||
if (c->peer.is_ip6) {
|
||||
#if MG_ENABLE_IPV6
|
||||
memcpy(c->peer.ip6, &usa.sin6.sin6_addr, sizeof(c->peer.ip6));
|
||||
c->peer.port = usa.sin6.sin6_port;
|
||||
#endif
|
||||
} else {
|
||||
c->peer.ip = *(uint32_t *) &usa.sin.sin_addr;
|
||||
c->peer.port = usa.sin.sin_port;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
n = recv(FD(c), buf, len, MSG_NONBLOCKING);
|
||||
@ -2431,7 +2518,11 @@ static int mg_sock_send(struct mg_connection *c, const void *buf, int len,
|
||||
int n = 0;
|
||||
if (c->is_udp) {
|
||||
union usa usa = tousa(&c->peer);
|
||||
n = sendto(FD(c), buf, len, 0, &usa.sa, sizeof(usa.sin));
|
||||
socklen_t slen = sizeof(usa.sin);
|
||||
#if MG_ENABLE_IPV6
|
||||
if (c->peer.is_ip6) slen = sizeof(usa.sin6);
|
||||
#endif
|
||||
n = sendto(FD(c), buf, len, 0, &usa.sa, slen);
|
||||
} else {
|
||||
n = send(FD(c), buf, len, MSG_NONBLOCKING);
|
||||
}
|
||||
@ -2447,8 +2538,8 @@ static int ll_read(struct mg_connection *c, void *buf, int len, int *fail) {
|
||||
c->is_udp ? 'U' : 'u', c->is_connecting ? 'C' : 'c', n, len,
|
||||
MG_SOCK_ERRNO, *fail));
|
||||
if (n > 0 && c->is_hexdumping) {
|
||||
char *s = mg_hexdump(buf, len);
|
||||
LOG(LL_INFO, ("\n-- %lu %s %s %d\n%s--", c->id, c->label, "<-", len, s));
|
||||
char *s = mg_hexdump(buf, n);
|
||||
LOG(LL_INFO, ("\n-- %lu %s %s %d\n%s--", c->id, c->label, "<-", n, s));
|
||||
free(s);
|
||||
}
|
||||
return n;
|
||||
@ -2569,22 +2660,19 @@ static int write_conn(struct mg_connection *c) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void close_conn(struct mg_mgr *mgr, struct mg_connection *c) {
|
||||
static void close_conn(struct mg_connection *c) {
|
||||
// Unlink this connection from the list
|
||||
LIST_DELETE(struct mg_connection, &mgr->conns, c);
|
||||
#if 0
|
||||
struct mg_connection **head = &mgr->conns;
|
||||
while (*head != c) head = &(*head)->next;
|
||||
*head = c->next;
|
||||
#endif
|
||||
mg_resolve_cancel(mgr, c);
|
||||
LIST_DELETE(struct mg_connection, &c->mgr->conns, c);
|
||||
mg_resolve_cancel(c);
|
||||
if (c == c->mgr->dns4.c) c->mgr->dns4.c = NULL;
|
||||
if (c == c->mgr->dns6.c) c->mgr->dns6.c = NULL;
|
||||
mg_call(c, MG_EV_CLOSE, NULL);
|
||||
// while (c->callbacks != NULL) mg_fn_del(c, c->callbacks->fn);
|
||||
LOG(LL_DEBUG, ("%lu closed", c->id));
|
||||
if (FD(c) != INVALID_SOCKET) {
|
||||
closesocket(FD(c));
|
||||
#if MG_ARCH == MG_ARCH_FREERTOS
|
||||
FreeRTOS_FD_CLR(c->fd, mgr->ss, eSELECT_ALL);
|
||||
FreeRTOS_FD_CLR(c->fd, c->mgr->ss, eSELECT_ALL);
|
||||
#endif
|
||||
}
|
||||
mg_tls_free(c);
|
||||
@ -2592,7 +2680,6 @@ static void close_conn(struct mg_mgr *mgr, struct mg_connection *c) {
|
||||
free(c->send.buf);
|
||||
memset(c, 0, sizeof(*c));
|
||||
free(c);
|
||||
if (c == mgr->dnsc) mgr->dnsc = NULL;
|
||||
}
|
||||
|
||||
static void setsockopts(struct mg_connection *c) {
|
||||
@ -2622,10 +2709,13 @@ static void setsockopts(struct mg_connection *c) {
|
||||
void mg_connect_resolved(struct mg_connection *c) {
|
||||
char buf[40];
|
||||
int type = c->is_udp ? SOCK_DGRAM : SOCK_STREAM;
|
||||
int af = AF_INET;
|
||||
#if MG_ENABLE_IPV6
|
||||
if (c->peer.is_ip6) af = AF_INET6;
|
||||
#endif
|
||||
mg_straddr(c, buf, sizeof(buf));
|
||||
c->fd = (void *) (long) socket(AF_INET, type, 0);
|
||||
LOG(LL_DEBUG, ("%lu resolved, sock %p -> %s, tosend %d", c->id, c->fd, buf,
|
||||
(int) c->send.len));
|
||||
c->fd = (void *) (long) socket(af, type, 0);
|
||||
LOG(LL_DEBUG, ("%lu resolved, sock %p -> %s", c->id, c->fd, buf));
|
||||
if (FD(c) == INVALID_SOCKET) {
|
||||
mg_error(c, "socket(): %d", MG_SOCK_ERRNO);
|
||||
return;
|
||||
@ -2635,7 +2725,12 @@ void mg_connect_resolved(struct mg_connection *c) {
|
||||
mg_call(c, MG_EV_RESOLVE, NULL);
|
||||
if (type == SOCK_STREAM) {
|
||||
union usa usa = tousa(&c->peer);
|
||||
int rc = connect(FD(c), &usa.sa, sizeof(usa.sin));
|
||||
socklen_t slen =
|
||||
#if MG_ENABLE_IPV6
|
||||
c->peer.is_ip6 ? sizeof(usa.sin6) :
|
||||
#endif
|
||||
sizeof(usa.sin);
|
||||
int rc = connect(FD(c), &usa.sa, slen);
|
||||
int fail = rc < 0 && mg_sock_failed() ? MG_SOCK_ERRNO : 0;
|
||||
if (fail) {
|
||||
mg_error(c, "connect: %d", MG_SOCK_ERRNO);
|
||||
@ -2659,7 +2754,7 @@ struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *url,
|
||||
c->fn = fn;
|
||||
c->fn_data = fn_data;
|
||||
LOG(LL_DEBUG, ("%lu -> %s", c->id, url));
|
||||
mg_resolve(mgr, c, &host, mgr->dnstimeout);
|
||||
mg_resolve(c, &host, mgr->dnstimeout);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
@ -2856,7 +2951,7 @@ void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
|
||||
}
|
||||
|
||||
if (c->is_draining && c->send.len == 0) c->is_closing = 1;
|
||||
if (c->is_closing) close_conn(mgr, c);
|
||||
if (c->is_closing) close_conn(c);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -3344,9 +3439,9 @@ int mg_tls_send(struct mg_connection *c, const void *buf, size_t len,
|
||||
#ifdef MG_ENABLE_LINES
|
||||
#line 1 "src/url.c"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
struct url {
|
||||
int key, user, pass, host, port, uri, end;
|
||||
};
|
||||
@ -3365,6 +3460,8 @@ static struct url urlparse(const char *url) {
|
||||
if (i > 0 && url[i - 1] == '/' && url[i] == '/') {
|
||||
u.host = i + 1;
|
||||
u.port = 0;
|
||||
} else if (url[i] == ']') {
|
||||
u.port = 0; // IPv6 URLs, like http://[::1]/bar
|
||||
} else if (url[i] == ':') {
|
||||
u.port = i + 1;
|
||||
} else if (url[i] == '@') {
|
||||
@ -3387,7 +3484,12 @@ struct mg_str mg_url_host(const char *url) {
|
||||
struct url u = urlparse(url);
|
||||
int n =
|
||||
u.port ? u.port - u.host - 1 : u.uri ? u.uri - u.host : u.end - u.host;
|
||||
return mg_str_n(url + u.host, n);
|
||||
struct mg_str s = mg_str_n(url + u.host, n);
|
||||
if (s.len > 2 && s.ptr[0] == '[' && s.ptr[s.len - 1] == ']') {
|
||||
s.len -= 2;
|
||||
s.ptr++;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
const char *mg_url_uri(const char *url) {
|
||||
@ -3538,16 +3640,16 @@ uint16_t mg_ntohs(uint16_t net) {
|
||||
return ((uint16_t) data[1] << 0) | ((uint32_t) data[0] << 8);
|
||||
}
|
||||
|
||||
char *mg_hexdump(const void *buf, int len) {
|
||||
char *mg_hexdump(const void *buf, size_t len) {
|
||||
const unsigned char *p = (const unsigned char *) buf;
|
||||
int i, idx, n = 0, ofs = 0, dlen = len * 5 + 100;
|
||||
size_t i, idx, n = 0, ofs = 0, dlen = len * 5 + 100;
|
||||
char ascii[17] = "", *dst = (char *) malloc(dlen);
|
||||
if (dst == NULL) return dst;
|
||||
for (i = 0; i < len; i++) {
|
||||
idx = i % 16;
|
||||
if (idx == 0) {
|
||||
if (i > 0 && dlen > n) n += snprintf(dst + n, dlen - n, " %s\n", ascii);
|
||||
if (dlen > n) n += snprintf(dst + n, dlen - n, "%04x ", i + ofs);
|
||||
if (dlen > n) n += snprintf(dst + n, dlen - n, "%04x ", (int) (i + ofs));
|
||||
}
|
||||
if (dlen < n) break;
|
||||
n += snprintf(dst + n, dlen - n, " %02x", p[i]);
|
||||
|
42
mongoose.h
42
mongoose.h
@ -250,6 +250,7 @@ enum { false = 0, true = 1 };
|
||||
#else
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <ws2tcpip.h>
|
||||
#endif
|
||||
|
||||
#include <winsock2.h>
|
||||
@ -421,7 +422,7 @@ bool mg_globmatch(const char *pattern, int plen, const char *s, int n);
|
||||
bool mg_next_comma_entry(struct mg_str *s, struct mg_str *k, struct mg_str *v);
|
||||
uint16_t mg_ntohs(uint16_t net);
|
||||
uint32_t mg_ntohl(uint32_t net);
|
||||
char *mg_hexdump(const void *buf, int len);
|
||||
char *mg_hexdump(const void *buf, size_t len);
|
||||
char *mg_hex(const void *buf, int len, char *dst);
|
||||
void mg_unhex(const char *buf, int len, unsigned char *to);
|
||||
unsigned long mg_unhexn(const char *s, int len);
|
||||
@ -581,15 +582,9 @@ enum {
|
||||
|
||||
|
||||
|
||||
struct mg_mgr {
|
||||
struct mg_connection *conns; // List of active connections
|
||||
struct mg_connection *dnsc; // DNS resolver connection
|
||||
const char *dnsserver; // DNS server URL
|
||||
int dnstimeout; // DNS resolve timeout in milliseconds
|
||||
unsigned long nextid; // Next connection ID
|
||||
#if MG_ARCH == MG_ARCH_FREERTOS
|
||||
SocketSet_t ss; // NOTE(lsm): referenced from socket struct
|
||||
#endif
|
||||
struct mg_dns {
|
||||
const char *url; // DNS server URL
|
||||
struct mg_connection *c; // DNS server connection
|
||||
};
|
||||
|
||||
struct mg_addr {
|
||||
@ -599,6 +594,17 @@ struct mg_addr {
|
||||
bool is_ip6; // True when address is IPv6 address
|
||||
};
|
||||
|
||||
struct mg_mgr {
|
||||
struct mg_connection *conns; // List of active connections
|
||||
struct mg_dns dns4; // DNS for IPv4
|
||||
struct mg_dns dns6; // DNS for IPv6
|
||||
int dnstimeout; // DNS resolve timeout in milliseconds
|
||||
unsigned long nextid; // Next connection ID
|
||||
#if MG_ARCH == MG_ARCH_FREERTOS
|
||||
SocketSet_t ss; // NOTE(lsm): referenced from socket struct
|
||||
#endif
|
||||
};
|
||||
|
||||
struct mg_connection {
|
||||
struct mg_connection *next; // Linkage in struct mg_mgr :: connections
|
||||
struct mg_mgr *mgr; // Our container
|
||||
@ -799,13 +805,17 @@ int mg_mqtt_next_sub(struct mg_mqtt_message *msg, struct mg_str *topic,
|
||||
|
||||
|
||||
|
||||
// Mongoose sends DNS queries that contain only one question:
|
||||
// either A (IPv4) or AAAA (IPv6) address lookup.
|
||||
// Therefore, we expect zero or one answer.
|
||||
// If `resolved` is true, then `addr` contains resolved IPv4 or IPV6 address.
|
||||
struct mg_dns_message {
|
||||
uint16_t txnid; // Transaction ID
|
||||
bool resolved; // Resolve successful, ipaddr is set
|
||||
uint32_t ipaddr; // Resolved IPv4 address
|
||||
char name[256]; // Host name
|
||||
uint16_t txnid; // Transaction ID
|
||||
bool resolved; // Resolve successful, addr is set
|
||||
struct mg_addr addr; // Resolved address
|
||||
char name[256]; // Host name
|
||||
};
|
||||
|
||||
void mg_resolve(struct mg_mgr *, struct mg_connection *, struct mg_str *, int);
|
||||
void mg_resolve_cancel(struct mg_mgr *, struct mg_connection *);
|
||||
void mg_resolve(struct mg_connection *, struct mg_str *, int);
|
||||
void mg_resolve_cancel(struct mg_connection *);
|
||||
bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *);
|
||||
|
@ -14,6 +14,7 @@ enum { false = 0, true = 1 };
|
||||
#else
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <ws2tcpip.h>
|
||||
#endif
|
||||
|
||||
#include <winsock2.h>
|
||||
|
115
src/dns.c
115
src/dns.c
@ -21,17 +21,21 @@ struct dns_data {
|
||||
uint16_t txnid;
|
||||
};
|
||||
|
||||
static void mg_dns_free(struct dns_data **head, struct dns_data *d) {
|
||||
LIST_DELETE(struct dns_data, head, d);
|
||||
static struct dns_data *s_reqs; // Active DNS requests
|
||||
|
||||
static void mg_sendnsreq(struct mg_connection *, struct mg_str *, int,
|
||||
struct mg_dns *, bool);
|
||||
|
||||
static void mg_dns_free(struct dns_data *d) {
|
||||
LIST_DELETE(struct dns_data, &s_reqs, d);
|
||||
free(d);
|
||||
}
|
||||
|
||||
void mg_resolve_cancel(struct mg_mgr *mgr, struct mg_connection *c) {
|
||||
struct dns_data *tmp, *d, **head;
|
||||
head = mgr->dnsc == NULL ? NULL : (struct dns_data **) &mgr->dnsc->pfn_data;
|
||||
for (d = head == NULL ? NULL : *head; d != NULL; d = tmp) {
|
||||
void mg_resolve_cancel(struct mg_connection *c) {
|
||||
struct dns_data *tmp, *d;
|
||||
for (d = s_reqs; d != NULL; d = tmp) {
|
||||
tmp = d->next;
|
||||
if (d->c == c) mg_dns_free(head, d);
|
||||
if (d->c == c) mg_dns_free(d);
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,11 +97,16 @@ bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *dm) {
|
||||
atype = ((int) s[j - 8] << 8) | s[j - 7];
|
||||
aclass = ((int) s[j - 6] << 8) | s[j - 5];
|
||||
n = ((int) s[j] << 8) | s[j + 1];
|
||||
// LOG(LL_DEBUG,
|
||||
//("NAME %s, IP len %zu t: %hu, c: %hu", dm->name, n, atype, aclass));
|
||||
LOG(LL_DEBUG, ("%s %d %hu %hu", dm->name, (int) n, atype, aclass));
|
||||
if (&s[j] + 2 + n > e) break;
|
||||
if (n == 4 && atype == 1 && aclass == 1) {
|
||||
memcpy(&dm->ipaddr, &s[j + 2], 4);
|
||||
dm->addr.is_ip6 = false;
|
||||
memcpy(&dm->addr.ip, &s[j + 2], n);
|
||||
dm->resolved = true;
|
||||
break; // Return success
|
||||
} else if (n == 16 && atype == 28 && aclass == 1) {
|
||||
dm->addr.is_ip6 = true;
|
||||
memcpy(&dm->addr.ip6, &s[j + 2], n);
|
||||
dm->resolved = true;
|
||||
break; // Return success
|
||||
}
|
||||
@ -108,7 +117,7 @@ bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *dm) {
|
||||
|
||||
static void dns_cb(struct mg_connection *c, int ev, void *ev_data,
|
||||
void *fn_data) {
|
||||
struct dns_data *d, *tmp, **head = (struct dns_data **) &c->pfn_data;
|
||||
struct dns_data *d, *tmp;
|
||||
if (ev == MG_EV_POLL) {
|
||||
unsigned long now = *(unsigned long *) ev_data;
|
||||
for (d = (struct dns_data *) fn_data; d != NULL; d = tmp) {
|
||||
@ -124,37 +133,44 @@ static void dns_cb(struct mg_connection *c, int ev, void *ev_data,
|
||||
LOG(LL_ERROR, ("Unexpected DNS response:\n%s\n", s));
|
||||
free(s);
|
||||
} else {
|
||||
for (d = (struct dns_data *) c->pfn_data; d != NULL; d = tmp) {
|
||||
LOG(LL_DEBUG, ("%s %d", dm.name, dm.resolved));
|
||||
for (d = s_reqs; d != NULL; d = tmp) {
|
||||
tmp = d->next;
|
||||
// LOG(LL_INFO, ("d %p %hu %hu", d, d->txnid, dm.txnid));
|
||||
if (dm.txnid != d->txnid) continue;
|
||||
if (d->c->is_resolving) {
|
||||
d->c->is_resolving = 0;
|
||||
if (dm.resolved) {
|
||||
d->c->peer.ip = dm.ipaddr;
|
||||
dm.addr.port = d->c->peer.port; // Save port
|
||||
d->c->peer = dm.addr; // Copy resolved address
|
||||
mg_connect_resolved(d->c);
|
||||
#if MG_ENABLE_IPV6
|
||||
} else if (dm.addr.is_ip6 == false && dm.name[0] != '\0') {
|
||||
struct mg_str x = mg_str(dm.name);
|
||||
mg_sendnsreq(d->c, &x, c->mgr->dnstimeout, &c->mgr->dns6, true);
|
||||
#endif
|
||||
} else {
|
||||
mg_error(d->c, "%s DNS lookup failed", dm.name);
|
||||
}
|
||||
} else {
|
||||
LOG(LL_ERROR, ("%lu already resolved", d->c->id));
|
||||
}
|
||||
mg_dns_free(head, d);
|
||||
mg_dns_free(d);
|
||||
resolved = 1;
|
||||
}
|
||||
}
|
||||
if (!resolved) LOG(LL_ERROR, ("stray DNS reply"));
|
||||
c->recv.len = 0;
|
||||
} else if (ev == MG_EV_CLOSE) {
|
||||
for (d = *head; d != NULL; d = tmp) {
|
||||
for (d = s_reqs; d != NULL; d = tmp) {
|
||||
tmp = d->next;
|
||||
mg_dns_free(head, d);
|
||||
mg_dns_free(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mg_dns_send(struct mg_connection *c, const struct mg_str *name,
|
||||
uint16_t txnid) {
|
||||
uint16_t txnid, bool ipv6) {
|
||||
struct {
|
||||
struct mg_dns_header header;
|
||||
uint8_t data[256];
|
||||
@ -163,7 +179,7 @@ void mg_dns_send(struct mg_connection *c, const struct mg_str *name,
|
||||
memset(&pkt, 0, sizeof(pkt));
|
||||
pkt.header.transaction_id = mg_htons(txnid);
|
||||
pkt.header.flags = mg_htons(0x100);
|
||||
pkt.header.num_questions = mg_htons(2);
|
||||
pkt.header.num_questions = mg_htons(1);
|
||||
for (i = n = 0; i < sizeof(pkt.data) - 5; i++) {
|
||||
if (name->ptr[i] == '.' || i >= name->len) {
|
||||
pkt.data[n] = (uint8_t)(i - n);
|
||||
@ -174,8 +190,9 @@ void mg_dns_send(struct mg_connection *c, const struct mg_str *name,
|
||||
}
|
||||
memcpy(&pkt.data[n], "\x00\x00\x01\x00\x01", 5); // A query
|
||||
n += 5;
|
||||
memcpy(&pkt.data[n], "\xc0\x0c\x00\x1c\x00\x01", 6); // AAAA query
|
||||
n += 6;
|
||||
if (ipv6) pkt.data[n - 3] = 0x1c; // AAAA query
|
||||
// memcpy(&pkt.data[n], "\xc0\x0c\x00\x1c\x00\x01", 6); // AAAA query
|
||||
// n += 6;
|
||||
mg_send(c, &pkt, sizeof(pkt.header) + n);
|
||||
#if 0
|
||||
// Immediately after A query, send AAAA query. Whatever reply comes first,
|
||||
@ -186,38 +203,42 @@ void mg_dns_send(struct mg_connection *c, const struct mg_str *name,
|
||||
#endif
|
||||
}
|
||||
|
||||
void mg_resolve(struct mg_mgr *mgr, struct mg_connection *c,
|
||||
struct mg_str *name, int ms) {
|
||||
static void mg_sendnsreq(struct mg_connection *c, struct mg_str *name, int ms,
|
||||
struct mg_dns *dnsc, bool ipv6) {
|
||||
struct dns_data *d = NULL;
|
||||
if (dnsc->url == NULL) {
|
||||
mg_error(c, "DNS server URL is NULL. Call mg_mgr_init()");
|
||||
} else if (dnsc->c == NULL) {
|
||||
dnsc->c = mg_connect(c->mgr, dnsc->url, NULL, NULL);
|
||||
if (dnsc->c != NULL) {
|
||||
dnsc->c->pfn = dns_cb;
|
||||
snprintf(dnsc->c->label, sizeof(dnsc->c->label), "%s", "DNS");
|
||||
// dnsc->c->is_hexdumping = 1;
|
||||
}
|
||||
}
|
||||
if (dnsc->c == NULL) {
|
||||
mg_error(c, "resolver");
|
||||
} else if ((d = (struct dns_data *) calloc(1, sizeof(*d))) == NULL) {
|
||||
mg_error(c, "resolve OOM");
|
||||
} else {
|
||||
d->txnid = s_reqs ? s_reqs->txnid + 1 : 1;
|
||||
d->next = s_reqs;
|
||||
s_reqs = d;
|
||||
d->expire = mg_millis() + ms;
|
||||
d->c = c;
|
||||
c->is_resolving = 1;
|
||||
LOG(LL_DEBUG, ("%lu resolving %.*s, txnid %hu", c->id, (int) name->len,
|
||||
name->ptr, d->txnid));
|
||||
mg_dns_send(dnsc->c, name, d->txnid, ipv6);
|
||||
}
|
||||
}
|
||||
|
||||
void mg_resolve(struct mg_connection *c, struct mg_str *name, int ms) {
|
||||
if (mg_aton(*name, &c->peer)) {
|
||||
// name is an IP address, do not fire name resolution
|
||||
mg_connect_resolved(c);
|
||||
} else {
|
||||
// name is not an IP, send DNS resolution request
|
||||
if (mgr->dnsc == NULL) {
|
||||
const char *srv = mgr->dnsserver ? mgr->dnsserver : "udp://8.8.8.8:53";
|
||||
mgr->dnsc = mg_connect(mgr, srv, NULL, NULL);
|
||||
if (mgr->dnsc != NULL) {
|
||||
mgr->dnsc->pfn = dns_cb;
|
||||
// mgr->dnsc->is_hexdumping = 1;
|
||||
snprintf(mgr->dnsc->label, sizeof(mgr->dnsc->label), "%s", "RESOLVER");
|
||||
}
|
||||
}
|
||||
if (mgr->dnsc == NULL) {
|
||||
mg_error(c, "resolver");
|
||||
} else if ((d = (struct dns_data *) calloc(1, sizeof(*d))) == NULL) {
|
||||
mg_error(c, "resolve OOM");
|
||||
} else {
|
||||
struct dns_data **head = (struct dns_data **) &mgr->dnsc->pfn_data;
|
||||
d->txnid = *head ? (*head)->txnid + 1 : 1;
|
||||
d->next = *head;
|
||||
*head = d;
|
||||
d->expire = mg_millis() + ms;
|
||||
d->c = c;
|
||||
c->is_resolving = 1;
|
||||
LOG(LL_DEBUG, ("%lu resolving %.*s, txnid %hu", c->id, (int) name->len,
|
||||
name->ptr, d->txnid));
|
||||
mg_dns_send(mgr->dnsc, name, d->txnid);
|
||||
}
|
||||
mg_sendnsreq(c, name, ms, &c->mgr->dns4, false);
|
||||
}
|
||||
}
|
||||
|
16
src/dns.h
16
src/dns.h
@ -3,13 +3,17 @@
|
||||
#include "net.h"
|
||||
#include "str.h"
|
||||
|
||||
// Mongoose sends DNS queries that contain only one question:
|
||||
// either A (IPv4) or AAAA (IPv6) address lookup.
|
||||
// Therefore, we expect zero or one answer.
|
||||
// If `resolved` is true, then `addr` contains resolved IPv4 or IPV6 address.
|
||||
struct mg_dns_message {
|
||||
uint16_t txnid; // Transaction ID
|
||||
bool resolved; // Resolve successful, ipaddr is set
|
||||
uint32_t ipaddr; // Resolved IPv4 address
|
||||
char name[256]; // Host name
|
||||
uint16_t txnid; // Transaction ID
|
||||
bool resolved; // Resolve successful, addr is set
|
||||
struct mg_addr addr; // Resolved address
|
||||
char name[256]; // Host name
|
||||
};
|
||||
|
||||
void mg_resolve(struct mg_mgr *, struct mg_connection *, struct mg_str *, int);
|
||||
void mg_resolve_cancel(struct mg_mgr *, struct mg_connection *);
|
||||
void mg_resolve(struct mg_connection *, struct mg_str *, int);
|
||||
void mg_resolve_cancel(struct mg_connection *);
|
||||
bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *);
|
||||
|
109
src/net.c
109
src/net.c
@ -20,9 +20,18 @@ int mg_printf(struct mg_connection *c, const char *fmt, ...) {
|
||||
}
|
||||
|
||||
char *mg_straddr(struct mg_connection *c, char *buf, size_t len) {
|
||||
unsigned char *p = (unsigned char *) &c->peer.ip;
|
||||
snprintf(buf, len, "%d.%d.%d.%d:%hu", p[0], p[1], p[2], p[3],
|
||||
mg_ntohs(c->peer.port));
|
||||
if (c->peer.is_ip6) {
|
||||
uint16_t *p = (uint16_t *) c->peer.ip6;
|
||||
snprintf(buf, len, "[%x:%x:%x:%x:%x:%x:%x:%x]:%hu", mg_htons(p[0]),
|
||||
mg_htons(p[1]), mg_htons(p[2]), mg_htons(p[3]), mg_htons(p[4]),
|
||||
mg_htons(p[5]), mg_htons(p[6]), mg_htons(p[7]),
|
||||
mg_ntohs(c->peer.port));
|
||||
|
||||
} else {
|
||||
unsigned char *p = (unsigned char *) &c->peer.ip;
|
||||
snprintf(buf, len, "%d.%d.%d.%d:%hu", p[0], p[1], p[2], p[3],
|
||||
mg_ntohs(c->peer.port));
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
@ -33,35 +42,73 @@ char *mg_ntoa(const struct mg_addr *addr, char *buf, size_t len) {
|
||||
return buf;
|
||||
}
|
||||
|
||||
static bool mg_atonl(struct mg_str str, struct mg_addr *addr) {
|
||||
if (mg_casecmp(str.ptr, "localhost") != 0) return false;
|
||||
addr->ip = mg_htonl(0x7f000001);
|
||||
addr->is_ip6 = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool mg_aton4(struct mg_str str, struct mg_addr *addr) {
|
||||
uint8_t data[4] = {0, 0, 0, 0};
|
||||
size_t i, num_dots = 0;
|
||||
for (i = 0; i < str.len; i++) {
|
||||
if (str.ptr[i] >= '0' && str.ptr[i] <= '9') {
|
||||
int octet = data[num_dots] * 10 + (str.ptr[i] - '0');
|
||||
if (octet > 255) return false;
|
||||
data[num_dots] = octet;
|
||||
} else if (str.ptr[i] == '.') {
|
||||
if (num_dots >= 3 || i == 0 || str.ptr[i - 1] == '.') return false;
|
||||
num_dots++;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (num_dots != 3 || str.ptr[i - 1] == '.') return false;
|
||||
memcpy(&addr->ip, data, sizeof(data));
|
||||
addr->is_ip6 = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool mg_aton6(struct mg_str str, struct mg_addr *addr) {
|
||||
size_t i, j = 0, n = 0, dc = 42;
|
||||
for (i = 0; i < str.len; i++) {
|
||||
if ((str.ptr[i] >= '0' && str.ptr[i] <= '9') ||
|
||||
(str.ptr[i] >= 'a' && str.ptr[i] <= 'f') ||
|
||||
(str.ptr[i] >= 'A' && str.ptr[i] <= 'F')) {
|
||||
unsigned long val;
|
||||
if (i > j + 3) return false;
|
||||
// LOG(LL_DEBUG, ("%zu %zu [%.*s]", i, j, (int) (i - j + 1),
|
||||
// &str.ptr[j]));
|
||||
val = mg_unhexn(&str.ptr[j], i - j + 1);
|
||||
addr->ip6[n] = (uint8_t)((val >> 8) & 255);
|
||||
addr->ip6[n + 1] = (uint8_t)(val & 255);
|
||||
} else if (str.ptr[i] == ':') {
|
||||
j = i + 1;
|
||||
if (i > 0 && str.ptr[i - 1] == ':') {
|
||||
dc = n; // Double colon
|
||||
if (i > 1 && str.ptr[i - 2] == ':') return false;
|
||||
} else if (i > 0) {
|
||||
n += 2;
|
||||
}
|
||||
if (n > 14) return false;
|
||||
addr->ip6[n] = addr->ip6[n + 1] = 0; // For trailing ::
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (n < 14 && dc == 42) return false;
|
||||
if (n < 14) {
|
||||
memmove(&addr->ip6[dc + (14 - n)], &addr->ip6[dc], n - dc + 2);
|
||||
memset(&addr->ip6[dc], 0, 14 - n);
|
||||
}
|
||||
addr->is_ip6 = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mg_aton(struct mg_str str, struct mg_addr *addr) {
|
||||
// LOG(LL_INFO, ("[%.*s]", (int) str.len, str.ptr));
|
||||
if (!mg_casecmp(str.ptr, "localhost")) {
|
||||
addr->ip = mg_htonl(0x7f000001);
|
||||
return true;
|
||||
} else if (addr->is_ip6) {
|
||||
return false;
|
||||
} else {
|
||||
uint8_t data[4] = {0, 0, 0, 0};
|
||||
size_t i, num_octets = 0;
|
||||
// LOG(LL_DEBUG, ("[%.*s]", (int) str.len, str.ptr));
|
||||
for (i = 0; i < str.len; i++) {
|
||||
// LOG(LL_DEBUG,
|
||||
//(" %c %zu %hhu %zu", str.ptr[i], i, data[num_octets], num_octets));
|
||||
if (str.ptr[i] >= '0' && str.ptr[i] <= '9') {
|
||||
int octet = data[num_octets] * 10 + (str.ptr[i] - '0');
|
||||
if (octet > 255) return false;
|
||||
data[num_octets] = octet;
|
||||
} else if (str.ptr[i] == '.') {
|
||||
if (num_octets >= 3 || i == 0 || str.ptr[i - 1] == '.') return false;
|
||||
num_octets++;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (num_octets != 3 || str.ptr[i - 1] == '.') return false;
|
||||
memcpy(&addr->ip, data, sizeof(data));
|
||||
return true;
|
||||
}
|
||||
return mg_atonl(str, addr) || mg_aton4(str, addr) || mg_aton6(str, addr);
|
||||
}
|
||||
|
||||
void mg_mgr_free(struct mg_mgr *mgr) {
|
||||
@ -87,4 +134,6 @@ void mg_mgr_init(struct mg_mgr *mgr) {
|
||||
#endif
|
||||
memset(mgr, 0, sizeof(*mgr));
|
||||
mgr->dnstimeout = 3000;
|
||||
mgr->dns4.url = "udp://8.8.8.8:53";
|
||||
mgr->dns6.url = "udp://[2001:4860:4860::8888]:53";
|
||||
}
|
||||
|
23
src/net.h
23
src/net.h
@ -4,15 +4,9 @@
|
||||
#include "event.h"
|
||||
#include "iobuf.h"
|
||||
|
||||
struct mg_mgr {
|
||||
struct mg_connection *conns; // List of active connections
|
||||
struct mg_connection *dnsc; // DNS resolver connection
|
||||
const char *dnsserver; // DNS server URL
|
||||
int dnstimeout; // DNS resolve timeout in milliseconds
|
||||
unsigned long nextid; // Next connection ID
|
||||
#if MG_ARCH == MG_ARCH_FREERTOS
|
||||
SocketSet_t ss; // NOTE(lsm): referenced from socket struct
|
||||
#endif
|
||||
struct mg_dns {
|
||||
const char *url; // DNS server URL
|
||||
struct mg_connection *c; // DNS server connection
|
||||
};
|
||||
|
||||
struct mg_addr {
|
||||
@ -22,6 +16,17 @@ struct mg_addr {
|
||||
bool is_ip6; // True when address is IPv6 address
|
||||
};
|
||||
|
||||
struct mg_mgr {
|
||||
struct mg_connection *conns; // List of active connections
|
||||
struct mg_dns dns4; // DNS for IPv4
|
||||
struct mg_dns dns6; // DNS for IPv6
|
||||
int dnstimeout; // DNS resolve timeout in milliseconds
|
||||
unsigned long nextid; // Next connection ID
|
||||
#if MG_ARCH == MG_ARCH_FREERTOS
|
||||
SocketSet_t ss; // NOTE(lsm): referenced from socket struct
|
||||
#endif
|
||||
};
|
||||
|
||||
struct mg_connection {
|
||||
struct mg_connection *next; // Linkage in struct mg_mgr :: connections
|
||||
struct mg_mgr *mgr; // Our container
|
||||
|
67
src/sock.c
67
src/sock.c
@ -44,6 +44,13 @@ static union usa tousa(struct mg_addr *a) {
|
||||
usa.sin.sin_family = AF_INET;
|
||||
usa.sin.sin_port = a->port;
|
||||
*(uint32_t *) &usa.sin.sin_addr = a->ip;
|
||||
#if MG_ENABLE_IPV6
|
||||
if (a->is_ip6) {
|
||||
usa.sin.sin_family = AF_INET6;
|
||||
usa.sin6.sin6_port = a->port;
|
||||
memcpy(&usa.sin6.sin6_addr, a->ip6, sizeof(a->ip6));
|
||||
}
|
||||
#endif
|
||||
return usa;
|
||||
}
|
||||
|
||||
@ -77,10 +84,20 @@ static int mg_sock_recv(struct mg_connection *c, void *buf, int len,
|
||||
if (c->is_udp) {
|
||||
union usa usa;
|
||||
socklen_t slen = sizeof(usa.sin);
|
||||
#if MG_ENABLE_IPV6
|
||||
if (c->peer.is_ip6) slen = sizeof(usa.sin6);
|
||||
#endif
|
||||
n = recvfrom(FD(c), buf, len, 0, &usa.sa, &slen);
|
||||
if (n > 0) {
|
||||
c->peer.ip = *(uint32_t *) &usa.sin.sin_addr;
|
||||
c->peer.port = usa.sin.sin_port;
|
||||
if (c->peer.is_ip6) {
|
||||
#if MG_ENABLE_IPV6
|
||||
memcpy(c->peer.ip6, &usa.sin6.sin6_addr, sizeof(c->peer.ip6));
|
||||
c->peer.port = usa.sin6.sin6_port;
|
||||
#endif
|
||||
} else {
|
||||
c->peer.ip = *(uint32_t *) &usa.sin.sin_addr;
|
||||
c->peer.port = usa.sin.sin_port;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
n = recv(FD(c), buf, len, MSG_NONBLOCKING);
|
||||
@ -94,7 +111,11 @@ static int mg_sock_send(struct mg_connection *c, const void *buf, int len,
|
||||
int n = 0;
|
||||
if (c->is_udp) {
|
||||
union usa usa = tousa(&c->peer);
|
||||
n = sendto(FD(c), buf, len, 0, &usa.sa, sizeof(usa.sin));
|
||||
socklen_t slen = sizeof(usa.sin);
|
||||
#if MG_ENABLE_IPV6
|
||||
if (c->peer.is_ip6) slen = sizeof(usa.sin6);
|
||||
#endif
|
||||
n = sendto(FD(c), buf, len, 0, &usa.sa, slen);
|
||||
} else {
|
||||
n = send(FD(c), buf, len, MSG_NONBLOCKING);
|
||||
}
|
||||
@ -110,8 +131,8 @@ static int ll_read(struct mg_connection *c, void *buf, int len, int *fail) {
|
||||
c->is_udp ? 'U' : 'u', c->is_connecting ? 'C' : 'c', n, len,
|
||||
MG_SOCK_ERRNO, *fail));
|
||||
if (n > 0 && c->is_hexdumping) {
|
||||
char *s = mg_hexdump(buf, len);
|
||||
LOG(LL_INFO, ("\n-- %lu %s %s %d\n%s--", c->id, c->label, "<-", len, s));
|
||||
char *s = mg_hexdump(buf, n);
|
||||
LOG(LL_INFO, ("\n-- %lu %s %s %d\n%s--", c->id, c->label, "<-", n, s));
|
||||
free(s);
|
||||
}
|
||||
return n;
|
||||
@ -232,22 +253,19 @@ static int write_conn(struct mg_connection *c) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void close_conn(struct mg_mgr *mgr, struct mg_connection *c) {
|
||||
static void close_conn(struct mg_connection *c) {
|
||||
// Unlink this connection from the list
|
||||
LIST_DELETE(struct mg_connection, &mgr->conns, c);
|
||||
#if 0
|
||||
struct mg_connection **head = &mgr->conns;
|
||||
while (*head != c) head = &(*head)->next;
|
||||
*head = c->next;
|
||||
#endif
|
||||
mg_resolve_cancel(mgr, c);
|
||||
LIST_DELETE(struct mg_connection, &c->mgr->conns, c);
|
||||
mg_resolve_cancel(c);
|
||||
if (c == c->mgr->dns4.c) c->mgr->dns4.c = NULL;
|
||||
if (c == c->mgr->dns6.c) c->mgr->dns6.c = NULL;
|
||||
mg_call(c, MG_EV_CLOSE, NULL);
|
||||
// while (c->callbacks != NULL) mg_fn_del(c, c->callbacks->fn);
|
||||
LOG(LL_DEBUG, ("%lu closed", c->id));
|
||||
if (FD(c) != INVALID_SOCKET) {
|
||||
closesocket(FD(c));
|
||||
#if MG_ARCH == MG_ARCH_FREERTOS
|
||||
FreeRTOS_FD_CLR(c->fd, mgr->ss, eSELECT_ALL);
|
||||
FreeRTOS_FD_CLR(c->fd, c->mgr->ss, eSELECT_ALL);
|
||||
#endif
|
||||
}
|
||||
mg_tls_free(c);
|
||||
@ -255,7 +273,6 @@ static void close_conn(struct mg_mgr *mgr, struct mg_connection *c) {
|
||||
free(c->send.buf);
|
||||
memset(c, 0, sizeof(*c));
|
||||
free(c);
|
||||
if (c == mgr->dnsc) mgr->dnsc = NULL;
|
||||
}
|
||||
|
||||
static void setsockopts(struct mg_connection *c) {
|
||||
@ -285,10 +302,13 @@ static void setsockopts(struct mg_connection *c) {
|
||||
void mg_connect_resolved(struct mg_connection *c) {
|
||||
char buf[40];
|
||||
int type = c->is_udp ? SOCK_DGRAM : SOCK_STREAM;
|
||||
int af = AF_INET;
|
||||
#if MG_ENABLE_IPV6
|
||||
if (c->peer.is_ip6) af = AF_INET6;
|
||||
#endif
|
||||
mg_straddr(c, buf, sizeof(buf));
|
||||
c->fd = (void *) (long) socket(AF_INET, type, 0);
|
||||
LOG(LL_DEBUG, ("%lu resolved, sock %p -> %s, tosend %d", c->id, c->fd, buf,
|
||||
(int) c->send.len));
|
||||
c->fd = (void *) (long) socket(af, type, 0);
|
||||
LOG(LL_DEBUG, ("%lu resolved, sock %p -> %s", c->id, c->fd, buf));
|
||||
if (FD(c) == INVALID_SOCKET) {
|
||||
mg_error(c, "socket(): %d", MG_SOCK_ERRNO);
|
||||
return;
|
||||
@ -298,7 +318,12 @@ void mg_connect_resolved(struct mg_connection *c) {
|
||||
mg_call(c, MG_EV_RESOLVE, NULL);
|
||||
if (type == SOCK_STREAM) {
|
||||
union usa usa = tousa(&c->peer);
|
||||
int rc = connect(FD(c), &usa.sa, sizeof(usa.sin));
|
||||
socklen_t slen =
|
||||
#if MG_ENABLE_IPV6
|
||||
c->peer.is_ip6 ? sizeof(usa.sin6) :
|
||||
#endif
|
||||
sizeof(usa.sin);
|
||||
int rc = connect(FD(c), &usa.sa, slen);
|
||||
int fail = rc < 0 && mg_sock_failed() ? MG_SOCK_ERRNO : 0;
|
||||
if (fail) {
|
||||
mg_error(c, "connect: %d", MG_SOCK_ERRNO);
|
||||
@ -322,7 +347,7 @@ struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *url,
|
||||
c->fn = fn;
|
||||
c->fn_data = fn_data;
|
||||
LOG(LL_DEBUG, ("%lu -> %s", c->id, url));
|
||||
mg_resolve(mgr, c, &host, mgr->dnstimeout);
|
||||
mg_resolve(c, &host, mgr->dnstimeout);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
@ -519,7 +544,7 @@ void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
|
||||
}
|
||||
|
||||
if (c->is_draining && c->send.len == 0) c->is_closing = 1;
|
||||
if (c->is_closing) close_conn(mgr, c);
|
||||
if (c->is_closing) close_conn(c);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
11
src/url.c
11
src/url.c
@ -1,5 +1,5 @@
|
||||
#include "url.h"
|
||||
#include <stdlib.h>
|
||||
#include "url.h"
|
||||
|
||||
struct url {
|
||||
int key, user, pass, host, port, uri, end;
|
||||
@ -19,6 +19,8 @@ static struct url urlparse(const char *url) {
|
||||
if (i > 0 && url[i - 1] == '/' && url[i] == '/') {
|
||||
u.host = i + 1;
|
||||
u.port = 0;
|
||||
} else if (url[i] == ']') {
|
||||
u.port = 0; // IPv6 URLs, like http://[::1]/bar
|
||||
} else if (url[i] == ':') {
|
||||
u.port = i + 1;
|
||||
} else if (url[i] == '@') {
|
||||
@ -41,7 +43,12 @@ struct mg_str mg_url_host(const char *url) {
|
||||
struct url u = urlparse(url);
|
||||
int n =
|
||||
u.port ? u.port - u.host - 1 : u.uri ? u.uri - u.host : u.end - u.host;
|
||||
return mg_str_n(url + u.host, n);
|
||||
struct mg_str s = mg_str_n(url + u.host, n);
|
||||
if (s.len > 2 && s.ptr[0] == '[' && s.ptr[s.len - 1] == ']') {
|
||||
s.len -= 2;
|
||||
s.ptr++;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
const char *mg_url_uri(const char *url) {
|
||||
|
@ -107,16 +107,16 @@ uint16_t mg_ntohs(uint16_t net) {
|
||||
return ((uint16_t) data[1] << 0) | ((uint32_t) data[0] << 8);
|
||||
}
|
||||
|
||||
char *mg_hexdump(const void *buf, int len) {
|
||||
char *mg_hexdump(const void *buf, size_t len) {
|
||||
const unsigned char *p = (const unsigned char *) buf;
|
||||
int i, idx, n = 0, ofs = 0, dlen = len * 5 + 100;
|
||||
size_t i, idx, n = 0, ofs = 0, dlen = len * 5 + 100;
|
||||
char ascii[17] = "", *dst = (char *) malloc(dlen);
|
||||
if (dst == NULL) return dst;
|
||||
for (i = 0; i < len; i++) {
|
||||
idx = i % 16;
|
||||
if (idx == 0) {
|
||||
if (i > 0 && dlen > n) n += snprintf(dst + n, dlen - n, " %s\n", ascii);
|
||||
if (dlen > n) n += snprintf(dst + n, dlen - n, "%04x ", i + ofs);
|
||||
if (dlen > n) n += snprintf(dst + n, dlen - n, "%04x ", (int) (i + ofs));
|
||||
}
|
||||
if (dlen < n) break;
|
||||
n += snprintf(dst + n, dlen - n, " %02x", p[i]);
|
||||
|
@ -11,7 +11,7 @@ bool mg_globmatch(const char *pattern, int plen, const char *s, int n);
|
||||
bool mg_next_comma_entry(struct mg_str *s, struct mg_str *k, struct mg_str *v);
|
||||
uint16_t mg_ntohs(uint16_t net);
|
||||
uint32_t mg_ntohl(uint32_t net);
|
||||
char *mg_hexdump(const void *buf, int len);
|
||||
char *mg_hexdump(const void *buf, size_t len);
|
||||
char *mg_hex(const void *buf, int len, char *dst);
|
||||
void mg_unhex(const char *buf, int len, unsigned char *to);
|
||||
unsigned long mg_unhexn(const char *s, int len);
|
||||
|
@ -118,6 +118,8 @@ static void test_url(void) {
|
||||
ASSERT(vcmp(mg_url_host("p://bar:1234/a"), "bar"));
|
||||
ASSERT(vcmp(mg_url_host("p://u@bar:1234/a"), "bar"));
|
||||
ASSERT(vcmp(mg_url_host("p://u:p@bar:1234/a"), "bar"));
|
||||
ASSERT(vcmp(mg_url_host("p://u:p@[::1]:1234/a"), "::1"));
|
||||
ASSERT(vcmp(mg_url_host("p://u:p@[1:2::3]:1234/a"), "1:2::3"));
|
||||
|
||||
// Port
|
||||
ASSERT(mg_url_port("foo:1234") == 1234);
|
||||
@ -134,6 +136,8 @@ static void test_url(void) {
|
||||
ASSERT(mg_url_port("wss://u:p@bar:123") == 123);
|
||||
ASSERT(mg_url_port("wss://u:p@bar:123/") == 123);
|
||||
ASSERT(mg_url_port("wss://u:p@bar:123/abc") == 123);
|
||||
ASSERT(mg_url_port("http://u:p@[::1]/abc") == 80);
|
||||
ASSERT(mg_url_port("http://u:p@[::1]:2121/abc") == 2121);
|
||||
|
||||
// User / pass
|
||||
ASSERT(vcmp(mg_url_user("p://foo"), ""));
|
||||
@ -155,6 +159,8 @@ static void test_url(void) {
|
||||
ASSERT(strcmp(mg_url_uri("p://foo:12/"), "/") == 0);
|
||||
ASSERT(strcmp(mg_url_uri("p://foo:12/abc"), "/abc") == 0);
|
||||
ASSERT(strcmp(mg_url_uri("p://foo:12/a/b/c"), "/a/b/c") == 0);
|
||||
ASSERT(strcmp(mg_url_uri("p://[::1]:12/a/b/c"), "/a/b/c") == 0);
|
||||
ASSERT(strcmp(mg_url_uri("p://[ab::1]:12/a/b/c"), "/a/b/c") == 0);
|
||||
}
|
||||
|
||||
static void test_base64(void) {
|
||||
@ -587,7 +593,8 @@ static void f3(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||
// LOG(LL_INFO, ("%d", ev));
|
||||
if (ev == MG_EV_CONNECT) {
|
||||
// c->is_hexdumping = 1;
|
||||
mg_printf(c, "GET / HTTP/1.0\r\nHost: cesanta.com\r\n\r\n");
|
||||
mg_printf(c, "GET / HTTP/1.0\r\nHost: %s\r\n\r\n",
|
||||
c->peer.is_ip6 ? "ipv6.google.com" : "cesanta.com");
|
||||
} else if (ev == MG_EV_HTTP_MSG) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
// LOG(LL_INFO, ("-->[%.*s]", (int) hm->message.len, hm->message.ptr));
|
||||
@ -626,12 +633,12 @@ static void test_http_client(void) {
|
||||
|
||||
#if MG_ENABLE_IPV6
|
||||
ok = 0;
|
||||
// ipv6.google.com does not have IPv4 address, only IPv6, therefore
|
||||
// it is guaranteed to hit IPv6 resolution path.
|
||||
c = mg_http_connect(&mgr, "http://ipv6.google.com", f3, &ok);
|
||||
ASSERT(c != NULL);
|
||||
for (i = 0; i < 500 && ok <= 0; i++) mg_mgr_poll(&mgr, 10);
|
||||
// ASSERT(ok == 200);
|
||||
// LOG(LL_DEBUG, (""));
|
||||
// exit(0);
|
||||
ASSERT(ok == 200);
|
||||
#endif
|
||||
|
||||
mg_mgr_free(&mgr);
|
||||
@ -1005,9 +1012,49 @@ static void test_util(void) {
|
||||
ASSERT(mg_aton(mg_str("0.0.0.256"), &a) == false);
|
||||
ASSERT(mg_aton(mg_str("0.0.0.-1"), &a) == false);
|
||||
ASSERT(mg_aton(mg_str("127.0.0.1"), &a) == true);
|
||||
ASSERT(a.is_ip6 == 0);
|
||||
ASSERT(a.is_ip6 == false);
|
||||
ASSERT(a.ip == 0x100007f);
|
||||
ASSERT(strcmp(mg_ntoa(&a, buf, sizeof(buf)), "127.0.0.1") == 0);
|
||||
|
||||
ASSERT(mg_aton(mg_str("1:2:3:4:5:6:7:8"), &a) == true);
|
||||
ASSERT(a.is_ip6 == true);
|
||||
ASSERT(
|
||||
memcmp(a.ip6,
|
||||
"\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08",
|
||||
sizeof(a.ip6)) == 0);
|
||||
|
||||
memset(a.ip6, 0xaa, sizeof(a.ip6));
|
||||
ASSERT(mg_aton(mg_str("1::1"), &a) == true);
|
||||
ASSERT(a.is_ip6 == true);
|
||||
ASSERT(
|
||||
memcmp(a.ip6,
|
||||
"\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01",
|
||||
sizeof(a.ip6)) == 0);
|
||||
|
||||
memset(a.ip6, 0xaa, sizeof(a.ip6));
|
||||
ASSERT(mg_aton(mg_str("::1"), &a) == true);
|
||||
ASSERT(a.is_ip6 == true);
|
||||
ASSERT(
|
||||
memcmp(a.ip6,
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01",
|
||||
sizeof(a.ip6)) == 0);
|
||||
|
||||
memset(a.ip6, 0xaa, sizeof(a.ip6));
|
||||
ASSERT(mg_aton(mg_str("1::"), &a) == true);
|
||||
ASSERT(a.is_ip6 == true);
|
||||
ASSERT(
|
||||
memcmp(a.ip6,
|
||||
"\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||
sizeof(a.ip6)) == 0);
|
||||
|
||||
memset(a.ip6, 0xaa, sizeof(a.ip6));
|
||||
ASSERT(mg_aton(mg_str("2001:4860:4860::8888"), &a) == true);
|
||||
ASSERT(a.is_ip6 == true);
|
||||
ASSERT(
|
||||
memcmp(a.ip6,
|
||||
"\x20\x01\x48\x60\x48\x60\x00\x00\x00\x00\x00\x00\x00\x00\x88\x88",
|
||||
sizeof(a.ip6)) == 0);
|
||||
|
||||
ASSERT(strcmp(mg_hex("abc", 3, buf), "616263") == 0);
|
||||
ASSERT(mg_url_decode("a=%", 3, buf, sizeof(buf), 0) < 0);
|
||||
ASSERT(mg_url_decode("&&&a=%", 6, buf, sizeof(buf), 0) < 0);
|
||||
|
Loading…
x
Reference in New Issue
Block a user