Skip to content

Commit a08f7f4

Browse files
committed
wip
1 parent 8a62633 commit a08f7f4

File tree

2 files changed

+432
-91
lines changed

2 files changed

+432
-91
lines changed

mongoose.c

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

406+
struct mg_dnssd_record {
407+
const char *srvcproto;
408+
const char *txt;
409+
uint16_t port;
410+
};
411+
412+
static const struct mg_dnssd_record srvcs[] = {{"_ember._tcp", "", 9000},
413+
{"_ember._udp", "", 9000}};
414+
406415
static const uint8_t mdns_answer[] = {
407416
0, 1, // 2 bytes - record type, A
408417
0, 1, // 2 bytes - address class, INET
409418
0, 0, 0, 120, // 4 bytes - TTL
410419
0, 4 // 2 bytes - address length
411420
};
412421

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

475-
476646
#ifdef MG_ENABLE_LINES
477647
#line 1 "src/event.c"
478648
#endif

0 commit comments

Comments
 (0)