@@ -403,62 +403,233 @@ void mg_resolve(struct mg_connection *c, const char *url) {
403
403
}
404
404
}
405
405
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
+
406
415
static const uint8_t mdns_answer[] = {
407
416
0, 1, // 2 bytes - record type, A
408
417
0, 1, // 2 bytes - address class, INET
409
418
0, 0, 0, 120, // 4 bytes - TTL
410
419
0, 4 // 2 bytes - address length
411
420
};
412
421
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);
451
434
#if MG_ENABLE_TCPIP
452
- memcpy(p, &c->mgr->ifp->ip, 4), p += 4;
435
+ memcpy(p, &c->mgr->ifp->ip, 4), p += 4;
453
436
#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;
459
548
}
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;
460
559
}
560
+ if (i == sizeof(srvcs) / sizeof(struct mg_dnssd_record)) return;
561
+ idx = i;
562
+ } else { // unhandled record
563
+ return;
461
564
}
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);
462
633
mg_iobuf_del(&c->recv, 0, c->recv.len);
463
634
}
464
635
(void) ev_data;
@@ -468,11 +639,10 @@ void mg_multicast_add(struct mg_connection *c, char *ip);
468
639
struct mg_connection *mg_mdns_listen(struct mg_mgr *mgr, char *name) {
469
640
struct mg_connection *c =
470
641
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");
472
643
return c;
473
644
}
474
645
475
-
476
646
#ifdef MG_ENABLE_LINES
477
647
#line 1 "src/event.c"
478
648
#endif
0 commit comments