@@ -403,62 +403,243 @@ void mg_resolve(struct mg_connection *c, const char *url) {
403
403
}
404
404
}
405
405
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
+
406
412
static const uint8_t mdns_answer[] = {
407
413
0, 1, // 2 bytes - record type, A
408
414
0, 1, // 2 bytes - address class, INET
409
415
0, 0, 0, 120, // 4 bytes - TTL
410
416
0, 4 // 2 bytes - address length
411
417
};
412
418
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);
451
431
#if MG_ENABLE_TCPIP
452
- memcpy(p, &c->mgr->ifp->ip, 4), p += 4;
432
+ memcpy(p, &c->mgr->ifp->ip, 4), p += 4;
453
433
#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;
459
555
}
556
+ if (i == db.num) return;
557
+ idx = i;
460
558
}
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;
461
572
}
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);
462
643
mg_iobuf_del(&c->recv, 0, c->recv.len);
463
644
}
464
645
(void) ev_data;
@@ -468,11 +649,10 @@ void mg_multicast_add(struct mg_connection *c, char *ip);
468
649
struct mg_connection *mg_mdns_listen(struct mg_mgr *mgr, char *name) {
469
650
struct mg_connection *c =
470
651
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");
472
653
return c;
473
654
}
474
655
475
-
476
656
#ifdef MG_ENABLE_LINES
477
657
#line 1 "src/event.c"
478
658
#endif
0 commit comments