#include <sys/stat.h>
#include <fcntl.h>
+#include <ifaddrs.h>
#include <time.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
-#include <asm/byteorder.h>
#include <arpa/nameser.h>
#include <resolv.h>
#include <stdlib.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*
dns_type_string(uint16_t type)
{
- switch (type) {
- case TYPE_A:
- return "A";
-
- case TYPE_AAAA:
- return "AAAA";
-
- case TYPE_PTR:
- return "PTR";
-
- case TYPE_TXT:
- return "TXT";
-
- case TYPE_SRV:
- return "SRV";
+ static const struct {
+ uint16_t type;
+ char str[5];
+ } type_str[] = {
+ { TYPE_A, "A" },
+ { TYPE_AAAA, "AAAA" },
+ { TYPE_PTR, "PTR" },
+ { TYPE_TXT, "TXT" },
+ { TYPE_SRV, "SRV" },
+ { TYPE_ANY, "ANY" },
+ };
+ int i;
- case TYPE_ANY:
- return "ANY";
+ for (i = 0; i < ARRAY_SIZE(type_str); i++) {
+ if (type == type_str[i].type)
+ return type_str[i].str;
}
return "N/A";
}
void
-dns_send_question(struct interface *iface, const char *question, int type)
+dns_send_question(struct interface *iface, const char *question, int type, int multicast)
{
- static struct dns_header h = {
- .questions = cpu_to_be16(1),
- };
- static struct dns_question q = {
- .class = cpu_to_be16(1),
- };
+ static struct dns_header h;
+ static struct dns_question q;
static struct iovec iov[] = {
{
.iov_base = &h,
.iov_len = sizeof(h),
},
{
- .iov_base = name_buffer,
+ .iov_base = dns_buffer,
},
{
.iov_base = &q,
};
int len;
- q.type = __cpu_to_be16(type);
+ h.questions = cpu_to_be16(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)
- fprintf(stderr, "failed to send question\n");
- else
- DBG(1, "Q <- %s %s\n", dns_type_string(type), question);
+ perror("failed to send question :");
}
char *buffer;
};
-#define MAX_ANSWER 8
-static struct dns_reply dns_reply[1 + (MAX_ANSWER * 3)];
static int dns_answer_cnt;
void
dns_init_answer(void)
{
dns_answer_cnt = 0;
+ blob_buf_init(&ans_buf, 0);
}
void
-dns_add_answer(int type, const uint8_t *rdata, uint16_t rdlength)
+dns_add_answer(int type, const uint8_t *rdata, uint16_t rdlength, int ttl)
{
- struct dns_reply *a = &dns_reply[dns_answer_cnt];
- if (dns_answer_cnt == MAX_ANSWER)
- return;
- a->rdata = memdup(rdata, rdlength);
- a->type = type;
- a->rdlength = rdlength;
+ struct blob_attr *attr;
+ struct dns_answer *a;
+
+ attr = blob_new(&ans_buf, 0, sizeof(*a) + rdlength);
+ a = blob_data(attr);
+ a->type = cpu_to_be16(type);
+ a->class = cpu_to_be16(1);
+ a->ttl = cpu_to_be32(ttl);
+ a->rdlength = cpu_to_be16(rdlength);
+ memcpy(a + 1, rdata, rdlength);
+
dns_answer_cnt++;
}
dns_send_answer(struct interface *iface, const char *answer)
{
uint8_t buffer[256];
+ struct blob_attr *attr;
struct dns_header h = { 0 };
struct iovec *iov;
- int len, i;
+ int answer_len, rem;
+ int n_iov = 0;
if (!dns_answer_cnt)
return;
- h.answers = __cpu_to_be16(dns_answer_cnt);
- h.flags = __cpu_to_be16(0x8400);
+ h.answers = cpu_to_be16(dns_answer_cnt);
+ h.flags = cpu_to_be16(0x8400);
- iov = alloca(sizeof(struct iovec) * ((dns_answer_cnt * 3) + 1));
- iov[0].iov_base = &h;
- iov[0].iov_len = sizeof(struct dns_header);
+ iov = alloca(sizeof(struct iovec) * ((dns_answer_cnt * 2) + 1));
- for (i = 0; i < dns_answer_cnt; i++) {
- struct dns_answer *a = &dns_reply[i].a;
- int id = (i * 3) + 1;
+ iov[n_iov].iov_base = &h;
+ iov[n_iov].iov_len = sizeof(struct dns_header);
+ n_iov++;
- memset(a, 0, sizeof(*a));
- a->type = __cpu_to_be16(dns_reply[i].type);
- a->class = __cpu_to_be16(1);
- a->ttl = __cpu_to_be32(announce_ttl);
- a->rdlength = __cpu_to_be16(dns_reply[i].rdlength);
-
- len = dn_comp(answer, buffer, sizeof(buffer), NULL, NULL);
- if (len < 1)
- return;
+ answer_len = dn_comp(answer, buffer, sizeof(buffer), NULL, NULL);
+ if (answer_len < 1)
+ return;
- dns_reply[i].buffer = iov[id].iov_base = memdup(buffer, len);
- iov[id].iov_len = len;
+ blob_for_each_attr(attr, ans_buf.head, rem) {
+ struct dns_answer *a = blob_data(attr);
- iov[id + 1].iov_base = a;
- iov[id + 1].iov_len = sizeof(struct dns_answer);
+ iov[n_iov].iov_base = buffer;
+ iov[n_iov].iov_len = answer_len;
+ n_iov++;
- iov[id + 2].iov_base = dns_reply[i].rdata;
- iov[id + 2].iov_len = dns_reply[i].rdlength;
+ iov[n_iov].iov_base = blob_data(attr);
+ iov[n_iov].iov_len = blob_len(attr);
+ n_iov++;
- DBG(1, "A <- %s %s\n", dns_type_string(dns_reply[i].type), answer);
+ DBG(1, "A <- %s %s\n", dns_type_string(be16_to_cpu(a->type)), answer);
}
- if (interface_send_packet(iface, iov, (dns_answer_cnt * 3) + 1) < 0)
+ if (interface_send_packet(iface, iov, n_iov) < 0)
fprintf(stderr, "failed to send question\n");
+}
- for (i = 0; i < dns_answer_cnt; i++) {
- free(dns_reply[i].buffer);
- free(dns_reply[i].rdata);
+void
+dns_reply_a(struct interface *iface, 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_answer_cnt = 0;
+ dns_send_answer(iface, mdns_hostname_local);
+
+ freeifaddrs(ifap);
}
static int
return NULL;
while (endianess--) {
- *swap = __be16_to_cpu(*swap);
+ *swap = be16_to_cpu(*swap);
swap++;
}
return NULL;
while (endianess--) {
- *swap = __be16_to_cpu(*swap);
+ *swap = be16_to_cpu(*swap);
swap++;
}
if (*len < sizeof(struct dns_answer))
return NULL;
- a->type = __be16_to_cpu(a->type);
- a->class = __be16_to_cpu(a->class);
- a->ttl = __be32_to_cpu(a->ttl);
- a->rdlength = __be16_to_cpu(a->rdlength);
+ a->type = be16_to_cpu(a->type);
+ a->class = be16_to_cpu(a->class);
+ a->ttl = be32_to_cpu(a->ttl);
+ a->rdlength = be16_to_cpu(a->rdlength);
*len -= sizeof(struct dns_answer);
*data += sizeof(struct dns_answer);
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, buffer, len, name, a, rdata, a->class & CLASS_FLUSH);
return 0;
}
{
char *host;
+ if ((q->class & CLASS_UNICAST) && iface->multicast)
+ iface = iface->peer;
+
DBG(1, "Q -> %s %s\n", dns_type_string(q->type), name);
switch (q->type) {
case TYPE_ANY:
- host = service_name("local");
- if (!strcmp(name, host))
- service_reply(iface, NULL);
+ if (!strcmp(name, mdns_hostname_local)) {
+ service_reply(iface, NULL, announce_ttl);
+ dns_reply_a(iface, announce_ttl);
+ }
break;
case TYPE_PTR:
- service_announce_services(iface, name);
- service_reply(iface, name);
+ if (!strcmp(name, sdudp))
+ service_announce_services(iface, 0, announce_ttl);
+ else if (!strcmp(name, sdtcp))
+ service_announce_services(iface, 1, announce_ttl);
+ service_reply(iface, name, announce_ttl);
break;
case TYPE_AAAA:
host = strstr(name, ".local");
if (host)
*host = '\0';
- if (!strcmp(hostname, name))
- service_reply_a(iface, q->type);
+ if (!strcmp(mdns_hostname, name))
+ dns_reply_a(iface, announce_ttl);
break;
};
}
void
-dns_handle_packet(struct interface *iface, uint8_t *buffer, int len)
+dns_handle_packet(struct interface *iface, struct sockaddr *s, uint16_t port, uint8_t *buffer, int len)
{
struct dns_header *h;
uint8_t *b = buffer;
return;
}
+ if (h->questions && !iface->multicast && port != 5353)
+ /* 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;
return;
while (h->answers-- > 0)
- parse_answer(iface, buffer, len, &b, &rlen, 1);
+ if (parse_answer(iface, buffer, len, &b, &rlen, 1))
+ return;
while (h->authority-- > 0)
- parse_answer(iface, buffer, len, &b, &rlen, 0);
+ if (parse_answer(iface, buffer, len, &b, &rlen, 1))
+ return;
while (h->additional-- > 0)
- parse_answer(iface, buffer, len, &b, &rlen, 1);
+ if (parse_answer(iface, buffer, len, &b, &rlen, 1))
+ return;
+
}