Remove mdns (we will use avahi instead)

This commit is contained in:
2017-03-18 22:36:06 +01:00
parent c84c0d8f9f
commit f601938a54
23 changed files with 0 additions and 6776 deletions

View File

@@ -1,7 +0,0 @@
config BR2_PACKAGE_MDNS
bool "mdns"
default n
select BR2_PACKAGE_LIBUBOX
select BR2_PACKAGE_UBUS
help
mdns.

View File

@@ -1,21 +0,0 @@
################################################################################
#
# UBOX TOOLS
#
################################################################################
MDNS_VERSION:= 2015.09.03
MDNS_SITE = $(TOPDIR)/../../src/3P/mdns/builders/cmake
MDNS_SITE_METHOD = local
MDNS_INSTALL_STAGING = YES
MDNS_DEPENDENCIES = libubox ubus
MDNS_CONF = SRC_DIR=$(TOPDIR)/../..
MDNS_CONF_ENV = $(UBOX_CONF)
MDNS_MAKE_ENV = $(UBOX_CONF)
MDNS_CONF_OPTS += -DMODULE_PATH=$(TOPDIR)/../../bsp/cmake-modules -DCMAKE_BUILD_TYPE=$(BUILD_TYPE)
$(eval $(cmake-package))

View File

@@ -1,22 +0,0 @@
cmake_minimum_required(VERSION 2.6)
PROJECT(mdns C)
ADD_DEFINITIONS(-Os -ggdb -Wall -Werror --std=gnu99 -Wmissing-declarations)
SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
SET(SOURCES main.c dns.c announce.c cache.c service.c util.c ubus.c interface.c)
SET(LIBS ubox ubus resolv blobmsg_json)
IF(DEBUG)
ADD_DEFINITIONS(-DDEBUG -g3)
ENDIF()
ADD_EXECUTABLE(mdns ${SOURCES})
TARGET_LINK_LIBRARIES(mdns ${LIBS})
INSTALL(TARGETS mdns
RUNTIME DESTINATION sbin
)

View File

@@ -1,3 +0,0 @@
* timeouts and delays as described in rfc
* automagic route setting
* ipv6

View File

@@ -1,85 +0,0 @@
/*
* Copyright (C) 2014 John Crispin <blogic@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <sys/types.h>
#include <stdio.h>
#include <libubox/uloop.h>
#include "cache.h"
#include "dns.h"
#include "util.h"
#include "service.h"
#include "announce.h"
#include "interface.h"
#define TTL_TIMEOUT 75
enum {
STATE_PROBE1 = 0,
STATE_PROBE2,
STATE_PROBE3,
STATE_PROBE_WAIT,
STATE_PROBE_END,
STATE_ANNOUNCE,
};
int announce_ttl = 75 * 60;
static void
announce_timer(struct uloop_timeout *timeout)
{
struct interface *iface = container_of(timeout, struct interface, announce_timer);
switch (iface->announce_state) {
case STATE_PROBE1:
case STATE_PROBE2:
case STATE_PROBE3:
dns_send_question(iface, mdns_hostname_local, TYPE_ANY, 0);
uloop_timeout_set(timeout, 250);
iface->announce_state++;
break;
case STATE_PROBE_WAIT:
uloop_timeout_set(timeout, 500);
iface->announce_state++;
break;
case STATE_PROBE_END:
if (cache_host_is_known(mdns_hostname_local)) {
fprintf(stderr, "the host %s already exists. stopping announce service\n", mdns_hostname_local);
return;
}
iface->announce_state++;
case STATE_ANNOUNCE:
service_announce(iface, announce_ttl);
uloop_timeout_set(timeout, announce_ttl * 800);
break;
}
}
void
announce_init(struct interface *iface)
{
iface->announce_state = STATE_PROBE1;
iface->announce_timer.cb = announce_timer;
uloop_timeout_set(&iface->announce_timer, 100);
}
void
announce_free(struct interface *iface)
{
uloop_timeout_cancel(&iface->announce_timer);
}

View File

@@ -1,23 +0,0 @@
/*
* Copyright (C) 2014 John Crispin <blogic@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _ANNOUNCE_H__
#define _ANNOUNCE_H__
struct interface;
extern int announce_ttl;
void announce_init(struct interface *iface);
void announce_free(struct interface *iface);
#endif

View File

@@ -1,26 +0,0 @@
cmake_minimum_required (VERSION 3.0)
project (mdns)
ADD_DEFINITIONS(-Os -ggdb -Wall -Werror --std=gnu99 -Wmissing-declarations)
set (CMAKE_MODULE_PATH "${MODULE_PATH}")
set(DISABLE_TARGET_OPTIMIZATION ON)
include (br)
# mdns
add_executable (mdns
$ENV{SRC_DIR}/src/3P/mdns/main.c
$ENV{SRC_DIR}/src/3P/mdns/dns.c
$ENV{SRC_DIR}/src/3P/mdns/announce.c
$ENV{SRC_DIR}/src/3P/mdns/cache.c
$ENV{SRC_DIR}/src/3P/mdns/service.c
$ENV{SRC_DIR}/src/3P/mdns/util.c
$ENV{SRC_DIR}/src/3P/mdns/ubus.c
$ENV{SRC_DIR}/src/3P/mdns/interface.c
)
target_link_libraries (mdns ubox ubus resolv blobmsg_json)
install (TARGETS mdns RUNTIME DESTINATION sbin)

View File

@@ -1,479 +0,0 @@
/*
* Copyright (C) 2014 John Crispin <blogic@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <resolv.h>
#include <time.h>
#include <libubox/usock.h>
#include <libubox/uloop.h>
#include <libubox/avl-cmp.h>
#include <libubox/blobmsg_json.h>
#include <libubox/kvlist.h>
#include <ubus/libubus.h>
#include "cache.h"
#include "util.h"
#include "dns.h"
#include "interface.h"
static struct uloop_timeout cache_gc;
struct avl_tree services;
static AVL_TREE(records, avl_strcmp, true, NULL);
static void
cache_record_free(struct cache_record *r)
{
DBG(2, "%s %s\n", dns_type_string(r->type), r->record);
avl_delete(&records, &r->avl);
free(r);
}
static void
cache_service_free(struct cache_service *s)
{
DBG(2, "%s\n", s->entry);
avl_delete(&services, &s->avl);
free(s);
}
static int
cache_is_expired(time_t t, uint32_t ttl, int frac)
{
if (monotonic_time() - t >= ttl * frac / 100)
return 1;
return 0;
}
static void
cache_gc_timer(struct uloop_timeout *timeout)
{
struct cache_record *r, *p;
struct cache_service *s, *t;
avl_for_each_element_safe(&records, r, avl, p)
if (cache_is_expired(r->time, r->ttl, 100))
cache_record_free(r);
avl_for_each_element_safe(&services, s, avl, t) {
if (!s->host)
continue;
if (!cache_is_expired(s->time, s->ttl, s->refresh))
continue;
if (s->refresh >= 100) {
cache_service_free(s);
continue;
}
s->refresh += 50;
dns_send_question(s->iface, s->entry, TYPE_PTR, 1);
}
uloop_timeout_set(timeout, 10000);
}
int
cache_init(void)
{
avl_init(&services, avl_strcmp, true, NULL);
cache_gc.cb = cache_gc_timer;
uloop_timeout_set(&cache_gc, 10000);
return 0;
}
void cache_cleanup(struct interface *iface)
{
struct cache_record *r, *p;
struct cache_service *s, *t;
avl_for_each_element_safe(&services, s, avl, t)
if (!iface || iface == s->iface)
cache_service_free(s);
avl_for_each_element_safe(&records, r, avl, p)
if (!iface || iface == r->iface)
cache_record_free(r);
}
void
cache_scan(void)
{
struct interface *iface;
struct cache_service *s;
vlist_for_each_element(&interfaces, iface, node)
avl_for_each_element(&services, s, avl)
dns_send_question(iface, s->entry, TYPE_PTR, 1);
}
static struct cache_service*
cache_service(struct interface *iface, char *entry, int hlen, int ttl)
{
struct cache_service *s, *t;
char *entry_buf;
char *host_buf;
char *type;
avl_for_each_element_safe(&services, s, avl, t)
if (!strcmp(s->entry, entry)) {
s->refresh = 50;
s->time = monotonic_time();
return s;
}
s = calloc_a(sizeof(*s),
&entry_buf, strlen(entry) + 1,
&host_buf, hlen ? hlen + 1 : 0);
s->avl.key = s->entry = strcpy(entry_buf, entry);
s->time = monotonic_time();
s->ttl = ttl;
s->iface = iface;
s->refresh = 50;
if (hlen)
s->host = strncpy(host_buf, s->entry, hlen);
type = strstr(entry_buf, "._");
if (type)
type++;
if (type)
s->avl.key = type;
avl_insert(&services, &s->avl);
if (!hlen)
dns_send_question(iface, entry, TYPE_PTR, !iface->multicast);
return s;
}
static struct cache_record*
cache_record_find(char *record, int type, int port, int rdlength, uint8_t *rdata)
{
struct cache_record *l = avl_find_element(&records, record, l, avl);
if (!l)
return NULL;
while (l && l->record && !strcmp(l->record, record)) {
struct cache_record *r = l;
l = avl_next_element(l, avl);
if (r->type != type)
continue;
if (r->type == TYPE_TXT || (r->type == TYPE_SRV))
return r;
if (r->port != port)
continue;
if (r->rdlength != rdlength)
continue;
if (!!r->rdata != !!rdata)
continue;
if (!r->rdata || !rdata || memcmp(r->rdata, rdata, rdlength))
continue;
return r;
}
return NULL;
}
int
cache_host_is_known(char *record)
{
struct cache_record *l = avl_find_element(&records, record, l, avl);
if (!l)
return 0;
while (l && !avl_is_last(&records, &l->avl) && !strcmp(l->record, record)) {
struct cache_record *r = l;
l = avl_next_element(l, avl);
if ((r->type != TYPE_A) && (r->type != TYPE_AAAA))
continue;
return 1;
}
return 0;
}
void
cache_answer(struct interface *iface, uint8_t *base, int blen, char *name, struct dns_answer *a, uint8_t *rdata, int flush)
{
struct dns_srv_data *dsd = (struct dns_srv_data *) rdata;
struct cache_record *r;
int port = 0, dlen = 0, tlen = 0, nlen, rdlength;
char *p = NULL;
char *name_buf;
void *rdata_ptr, *txt_ptr;
int host_len = 0;
static char *rdata_buffer = (char *) mdns_buf;
time_t now = monotonic_time();
nlen = strlen(name);
switch (a->type) {
case TYPE_PTR:
if (a->rdlength < 2)
return;
if (dn_expand(base, base + blen, rdata, rdata_buffer, MAX_DATA_LEN) < 0) {
perror("process_answer/dn_expand");
return;
}
DBG(1, "A -> %s %s %s ttl:%d\n", dns_type_string(a->type), name, rdata_buffer, a->ttl);
rdlength = strlen(rdata_buffer);
if (strcmp(C_DNS_SD, name) != 0 &&
nlen + 1 < rdlength && !strcmp(rdata_buffer + rdlength - nlen, name))
host_len = rdlength - nlen - 1;
cache_service(iface, rdata_buffer, host_len, a->ttl);
dlen = strlen(rdata_buffer) + 1;
rdata = (uint8_t*)rdata_buffer;
break;
case TYPE_SRV:
if (a->rdlength < 8)
return;
port = be16_to_cpu(dsd->port);
memcpy(rdata_buffer, dsd, sizeof(*dsd));
if (dn_expand(base, base + blen, (const uint8_t*)&dsd[1],
&rdata_buffer[sizeof(*dsd)], MAX_DATA_LEN - sizeof(*dsd)) < 0) {
perror("process_answer/dn_expand");
return;
}
dlen = sizeof(*dsd) + strlen(&rdata_buffer[sizeof(*dsd)]) + 1;
rdata = (uint8_t*)rdata_buffer;
break;
case TYPE_TXT:
rdlength = a->rdlength;
if (rdlength <= 2)
return;
memcpy(rdata_buffer, &rdata[1], rdlength);
rdata_buffer[rdlength] = rdata_buffer[rdlength + 1] = '\0';
tlen = rdlength + 1;
p = &rdata_buffer[*rdata];
do {
uint8_t v = *p;
*p = '\0';
if (v && p + v < &rdata_buffer[rdlength])
p += v + 1;
} while (*p);
break;
case TYPE_A:
cache_service(iface, name, strlen(name), a->ttl);
if (a->rdlength != 4)
return;
dlen = 4;
break;
case TYPE_AAAA:
cache_service(iface, name, strlen(name), a->ttl);
if (a->rdlength != 16)
return;
dlen = 16;
break;
default:
return;
}
r = cache_record_find(name, a->type, port, dlen, rdata);
if (r) {
if (!a->ttl) {
DBG(1, "D -> %s %s ttl:%d\n", dns_type_string(r->type), r->record, r->ttl);
r->time = now + 1 - r->ttl;
} else {
r->ttl = a->ttl;
r->time = now;
DBG(1, "A -> %s %s ttl:%d\n", dns_type_string(r->type), r->record, r->ttl);
}
return;
}
if (!a->ttl)
return;
r = calloc_a(sizeof(*r),
&name_buf, strlen(name) + 1,
&txt_ptr, tlen,
&rdata_ptr, dlen);
r->avl.key = r->record = strcpy(name_buf, name);
r->type = a->type;
r->ttl = a->ttl;
r->port = port;
r->rdlength = dlen;
r->time = now;
r->iface = iface;
if (tlen)
r->txt = memcpy(txt_ptr, rdata_buffer, tlen);
if (dlen)
r->rdata = memcpy(rdata_ptr, rdata, dlen);
if (avl_insert(&records, &r->avl))
free(r);
else
DBG(1, "A -> %s %s ttl:%d\n", dns_type_string(r->type), r->record, r->ttl);
}
void
cache_dump_records(struct blob_buf *buf, const char *name)
{
struct cache_record *r, *last, *next;
const char *txt;
char buffer[INET6_ADDRSTRLEN];
last = avl_last_element(&records, last, avl);
for (r = avl_find_element(&records, name, r, avl); r; r = next) {
switch (r->type) {
case TYPE_TXT:
if (r->txt && strlen(r->txt)) {
txt = r->txt;
do {
blobmsg_add_string(buf, "txt", txt);
txt = &txt[strlen(txt) + 1];
} while (*txt);
}
break;
case TYPE_SRV:
if (r->port)
blobmsg_add_u32(buf, "port", r->port);
break;
case TYPE_A:
if ((r->rdlength == 4) && inet_ntop(AF_INET, r->rdata, buffer, INET6_ADDRSTRLEN))
blobmsg_add_string(buf, "ipv4", buffer);
break;
case TYPE_AAAA:
if ((r->rdlength == 16) && inet_ntop(AF_INET6, r->rdata, buffer, INET6_ADDRSTRLEN))
blobmsg_add_string(buf, "ipv6", buffer);
break;
}
if (r == last)
break;
next = avl_next_element(r, avl);
if (strcmp(r->record, next->record) != 0)
break;
}
}
void
cache_dump_recursive(struct blob_buf *b, const char *name, uint16_t type, struct interface *iface)
{
time_t now = monotonic_time();
for (struct cache_record *r = avl_find_ge_element(&records, name, r, avl);
r && !strcmp(r->record, name);
r = !avl_is_last(&records, &r->avl) ? avl_next_element(r, avl) : NULL) {
int32_t ttl = r->ttl - (now - r->time);
if (ttl <= 0 || (iface && iface->ifindex != r->iface->ifindex) ||
(type != TYPE_ANY && type != r->type))
continue;
const char *txt;
char buf[INET6_ADDRSTRLEN];
void *k = blobmsg_open_table(b, NULL), *l;
const struct dns_srv_data *dsd = (const struct dns_srv_data*)r->rdata;
blobmsg_add_string(b, "name", r->record);
blobmsg_add_string(b, "type", dns_type_string(r->type));
blobmsg_add_u32(b, "ttl", ttl);
switch (r->type) {
case TYPE_TXT:
if ((txt = r->txt) && strlen(txt)) {
l = blobmsg_open_array(b, "data");
do {
blobmsg_add_string(b, NULL, txt);
txt = &txt[strlen(txt) + 1];
} while (*txt);
blobmsg_close_array(b, l);
}
break;
case TYPE_SRV:
if (r->rdlength > sizeof(*dsd)) {
blobmsg_add_u32(b, "priority", be16_to_cpu(dsd->priority));
blobmsg_add_u32(b, "weight", be16_to_cpu(dsd->weight));
blobmsg_add_u32(b, "port", be16_to_cpu(dsd->port));
blobmsg_add_string(b, "target", (const char*)&dsd[1]);
}
break;
case TYPE_PTR:
if (r->rdlength > 0)
blobmsg_add_string(b, "target", (const char*)r->rdata);
break;
case TYPE_A:
if ((r->rdlength == 4) && inet_ntop(AF_INET, r->rdata, buf, sizeof(buf)))
blobmsg_add_string(b, "target", buf);
break;
case TYPE_AAAA:
if ((r->rdlength == 16) && inet_ntop(AF_INET6, r->rdata, buf, sizeof(buf)))
blobmsg_add_string(b, "target", buf);
break;
}
blobmsg_close_table(b, k);
if (r->type == TYPE_PTR) {
cache_dump_recursive(b, (const char*)r->rdata, TYPE_SRV, iface);
cache_dump_recursive(b, (const char*)r->rdata, TYPE_TXT, iface);
}
if (r->type == TYPE_SRV) {
cache_dump_recursive(b, (const char*)&dsd[1], TYPE_A, iface);
cache_dump_recursive(b, (const char*)&dsd[1], TYPE_AAAA, iface);
}
}
}

View File

@@ -1,61 +0,0 @@
/*
* Copyright (C) 2014 John Crispin <blogic@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _CACHE_H__
#define _CACHE_H__
#include <libubox/avl.h>
#include <libubox/list.h>
#include <libubox/blob.h>
#include "dns.h"
#include "interface.h"
struct cache_service {
struct avl_node avl;
const char *entry;
const char *host;
uint32_t ttl;
time_t time;
struct interface *iface;
int refresh;
};
struct cache_record {
struct avl_node avl;
const char *record;
uint16_t type;
uint32_t ttl;
int port;
const char *txt;
const uint8_t *rdata;
uint16_t rdlength;
time_t time;
struct interface *iface;
};
extern struct avl_tree services;
int cache_init(void);
void cache_scan(void);
void cache_cleanup(struct interface *iface);
void cache_answer(struct interface *iface, uint8_t *base, int blen,
char *name, struct dns_answer *a, uint8_t *rdata, int flush);
int cache_host_is_known(char *record);
char *cache_lookup_name(const char *key);
void cache_dump_records(struct blob_buf *buf, const char *name);
void cache_dump_recursive(struct blob_buf *b, const char *name, uint16_t type, struct interface *iface);
#endif

View File

@@ -1,404 +0,0 @@
/*
* Copyright (C) 2014 John Crispin <blogic@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <resolv.h>
#include <stdlib.h>
#include <string.h>
#include <libubox/uloop.h>
#include <libubox/usock.h>
#include <libubox/utils.h>
#include "announce.h"
#include "util.h"
#include "dns.h"
#include "cache.h"
#include "service.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)
{
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;
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, int unicast)
{
static struct dns_header h;
static struct dns_question q;
static struct iovec iov[] = {
{
.iov_base = &h,
.iov_len = sizeof(h),
},
{
.iov_base = dns_buffer,
},
{
.iov_base = &q,
.iov_len = sizeof(q),
}
};
int len;
h.questions = cpu_to_be16(1);
q.class = cpu_to_be16(((unicast) ? (CLASS_UNICAST) : (0)) | 1);
q.type = cpu_to_be16(type);
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 :");
}
struct dns_reply {
int type;
struct dns_answer a;
uint16_t rdlength;
uint8_t *rdata;
char *buffer;
};
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, int ttl)
{
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++;
}
void
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 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);
iov = alloca(sizeof(struct iovec) * ((dns_answer_cnt * 2) + 1));
iov[n_iov].iov_base = &h;
iov[n_iov].iov_len = sizeof(struct dns_header);
n_iov++;
answer_len = dn_comp(answer, buffer, sizeof(buffer), NULL, NULL);
if (answer_len < 1)
return;
blob_for_each_attr(attr, ans_buf.head, rem) {
struct dns_answer *a = blob_data(attr);
iov[n_iov].iov_base = buffer;
iov[n_iov].iov_len = answer_len;
n_iov++;
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(be16_to_cpu(a->type)), answer);
}
if (interface_send_packet(iface, iov, n_iov) < 0)
fprintf(stderr, "failed to send question\n");
}
static int
scan_name(const uint8_t *buffer, int len)
{
int offset = 0;
while (len && (*buffer != '\0')) {
int l = *buffer;
if (IS_COMPRESSED(l))
return offset + 2;
len -= l + 1;
offset += l + 1;
buffer += l + 1;
}
if (!len || !offset || (*buffer != '\0'))
return -1;
return offset + 1;
}
static struct dns_header*
dns_consume_header(uint8_t **data, int *len)
{
struct dns_header *h = (struct dns_header *) *data;
uint16_t *swap = (uint16_t *) h;
int endianess = 6;
if (*len < sizeof(struct dns_header))
return NULL;
while (endianess--) {
*swap = be16_to_cpu(*swap);
swap++;
}
*len -= sizeof(struct dns_header);
*data += sizeof(struct dns_header);
return h;
}
static struct dns_question*
dns_consume_question(uint8_t **data, int *len)
{
struct dns_question *q = (struct dns_question *) *data;
uint16_t *swap = (uint16_t *) q;
int endianess = 2;
if (*len < sizeof(struct dns_question))
return NULL;
while (endianess--) {
*swap = be16_to_cpu(*swap);
swap++;
}
*len -= sizeof(struct dns_question);
*data += sizeof(struct dns_question);
return q;
}
static struct dns_answer*
dns_consume_answer(uint8_t **data, int *len)
{
struct dns_answer *a = (struct dns_answer *) *data;
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);
*len -= sizeof(struct dns_answer);
*data += sizeof(struct dns_answer);
return a;
}
static char *
dns_consume_name(const uint8_t *base, int blen, uint8_t **data, int *len)
{
int nlen = scan_name(*data, *len);
if (nlen < 1)
return NULL;
if (dn_expand(base, base + blen, *data, name_buffer, MAX_NAME_LEN) < 0) {
perror("dns_consume_name/dn_expand");
return NULL;
}
*len -= nlen;
*data += nlen;
return name_buffer;
}
static int
parse_answer(struct interface *iface, 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;
uint8_t *rdata;
if (!name) {
fprintf(stderr, "dropping: bad question\n");
return -1;
}
a = dns_consume_answer(b, rlen);
if (!a) {
fprintf(stderr, "dropping: bad question\n");
return -1;
}
if ((a->class & ~CLASS_FLUSH) != CLASS_IN)
return -1;
rdata = *b;
if (a->rdlength > *rlen) {
fprintf(stderr, "dropping: bad question\n");
return -1;
}
*rlen -= a->rdlength;
*b += a->rdlength;
if (cache)
cache_answer(iface, buffer, len, name, a, rdata, a->class & CLASS_FLUSH);
return 0;
}
static void
parse_question(struct interface *iface, char *name, struct dns_question *q)
{
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:
if (!strcmp(name, mdns_hostname_local))
service_reply(iface, NULL, announce_ttl);
break;
case TYPE_PTR:
service_announce_services(iface, name, announce_ttl);
service_reply(iface, name, announce_ttl);
break;
case TYPE_AAAA:
case TYPE_A:
host = strstr(name, ".local");
if (host)
*host = '\0';
if (!strcmp(mdns_hostname, name))
service_reply_a(iface, announce_ttl);
break;
};
}
void
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;
int rlen = len;
h = dns_consume_header(&b, &rlen);
if (!h) {
fprintf(stderr, "dropping: bad header\n");
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;
if (!name) {
fprintf(stderr, "dropping: bad name\n");
return;
}
q = dns_consume_question(&b, &rlen);
if (!q) {
fprintf(stderr, "dropping: bad question\n");
return;
}
if (!(h->flags & FLAG_RESPONSE))
parse_question(iface, name, q);
}
if (!(h->flags & FLAG_RESPONSE))
return;
while (h->answers-- > 0)
if (parse_answer(iface, buffer, len, &b, &rlen, 1))
return;
while (h->authority-- > 0)
if (parse_answer(iface, buffer, len, &b, &rlen, 1))
return;
while (h->additional-- > 0)
if (parse_answer(iface, buffer, len, &b, &rlen, 1))
return;
}

View File

@@ -1,83 +0,0 @@
/*
* Copyright (C) 2014 John Crispin <blogic@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _DNS_H__
#define _DNS_H__
#include <stdint.h>
#include <arpa/inet.h>
#define FLAG_RESPONSE 0x8000
#define FLAG_AUTHORATIVE 0x0400
#define TYPE_A 0x0001
#define TYPE_PTR 0x000C
#define TYPE_TXT 0x0010
#define TYPE_AAAA 0x001c
#define TYPE_SRV 0x0021
#define TYPE_ANY 0x00ff
#define IS_COMPRESSED(x) ((x & 0xc0) == 0xc0)
#define MCAST_ADDR "224.0.0.251"
#define MCAST_ADDR6 "ff02::fb"
#define MCAST_PORT 5353
#define CLASS_FLUSH 0x8000
#define CLASS_UNICAST 0x8000
#define CLASS_IN 0x0001
#define MAX_NAME_LEN 8096
#define MAX_DATA_LEN 8096
#define C_DNS_SD "_services._dns-sd._udp.local"
struct dns_header {
uint16_t id;
uint16_t flags;
uint16_t questions;
uint16_t answers;
uint16_t authority;
uint16_t additional;
};
struct dns_srv_data {
uint16_t priority;
uint16_t weight;
uint16_t port;
} __attribute__((packed, aligned(2)));
struct dns_answer {
uint16_t type;
uint16_t class;
uint32_t ttl;
uint16_t rdlength;
} __attribute__((packed, aligned(2)));
struct dns_question {
uint16_t type;
uint16_t class;
} __attribute__((packed, aligned(2)));
struct interface;
extern int cfg_proto;
extern int cfg_no_subnet;
void dns_send_question(struct interface *iface, const char *question, int type, int unicast);
void dns_init_answer(void);
void dns_add_answer(int type, const uint8_t *rdata, uint16_t rdlength, int ttl);
void dns_send_answer(struct interface *iface, const char *answer);
const char* dns_type_string(uint16_t type);
void dns_handle_packet(struct interface *iface, struct sockaddr *s, uint16_t port, uint8_t *buf, int len);
#endif

View File

@@ -1,639 +0,0 @@
/*
* Copyright (C) 2014 John Crispin <blogic@openwrt.org>
* Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define _GNU_SOURCE
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/utsname.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <ifaddrs.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <libubox/usock.h>
#include <libubox/uloop.h>
#include <libubox/avl-cmp.h>
#include <libubox/utils.h>
#include "interface.h"
#include "util.h"
#include "dns.h"
#include "announce.h"
#include "service.h"
static int
interface_send_packet4(struct interface *iface, struct iovec *iov, int iov_len)
{
static size_t cmsg_data[( CMSG_SPACE(sizeof(struct in_pktinfo)) / sizeof(size_t)) + 1];
static struct sockaddr_in a;
static struct msghdr m = {
.msg_name = (struct sockaddr *) &a,
.msg_namelen = sizeof(a),
.msg_control = cmsg_data,
.msg_controllen = CMSG_LEN(sizeof(struct in_pktinfo)),
};
struct in_pktinfo *pkti;
struct cmsghdr *cmsg;
int fd = iface->fd.fd;
a.sin_family = AF_INET;
a.sin_port = htons(MCAST_PORT);
m.msg_iov = iov;
m.msg_iovlen = iov_len;
memset(cmsg_data, 0, sizeof(cmsg_data));
cmsg = CMSG_FIRSTHDR(&m);
cmsg->cmsg_len = m.msg_controllen;
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_PKTINFO;
pkti = (struct in_pktinfo*) CMSG_DATA(cmsg);
pkti->ipi_ifindex = iface->ifindex;
a.sin_addr.s_addr = inet_addr(MCAST_ADDR);
return sendmsg(fd, &m, 0);
}
static int
interface_send_packet6(struct interface *iface, struct iovec *iov, int iov_len)
{
static size_t cmsg_data[( CMSG_SPACE(sizeof(struct in6_pktinfo)) / sizeof(size_t)) + 1];
static struct sockaddr_in6 a;
static struct msghdr m = {
.msg_name = (struct sockaddr *) &a,
.msg_namelen = sizeof(a),
.msg_control = cmsg_data,
.msg_controllen = CMSG_LEN(sizeof(struct in6_pktinfo)),
};
struct in6_pktinfo *pkti;
struct cmsghdr *cmsg;
int fd = iface->fd.fd;
a.sin6_family = AF_INET6;
a.sin6_port = htons(MCAST_PORT);
m.msg_iov = iov;
m.msg_iovlen = iov_len;
memset(cmsg_data, 0, sizeof(cmsg_data));
cmsg = CMSG_FIRSTHDR(&m);
cmsg->cmsg_len = m.msg_controllen;
cmsg->cmsg_level = IPPROTO_IPV6;
cmsg->cmsg_type = IPV6_PKTINFO;
pkti = (struct in6_pktinfo*) CMSG_DATA(cmsg);
pkti->ipi6_ifindex = iface->ifindex;
inet_pton(AF_INET6, MCAST_ADDR6, &a.sin6_addr);
return sendmsg(fd, &m, 0);
}
int
interface_send_packet(struct interface *iface, struct iovec *iov, int iov_len)
{
if (debug > 1) {
fprintf(stderr, "TX ipv%d: %s\n", iface->v6 * 2 + 4, iface->name);
fprintf(stderr, " multicast: %d\n", iface->multicast);
}
if (iface->v6)
return interface_send_packet6(iface, iov, iov_len);
return interface_send_packet4(iface, iov, iov_len);
}
static void interface_close(struct interface *iface)
{
if (iface->fd.fd < 0)
return;
announce_free(iface);
uloop_fd_delete(&iface->fd);
close(iface->fd.fd);
iface->fd.fd = -1;
}
static void interface_free(struct interface *iface)
{
interface_close(iface);
free(iface);
}
static int
interface_valid_src(void *ip1, void *mask, void *ip2, int len)
{
uint8_t *i1 = ip1;
uint8_t *i2 = ip2;
uint8_t *m = mask;
int i;
if (cfg_no_subnet)
return 0;
for (i = 0; i < len; i++, i1++, i2++, m++) {
if ((*i1 & *m) != (*i2 & *m))
return -1;
}
return 0;
}
static void
read_socket4(struct uloop_fd *u, unsigned int events)
{
struct interface *iface = container_of(u, struct interface, fd);
static uint8_t buffer[8 * 1024];
struct iovec iov[1];
char cmsg[CMSG_SPACE(sizeof(struct in_pktinfo)) + CMSG_SPACE(sizeof(int)) + 1];
struct cmsghdr *cmsgptr;
struct msghdr msg;
socklen_t len;
struct sockaddr_in from;
int flags = 0, ifindex = -1;
uint8_t ttl = 0;
struct in_pktinfo *inp = NULL;
if (u->eof) {
interface_close(iface);
uloop_timeout_set(&iface->reconnect, 1000);
return;
}
iov[0].iov_base = buffer;
iov[0].iov_len = sizeof(buffer);
memset(&msg, 0, sizeof(msg));
msg.msg_name = (struct sockaddr *) &from;
msg.msg_namelen = sizeof(struct sockaddr_in);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = &cmsg;
msg.msg_controllen = sizeof(cmsg);
len = recvmsg(u->fd, &msg, flags);
if (len == -1) {
perror("read failed");
return;
}
for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) {
void *c = CMSG_DATA(cmsgptr);
switch (cmsgptr->cmsg_type) {
case IP_PKTINFO:
inp = ((struct in_pktinfo *) c);
break;
case IP_TTL:
ttl = (uint8_t) *((int *) c);
break;
default:
fprintf(stderr, "unknown cmsg %x\n", cmsgptr->cmsg_type);
return;
}
}
if (ttl != 255)
return;
if (debug > 1) {
char buf[256];
fprintf(stderr, "RX ipv4: %s\n", iface->name);
fprintf(stderr, " multicast: %d\n", iface->multicast);
inet_ntop(AF_INET, &from.sin_addr, buf, 256);
fprintf(stderr, " src %s:%d\n", buf, from.sin_port);
inet_ntop(AF_INET, &inp->ipi_spec_dst, buf, 256);
fprintf(stderr, " dst %s\n", buf);
inet_ntop(AF_INET, &inp->ipi_addr, buf, 256);
fprintf(stderr, " real %s\n", buf);
}
if (inp->ipi_ifindex != iface->ifindex)
fprintf(stderr, "invalid iface index %d != %d\n", ifindex, iface->ifindex);
else if (!interface_valid_src((void *) &iface->v4_addr, (void *) &iface->v4_netmask, (void *) &from.sin_addr, 4))
dns_handle_packet(iface, (struct sockaddr *) &from, from.sin_port, buffer, len);
}
static void
read_socket6(struct uloop_fd *u, unsigned int events)
{
struct interface *iface = container_of(u, struct interface, fd);
static uint8_t buffer[8 * 1024];
struct iovec iov[1];
char cmsg6[CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(int)) + 1];
struct cmsghdr *cmsgptr;
struct msghdr msg;
socklen_t len;
struct sockaddr_in6 from;
int flags = 0, ifindex = -1;
int ttl = 0;
struct in6_pktinfo *inp = NULL;
if (u->eof) {
interface_close(iface);
uloop_timeout_set(&iface->reconnect, 1000);
return;
}
iov[0].iov_base = buffer;
iov[0].iov_len = sizeof(buffer);
memset(&msg, 0, sizeof(msg));
msg.msg_name = (struct sockaddr *) &from;
msg.msg_namelen = sizeof(struct sockaddr_in6);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = &cmsg6;
msg.msg_controllen = sizeof(cmsg6);
len = recvmsg(u->fd, &msg, flags);
if (len == -1) {
perror("read failed");
return;
}
for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) {
void *c = CMSG_DATA(cmsgptr);
switch (cmsgptr->cmsg_type) {
case IPV6_PKTINFO:
inp = ((struct in6_pktinfo *) c);
break;
case IPV6_HOPLIMIT:
ttl = (uint8_t) *((int *) c);
break;
default:
fprintf(stderr, "unknown cmsg %x\n", cmsgptr->cmsg_type);
return;
}
}
if (ttl != 255)
return;
if (debug > 1) {
char buf[256];
fprintf(stderr, "RX ipv6: %s\n", iface->name);
fprintf(stderr, " multicast: %d\n", iface->multicast);
inet_ntop(AF_INET6, &from.sin6_addr, buf, 256);
fprintf(stderr, " src %s:%d\n", buf, from.sin6_port);
inet_ntop(AF_INET6, &inp->ipi6_addr, buf, 256);
fprintf(stderr, " dst %s\n", buf);
}
if (inp->ipi6_ifindex != iface->ifindex)
fprintf(stderr, "invalid iface index %d != %d\n", ifindex, iface->ifindex);
else if (!interface_valid_src((void *) &iface->v4_addr, (void *) &iface->v4_netmask, (void *) &from.sin6_addr, 16))
dns_handle_packet(iface, (struct sockaddr *) &from, from.sin6_port, buffer, len);
}
static int
interface_mcast_setup4(struct interface *iface)
{
struct ip_mreqn mreq;
uint8_t ttl = 255;
int no = 0;
struct sockaddr_in sa = { 0 };
int fd = iface->fd.fd;
sa.sin_family = AF_INET;
sa.sin_port = htons(MCAST_PORT);
inet_pton(AF_INET, MCAST_ADDR, &sa.sin_addr);
memset(&mreq, 0, sizeof(mreq));
mreq.imr_address.s_addr = iface->v4_addr.s_addr;
mreq.imr_multiaddr = sa.sin_addr;
mreq.imr_ifindex = iface->ifindex;
if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0)
fprintf(stderr, "ioctl failed: IP_MULTICAST_TTL\n");
/* Some network drivers have issues with dropping membership of
* mcast groups when the iface is down, but don't allow rejoining
* when it comes back up. This is an ugly workaround
* -- this was copied from avahi --
*/
setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq));
if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
fprintf(stderr, "failed to join multicast group: %s\n", strerror(errno));
close(fd);
fd = -1;
return -1;
}
if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &no, sizeof(no)) < 0)
fprintf(stderr, "ioctl failed: IP_MULTICAST_LOOP\n");
return 0;
}
static int
interface_socket_setup6(struct interface *iface)
{
struct ipv6_mreq mreq;
int ttl = 255;
int no = 0;
struct sockaddr_in6 sa = { 0 };
int fd = iface->fd.fd;
sa.sin6_family = AF_INET6;
sa.sin6_port = htons(MCAST_PORT);
inet_pton(AF_INET6, MCAST_ADDR6, &sa.sin6_addr);
memset(&mreq, 0, sizeof(mreq));
mreq.ipv6mr_multiaddr = sa.sin6_addr;
mreq.ipv6mr_interface = iface->ifindex;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) < 0)
fprintf(stderr, "ioctl failed: IPV6_MULTICAST_HOPS\n");
setsockopt(fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq, sizeof(mreq));
if (setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
fprintf(stderr, "failed to join multicast group: %s\n", strerror(errno));
close(fd);
fd = -1;
return -1;
}
if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &no, sizeof(no)) < 0)
fprintf(stderr, "ioctl failed: IPV6_MULTICAST_LOOP\n");
return 0;
}
static void
reconnect_socket4(struct uloop_timeout *timeout)
{
struct interface *iface = container_of(timeout, struct interface, reconnect);
int yes = 1;
iface->fd.fd = usock(USOCK_UDP | USOCK_SERVER | USOCK_NONBLOCK | USOCK_IPV4ONLY,
(iface->multicast) ? (iface->mcast_addr) : (iface->v4_addrs), "5353");
if (iface->fd.fd < 0) {
fprintf(stderr, "failed to add listener %s: %s\n", iface->mcast_addr, strerror(errno));
goto retry;
}
if (setsockopt(iface->fd.fd, SOL_SOCKET, SO_BINDTODEVICE, iface->name, strlen(iface->name) < 0))
fprintf(stderr, "ioctl failed: SO_BINDTODEVICE\n");
if (setsockopt(iface->fd.fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0)
fprintf(stderr, "ioctl failed: SO_REUSEADDR\n");
if (setsockopt(iface->fd.fd, IPPROTO_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0)
fprintf(stderr, "ioctl failed: IP_RECVTTL\n");
if (setsockopt(iface->fd.fd, IPPROTO_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0)
fprintf(stderr, "ioctl failed: IP_PKTINFO\n");
if (iface->multicast && interface_mcast_setup4(iface)) {
iface->fd.fd = -1;
goto retry;
}
uloop_fd_add(&iface->fd, ULOOP_READ);
if (iface->multicast) {
dns_send_question(iface, "_services._dns-sd._udp.local", TYPE_PTR, 1);
announce_init(iface);
}
return;
retry:
uloop_timeout_set(timeout, 1000);
}
static void
reconnect_socket6(struct uloop_timeout *timeout)
{
struct interface *iface = container_of(timeout, struct interface, reconnect);
char mcast_addr[128];
int ttl = 255;
int yes = 1;
snprintf(mcast_addr, sizeof(mcast_addr), "%s%%%s", (iface->multicast) ? (iface->mcast_addr) : (iface->v6_addrs), iface->name);
iface->fd.fd = usock(USOCK_UDP | USOCK_SERVER | USOCK_NONBLOCK | USOCK_IPV6ONLY, mcast_addr, "5353");
if (iface->fd.fd < 0) {
fprintf(stderr, "failed to add listener %s: %s\n", mcast_addr, strerror(errno));
goto retry;
}
if (setsockopt(iface->fd.fd, SOL_SOCKET, SO_BINDTODEVICE, iface->name, strlen(iface->name) < 0))
fprintf(stderr, "ioctl failed: SO_BINDTODEVICE\n");
if (setsockopt(iface->fd.fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)) < 0)
fprintf(stderr, "ioctl failed: IPV6_UNICAST_HOPS\n");
if (setsockopt(iface->fd.fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &yes, sizeof(yes)) < 0)
fprintf(stderr, "ioctl failed: IPV6_RECVPKTINFO\n");
if (setsockopt(iface->fd.fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &yes, sizeof(yes)) < 0)
fprintf(stderr, "ioctl failed: IPV6_RECVHOPLIMIT\n");
if (setsockopt(iface->fd.fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0)
fprintf(stderr, "ioctl failed: SO_REUSEADDR\n");
if (iface->multicast && interface_socket_setup6(iface)) {
iface->fd.fd = -1;
goto retry;
}
uloop_fd_add(&iface->fd, ULOOP_READ);
if (iface->multicast) {
dns_send_question(iface, "_services._dns-sd._udp.local", TYPE_PTR, 1);
announce_init(iface);
}
return;
retry:
uloop_timeout_set(timeout, 1000);
}
static void interface_start(struct interface *iface)
{
if (iface->v6) {
iface->fd.cb = read_socket6;
iface->reconnect.cb = reconnect_socket6;
} else {
iface->fd.cb = read_socket4;
iface->reconnect.cb = reconnect_socket4;
}
uloop_timeout_set(&iface->reconnect, 100);
}
static void
iface_update_cb(struct vlist_tree *tree, struct vlist_node *node_new,
struct vlist_node *node_old)
{
struct interface *iface;
if (node_old) {
iface = container_of(node_old, struct interface, node);
interface_free(iface);
}
if (node_new) {
iface = container_of(node_new, struct interface, node);
interface_start(iface);
}
}
static struct interface* _interface_add(const char *name, int multicast, int v6)
{
struct interface *iface;
char *name_buf;
char *id_buf;
iface = calloc_a(sizeof(*iface),
&name_buf, strlen(name) + 1,
&id_buf, strlen(name) + 5);
sprintf(id_buf, "%d_%d_%s", multicast, v6, name);
iface->name = strcpy(name_buf, name);
iface->id = id_buf;
iface->ifindex = if_nametoindex(name);
iface->fd.fd = -1;
iface->multicast = multicast;
iface->v6 = v6;
if (v6)
iface->mcast_addr = MCAST_ADDR6;
else
iface->mcast_addr = MCAST_ADDR;
if (iface->ifindex <= 0)
goto error;
vlist_add(&interfaces, &iface->node, iface->id);
return iface;
error:
free(iface);
return NULL;
}
int interface_add(const char *name)
{
struct interface *v4 = NULL, *v6 = NULL, *unicast;
struct ifaddrs *ifap, *ifa;
getifaddrs(&ifap);
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
if (strcmp(ifa->ifa_name, name))
continue;
if (ifa->ifa_addr->sa_family == AF_INET && !v4) {
struct sockaddr_in *sa;
if (cfg_proto && (cfg_proto != 4))
continue;
unicast = _interface_add(name, 0, 0);
if (!unicast)
continue;
v4 = _interface_add(name, 1, 0);
if (!v4)
continue;
sa = (struct sockaddr_in *) ifa->ifa_addr;
memcpy(&v4->v4_addr, &sa->sin_addr, sizeof(v4->v4_addr));
memcpy(&unicast->v4_addr, &sa->sin_addr, sizeof(unicast->v4_addr));
inet_ntop(AF_INET, &sa->sin_addr, v4->v4_addrs, sizeof(v4->v4_addrs));
inet_ntop(AF_INET, &sa->sin_addr, unicast->v4_addrs, sizeof(unicast->v4_addrs));
sa = (struct sockaddr_in *) ifa->ifa_netmask;
memcpy(&unicast->v4_netmask, &sa->sin_addr, sizeof(unicast->v4_netmask));
memcpy(&v4->v4_netmask, &sa->sin_addr, sizeof(v4->v4_netmask));
v4->peer = unicast;
unicast->peer = v4;
}
if (ifa->ifa_addr->sa_family == AF_INET6 && !v6) {
uint8_t ll_prefix[] = {0xfe, 0x80 };
struct sockaddr_in6 *sa6;
if (cfg_proto && (cfg_proto != 6))
continue;
sa6 = (struct sockaddr_in6 *) ifa->ifa_addr;
if (memcmp(&sa6->sin6_addr, &ll_prefix, 2))
continue;
unicast = _interface_add(name, 0, 1);
if (!unicast)
continue;
v6 = _interface_add(name, 1, 1);
if (!v6)
continue;
memcpy(&v6->v6_addr, &sa6->sin6_addr, sizeof(v6->v6_addr));
memcpy(&unicast->v6_addr, &sa6->sin6_addr, sizeof(unicast->v6_addr));
inet_ntop(AF_INET6, &sa6->sin6_addr, v6->v6_addrs, sizeof(v6->v6_addrs));
inet_ntop(AF_INET6, &sa6->sin6_addr, unicast->v6_addrs, sizeof(unicast->v6_addrs));
sa6 = (struct sockaddr_in6 *) ifa->ifa_netmask;
memcpy(&v6->v6_netmask, &sa6->sin6_addr, sizeof(v6->v6_netmask));
memcpy(&unicast->v6_netmask, &sa6->sin6_addr, sizeof(unicast->v6_netmask));
v6->peer = unicast;
unicast->peer = v6;
}
}
freeifaddrs(ifap);
return !v4 && !v6;
}
void interface_shutdown(void)
{
struct interface *iface;
vlist_for_each_element(&interfaces, iface, node)
if (iface->fd.fd > 0 && iface->multicast) {
service_announce(iface, 0);
service_reply_a(iface, 0);
}
vlist_for_each_element(&interfaces, iface, node)
interface_close(iface);
}
struct interface*
interface_get(const char *name, int v6, int multicast)
{
char id_buf[32];
snprintf(id_buf, sizeof(id_buf), "%d_%d_%s", multicast, v6, name);
struct interface *iface = vlist_find(&interfaces, id_buf, iface, node);
return iface;
}
VLIST_TREE(interfaces, avl_strcmp, iface_update_cb, false, false);

View File

@@ -1,59 +0,0 @@
/*
* Copyright (C) 2014 John Crispin <blogic@openwrt.org>
* Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __MDNS_INTERFACE_H
#define __MDNS_INTERFACE_H
#include <sys/types.h>
#include <sys/uio.h>
#include <arpa/inet.h>
#include <libubox/uloop.h>
#include <libubox/vlist.h>
extern struct vlist_tree interfaces;
struct interface {
struct vlist_node node;
struct interface *peer;
const char *name;
char *id;
struct uloop_fd fd;
struct uloop_timeout reconnect;
int v6;
int multicast;
int ifindex;
struct in_addr v4_addr;
struct in_addr v4_netmask;
struct in6_addr v6_addr;
struct in6_addr v6_netmask;
char v4_addrs[16];
char v6_addrs[64];
struct uloop_timeout announce_timer;
int announce_state;
char *mcast_addr;
};
int interface_add(const char *name);
void interface_shutdown(void);
int interface_send_packet(struct interface *iface, struct iovec *iov, int iov_len);
struct interface* interface_get(const char *name, int v6, int multicast);
#endif

View File

@@ -1,3 +0,0 @@
{
"http_80": { "service": "_http._tcp.local", "port": 80, "txt": [ "foo=bar"] },
}

View File

@@ -1,3 +0,0 @@
{
"ssh_22": { "service": "_ssh._tcp.local", "port": 22, "txt": [ "daemon=dropbear" ] }
}

View File

@@ -1,106 +0,0 @@
/*
* Copyright (C) 2014 John Crispin <blogic@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <stdio.h>
#include <fcntl.h>
#include <getopt.h>
#include <resolv.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <ubus/libubus.h>
#include <libubox/uloop.h>
#include "dns.h"
#include "ubus.h"
#include "util.h"
#include "cache.h"
#include "service.h"
#include "announce.h"
#include "interface.h"
int cfg_proto = 0;
int cfg_no_subnet = 0;
static void
signal_shutdown(int signal)
{
uloop_end();
}
int
main(int argc, char **argv)
{
int ch, ttl;
uloop_init();
while ((ch = getopt(argc, argv, "t:i:d46n")) != -1) {
switch (ch) {
case 't':
ttl = atoi(optarg);
if (ttl > 0)
announce_ttl = ttl;
else
fprintf(stderr, "invalid ttl\n");
break;
case 'd':
debug++;
break;
case 'i':
interface_add(optarg);
break;
case '4':
cfg_proto = 4;
break;
case '6':
cfg_proto = 6;
break;
case 'n':
cfg_no_subnet = 1;
break;
default:
return -1;
}
}
signal(SIGPIPE, SIG_IGN);
signal(SIGTERM, signal_shutdown);
signal(SIGKILL, signal_shutdown);
if (cache_init())
return -1;
ubus_startup();
service_init(0);
uloop_run();
uloop_done();
interface_shutdown();
cache_cleanup(NULL);
service_cleanup();
vlist_flush(&interfaces);
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,390 +0,0 @@
/*
* Copyright (C) 2014 John Crispin <blogic@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <sys/types.h>
#include <arpa/nameser.h>
#include <sys/socket.h>
#include <ifaddrs.h>
#include <resolv.h>
#include <glob.h>
#include <stdio.h>
#include <time.h>
#include <ubus/libubus.h>
#include <libubox/vlist.h>
#include <libubox/uloop.h>
#include <libubox/avl-cmp.h>
#include <libubox/blobmsg_json.h>
#include "ubus.h"
#include "dns.h"
#include "service.h"
#include "util.h"
#include "interface.h"
#include "announce.h"
enum {
SERVICE_SERVICE,
SERVICE_PORT,
SERVICE_TXT,
__SERVICE_MAX,
};
struct service {
struct vlist_node node;
time_t t;
const char *id;
const char *service;
const uint8_t *txt;
int txt_len;
int port;
int active;
};
static const struct blobmsg_policy service_policy[__SERVICE_MAX] = {
[SERVICE_SERVICE] = { .name = "service", .type = BLOBMSG_TYPE_STRING },
[SERVICE_PORT] = { .name = "port", .type = BLOBMSG_TYPE_INT32 },
[SERVICE_TXT] = { .name = "txt", .type = BLOBMSG_TYPE_ARRAY },
};
static void
service_update(struct vlist_tree *tree, struct vlist_node *node_new,
struct vlist_node *node_old);
static struct blob_buf b;
static VLIST_TREE(services, avl_strcmp, service_update, false, false);
static char *sdudp = "_services._dns-sd._udp.local";
static char *sdtcp = "_services._dns-sd._tcp.local";
static int service_init_announce;
static const char *
service_name(const char *domain)
{
static char buffer[256];
snprintf(buffer, sizeof(buffer), "%s.%s", mdns_hostname, domain);
return buffer;
}
static void
service_add_ptr(const char *host, int ttl)
{
int len = dn_comp(host, mdns_buf, sizeof(mdns_buf), NULL, NULL);
if (len < 1)
return;
dns_add_answer(TYPE_PTR, mdns_buf, len, ttl);
}
static void
service_add_srv(struct service *s, int ttl)
{
struct dns_srv_data *sd = (struct dns_srv_data *) mdns_buf;
int len = sizeof(*sd);
len += dn_comp(mdns_hostname_local, mdns_buf + len, sizeof(mdns_buf) - len, NULL, NULL);
if (len <= sizeof(*sd))
return;
sd->port = cpu_to_be16(s->port);
dns_add_answer(TYPE_SRV, mdns_buf, len, ttl);
}
#define TOUT_LOOKUP 60
static int
service_timeout(struct service *s)
{
time_t t = monotonic_time();
if (t - s->t <= TOUT_LOOKUP)
return 0;
s->t = t;
return 1;
}
void
service_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_send_answer(iface, mdns_hostname_local);
freeifaddrs(ifap);
}
static void
service_reply_single(struct interface *iface, struct service *s, const char *match, int ttl, int force)
{
const char *host = service_name(s->service);
char *service = strstr(host, "._");
if (!force && (!s->active || !service || !service_timeout(s)))
return;
service++;
if (match && strcmp(match, s->service))
return;
dns_init_answer();
service_add_ptr(service_name(s->service), ttl);
dns_send_answer(iface, service);
dns_init_answer();
service_add_srv(s, ttl);
if (s->txt && s->txt_len)
dns_add_answer(TYPE_TXT, (uint8_t *) s->txt, s->txt_len, ttl);
dns_send_answer(iface, host);
}
void
service_reply(struct interface *iface, const char *match, int ttl)
{
struct service *s;
vlist_for_each_element(&services, s, node)
service_reply_single(iface, s, match, ttl, 0);
if (match)
return;
if (ttl)
service_reply_a(iface, ttl);
}
void
service_announce_services(struct interface *iface, const char *service, int ttl)
{
struct service *s;
int tcp = 1;
if (!strcmp(service, sdudp))
tcp = 0;
else if (strcmp(service, sdtcp))
return;
vlist_for_each_element(&services, s, node) {
if (!strstr(s->service, "._tcp") && tcp)
continue;
if (!strstr(s->service, "._udp") && !tcp)
continue;
s->t = 0;
if (ttl) {
dns_init_answer();
service_add_ptr(s->service, ttl);
if (tcp)
dns_send_answer(iface, sdtcp);
else
dns_send_answer(iface, sdudp);
}
service_reply(iface, s->service, ttl);
}
}
void
service_announce(struct interface *iface, int ttl)
{
service_announce_services(iface, sdudp, ttl);
service_announce_services(iface, sdtcp, ttl);
}
static void
service_update(struct vlist_tree *tree, struct vlist_node *node_new,
struct vlist_node *node_old)
{
struct interface *iface;
struct service *s;
if (!node_old) {
s = container_of(node_new, struct service, node);
if (service_init_announce)
vlist_for_each_element(&interfaces, iface, node) {
s->t = 0;
service_reply_single(iface, s, NULL, announce_ttl, 1);
}
return;
}
s = container_of(node_old, struct service, node);
if (!node_new && service_init_announce)
vlist_for_each_element(&interfaces, iface, node)
service_reply_single(iface, s, NULL, 0, 1);
free(s);
}
static void
service_load_blob(struct blob_attr *b)
{
struct blob_attr *txt, *_tb[__SERVICE_MAX];
struct service *s;
char *d_service, *d_id;
uint8_t *d_txt;
int rem2;
int txt_len = 0;
blobmsg_parse(service_policy, ARRAY_SIZE(service_policy),
_tb, blobmsg_data(b), blobmsg_data_len(b));
if (!_tb[SERVICE_PORT] || !_tb[SERVICE_SERVICE])
return;
if (_tb[SERVICE_SERVICE])
blobmsg_for_each_attr(txt, _tb[SERVICE_TXT], rem2)
txt_len += 1 + strlen(blobmsg_get_string(txt));
s = calloc_a(sizeof(*s),
&d_id, strlen(blobmsg_name(b)) + 1,
&d_service, strlen(blobmsg_get_string(_tb[SERVICE_SERVICE])) + 1,
&d_txt, txt_len);
if (!s)
return;
s->port = blobmsg_get_u32(_tb[SERVICE_PORT]);
s->id = strcpy(d_id, blobmsg_name(b));
s->service = strcpy(d_service, blobmsg_get_string(_tb[SERVICE_SERVICE]));
s->active = 1;
s->t = 0;
s->txt_len = txt_len;
s->txt = d_txt;
if (_tb[SERVICE_SERVICE])
blobmsg_for_each_attr(txt, _tb[SERVICE_TXT], rem2) {
int len = strlen(blobmsg_get_string(txt));
if (!len)
return;
if (len > 0xff)
len = 0xff;
*d_txt = len;
d_txt++;
memcpy(d_txt, blobmsg_get_string(txt), len);
d_txt += len;
}
vlist_add(&services, &s->node, s->id);
}
static void
service_load(char *path)
{
struct blob_attr *cur;
glob_t gl;
int i, rem;
if (glob(path, GLOB_NOESCAPE | GLOB_MARK, NULL, &gl))
return;
for (i = 0; i < gl.gl_pathc; i++) {
blob_buf_init(&b, 0);
if (blobmsg_add_json_from_file(&b, gl.gl_pathv[i]))
blob_for_each_attr(cur, b.head, rem)
service_load_blob(cur);
}
globfree(&gl);
}
static void
service_init_cb(struct ubus_request *req, int type, struct blob_attr *msg)
{
struct blob_attr *cur;
int rem;
get_hostname();
vlist_update(&services);
service_load("/tmp/run/mdns/*");
blob_for_each_attr(cur, msg, rem) {
struct blob_attr *cur2;
int rem2;
blobmsg_for_each_attr(cur2, cur, rem2) {
struct blob_attr *cur3;
int rem3;
if (strcmp(blobmsg_name(cur2), "instances"))
continue;
blobmsg_for_each_attr(cur3, cur2, rem3) {
struct blob_attr *cur4;
int rem4;
int running = 0;
blobmsg_for_each_attr(cur4, cur3, rem4) {
const char *name = blobmsg_name(cur4);
if (!strcmp(name, "running")) {
running = blobmsg_get_bool(cur4);
} else if (running && !strcmp(name, "data")) {
struct blob_attr *cur5;
int rem5;
blobmsg_for_each_attr(cur5, cur4, rem5) {
struct blob_attr *cur6;
int rem6;
if (strcmp(blobmsg_name(cur5), "mdns"))
continue;
blobmsg_for_each_attr(cur6, cur5, rem6)
service_load_blob(cur6);
}
break;
}
}
}
}
}
vlist_flush(&services);
}
void
service_init(int announce)
{
get_hostname();
service_init_announce = announce;
ubus_service_list(service_init_cb);
}
void
service_cleanup(void)
{
vlist_flush(&services);
blob_buf_free(&b);
}

View File

@@ -1,24 +0,0 @@
/*
* Copyright (C) 2014 John Crispin <blogic@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _SERVICE_H__
#define _SERVICE_H__
extern void service_init(int announce);
extern void service_cleanup(void);
extern void service_announce(struct interface *iface, int ttl);
extern void service_announce_services(struct interface *iface, const char *service, int ttl);
extern void service_reply(struct interface *iface, const char *match, int ttl);
extern void service_reply_a(struct interface *iface, int ttl);
#endif

View File

@@ -1,275 +0,0 @@
/*
* Copyright (C) 2014 John Crispin <blogic@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <sys/types.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <ubus/libubus.h>
#include <libubox/vlist.h>
#include <libubox/uloop.h>
#include "util.h"
#include "ubus.h"
#include "cache.h"
#include "service.h"
#include "interface.h"
static struct ubus_auto_conn conn;
static struct blob_buf b;
static int
mdns_reload(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
service_init(1);
return 0;
}
static int
mdns_scan(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
cache_scan();
return 0;
}
static int
mdns_browse(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct cache_service *s, *q;
char *buffer = (char *) mdns_buf;
void *c1 = NULL, *c2;
blob_buf_init(&b, 0);
avl_for_each_element(&services, s, avl) {
char *local;
if (*((char *) s->avl.key) != '_')
continue;
snprintf(buffer, MAX_NAME_LEN, "%s", (const char *) s->avl.key);
local = strstr(buffer, ".local");
if (local)
*local = '\0';
if (!strcmp(buffer, "_tcp") || !strcmp(buffer, "_udp"))
continue;
if (!c1) {
c1 = blobmsg_open_table(&b, buffer);
}
snprintf(buffer, MAX_NAME_LEN, "%s", (const char *) s->entry);
local = strstr(buffer, "._");
if (local)
*local = '\0';
c2 = blobmsg_open_table(&b, buffer);
strncat(buffer, ".local", MAX_NAME_LEN);
cache_dump_records(&b, buffer);
cache_dump_records(&b, s->entry);
blobmsg_close_table(&b, c2);
q = avl_next_element(s, avl);
if (!q || avl_is_last(&services, &s->avl) || strcmp(s->avl.key, q->avl.key)) {
blobmsg_close_table(&b, c1);
c1 = NULL;
}
}
ubus_send_reply(ctx, req, b.head);
return UBUS_STATUS_OK;
}
static int
mdns_hosts(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct cache_service *s;
char *buffer = (char *) mdns_buf;
void *c;
blob_buf_init(&b, 0);
avl_for_each_element(&services, s, avl) {
char *local;
if (*((char *) s->avl.key) == '_')
continue;
snprintf(buffer, MAX_NAME_LEN, "%s", (const char *) s->entry);
local = strstr(buffer, "._");
if (local)
*local = '\0';
c = blobmsg_open_table(&b, buffer);
strncat(buffer, ".local", MAX_NAME_LEN);
cache_dump_records(&b, buffer);
cache_dump_records(&b, s->entry);
blobmsg_close_table(&b, c);
}
ubus_send_reply(ctx, req, b.head);
return UBUS_STATUS_OK;
}
enum {
CFG_INTERFACES,
CFG_KEEP,
CFG_MAX
};
static const struct blobmsg_policy config_policy[] = {
[CFG_INTERFACES] = { "interfaces", BLOBMSG_TYPE_ARRAY },
[CFG_KEEP] = { "keep", BLOBMSG_TYPE_BOOL },
};
static int
mdns_set_config(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *data[CFG_MAX], *cur;
int rem, keep = false;
blobmsg_parse(config_policy, CFG_MAX, data, blob_data(msg), blob_len(msg));
if (!data[CFG_INTERFACES])
return UBUS_STATUS_INVALID_ARGUMENT;
if (!blobmsg_check_attr_list(data[CFG_INTERFACES], BLOBMSG_TYPE_STRING))
return UBUS_STATUS_INVALID_ARGUMENT;
keep = data[CFG_KEEP] && blobmsg_get_bool(data[CFG_KEEP]);
if (!keep) {
vlist_update(&interfaces);
ubus_notify(ctx, obj, "set_config", NULL, 1000);
}
blobmsg_for_each_attr(cur, data[CFG_INTERFACES], rem)
interface_add(blobmsg_data(cur));
if (!keep)
vlist_flush(&interfaces);
return 0;
}
enum query_attr {
QUERY_QUESTION,
QUERY_IFACE,
QUERY_TYPE,
QUERY_MAX
};
static const struct blobmsg_policy query_policy[QUERY_MAX] = {
[QUERY_QUESTION]= { "question", BLOBMSG_TYPE_STRING },
[QUERY_IFACE] = { "interface", BLOBMSG_TYPE_STRING },
[QUERY_TYPE] = { "type", BLOBMSG_TYPE_INT32 },
};
static int
mdns_query(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[QUERY_MAX], *c;
const char *question = "_services._dns-sd._udp.local";
const char *ifname;
int type = TYPE_ANY;
blobmsg_parse(query_policy, QUERY_MAX, tb, blob_data(msg), blob_len(msg));
if (!(c = tb[QUERY_IFACE]))
return UBUS_STATUS_INVALID_ARGUMENT;
ifname = blobmsg_get_string(c);
if ((c = tb[QUERY_QUESTION]))
question = blobmsg_get_string(c);
if ((c = tb[QUERY_TYPE]))
type = blobmsg_get_u32(c);
struct interface *iface_v4 = interface_get(ifname, 0, 1);
struct interface *iface_v6 = interface_get(ifname, 1, 1);
if (!iface_v4 && !iface_v6)
return UBUS_STATUS_NOT_FOUND;
if (!strcmp(method, "query")) {
if (iface_v4)
dns_send_question(iface_v4, question, type, 0);
if (iface_v6)
dns_send_question(iface_v6, question, type, 0);
return UBUS_STATUS_OK;
} else if (!strcmp(method, "fetch")) {
blob_buf_init(&b, 0);
void *k = blobmsg_open_array(&b, "records");
cache_dump_recursive(&b, question, type, iface_v4 ? iface_v4 : iface_v6);
blobmsg_close_array(&b, k);
ubus_send_reply(ctx, req, b.head);
return UBUS_STATUS_OK;
} else {
return UBUS_STATUS_INVALID_ARGUMENT;
}
}
static const struct ubus_method mdns_methods[] = {
UBUS_METHOD("set_config", mdns_set_config, config_policy),
UBUS_METHOD("query", mdns_query, query_policy),
UBUS_METHOD("fetch", mdns_query, query_policy),
UBUS_METHOD_NOARG("scan", mdns_scan),
UBUS_METHOD_NOARG("browse", mdns_browse),
UBUS_METHOD_NOARG("hosts", mdns_hosts),
UBUS_METHOD_NOARG("reload", mdns_reload),
};
static struct ubus_object_type mdns_object_type =
UBUS_OBJECT_TYPE("mdns", mdns_methods);
static struct ubus_object mdns_object = {
.name = "mdns",
.type = &mdns_object_type,
.methods = mdns_methods,
.n_methods = ARRAY_SIZE(mdns_methods),
};
static void
ubus_connect_handler(struct ubus_context *ctx)
{
int ret;
ret = ubus_add_object(ctx, &mdns_object);
if (ret)
fprintf(stderr, "Failed to add object: %s\n", ubus_strerror(ret));
}
void
ubus_startup(void)
{
conn.cb = ubus_connect_handler;
ubus_auto_connect(&conn);
}
int ubus_service_list(ubus_data_handler_t cb)
{
uint32_t id;
int ret;
blob_buf_init(&b, 0);
ret = ubus_lookup_id(&conn.ctx, "service", &id);
if (ret)
return ret;
return ubus_invoke(&conn.ctx, id, "list", b.head, cb, NULL, 5 * 1000);
}

View File

@@ -1,20 +0,0 @@
/*
* Copyright (C) 2014 John Crispin <blogic@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _UBUS_H__
#define _UBUS_H__
extern void ubus_startup(void);
extern int ubus_service_list(ubus_data_handler_t cb);
#endif

View File

@@ -1,83 +0,0 @@
/*
* Copyright (C) 2014 John Crispin <blogic@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/utsname.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <signal.h>
#include <libubox/uloop.h>
#include <libubox/utils.h>
#include "dns.h"
#include "util.h"
uint8_t mdns_buf[MDNS_BUF_LEN];
int debug = 0;
char mdns_hostname[HOSTNAME_LEN];
char mdns_hostname_local[HOSTNAME_LEN + 6];
uint32_t
rand_time_delta(uint32_t t)
{
uint32_t val;
int fd = open("/dev/urandom", O_RDONLY);
if (!fd)
return t;
if (read(fd, &val, sizeof(val)) == sizeof(val)) {
int range = t / 30;
srand(val);
val = t + (rand() % range) - (range / 2);
} else {
val = t;
}
close(fd);
return val;
}
void get_hostname(void)
{
struct utsname utsname;
mdns_hostname[0] = 0;
mdns_hostname_local[0] = 0;
if (uname(&utsname) < 0)
return;
snprintf(mdns_hostname, sizeof(mdns_hostname), "%s", utsname.nodename);
snprintf(mdns_hostname_local, sizeof(mdns_hostname_local), "%s.local", utsname.nodename);
}
time_t monotonic_time(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec;
}

View File

@@ -1,37 +0,0 @@
/*
* Copyright (C) 2014 John Crispin <blogic@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _UTIL_H__
#define _UTIL_H__
#include <stdint.h>
#include <time.h>
#define DBG(level, fmt, ...) do { \
if (debug >= level) \
fprintf(stderr, "mdnsd: %s (%d): " fmt, __func__, __LINE__, ## __VA_ARGS__); \
} while (0)
#define MDNS_BUF_LEN (8 * 1024)
#define HOSTNAME_LEN 256
extern int debug;
extern uint8_t mdns_buf[MDNS_BUF_LEN];
extern char mdns_hostname[HOSTNAME_LEN];
extern char mdns_hostname_local[HOSTNAME_LEN + 6];
extern void get_hostname(void);
extern uint32_t rand_time_delta(uint32_t t);
extern time_t monotonic_time(void);
#endif