#include <sys/stat.h>
#include <fcntl.h>
+#include <ifaddrs.h>
#include <time.h>
#include <stdio.h>
#include <unistd.h>
#include "interface.h"
static char name_buffer[MAX_NAME_LEN + 1];
+static char dns_buffer[MAX_NAME_LEN];
static struct blob_buf ans_buf;
const char*
}
void
-dns_send_question(struct interface *iface, const char *question, int type, int unicast)
+dns_send_question(struct interface *iface, struct sockaddr *to,
+ const char *question, int type, int multicast)
{
static struct dns_header h;
static struct dns_question q;
.iov_len = sizeof(h),
},
{
- .iov_base = name_buffer,
+ .iov_base = dns_buffer,
},
{
.iov_base = &q,
int len;
h.questions = cpu_to_be16(1);
- q.class = cpu_to_be16(((unicast) ? (CLASS_UNICAST) : (0)) | 1);
+ q.class = cpu_to_be16((multicast ? 0 : CLASS_UNICAST) | 1);
q.type = cpu_to_be16(type);
- len = dn_comp(question, (void *) name_buffer, sizeof(name_buffer), NULL, NULL);
+ len = dn_comp(question, (void *) dns_buffer, sizeof(dns_buffer), NULL, NULL);
if (len < 1)
return;
iov[1].iov_len = len;
DBG(1, "Q <- %s %s\n", dns_type_string(type), question);
- if (interface_send_packet(iface, iov, ARRAY_SIZE(iov)) < 0)
- perror("failed to send question :");
+ if (interface_send_packet(iface, to, iov, ARRAY_SIZE(iov)) < 0)
+ perror("failed to send question");
}
}
void
-dns_send_answer(struct interface *iface, const char *answer)
+dns_send_answer(struct interface *iface, struct sockaddr *to, const char *answer)
{
uint8_t buffer[256];
struct blob_attr *attr;
DBG(1, "A <- %s %s\n", dns_type_string(be16_to_cpu(a->type)), answer);
}
- if (interface_send_packet(iface, iov, n_iov) < 0)
- fprintf(stderr, "failed to send question\n");
+ if (interface_send_packet(iface, to, iov, n_iov) < 0)
+ perror("failed to send answer");
+}
+
+void
+dns_reply_a(struct interface *iface, struct sockaddr *to, int ttl)
+{
+ struct ifaddrs *ifap, *ifa;
+ struct sockaddr_in *sa;
+ struct sockaddr_in6 *sa6;
+
+ getifaddrs(&ifap);
+
+ dns_init_answer();
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ if (strcmp(ifa->ifa_name, iface->name))
+ continue;
+ if (ifa->ifa_addr->sa_family == AF_INET) {
+ sa = (struct sockaddr_in *) ifa->ifa_addr;
+ dns_add_answer(TYPE_A, (uint8_t *) &sa->sin_addr, 4, ttl);
+ }
+ if (ifa->ifa_addr->sa_family == AF_INET6) {
+ uint8_t ll_prefix[] = {0xfe, 0x80 };
+ sa6 = (struct sockaddr_in6 *) ifa->ifa_addr;
+ if (!memcmp(&sa6->sin6_addr, &ll_prefix, 2))
+ dns_add_answer(TYPE_AAAA, (uint8_t *) &sa6->sin6_addr, 16, ttl);
+ }
+ }
+ dns_send_answer(iface, to, mdns_hostname_local);
+
+ freeifaddrs(ifap);
}
static int
return name_buffer;
}
-static int
-parse_answer(struct interface *iface, uint8_t *buffer, int len, uint8_t **b, int *rlen, int cache)
+static int parse_answer(struct interface *iface, struct sockaddr *from,
+ uint8_t *buffer, int len, uint8_t **b, int *rlen,
+ int cache)
{
char *name = dns_consume_name(buffer, len, b, rlen);
struct dns_answer *a;
return -1;
}
+ if ((a->class & ~CLASS_FLUSH) != CLASS_IN)
+ return -1;
+
rdata = *b;
if (a->rdlength > *rlen) {
fprintf(stderr, "dropping: bad question\n");
*b += a->rdlength;
if (cache)
- cache_answer(iface, buffer, len, name, a, rdata);
+ cache_answer(iface, from, buffer, len, name, a, rdata, a->class & CLASS_FLUSH);
return 0;
}
static void
-parse_question(struct interface *iface, char *name, struct dns_question *q)
+parse_question(struct interface *iface, struct sockaddr *from, char *name, struct dns_question *q)
{
+ struct sockaddr *to = NULL;
char *host;
- if ((q->class & CLASS_UNICAST) && iface->multicast)
- iface = iface->peer;
+ /* TODO: Multicast if more than one quarter of TTL has passed */
+ if (q->class & CLASS_UNICAST) {
+ to = from;
+ if (iface->multicast)
+ iface = iface->peer;
+ }
DBG(1, "Q -> %s %s\n", dns_type_string(q->type), name);
switch (q->type) {
case TYPE_ANY:
- if (!strcmp(name, mdns_hostname_local))
- service_reply(iface, NULL, announce_ttl);
+ if (!strcmp(name, mdns_hostname_local)) {
+ dns_reply_a(iface, to, announce_ttl);
+ service_reply(iface, to, NULL, NULL, announce_ttl);
+ }
break;
case TYPE_PTR:
- service_announce_services(iface, name, announce_ttl);
- service_reply(iface, name, announce_ttl);
+ if (!strcmp(name, C_DNS_SD)) {
+ dns_reply_a(iface, to, announce_ttl);
+ service_announce_services(iface, to, announce_ttl);
+ } else {
+ if (name[0] == '_') {
+ service_reply(iface, to, NULL, name, announce_ttl);
+ } else {
+ /* First dot separates instance name from the rest */
+ char *dot = strchr(name, '.');
+
+ if (dot) {
+ *dot = '\0';
+ service_reply(iface, to, name, dot + 1, announce_ttl);
+ *dot = '.';
+ }
+ }
+ }
break;
case TYPE_AAAA:
host = strstr(name, ".local");
if (host)
*host = '\0';
- if (!strcmp(mdns_hostname, name))
- service_reply_a(iface, announce_ttl);
+ if (!strcmp(umdns_host_label, name))
+ dns_reply_a(iface, to, announce_ttl);
break;
};
}
void
-dns_handle_packet(struct interface *iface, uint8_t *buffer, int len, int unicast)
+dns_handle_packet(struct interface *iface, struct sockaddr *from, uint16_t port, uint8_t *buffer, int len)
{
struct dns_header *h;
uint8_t *b = buffer;
return;
}
+ if (h->questions && !iface->multicast && port != MCAST_PORT)
+ /* silently drop unicast questions that dont originate from port 5353 */
+ return;
+
while (h->questions-- > 0) {
char *name = dns_consume_name(buffer, len, &b, &rlen);
struct dns_question *q;
}
if (!(h->flags & FLAG_RESPONSE))
- parse_question(iface, name, q);
+ parse_question(iface, from, name, q);
}
if (!(h->flags & FLAG_RESPONSE))
return;
while (h->answers-- > 0)
- if (parse_answer(iface, buffer, len, &b, &rlen, 1))
+ if (parse_answer(iface, from, buffer, len, &b, &rlen, 1))
return;
while (h->authority-- > 0)
- if (parse_answer(iface, buffer, len, &b, &rlen, 1))
+ if (parse_answer(iface, from, buffer, len, &b, &rlen, 1))
return;
while (h->additional-- > 0)
- if (parse_answer(iface, buffer, len, &b, &rlen, 1))
+ if (parse_answer(iface, from, buffer, len, &b, &rlen, 1))
return;
}