Skip to content

Commit 74a2641

Browse files
committed
wip
1 parent 8a62633 commit 74a2641

File tree

4 files changed

+477
-91
lines changed

4 files changed

+477
-91
lines changed

mongoose.c

Lines changed: 226 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -403,62 +403,243 @@ void mg_resolve(struct mg_connection *c, const char *url) {
403403
}
404404
}
405405

406+
static const struct mg_dnssd_record srvcs[] = {{"_ember._tcp", "", 9000},
407+
{"_ember._udp", "", 9000}};
408+
static const struct mg_dnssd_db db = {
409+
(struct mg_dnssd_record *) srvcs,
410+
sizeof(srvcs) / sizeof(struct mg_dnssd_record)};
411+
406412
static const uint8_t mdns_answer[] = {
407413
0, 1, // 2 bytes - record type, A
408414
0, 1, // 2 bytes - address class, INET
409415
0, 0, 0, 120, // 4 bytes - TTL
410416
0, 4 // 2 bytes - address length
411417
};
412418

413-
static void mdns_cb(struct mg_connection *c, int ev, void *ev_data) {
414-
if (ev == MG_EV_READ) {
415-
struct mg_dns_header *qh = (struct mg_dns_header *) c->recv.buf;
416-
if (c->recv.len > 12 && (qh->flags & mg_htons(0xF800)) == 0) {
417-
// flags -> !resp, opcode=0 => query; ignore other opcodes and responses
418-
struct mg_dns_rr rr; // Parse first question, offset 12 is header size
419-
size_t n = mg_dns_parse_rr(c->recv.buf, c->recv.len, 12, true, &rr);
420-
MG_VERBOSE(("mDNS request parsed, result=%d", (int) n));
421-
if (n > 0) {
422-
// RFC-6762 Appendix C, RFC2181 11: m(n + 1-63), max 255 + 0x0
423-
// buf and h declared here to ease future expansion to DNS-SD
424-
uint8_t buf[sizeof(struct mg_dns_header) + 256 + sizeof(mdns_answer) + 4];
425-
struct mg_dns_header *h = (struct mg_dns_header *) buf;
426-
char local_name[63 + 7]; // name label + '.' + local label + '\0'
427-
uint8_t name_len = (uint8_t) strlen((char *)c->fn_data);
428-
struct mg_dns_message dm;
429-
bool unicast = (rr.aclass & MG_BIT(15)) != 0; // QU
430-
// uint16_t q = mg_ntohs(qh->num_questions);
431-
rr.aclass &= (uint16_t) ~MG_BIT(15); // remove "QU" (unicast response)
432-
qh->num_questions = mg_htons(1); // parser sanity
433-
mg_dns_parse(c->recv.buf, c->recv.len, &dm);
434-
if (name_len > (sizeof(local_name) - 7)) // leave room for .local\0
435-
name_len = sizeof(local_name) - 7;
436-
memcpy(local_name, c->fn_data, name_len);
437-
strcpy(local_name + name_len, ".local"); // ensure proper name.local\0
438-
if (strcmp(local_name, dm.name) == 0) {
439-
uint8_t *p = &buf[sizeof(*h)];
440-
memset(h, 0, sizeof(*h)); // clear header
441-
h->txnid = unicast ? qh->txnid : 0; // RFC-6762 18.1
442-
// RFC-6762 6: 0 questions, 1 Answer, 0 Auth, 0 Additional RRs
443-
h->num_answers = mg_htons(1); // only one answer
444-
h->flags = mg_htons(0x8400); // Authoritative response
445-
*p++ = name_len; // label 1
446-
memcpy(p, c->fn_data, name_len), p += name_len;
447-
*p++ = 5; // label 2
448-
memcpy(p, "local", 5), p += 5;
449-
*p++ = 0; // no more labels
450-
memcpy(p, mdns_answer, sizeof(mdns_answer)), p += sizeof(mdns_answer);
419+
static uint8_t *build_name(struct mg_connection *c, uint8_t *p) {
420+
uint8_t name_len = (uint8_t) strlen((char *) c->fn_data);
421+
*p++ = name_len; // label 1
422+
memcpy(p, c->fn_data, name_len), p += name_len;
423+
*p++ = 5; // label 2
424+
memcpy(p, "local", 5), p += 5;
425+
*p++ = 0; // no more labels
426+
return p;
427+
}
428+
429+
static uint8_t *build_a_record(struct mg_connection *c, uint8_t *p) {
430+
memcpy(p, mdns_answer, sizeof(mdns_answer)), p += sizeof(mdns_answer);
451431
#if MG_ENABLE_TCPIP
452-
memcpy(p, &c->mgr->ifp->ip, 4), p += 4;
432+
memcpy(p, &c->mgr->ifp->ip, 4), p += 4;
453433
#else
454-
memcpy(p, c->data, 4), p += 4;
455-
#endif
456-
if (!unicast) memcpy(&c->rem, &c->loc, sizeof(c->rem));
457-
mg_send(c, buf, (size_t)(p - buf)); // And send it!
458-
MG_DEBUG(("mDNS %c response sent", unicast ? 'U' : 'M'));
434+
memcpy(p, c->data, 4), p += 4;
435+
#endif
436+
return p;
437+
}
438+
439+
static uint8_t *build_srv_name(uint8_t *p, struct mg_dnssd_record *r) {
440+
uint8_t name_len = (uint8_t) strlen(r->srvcproto);
441+
*p++ = name_len - 5; // label 1, up to '._tcp'
442+
memcpy(p, r->srvcproto, name_len), p += name_len;
443+
p[-5] = 4; // label 2, '_tcp', overwrite '.'
444+
*p++ = 5; // label 3
445+
memcpy(p, "local", 5), p += 5;
446+
*p++ = 0; // no more labels
447+
return p;
448+
}
449+
450+
static uint8_t *build_mysrv_name(struct mg_connection *c, uint8_t *p,
451+
struct mg_dnssd_record *r) {
452+
uint8_t name_len = (uint8_t) strlen((char *) c->fn_data);
453+
*p++ = name_len; // label 1
454+
memcpy(p, c->fn_data, name_len), p += name_len;
455+
return build_srv_name(p, r);
456+
}
457+
458+
static uint8_t *build_ptr_record(struct mg_connection *c, uint8_t *p,
459+
uint16_t o) {
460+
uint16_t offset = mg_htons(o);
461+
uint8_t name_len = (uint8_t) strlen((char *) c->fn_data);
462+
memcpy(p, mdns_answer, sizeof(mdns_answer));
463+
p[1] = 12; // overwrite record type
464+
p += sizeof(mdns_answer);
465+
p[-1] =
466+
name_len + 3; // overwrite response length, label length + label + offset
467+
*p++ = name_len; // response: label 1
468+
memcpy(p, c->fn_data, name_len), p += name_len; // copy label
469+
memcpy(p, &offset, 2);
470+
*p |= 0xC0, p += 2;
471+
return p;
472+
}
473+
474+
static uint8_t *build_srv_record(struct mg_connection *c, uint8_t *p,
475+
struct mg_dnssd_record *r, uint16_t o) {
476+
uint8_t name_len = (uint8_t) strlen((char *) c->fn_data);
477+
uint16_t port = mg_htons(r->port);
478+
uint16_t offset = mg_htons(o);
479+
memcpy(p, mdns_answer, sizeof(mdns_answer));
480+
p[1] = 33; // overwrite record type
481+
p += sizeof(mdns_answer);
482+
p[-1] = name_len + 9; // overwrite response length (4+2+1+2)
483+
*p++ = 0; // priority
484+
*p++ = 0;
485+
*p++ = 0; // weight
486+
*p++ = 0;
487+
memcpy(p, &port, 2), p += 2; // port
488+
*p++ = name_len; // label 1
489+
memcpy(p, c->fn_data, name_len), p += name_len;
490+
memcpy(p, &offset, 2);
491+
*p |= 0xC0, p += 2;
492+
return p;
493+
}
494+
495+
static uint8_t *build_txt_record(uint8_t *p, struct mg_dnssd_record *r) {
496+
uint16_t txt_len = (uint16_t) strlen(r->txt);
497+
uint16_t len = mg_htons(txt_len);
498+
memcpy(p, mdns_answer, sizeof(mdns_answer));
499+
p[1] = 16; // overwrite record type
500+
p += sizeof(mdns_answer);
501+
memcpy(p - 2, &len, 2); // overwrite response length
502+
memcpy(p, r->txt, txt_len), p += txt_len; // copy record verbatim
503+
return p;
504+
}
505+
506+
// TODO(): RFC-6762 16: case-insensitivity --> RFC-1034, 1035
507+
508+
// TODO(): use c->pfn_data to store a pointer to db. Use db-> in code
509+
// TODO(): write a function to receive a pointer to db and store it in
510+
// c->pfn_data
511+
512+
static void handle_mdns_record(struct mg_connection *c) {
513+
struct mg_dns_header *qh = (struct mg_dns_header *) c->recv.buf;
514+
struct mg_dns_rr rr;
515+
size_t n;
516+
// flags -> !resp, opcode=0 => query; ignore other opcodes and responses
517+
if (c->recv.len <= 12 || (qh->flags & mg_htons(0xF800)) != 0) return;
518+
// Parse first question, offset 12 is header size
519+
n = mg_dns_parse_rr(c->recv.buf, c->recv.len, 12, true, &rr);
520+
MG_VERBOSE(("mDNS request parsed, result=%d", (int) n));
521+
if (n > 0) {
522+
// RFC-6762 Appendix C, RFC2181 11: m(n + 1-63), max 255 + 0x0
523+
// buf and h declared here to ease future expansion to DNS-SD
524+
uint8_t buf[sizeof(struct mg_dns_header) + 256 + sizeof(mdns_answer) + 4];
525+
struct mg_dns_header *h = (struct mg_dns_header *) buf;
526+
uint8_t *p = &buf[sizeof(*h)];
527+
char name[256];
528+
uint8_t name_len;
529+
bool unicast = (rr.aclass & MG_BIT(15)) != 0; // QU
530+
bool listing = false;
531+
unsigned int idx;
532+
// uint16_t q = mg_ntohs(qh->num_questions);
533+
rr.aclass &= (uint16_t) ~MG_BIT(15); // remove "QU" (unicast response)
534+
qh->num_questions = mg_htons(1); // parser sanity
535+
mg_dns_parse_name(c->recv.buf, c->recv.len, 12, name, sizeof(name));
536+
name_len = (uint8_t) strlen(name); // verify it ends in .local
537+
if (strcmp(".local", &name[name_len - 6]) != 0 ||
538+
(rr.aclass != 1 && rr.aclass != 0xff))
539+
return;
540+
name[name_len - 6] = '\0'; // remove .local
541+
MG_VERBOSE(("RR %u %u %s", (unsigned int) rr.atype,
542+
(unsigned int) rr.aclass, name));
543+
if (rr.atype == 1) { // A
544+
// TODO(): ensure c->fn_data ends in \0
545+
if (strcmp(c->fn_data, name) != 0) return;
546+
} else if (&db == NULL) { // no DNS-SD functions required
547+
return;
548+
} else if (rr.atype == 12) { // PTR
549+
if (strcmp("_services._dns-sd._udp", name) == 0) {
550+
listing = true;
551+
} else {
552+
unsigned int i;
553+
for (i = 0; i < db.num; i++) {
554+
if (strcmp(db.srvcs[i].srvcproto, name) == 0) break;
459555
}
556+
if (i == db.num) return;
557+
idx = i;
460558
}
559+
} else if (rr.atype == 33 || rr.atype == 16) { // SRV or TXT
560+
// check it starts with our name and ends in a service name we handle
561+
unsigned int i;
562+
name_len = (uint8_t) strlen((char *) c->fn_data);
563+
if (strncmp(c->fn_data, name, name_len) != 0 || name[name_len] != '.')
564+
return;
565+
for (i = 0; i < db.num; i++) {
566+
if (strcmp(db.srvcs[i].srvcproto, &name[name_len + 1]) == 0) break;
567+
}
568+
if (i == db.num) return;
569+
idx = i;
570+
} else { // unhandled record
571+
return;
461572
}
573+
574+
memset(h, 0, sizeof(*h)); // clear header
575+
h->txnid = unicast ? qh->txnid : 0; // RFC-6762 18.1
576+
h->num_answers = mg_htons(1); // RFC-6762 6: 0 questions, 1 Answer
577+
h->flags = mg_htons(0x8400); // Authoritative response
578+
if (listing) {
579+
// TODO(): RFC-6762 6: each responder SHOULD delay its response by a
580+
// random amount of time selected with uniform random distribution in the
581+
// range 20-120 ms.
582+
MG_INFO(("PTR request for services listing"));
583+
// TODO():
584+
return;
585+
} else if (rr.atype == 12) { // PTR requested, serve PTR + SRV + TXT + A
586+
// TODO(): RFC-6762 6: each responder SHOULD delay its response by a
587+
// random amount of time selected with uniform random distribution in the
588+
// range 20-120 ms. Response to PTR is local_name._myservice._tcp.local
589+
uint8_t *o = p, *aux;
590+
uint16_t offset;
591+
MG_INFO(("PTR request for a service we handle"));
592+
h->num_other_prs = mg_htons(3); // 3 additional records
593+
p = build_srv_name(p, &db.srvcs[idx]);
594+
aux = build_ptr_record(c, p, (uint16_t) (o - buf));
595+
o = p + sizeof(mdns_answer); // point to PTR response (full srvc name)
596+
offset = mg_htons((uint16_t) (o - buf));
597+
o = p - 7; // point to '.local' label (\x05local\x00)
598+
p = aux;
599+
memcpy(p, &offset, 2); // point to full srvc name, in record
600+
*p |= 0xC0, p += 2;
601+
aux = p;
602+
p = build_srv_record(c, p, &db.srvcs[idx], (uint16_t) (o - buf));
603+
o = aux + sizeof(mdns_answer) + 6; // point to target in SRV
604+
memcpy(p, &offset, 2); // point to full srvc name, in record
605+
*p |= 0xC0, p += 2;
606+
p = build_txt_record(p, &db.srvcs[idx]);
607+
offset = mg_htons((uint16_t) (o - buf));
608+
memcpy(p, &offset, 2); // point to target name, in record
609+
*p |= 0xC0, p += 2;
610+
p = build_a_record(c, p);
611+
} else if (rr.atype == 16) { // TXT requested
612+
MG_INFO(("TXT request to us, for a service we handle"));
613+
p = build_srv_name(p, &db.srvcs[idx]);
614+
p = build_txt_record(p, &db.srvcs[idx]);
615+
} else if (rr.atype == 33) { // SRV requested, serve SRV + A
616+
uint8_t *o, *aux;
617+
uint16_t offset;
618+
MG_INFO(("SRV request to us, for a service we handle"));
619+
h->num_other_prs = mg_htons(1); // 1 additional record
620+
p = build_srv_name(p, &db.srvcs[idx]);
621+
o = p - 7; // point to '.local' label (\x05local\x00)
622+
aux = p;
623+
p = build_srv_record(c, p, &db.srvcs[idx], (uint16_t) (o - buf));
624+
o = aux + sizeof(mdns_answer) + 6; // point to target in SRV
625+
offset = mg_htons((uint16_t) (o - buf));
626+
memcpy(p, &offset, 2); // point to target name, in record
627+
*p |= 0xC0, p += 2;
628+
p = build_a_record(c, p);
629+
} else { // A requested
630+
// RFC-6762 6: 0 Auth, 0 Additional RRs
631+
p = build_name(c, p);
632+
p = build_a_record(c, p);
633+
}
634+
if (!unicast) memcpy(&c->rem, &c->loc, sizeof(c->rem));
635+
mg_send(c, buf, (size_t) (p - buf)); // And send it!
636+
MG_DEBUG(("mDNS %c response sent", unicast ? 'U' : 'M'));
637+
}
638+
}
639+
640+
static void mdns_cb(struct mg_connection *c, int ev, void *ev_data) {
641+
if (ev == MG_EV_READ) {
642+
handle_mdns_record(c);
462643
mg_iobuf_del(&c->recv, 0, c->recv.len);
463644
}
464645
(void) ev_data;
@@ -468,11 +649,10 @@ void mg_multicast_add(struct mg_connection *c, char *ip);
468649
struct mg_connection *mg_mdns_listen(struct mg_mgr *mgr, char *name) {
469650
struct mg_connection *c =
470651
mg_listen(mgr, "udp://224.0.0.251:5353", mdns_cb, name);
471-
if (c != NULL) mg_multicast_add(c, (char *)"224.0.0.251");
652+
if (c != NULL) mg_multicast_add(c, (char *) "224.0.0.251");
472653
return c;
473654
}
474655

475-
476656
#ifdef MG_ENABLE_LINES
477657
#line 1 "src/event.c"
478658
#endif

mongoose.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2892,6 +2892,19 @@ struct mg_dns_rr {
28922892
uint16_t alen; // Address length
28932893
};
28942894

2895+
// DNS-SD service record
2896+
struct mg_dnssd_record {
2897+
char *srvcproto; // service.proto, service name
2898+
char *txt; // TXT record contents
2899+
uint16_t port; // SRV record port
2900+
};
2901+
2902+
// DNS-SD service database
2903+
struct mg_dnssd_db {
2904+
struct mg_dnssd_record *srvcs; // service record data
2905+
uint8_t num; // number of records in db
2906+
};
2907+
28952908
void mg_resolve(struct mg_connection *, const char *url);
28962909
void mg_resolve_cancel(struct mg_connection *);
28972910
bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *);

0 commit comments

Comments
 (0)