mirror of
https://github.com/urbit/vere.git
synced 2024-11-08 21:54:38 +03:00
mdns: add mdns for local network routing (#511)
This PR implements [mDNS](https://en.wikipedia.org/wiki/Multicast_DNS) for vere, significantly improving Urbit routing inside local networks. On startup vere will now broadcast a service with the format `dinleb-rambep._ames._udp.local`. Vere will also listen for any services on the local network with the signature of `_ames._udp`. Whenever new services are found vere will send the lane to arvo. On macOS we can use [Bonjour](https://developer.apple.com/bonjour/) and don't need to pull down any dependencies at all. On Linux we can use [Avahi](https://www.avahi.org/), specifically the bonjour compatibility layer. The header we're using is [dns_sd.h](https://github.com/lathiat/avahi/blob/master/avahi-compat-libdns_sd/dns_sd.h), [documented here](https://developer.apple.com/library/archive/documentation/Networking/Conceptual/dns_discovery_api/Introduction.html#//apple_ref/doc/uid/TP40002475-SW1). Tested on macos aarch64 and linux x86_64 so far. To try it out, build vere from this PR, start a fakezod and observe: ``` # macos dns-sd -B _ames._udp # linux avahi-browse -r _ames._udp ``` You can also start another fake ship on the same network and do `|ames-verb %rcv %odd` to see your fakeships find eachother. For this second part you need the companion arvo PR, here's a pill that has it already: ``` ./urbit -F zod -u https://urbit-foundation.s3.us-east-2.amazonaws.com/dear.pill ```
This commit is contained in:
commit
e7e06d031c
@ -121,6 +121,15 @@ versioned_http_archive(
|
||||
version = "9681279cfaa6e6399bb7ca3afbbc27fc2e19df4b",
|
||||
)
|
||||
|
||||
versioned_http_archive(
|
||||
name = "avahi",
|
||||
build_file = "//bazel/third_party/avahi:avahi.BUILD",
|
||||
sha256 = "060309d7a333d38d951bc27598c677af1796934dbd98e1024e7ad8de798fedda",
|
||||
strip_prefix = "avahi-{version}",
|
||||
url = "https://github.com/lathiat/avahi/releases/download/v{version}/avahi-{version}.tar.gz",
|
||||
version = "0.8",
|
||||
)
|
||||
|
||||
versioned_http_archive(
|
||||
name = "bazel_gazelle",
|
||||
sha256 = "efbbba6ac1a4fd342d5122cbdfdb82aeb2cf2862e35022c752eaddffada7c3f3",
|
||||
@ -145,6 +154,25 @@ versioned_http_archive(
|
||||
version = "7.85.0",
|
||||
)
|
||||
|
||||
versioned_http_archive(
|
||||
name = "dbus",
|
||||
build_file = "//bazel/third_party/dbus:dbus.BUILD",
|
||||
sha256 = "a6bd5bac5cf19f0c3c594bdae2565a095696980a683a0ef37cb6212e093bde35",
|
||||
strip_prefix = "dbus-{version}",
|
||||
url = "https://dbus.freedesktop.org/releases/dbus/dbus-{version}.tar.xz",
|
||||
version = "1.14.8",
|
||||
)
|
||||
|
||||
versioned_http_archive(
|
||||
name = "expat",
|
||||
build_file = "//bazel/third_party/expat:expat.BUILD",
|
||||
strip_prefix = "expat-{version}",
|
||||
sha256 = "ef2420f0232c087801abf705e89ae65f6257df6b7931d37846a193ef2e8cdcbe",
|
||||
# TODO: fix the R_2_5_0 nonsense
|
||||
url = "https://github.com/libexpat/libexpat/releases/download/R_2_5_0/expat-{version}.tar.xz",
|
||||
version = "2.5.0",
|
||||
)
|
||||
|
||||
versioned_http_archive(
|
||||
name = "gmp",
|
||||
build_file = "//bazel/third_party/gmp:gmp.BUILD",
|
||||
|
0
bazel/third_party/avahi/BUILD.bazel
vendored
Normal file
0
bazel/third_party/avahi/BUILD.bazel
vendored
Normal file
30
bazel/third_party/avahi/avahi.BUILD
vendored
Normal file
30
bazel/third_party/avahi/avahi.BUILD
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
load("@rules_foreign_cc//foreign_cc:defs.bzl", "configure_make")
|
||||
|
||||
filegroup(
|
||||
name = "all",
|
||||
srcs = glob(["**"]),
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "dns-sd",
|
||||
hdrs = ["dns_sd.h"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
configure_make(
|
||||
name = "avahi",
|
||||
args = select({
|
||||
"@platforms//os:macos": ["--jobs=`sysctl -n hw.logicalcpu`"],
|
||||
"//conditions:default": ["--jobs=`nproc`"],
|
||||
}),
|
||||
configure_options = ["--with-dbus-system-address='unix:path=/var/run/dbus/system_bus_socket' --with-xml=none --disable-libevent --disable-glib --disable-gobject --disable-gdbm --disable-qt3 --disable-qt4 --disable-qt5 --disable-gtk --disable-gtk3 --disable-mono --disable-monodoc --disable-python --disable-libdaemon --enable-compat-libdns_sd --disable-rpath"],
|
||||
lib_source = ":all",
|
||||
# out_include_dir = "avahi-compat-libdns_sd",
|
||||
deps = ["@dbus"],
|
||||
configure_in_place = True,
|
||||
autogen = True,
|
||||
autoconf = True,
|
||||
autogen_command = "bootstrap.sh",
|
||||
out_static_libs = ["libdns_sd.a", "libavahi-client.a", "libavahi-common.a"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
0
bazel/third_party/dbus/BUILD.bazel
vendored
Normal file
0
bazel/third_party/dbus/BUILD.bazel
vendored
Normal file
21
bazel/third_party/dbus/dbus.BUILD
vendored
Normal file
21
bazel/third_party/dbus/dbus.BUILD
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
load("@rules_foreign_cc//foreign_cc:defs.bzl", "configure_make")
|
||||
|
||||
filegroup(
|
||||
name = "all",
|
||||
srcs = glob(["**"]),
|
||||
)
|
||||
|
||||
configure_make(
|
||||
name = "dbus",
|
||||
lib_name = "libdbus-1",
|
||||
args = select({
|
||||
"@platforms//os:macos": ["--jobs=`sysctl -n hw.logicalcpu`"],
|
||||
"//conditions:default": ["--jobs=`nproc`"],
|
||||
}),
|
||||
copts = ["-O3"],
|
||||
configure_options = ["--disable-selinux --without-x --disable-tests"],
|
||||
lib_source = ":all",
|
||||
configure_in_place = True,
|
||||
deps = ["@expat"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
0
bazel/third_party/expat/BUILD.bazel
vendored
Normal file
0
bazel/third_party/expat/BUILD.bazel
vendored
Normal file
18
bazel/third_party/expat/expat.BUILD
vendored
Normal file
18
bazel/third_party/expat/expat.BUILD
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
load("@rules_foreign_cc//foreign_cc:defs.bzl", "configure_make")
|
||||
|
||||
filegroup(
|
||||
name = "all",
|
||||
srcs = glob(["**"]),
|
||||
)
|
||||
|
||||
configure_make(
|
||||
name = "expat",
|
||||
args = select({
|
||||
"@platforms//os:macos": ["--jobs=`sysctl -n hw.logicalcpu`"],
|
||||
"//conditions:default": ["--jobs=`nproc`"],
|
||||
}),
|
||||
copts = ["-O3"],
|
||||
lib_source = ":all",
|
||||
out_static_libs = ["libexpat.a"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
@ -308,6 +308,7 @@
|
||||
# define c3__deep c3_s4('d','e','e','p')
|
||||
# define c3__defn c3_s4('d','e','f','n')
|
||||
# define c3__del c3_s3('d','e','l')
|
||||
# define c3__dear c3_s4('d','e','a','r')
|
||||
# define c3__delc c3_s4('d','e','l','c')
|
||||
# define c3__delt c3_s4('d','e','l','t')
|
||||
# define c3__dept c3_s4('d','e','p','t')
|
||||
|
@ -119,7 +119,13 @@ vere_library(
|
||||
hdrs = [
|
||||
"db/lmdb.h",
|
||||
"vere.h",
|
||||
],
|
||||
"mdns.h",
|
||||
] + select({
|
||||
"@platforms//os:macos": [],
|
||||
"@platforms//os:linux": [
|
||||
"dns_sd.h",
|
||||
],
|
||||
}),
|
||||
includes = ["."],
|
||||
linkstatic = True,
|
||||
visibility = ["//pkg:__subpackages__"],
|
||||
@ -133,7 +139,12 @@ vere_library(
|
||||
"@lmdb",
|
||||
"@openssl",
|
||||
"@uv",
|
||||
],
|
||||
] + select({
|
||||
"@platforms//os:macos": [],
|
||||
"@platforms//os:linux": [
|
||||
"@avahi",
|
||||
],
|
||||
})
|
||||
)
|
||||
|
||||
#
|
||||
|
1722
pkg/vere/dns_sd.h
Normal file
1722
pkg/vere/dns_sd.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,7 @@
|
||||
/// @file
|
||||
|
||||
#include "vere.h"
|
||||
#include "mdns.h"
|
||||
|
||||
#include "noun.h"
|
||||
#include "ur.h"
|
||||
@ -2083,6 +2084,50 @@ _ames_recv_cb(uv_udp_t* wax_u,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_mdns_dear_bail(u3_ovum* egg_u, u3_noun lud)
|
||||
{
|
||||
u3z(lud);
|
||||
u3_ovum_free(egg_u);
|
||||
}
|
||||
|
||||
/* _ames_put_dear(): send lane to arvo after hearing mdns response
|
||||
*/
|
||||
static void
|
||||
_ames_put_dear(c3_c* ship, c3_w s_addr, c3_s port, void* context)
|
||||
{
|
||||
u3_ames* sam_u = (u3_ames*)context;
|
||||
|
||||
u3_lane lan;
|
||||
lan.pip_w = ntohl(s_addr);
|
||||
lan.por_s = ntohs(port);
|
||||
|
||||
u3_noun whu = u3dc("slaw", c3__p, u3i_string(ship));
|
||||
|
||||
if (u3_nul == whu) {
|
||||
u3l_log("ames: strange ship from mdns: %s", ship);
|
||||
return;
|
||||
}
|
||||
|
||||
u3_noun our = u3i_chubs(2, sam_u->pir_u->who_d);
|
||||
if (our == u3t(whu)) {
|
||||
u3z(whu);
|
||||
u3z(our);
|
||||
return;
|
||||
}
|
||||
|
||||
u3z(our);
|
||||
|
||||
u3_noun wir = u3nc(c3__ames, u3_nul);
|
||||
u3_noun cad = u3nt(c3__dear, u3k(u3t(whu)), u3nc(c3n, u3_ames_encode_lane(lan)));
|
||||
|
||||
u3_auto_peer(
|
||||
u3_auto_plan(&sam_u->car_u,
|
||||
u3_ovum_init(0, c3__a, wir, cad)),
|
||||
0, 0, _mdns_dear_bail);
|
||||
u3z(whu);
|
||||
}
|
||||
|
||||
/* _ames_io_start(): initialize ames I/O.
|
||||
*/
|
||||
static void
|
||||
@ -2147,6 +2192,15 @@ _ames_io_start(u3_ames* sam_u)
|
||||
u3l_log("ames: live on %d (localhost only)", sam_u->pir_u->por_s);
|
||||
}
|
||||
|
||||
{
|
||||
u3_noun our = u3dc("scot", 'p', u3k(who));
|
||||
char* our_s = u3r_string(our);
|
||||
u3z(our);
|
||||
|
||||
mdns_init(por_s, our_s, _ames_put_dear, (void *)sam_u);
|
||||
c3_free(our_s);
|
||||
}
|
||||
|
||||
uv_udp_recv_start(&sam_u->wax_u, _ames_alloc, _ames_recv_cb);
|
||||
|
||||
sam_u->car_u.liv_o = c3y;
|
||||
|
210
pkg/vere/mdns.c
Normal file
210
pkg/vere/mdns.c
Normal file
@ -0,0 +1,210 @@
|
||||
/// @file
|
||||
|
||||
#include "vere.h"
|
||||
#include "mdns.h"
|
||||
#include "dns_sd.h"
|
||||
|
||||
typedef struct _mdns_payload {
|
||||
mdns_cb* cb;
|
||||
DNSServiceRef sref;
|
||||
char who[58];
|
||||
uint16_t port;
|
||||
uv_poll_t poll;
|
||||
void* context;
|
||||
} mdns_payload;
|
||||
|
||||
static void close_cb(uv_handle_t* poll) {
|
||||
mdns_payload* payload = (mdns_payload*)poll->data;
|
||||
DNSServiceRefDeallocate(payload->sref);
|
||||
c3_free(payload);
|
||||
}
|
||||
|
||||
static void getaddrinfo_cb(uv_getaddrinfo_t* req, int status, struct addrinfo* res) {
|
||||
mdns_payload* payload = (mdns_payload*)req->data;
|
||||
|
||||
if (status < 0) {
|
||||
u3l_log("mdns: getaddrinfo error: %s", uv_strerror(status));
|
||||
} else {
|
||||
struct sockaddr_in* addr = (struct sockaddr_in*)res->ai_addr;
|
||||
payload->cb(payload->who, addr->sin_addr.s_addr, payload->port, payload->context);
|
||||
}
|
||||
|
||||
payload->poll.data = payload;
|
||||
uv_close((uv_handle_t*)&payload->poll, close_cb);
|
||||
c3_free(req);
|
||||
uv_freeaddrinfo(res);
|
||||
}
|
||||
|
||||
static void resolve_cb(DNSServiceRef sref,
|
||||
DNSServiceFlags f,
|
||||
uint32_t interface,
|
||||
DNSServiceErrorType err,
|
||||
const char *name,
|
||||
const char *host,
|
||||
uint16_t port,
|
||||
uint16_t tl,
|
||||
const unsigned char *t,
|
||||
void *context)
|
||||
{
|
||||
mdns_payload* payload = (mdns_payload*)context;
|
||||
|
||||
uv_poll_stop(&payload->poll);
|
||||
|
||||
if (err != kDNSServiceErr_NoError) {
|
||||
u3l_log("mdns: dns resolve error %d", err);
|
||||
payload->poll.data = payload;
|
||||
uv_close((uv_handle_t*)&payload->poll, close_cb);
|
||||
return;
|
||||
}
|
||||
|
||||
payload->sref = sref;
|
||||
payload->port = port;
|
||||
|
||||
int i;
|
||||
payload->who[0] = '~';
|
||||
for (i = 0; name[i] != '\0' && name[i] != '.' && i < 58; ++i)
|
||||
{
|
||||
payload->who[i+1] = name[i];
|
||||
}
|
||||
|
||||
struct addrinfo hints;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_INET; // Request only IPv4 addresses
|
||||
hints.ai_socktype = SOCK_STREAM; // TCP socket
|
||||
|
||||
uv_getaddrinfo_t* req = (uv_getaddrinfo_t*)c3_malloc(sizeof(uv_getaddrinfo_t));
|
||||
req->data = (void*)payload;
|
||||
|
||||
uv_loop_t* loop = uv_default_loop();
|
||||
|
||||
int error = uv_getaddrinfo(loop, req, getaddrinfo_cb, host, NULL, &hints);
|
||||
|
||||
if (error < 0) {
|
||||
u3l_log("mdns: getaddrinfo error: %s\n", uv_strerror(error));
|
||||
payload->poll.data = payload;
|
||||
uv_close((uv_handle_t*)&payload->poll, close_cb);
|
||||
c3_free(req);
|
||||
}
|
||||
}
|
||||
|
||||
static void poll_cb(uv_poll_t* handle, int status, int events) {
|
||||
DNSServiceRef sref = (DNSServiceRef) handle->data;
|
||||
int err = DNSServiceProcessResult(sref);
|
||||
}
|
||||
|
||||
static void init_sref_poll(DNSServiceRef sref, mdns_payload* payload) {
|
||||
int fd = DNSServiceRefSockFD(sref);
|
||||
uv_loop_t* loop = uv_default_loop();
|
||||
payload->poll.data = (void*)sref;
|
||||
uv_poll_init(loop, &payload->poll, fd);
|
||||
uv_poll_start(&payload->poll, UV_READABLE, poll_cb);
|
||||
}
|
||||
|
||||
static void browse_cb(DNSServiceRef s,
|
||||
DNSServiceFlags f,
|
||||
uint32_t interface,
|
||||
DNSServiceErrorType err,
|
||||
const char* name,
|
||||
const char* type,
|
||||
const char* domain,
|
||||
void* context)
|
||||
{
|
||||
if (err != kDNSServiceErr_NoError) {
|
||||
u3l_log("mdns: service browse error %i", err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (f & kDNSServiceFlagsAdd) { // Add
|
||||
// we are leaking payload because we don't know when we are done
|
||||
// browsing, luckily we only browse once
|
||||
mdns_payload* payload = (mdns_payload*)context;
|
||||
mdns_payload* payload_copy = c3_malloc(sizeof *payload_copy);
|
||||
|
||||
// copy to prevent asynchronous thrashing of payload
|
||||
memcpy(payload_copy, payload, sizeof(mdns_payload));
|
||||
|
||||
|
||||
DNSServiceErrorType err =
|
||||
DNSServiceResolve(&payload_copy->sref, 0, interface,
|
||||
name, type, domain, resolve_cb,
|
||||
(void*)payload_copy);
|
||||
|
||||
init_sref_poll(payload_copy->sref, payload_copy);
|
||||
|
||||
if (err != kDNSServiceErr_NoError) {
|
||||
u3l_log("mdns: dns service resolve error %i", err);
|
||||
payload_copy->poll.data = payload_copy;
|
||||
uv_close((uv_handle_t*)&payload_copy->poll, close_cb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void register_close_cb(uv_handle_t* poll) {
|
||||
// not freeing sref with DNSServiceRefDeallocate since that
|
||||
// deregisters us from mdns
|
||||
mdns_payload* payload = (mdns_payload*)poll->data;
|
||||
c3_free(payload);
|
||||
}
|
||||
|
||||
|
||||
static void register_cb(DNSServiceRef sref,
|
||||
DNSServiceFlags f,
|
||||
DNSServiceErrorType err,
|
||||
const char* name,
|
||||
const char* type,
|
||||
const char* domain,
|
||||
void* context)
|
||||
{
|
||||
mdns_payload* payload = (mdns_payload*)context;
|
||||
|
||||
if (err != kDNSServiceErr_NoError) {
|
||||
u3l_log("mdns: service register error %i", err);
|
||||
} else {
|
||||
u3l_log("mdns: %s registered on all interfaces", name);
|
||||
}
|
||||
uv_poll_stop(&payload->poll);
|
||||
uv_close((uv_handle_t*)&payload->poll, register_close_cb);
|
||||
}
|
||||
|
||||
void mdns_init(uint16_t port, char* our, mdns_cb* cb, void* context)
|
||||
{
|
||||
#if defined(U3_OS_linux)
|
||||
setenv("AVAHI_COMPAT_NOWARN", "1", 0);
|
||||
setenv("DBUS_SYSTEM_BUS_ADDRESS", "unix:path=/var/run/dbus/system_bus_socket", 0);
|
||||
# endif
|
||||
|
||||
mdns_payload* register_payload = (mdns_payload*)c3_malloc(sizeof(mdns_payload));
|
||||
|
||||
DNSServiceRef sref;
|
||||
DNSServiceErrorType err;
|
||||
char* our_no_sig = our + 1; // certain url parsers don't like the ~
|
||||
err = DNSServiceRegister(&sref, 0, 0, our_no_sig, "_ames._udp",
|
||||
NULL, NULL, htons(port), 0, NULL, register_cb, (void*)register_payload);
|
||||
|
||||
if (err != kDNSServiceErr_NoError) {
|
||||
u3l_log("mdns: service register error %i", err);
|
||||
DNSServiceRefDeallocate(sref);
|
||||
return;
|
||||
}
|
||||
|
||||
init_sref_poll(sref, register_payload);
|
||||
|
||||
mdns_payload* browse_payload = (mdns_payload*)c3_malloc(sizeof(mdns_payload));
|
||||
|
||||
browse_payload->cb = cb;
|
||||
browse_payload->context = context;
|
||||
|
||||
DNSServiceErrorType dnserr;
|
||||
DNSServiceRef sref2;
|
||||
|
||||
dnserr = DNSServiceBrowse(&sref2, 0, 0, "_ames._udp", NULL, browse_cb, (void *)browse_payload);
|
||||
|
||||
if (dnserr != kDNSServiceErr_NoError) {
|
||||
u3l_log("mdns: service browse error %i", dnserr);
|
||||
DNSServiceRefDeallocate(sref2);
|
||||
return;
|
||||
}
|
||||
|
||||
init_sref_poll(sref2, browse_payload);
|
||||
|
||||
}
|
5
pkg/vere/mdns.h
Normal file
5
pkg/vere/mdns.h
Normal file
@ -0,0 +1,5 @@
|
||||
#include "noun.h"
|
||||
|
||||
typedef void mdns_cb(c3_c* ship, c3_w s_addr, c3_s port, void* context);
|
||||
|
||||
void mdns_init(uint16_t port, char* our, mdns_cb* cb, void* context);
|
Loading…
Reference in New Issue
Block a user