diff --git a/.gitignore b/.gitignore index da5bf8e6c8..fe7281c2cc 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ /.user.bazelrc /bazel-* /urbit +MODULE.bazel +MODULE.bazel.lock # Swap files. *.swo diff --git a/INSTALL.md b/INSTALL.md index 5f3d2774f8..a7a7946a4b 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -26,7 +26,7 @@ brew install automake libtool ### Linux -After installing `automake` and `libtool`, you need to install the [musl libc] toolchain. We use [musl libc][musl libc] instead of [glibc][glibc] on Linux, which enables us to generate statically linked binaries. As a prerequisite, you need to install the [musl libc][musl libc] toolchain appropriate for your target platform. +After installing `automake`, `autoconf-archive`, and `libtool`, you need to install the [musl libc] toolchain. We use [musl libc][musl libc] instead of [glibc][glibc] on Linux, which enables us to generate statically linked binaries. As a prerequisite, you need to install the [musl libc][musl libc] toolchain appropriate for your target platform. #### x86_64 @@ -52,6 +52,20 @@ This will take a few minutes. After installing `automake`, `autoconf-archive`, `pkg-config`, and `libtool`, you're ready to build Vere. +#### Debugger + +macOS is curious operating system because the kernel is derived from from two codebases, the Mach kernel and the BSD kernel. It inherits two different hardware exception handling facilities, Mach exceptions and POSIX signals. We use `libsigsegv` and utilize the POSIX signals which is usually fine except when it comes to debugging with `lldb`. + +`lldb` hijacks the Mach exception ports for the task when it attaches to the process. Mach exceptions get handled before POSIX signals which means that as soon as vere faults (this happens often) `lldb` stop with a `EXC_BAD_ACCESS`. It is impossible to continue debugging from this state without the workaround we implemented in https://github.com/urbit/vere/pull/611. + +There are more annoying warts with `lldb` currently. First, if you attach the debugger when booting the ship with `lldb -- your-ship/.run` you have to specify `-t`, otherwise the ship is unable to boot for mysterious reasons. The other option is to start the ship and attach afterwards with `lldb -p PID`. Afterwards you should do this dance: + +``` +p (void)darwin_register_mach_exception_handler() +pro hand -p true -s false -n false SIGBUS +pro hand -p true -s false -n false SIGSEGV +``` + ## Build Commands Once you install the prerequisites, you're ready to build: @@ -99,6 +113,17 @@ you can pass them with [`--per_file_copt`][per_file_copt] like so: bazel build --per_file_copt='pkg/.*@-DMACRO' ``` +## LSP Integration + +```console +bazel run //bazel:refresh_compile_commands +``` + +Running this command will generate a `compile_commands.json` file in the root +of the repository, which `clangd` (or other language server processors) will +use automatically to provide modern editor features like syntax highlighting, +go-to definitions, call hierarchies, symbol manipulation, etc. + ## Test Commands You can build and run unit tests only on native builds. If you have a native @@ -136,6 +161,13 @@ doesn't match the version of `clang` installed on your system. To address this, run `clang --version` and pass the version number via `--clang_version=""` to the failing command. +If build fails on nix/NixOS, you should pass `ACLOCAL_PATH` environment +variable to bazel, using `--action_env=ACLOCAL_PATH=$ACLOCAL_PATH`, like so: + +``` +bazel build :urbit --action_env=ACLOCAL_PATH=$ACLOCAL_PATH +``` + [^1]: If you're interested in digging into the details of the build system, check out [`WORKSPACE.bazel`](WORKSPACE.bazel), [`BUILD.bazel`](BUILD.bazel), [`bazel/`](bazel), and the multiple diff --git a/VERSION b/VERSION index 9f55b2ccb5..8c50098d8a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.0 +3.1 diff --git a/WORKSPACE.bazel b/WORKSPACE.bazel index 4fcb5950f3..6661562bee 100644 --- a/WORKSPACE.bazel +++ b/WORKSPACE.bazel @@ -217,6 +217,15 @@ versioned_http_file( version = "721fa05", ) +versioned_http_archive( + name = "natpmp", + build_file = "//bazel/third_party/natpmp:natpmp.BUILD", + sha256 = "0684ed2c8406437e7519a1bd20ea83780db871b3a3a5d752311ba3e889dbfc70", + strip_prefix = "libnatpmp-{version}", + url = "http://download.openpkg.org/components/cache/libnatpmp/libnatpmp-{version}.tar.gz", + version = "20230423", +) + versioned_http_file( name = "solid_pill", sha256 = "8b658fcee6978e2b19004a54233cab953e77ea0bb6c3a04d1bfda4ddc6be63c5", @@ -253,10 +262,10 @@ versioned_http_archive( versioned_http_archive( name = "openssl", build_file = "//bazel/third_party/openssl:openssl.BUILD", - sha256 = "8dee9b24bdb1dcbf0c3d1e9b02fb8f6bf22165e807f45adeb7c9677536859d3b", + sha256 = "cf3098950cb4d853ad95c0841f1f9c6d3dc102dccfcacd521d93925208b76ac8", strip_prefix = "openssl-{version}", - url = "https://www.openssl.org/source/openssl-{version}.tar.gz", - version = "1.1.1t", + url = "https://github.com/openssl/openssl/releases/download/OpenSSL_1_1_1w/openssl-{version}.tar.gz", + version = "1.1.1w", ) versioned_http_archive( @@ -327,10 +336,10 @@ versioned_http_file( versioned_http_archive( name = "urcrypt", build_file = "//bazel/third_party/urcrypt:urcrypt.BUILD", - sha256 = "afc1182e10eeebaeb2a111c2bd889747792d255e26aba7fdcd6751d0d3c2bb35", + sha256 = "d27ec04d3854da7c479dd815af92ffef986616bc7ff400022e2dfb7971853d86", strip_prefix = "urcrypt-{version}", url = "https://github.com/urbit/urcrypt/archive/{version}.tar.gz", - version = "43479c3262a11e20da5f6218f3b0b3d63931ceea", + version = "9ae5d604528bc54ae48430f55ebbb17b1ad7956c", ) versioned_http_archive( diff --git a/bazel/common_settings.bzl b/bazel/common_settings.bzl index 6a61de1292..68a2232cd1 100644 --- a/bazel/common_settings.bzl +++ b/bazel/common_settings.bzl @@ -14,7 +14,7 @@ string_flag = rule( def vere_library(copts = [], linkopts = [], **kwargs): native.cc_library( copts = copts + select({ - "//:debug": ["-O0", "-g3", "-DC3DBG"], + "//:debug": ["-O0", "-g3", "-DC3DBG", "-fdebug-compilation-dir=."], "//conditions:default": ["-O3"] }) + select({ "//:lto": ['-flto'], @@ -39,7 +39,7 @@ def vere_library(copts = [], linkopts = [], **kwargs): def vere_binary(copts = [], linkopts = [], **kwargs): native.cc_binary( copts = copts + select({ - "//:debug": ["-O0", "-g3", "-DC3DBG"], + "//:debug": ["-O0", "-g3", "-DC3DBG", "-fdebug-compilation-dir=."], "//conditions:default": ["-O3"] }) + select({ "//:lto": ['-flto'], diff --git a/bazel/third_party/avahi/avahi.BUILD b/bazel/third_party/avahi/avahi.BUILD index 84d27171c2..2c0896f32d 100644 --- a/bazel/third_party/avahi/avahi.BUILD +++ b/bazel/third_party/avahi/avahi.BUILD @@ -17,7 +17,7 @@ configure_make( "@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"], + 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 --with-distro=none"], lib_source = ":all", # out_include_dir = "avahi-compat-libdns_sd", deps = ["@dbus"], diff --git a/bazel/third_party/dbus/dbus.BUILD b/bazel/third_party/dbus/dbus.BUILD index 0642883163..25fd750092 100644 --- a/bazel/third_party/dbus/dbus.BUILD +++ b/bazel/third_party/dbus/dbus.BUILD @@ -13,7 +13,7 @@ configure_make( "//conditions:default": ["--jobs=`nproc`"], }), copts = ["-O3"], - configure_options = ["--disable-selinux --without-x --disable-tests"], + configure_options = ["--disable-selinux --without-x --disable-tests --disable-systemd --disable-apparmor"], lib_source = ":all", configure_in_place = True, deps = ["@expat"], diff --git a/bazel/third_party/natpmp/BUILD.bazel b/bazel/third_party/natpmp/BUILD.bazel new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bazel/third_party/natpmp/natpmp.BUILD b/bazel/third_party/natpmp/natpmp.BUILD new file mode 100644 index 0000000000..b3a7edd7e0 --- /dev/null +++ b/bazel/third_party/natpmp/natpmp.BUILD @@ -0,0 +1,8 @@ +cc_library( + name = "natpmp", + srcs = ["natpmp.c", "getgateway.c"], + hdrs = ["natpmp.h", "getgateway.h", "natpmp_declspec.h"], + copts = ["-O3"], + linkstatic = True, + visibility = ["//visibility:public"], +) diff --git a/flake.lock b/flake.lock index 45c4d968fb..10f88e0e43 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1683236849, - "narHash": "sha256-Y7PNBVLOBvZrmrFmHgXUBUA1lM72tl6JGIn1trOeuyE=", + "lastModified": 1712849433, + "narHash": "sha256-flQtf/ZPJgkLY/So3Fd+dGilw2DKIsiwgMEn7BbBHL0=", "owner": "nixos", "repo": "nixpkgs", - "rev": "374ffe54403c3c42d97a513ac7a14ce1b5b86e30", + "rev": "f173d0881eff3b21ebb29a2ef8bedbc106c86ea5", "type": "github" }, "original": { @@ -23,11 +23,11 @@ ] }, "locked": { - "lastModified": 1682984683, - "narHash": "sha256-fSMthG+tp60AHhNmaHc4StT3ltfHkQsJtN8GhfLWmtI=", + "lastModified": 1712014858, + "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "86684881e184f41aa322e653880e497b66429f3e", + "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 336f0f3ba2..05237289b3 100644 --- a/flake.nix +++ b/flake.nix @@ -104,12 +104,16 @@ ] ++ (with pkgs; [ autoconf + autoconf-archive automake - bazel_5 + bazel_6 binutils # for `nm` jdk11_headless libtool m4 + pkg-config + git + perl ]); extraBuildCommands = '' chmod +w usr diff --git a/pkg/c3/defs.c b/pkg/c3/defs.c index ce968cdab3..24c1d13c9b 100644 --- a/pkg/c3/defs.c +++ b/pkg/c3/defs.c @@ -1,5 +1,19 @@ #include "defs.h" +c3_s +c3_sift_short(c3_y buf_y[2]); +c3_w +c3_sift_word(c3_y buf_y[4]); +c3_d +c3_sift_chub(c3_y byt_y[8]); + +void +c3_etch_short(c3_y buf_y[2], c3_s sot_s); +void +c3_etch_word(c3_y buf_y[4], c3_w wod_w); +void +c3_etch_chub(c3_y byt_y[8], c3_d num_d); + c3_w c3_align_w(c3_w x, c3_w al, align_dir hilo); c3_d c3_align_d(c3_d x, c3_d al, align_dir hilo); void *c3_align_p(void const * p, size_t al, align_dir hilo); diff --git a/pkg/c3/defs.h b/pkg/c3/defs.h index bbf8d91ef7..aef4b6f93c 100644 --- a/pkg/c3/defs.h +++ b/pkg/c3/defs.h @@ -101,6 +101,60 @@ | (((w) >> 8) & 0xff) << 16 \ | ( (w) & 0xff) << 24 ) + inline c3_s + c3_sift_short(c3_y buf_y[2]) + { + return (buf_y[1] << 8 | buf_y[0]); + } + + inline c3_w + c3_sift_word(c3_y buf_y[4]) + { + return (buf_y[3] << 24 | buf_y[2] << 16 | buf_y[1] << 8 | buf_y[0]); + } + + inline c3_d + c3_sift_chub(c3_y byt_y[8]) + { + return (c3_d)byt_y[0] + | (c3_d)byt_y[1] << 8 + | (c3_d)byt_y[2] << 16 + | (c3_d)byt_y[3] << 24 + | (c3_d)byt_y[4] << 32 + | (c3_d)byt_y[5] << 40 + | (c3_d)byt_y[6] << 48 + | (c3_d)byt_y[7] << 56; + } + + inline void + c3_etch_short(c3_y buf_y[2], c3_s sot_s) + { + buf_y[0] = sot_s & 0xff; + buf_y[1] = (sot_s >> 8) & 0xff; + } + + inline void + c3_etch_word(c3_y buf_y[4], c3_w wod_w) + { + buf_y[0] = wod_w & 0xff; + buf_y[1] = (wod_w >> 8) & 0xff; + buf_y[2] = (wod_w >> 16) & 0xff; + buf_y[3] = (wod_w >> 24) & 0xff; + } + + inline void + c3_etch_chub(c3_y byt_y[8], c3_d num_d) + { + byt_y[0] = num_d & 0xff; + byt_y[1] = (num_d >> 8) & 0xff; + byt_y[2] = (num_d >> 16) & 0xff; + byt_y[3] = (num_d >> 24) & 0xff; + byt_y[4] = (num_d >> 32) & 0xff; + byt_y[5] = (num_d >> 40) & 0xff; + byt_y[6] = (num_d >> 48) & 0xff; + byt_y[7] = (num_d >> 56) & 0xff; + } + /* Asserting allocators. */ # define c3_free(s) free(s) diff --git a/pkg/c3/motes.h b/pkg/c3/motes.h index beaa33d63b..975554e94b 100644 --- a/pkg/c3/motes.h +++ b/pkg/c3/motes.h @@ -937,6 +937,7 @@ # define c3__pril c3_s4('p','r','i','l') # define c3__pro c3_s3('p','r','o') # define c3__prod c3_s4('p','r','o','d') +# define c3__prop c3_s4('p','r','o','p') # define c3__prof c3_s4('p','r','o','f') # define c3__prox c3_s4('p','r','o','x') # define c3__psdg c3_s4('p','s','d','g') diff --git a/pkg/noun/events.c b/pkg/noun/events.c index d5e0ddac0f..661e07d20f 100644 --- a/pkg/noun/events.c +++ b/pkg/noun/events.c @@ -324,9 +324,15 @@ u3e_fault(u3_post low_p, u3_post hig_p, u3_post off_p) return u3e_flaw_good; } +typedef enum { + _ce_img_good = 0, + _ce_img_fail = 1, + _ce_img_size = 2 +} _ce_img_stat; + /* _ce_image_stat(): measure image. */ -static c3_o +static _ce_img_stat _ce_image_stat(u3e_image* img_u, c3_w* pgs_w) { struct stat buf_u; @@ -334,7 +340,7 @@ _ce_image_stat(u3e_image* img_u, c3_w* pgs_w) if ( -1 == fstat(img_u->fid_i, &buf_u) ) { fprintf(stderr, "loom: stat %s: %s\r\n", img_u->nam_c, strerror(errno)); u3_assert(0); - return c3n; + return _ce_img_fail; } else { c3_z siz_z = buf_u.st_size; @@ -342,19 +348,19 @@ _ce_image_stat(u3e_image* img_u, c3_w* pgs_w) if ( !siz_z ) { *pgs_w = 0; - return c3y; + return _ce_img_good; } else if ( siz_z != _ce_len(pgs_z) ) { fprintf(stderr, "loom: %s corrupt size %zu\r\n", img_u->nam_c, siz_z); - return c3n; + return _ce_img_size; } else if ( pgs_z > UINT32_MAX ) { fprintf(stderr, "loom: %s overflow %zu\r\n", img_u->nam_c, siz_z); - return c3n; + return _ce_img_fail; } else { *pgs_w = (c3_w)pgs_z; - return c3y; + return _ce_img_good; } } } @@ -397,7 +403,7 @@ _ce_ephemeral_open(c3_i* eph_i) /* _ce_image_open(): open or create image. */ -static c3_o +static _ce_img_stat _ce_image_open(u3e_image* img_u, c3_c* ful_c) { c3_i mod_i = O_RDWR | O_CREAT; @@ -406,14 +412,10 @@ _ce_image_open(u3e_image* img_u, c3_c* ful_c) snprintf(pax_c, 8192, "%s/%s.bin", ful_c, img_u->nam_c); if ( -1 == (img_u->fid_i = c3_open(pax_c, mod_i, 0666)) ) { fprintf(stderr, "loom: c3_open %s: %s\r\n", pax_c, strerror(errno)); - return c3n; - } - else if ( c3n == _ce_image_stat(img_u, &img_u->pgs_w) ) { - return c3n; - } - else { - return c3y; + return _ce_img_fail; } + + return _ce_image_stat(img_u, &img_u->pgs_w); } /* _ce_patch_write_control(): write control block file. @@ -657,6 +659,7 @@ _ce_patch_write_page(u3_ce_patch* pat_u, else { fprintf(stderr, "loom: patch write: fail: %s\r\n", strerror(errno)); } + fprintf(stderr, "info: you probably have insufficient disk space"); u3_assert(0); } } @@ -877,6 +880,7 @@ _ce_patch_apply(u3_ce_patch* pat_u) else { fprintf(stderr, "loom: patch apply write: %s\r\n", strerror(errno)); } + fprintf(stderr, "info: you probably have insufficient disk space"); u3_assert(0); } } @@ -1330,6 +1334,7 @@ _ce_image_copy(u3e_image* fom_u, u3e_image* tou_u) fprintf(stderr, "loom: image (%s) copy write: %s\r\n", tou_u->nam_c, strerror(errno)); } + fprintf(stderr, "info: you probably have insufficient disk space"); return c3n; } } @@ -1371,13 +1376,18 @@ u3e_backup(c3_c* pux_c, c3_c* pax_c, c3_o ovw_o) // c3_c nux_c[8193]; snprintf(nux_c, 8192, "%s/%s.bin", pux_c, nux_u.nam_c); - if ( (0 != access(nux_c, F_OK)) || (c3n == _ce_image_open(&nux_u, pux_c)) ) { + if ( (0 != access(nux_c, F_OK)) + || (_ce_img_good != _ce_image_open(&nux_u, pux_c)) ) + { fprintf(stderr, "loom: couldn't open north image at %s\r\n", pux_c); return c3n; } + c3_c sux_c[8193]; snprintf(sux_c, 8192, "%s/%s.bin", pux_c, sux_u.nam_c); - if ( (0 != access(sux_c, F_OK)) || (c3n == _ce_image_open(&sux_u, pux_c)) ) { + if ( (0 != access(sux_c, F_OK)) + || (_ce_img_good != _ce_image_open(&sux_u, pux_c)) ) + { fprintf(stderr, "loom: couldn't open south image at %s\r\n", pux_c); return c3n; } @@ -1487,9 +1497,9 @@ u3e_save(u3_post low_p, u3_post hig_p) #ifdef U3_SNAPSHOT_VALIDATION { c3_w pgs_w; - u3_assert( c3y == _ce_image_stat(&u3P.nor_u, &pgs_w) ); + u3_assert( _ce_img_good == _ce_image_stat(&u3P.nor_u, &pgs_w) ); u3_assert( pgs_w == u3P.nor_u.pgs_w ); - u3_assert( c3y == _ce_image_stat(&u3P.sou_u, &pgs_w) ); + u3_assert( _ce_img_good == _ce_image_stat(&u3P.sou_u, &pgs_w) ); u3_assert( pgs_w == u3P.sou_u.pgs_w ); } #endif @@ -1590,9 +1600,11 @@ u3e_live(c3_o nuu_o, c3_c* dir_c) // c3_c chk_c[8193]; snprintf(chk_c, 8193, "%s/.urb/chk", u3P.dir_c); - if ( (c3n == _ce_image_open(&u3P.nor_u, chk_c)) || - (c3n == _ce_image_open(&u3P.sou_u, chk_c)) ) - { + + _ce_img_stat nor_e = _ce_image_open(&u3P.nor_u, chk_c); + _ce_img_stat sou_e = _ce_image_open(&u3P.sou_u, chk_c); + + if ( (_ce_img_fail == nor_e) || (_ce_img_fail == sou_e) ) { fprintf(stderr, "boot: image failed\r\n"); exit(1); } @@ -1609,6 +1621,10 @@ u3e_live(c3_o nuu_o, c3_c* dir_c) _ce_patch_free(pat_u); _ce_patch_delete(); } + else if ( (_ce_img_size == nor_e) || (_ce_img_size == sou_e) ) { + fprintf(stderr, "boot: image failed (size)\r\n"); + exit(1); + } nor_w = u3P.nor_u.pgs_w; sou_w = u3P.sou_u.pgs_w; diff --git a/pkg/noun/jets/c/swp.c b/pkg/noun/jets/c/swp.c index c31e0807ad..1d0a11b434 100644 --- a/pkg/noun/jets/c/swp.c +++ b/pkg/noun/jets/c/swp.c @@ -10,9 +10,15 @@ u3_noun u3qc_swp(u3_atom a, u3_atom b) { - //XX write a proper c-style swp, maybe - // - return u3kc_rep(u3k(a), 1, u3kb_flop(u3qc_rip(a, 1, b))); + c3_w len_w = u3r_met(a, b); + u3i_slab sab_u; + u3i_slab_init(&sab_u, a, len_w); + + for (c3_w i = 0; i < len_w; i++) { + u3r_chop(a, i, 1, len_w - i - 1, sab_u.buf_w, b); + } + + return u3i_slab_mint(&sab_u); } u3_noun diff --git a/pkg/noun/jets/e/blake.c b/pkg/noun/jets/e/blake.c index a0f06f2186..f21f229ceb 100644 --- a/pkg/noun/jets/e/blake.c +++ b/pkg/noun/jets/e/blake.c @@ -6,9 +6,8 @@ #include "noun.h" #include "urcrypt.h" - static u3_atom - _cqe_blake(u3_atom wid, u3_atom dat, + _cqe_blake2b(u3_atom wid, u3_atom dat, u3_atom wik, u3_atom dak, u3_atom out) { @@ -39,7 +38,7 @@ } u3_noun - u3we_blake(u3_noun cor) + u3we_blake2b(u3_noun cor) { u3_noun msg, key, out, // arguments wid, dat, // destructured msg @@ -54,6 +53,110 @@ { return u3m_bail(c3__exit); } else { - return u3l_punt("blake", _cqe_blake(wid, dat, wik, dak, out)); + return u3l_punt("blake2b", _cqe_blake2b(wid, dat, wik, dak, out)); + } + } + + static u3_atom + _cqe_blake3_hash(u3_atom wid, u3_atom dat, + u3_atom key, u3_atom out) + { + c3_w wid_w, out_w; + if ( !u3r_word_fit(&wid_w, wid) || !u3r_word_fit(&out_w, out) ) { + return u3m_bail(c3__fail); + } + else { + c3_y key_y[32]; + u3r_bytes(0, 32, key_y, key); + c3_y *dat_y = u3r_bytes_alloc(0, wid_w, dat); + u3i_slab sab_u; + u3i_slab_bare(&sab_u, 3, out_w); + c3_y* out_y = sab_u.buf_y; + urcrypt_blake3_hash(wid_w, dat_y, key_y, out, out_y); + u3a_free(dat_y); + return u3i_slab_mint(&sab_u); + } + } + + u3_noun + u3we_blake3_hash(u3_noun cor) + { + u3_noun out, msg, // arguments + wid, dat, // destructured msg + key; // context + + if ( c3n == u3r_mean(cor, u3x_sam_2, &out, + u3x_sam_3, &msg, + u3x_con_sam_2, &key, 0) || + u3ud(out) || + u3r_cell(msg, &wid, &dat) || u3ud(wid) || u3ud(dat) || + u3ud(key)) + { + return u3m_bail(c3__exit); + } else { + return u3l_punt("blake3_hash", _cqe_blake3_hash(wid, dat, key, out)); + } + } + + static u3_noun + _cqe_blake3_chunk_output(u3_atom wid, u3_atom dat, u3_atom cv, u3_atom counter, u3_atom flags) + { + c3_w wid_w; + if ( !u3r_word_fit(&wid_w, wid) ) { + return u3m_bail(c3__fail); + } else { + c3_y cv_y[32], block_y[64], block_len; + c3_y *dat_y = u3r_bytes_alloc(0, wid_w, dat); + c3_d counter_d = u3r_chub(0, counter); + c3_y flags_y = u3r_byte(0, flags); + u3r_bytes(0, 32, cv_y, cv); + urcrypt_blake3_chunk_output(wid_w, dat_y, cv_y, block_y, &block_len, &counter_d, &flags_y); + return u3i_cell(u3i_bytes(32, cv_y), u3i_qual(u3k(counter), u3i_bytes(64, block_y), block_len, flags_y)); + } + } + + u3_noun + u3we_blake3_chunk_output(u3_noun cor) + { + u3_noun counter, msg, // arguments + wid, dat, // destructured msg + key, flags; // context + + if ( c3n == u3r_mean(cor, u3x_sam_2, &counter, + u3x_sam_3, &msg, + u3x_con_sam_2, &key, + u3x_con_sam_3, &flags, 0) || + u3r_cell(msg, &wid, &dat) || u3ud(wid) || u3ud(dat) || + u3ud(key) || u3ud(flags)) + { + return u3m_bail(c3__exit); + } else { + return u3l_punt("blake3_chunk_output", _cqe_blake3_chunk_output(wid, dat, key, counter, flags)); + } + } + + static u3_atom + _cqe_blake3_compress(u3_atom cv, u3_atom counter, + u3_atom block, u3_atom block_len, u3_atom flags) + { + c3_y cv_y[32], block_y[64], out_y[64]; + u3r_bytes(0, 32, cv_y, cv); + u3r_bytes(0, 64, block_y, block); + urcrypt_blake3_compress(cv_y, block_y, block_len, counter, flags, out_y); + return u3i_bytes(64, out_y); + } + + u3_noun + u3we_blake3_compress(u3_noun cor) + { + u3_noun output = u3x_at(u3x_sam, cor); + u3_noun cv, counter, block, block_len, flags; // destructured output + + if ( u3r_quil(output, &cv, &counter, &block, &block_len, &flags) || + u3ud(cv) || u3ud(block) || u3ud(block_len) || u3ud(counter) || u3ud(flags)) + { + return u3m_bail(c3__exit); + } else { + return u3l_punt("blake3_compress", _cqe_blake3_compress(cv, counter, block, block_len, flags)); } } diff --git a/pkg/noun/jets/tree.c b/pkg/noun/jets/tree.c index 9e920becda..e40fe03846 100644 --- a/pkg/noun/jets/tree.c +++ b/pkg/noun/jets/tree.c @@ -445,13 +445,10 @@ static c3_c* _140_hex_secp_ha[] = { 0 }; - static u3j_harm _140_hex_blake2b_a[] = {{".2", u3we_blake, c3y}, {}}; - static c3_c* _140_hex_blake2b_ha[] = { - "c432216ca53b5ad2284259167952761bb1046e280268c4d3b9ca70a2024e1934", - 0 - }; + static u3j_harm _140_hex_blake2b_a[] = {{".2", u3we_blake2b, c3y}, {}}; + static u3j_core _140_hex_blake_d[] = - { { "blake2b", 7, _140_hex_blake2b_a, 0, _140_hex_blake2b_ha }, + { { "blake2b", 7, _140_hex_blake2b_a, 0, no_hashes }, {} }; static c3_c* _140_hex_blake_ha[] = { @@ -2325,8 +2322,48 @@ u3j_core _k139_d[] = {} }; + static u3j_harm _138_hex_blake3_hash_a[] = {{".2", u3we_blake3_hash, c3y}, {}}; + static u3j_harm _138_hex_blake3_compress_a[] = {{".2", u3we_blake3_compress, c3y}, {}}; + static u3j_harm _138_hex_blake3_chunk_output_a[] = {{".2", u3we_blake3_chunk_output, c3y}, {}}; + static u3j_core _138_hex_blake3_d[] = + { { "hash", 7, _138_hex_blake3_hash_a, 0, no_hashes }, + { "chunk-output", 7, _138_hex_blake3_chunk_output_a, 0, no_hashes }, + {} + }; + static u3j_core _138_hex_blake3_impl_d[] = + { { "compress", 7, _138_hex_blake3_compress_a, 0, no_hashes }, + { "blake3", 7, 0, _138_hex_blake3_d, no_hashes }, + {} + }; +static u3j_core _138_hex_blake_d[] = + { { "blake2b", 7, _140_hex_blake2b_a, 0, no_hashes }, + { "blake3-impl", 7, 0, _138_hex_blake3_impl_d, no_hashes }, + {} + }; + +static u3j_core _138_hex_d[] = +{ { "lore", 63, _140_hex_lore_a, 0, no_hashes }, + { "leer", 63, _140_hex_leer_a, 0, no_hashes }, + { "loss", 63, _140_hex_loss_a, 0, no_hashes }, + { "lune", 127, _140_hex_lune_a, 0, no_hashes }, + + { "coed", 63, 0, _140_hex_coed_d, no_hashes }, + { "aes", 31, 0, _140_hex_aes_d, no_hashes }, + + { "hmac", 63, 0, _140_hex_hmac_d, no_hashes }, + { "argon", 31, 0, _140_hex_argon_d, no_hashes }, + { "blake", 31, 0, _138_hex_blake_d, no_hashes }, + { "kecc", 31, 0, _140_hex_kecc_d, no_hashes }, + { "ripemd", 31, 0, _140_hex_ripe_d, no_hashes }, + { "scr", 31, 0, _140_hex_scr_d, no_hashes }, + { "secp", 6, 0, _140_hex_secp_d, no_hashes }, + { "mimes", 31, 0, _140_hex_mimes_d, no_hashes }, + { "json", 31, 0, _139_hex_json_d, no_hashes }, + {} +}; + static u3j_core _138_pen_d[] = -{ { "hex", 7, 0, _139_hex_d, no_hashes }, +{ { "hex", 7, 0, _138_hex_d, no_hashes }, { "cell", 7, _140_pen_cell_a, 0, no_hashes }, { "comb", 7, _140_pen_comb_a, 0, no_hashes }, diff --git a/pkg/noun/jets/w.h b/pkg/noun/jets/w.h index 51dbd4471f..34716971bd 100644 --- a/pkg/noun/jets/w.h +++ b/pkg/noun/jets/w.h @@ -206,7 +206,10 @@ u3_noun u3we_argon2(u3_noun); - u3_noun u3we_blake(u3_noun); + u3_noun u3we_blake2b(u3_noun); + u3_noun u3we_blake3_hash(u3_noun); + u3_noun u3we_blake3_chunk_output(u3_noun); + u3_noun u3we_blake3_compress(u3_noun); u3_noun u3we_ripe(u3_noun); diff --git a/pkg/noun/manage.c b/pkg/noun/manage.c index d880a42032..71d673e357 100644 --- a/pkg/noun/manage.c +++ b/pkg/noun/manage.c @@ -2065,7 +2065,7 @@ u3m_init(size_t len_i) -1, 0); u3l_log("boot: mapping %zuMB failed", len_i >> 20); - u3l_log("see urbit.org/using/install/#about-swap-space" + u3l_log("see https://docs.urbit.org/manual/getting-started/self-hosted/cloud-hosting" " for adding swap space"); if ( -1 != (c3_ps)map_v ) { u3l_log("if porting to a new platform, try U3_OS_LoomBase %p", diff --git a/pkg/noun/nock.c b/pkg/noun/nock.c index 452b04c309..eb459d17f7 100644 --- a/pkg/noun/nock.c +++ b/pkg/noun/nock.c @@ -1400,7 +1400,7 @@ _n_comp(u3_noun* ops, u3_noun fol, c3_o los_o, c3_o tel_o) case 9: u3x_cell(arg, &hed, &tel); - if ( (1 == hed) || (3 == u3qc_cap(hed)) ) { + if ( (1 == hed) || (3 == u3qc_cap(u3x_atom(hed))) ) { u3_noun mac = u3nq(7, u3k(tel), 2, u3nt(u3nc(0, 1), 0, u3k(hed))); tot_w += _n_comp(ops, mac, los_o, tel_o); u3z(mac); diff --git a/pkg/vere/BUILD.bazel b/pkg/vere/BUILD.bazel index f50642ab89..33882e9244 100644 --- a/pkg/vere/BUILD.bazel +++ b/pkg/vere/BUILD.bazel @@ -96,6 +96,8 @@ vere_library( "*.h", "db/*.c", "io/*.c", + "io/*/*.h", + "io/*/*.c", ], exclude = [ "main.c", @@ -110,6 +112,7 @@ vere_library( "@platforms//os:macos": [ "platform/darwin/daemon.c", "platform/darwin/ptty.c", + "platform/darwin/mach.c", ], "@platforms//os:linux": [ "platform/linux/daemon.c", @@ -139,6 +142,7 @@ vere_library( "@lmdb", "@openssl", "@uv", + "@natpmp", ] + select({ "@platforms//os:macos": [], "@platforms//os:linux": [ @@ -183,6 +187,12 @@ vere_binary( # TESTS # + +cc_library( + name = "ames_src", + hdrs = ["io/ames.c"], +) + cc_test( name = "ames_tests", timeout = "short", @@ -192,7 +202,7 @@ cc_test( "//conditions:default": [], }), visibility = ["//visibility:private"], - deps = [":vere"], + deps = [":vere", ":ames_src"] ) cc_test( diff --git a/pkg/vere/ames_tests.c b/pkg/vere/ames_tests.c index 373788740f..e76622fb78 100644 --- a/pkg/vere/ames_tests.c +++ b/pkg/vere/ames_tests.c @@ -1,7 +1,6 @@ /// @file -#include "noun.h" -#include "vere.h" +#include "./io/ames.c" /* _setup(): prepare for tests. */ @@ -32,6 +31,55 @@ _test_ames(void) } } +static c3_i +_test_stun_addr_roundtrip(u3_lane* inn_u) +{ + c3_c res_c[16] = {0}; + c3_y rep_y[40]; + c3_y req_y[20] = {0}; + c3_i ret_i = 0; + + u3_stun_make_response(req_y, inn_u, rep_y); + + u3_lane lan_u; + + if ( c3n == u3_stun_find_xor_mapped_address(rep_y, sizeof(rep_y), &lan_u) ) { + fprintf(stderr, "stun: failed to find addr in response\r\n"); + ret_i = 1; + } + else { + if ( lan_u.pip_w != inn_u->pip_w ) { + fprintf(stderr, "stun: addr mismatch %x %x\r\n", lan_u.pip_w, inn_u->pip_w); + ret_i = 1; + } + + if ( lan_u.por_s != inn_u->por_s ) { + fprintf(stderr, "stun: addr mismatch %u %u\r\n", lan_u.por_s, inn_u->por_s); + ret_i = 1; + } + } + + return ret_i; +} + +static c3_i +_test_stun(void) +{ + u3_lane inn_u = { .pip_w = 0x7f000001, .por_s = 13337 }; + c3_w len_w = 256; + + while ( len_w-- ) { + if ( _test_stun_addr_roundtrip(&inn_u) ) { + return 1; + } + + inn_u.pip_w++; + inn_u.por_s++; + } + + return 0; +} + /* main(): run all test cases. */ int @@ -41,6 +89,10 @@ main(int argc, char* argv[]) _test_ames(); + if ( _test_stun() ) { + fprintf(stderr, "ames: stun tests failed\r\n"); + } + // GC // u3m_grab(u3_none); diff --git a/pkg/vere/disk.c b/pkg/vere/disk.c index 18fdf31fec..6cb3b62ca9 100644 --- a/pkg/vere/disk.c +++ b/pkg/vere/disk.c @@ -31,15 +31,6 @@ struct _u3_disk_walk { c3_o liv_o; }; -// for u3_lmdb_init() calls -static const size_t siz_i = -#if (defined(U3_CPU_aarch64) && defined(U3_OS_linux)) - // 500 GiB is as large as musl on aarch64 wants to allow - 0x7d00000000; -#else - 0x10000000000; -#endif - #undef VERBOSE_DISK #undef DISK_TRACE_JAM #undef DISK_TRACE_CUE @@ -680,7 +671,7 @@ u3_disk_save_meta_meta(c3_c* log_c, { MDB_env* dbm_u; - if ( 0 == (dbm_u = u3_lmdb_init(log_c, siz_i)) ) { + if ( 0 == (dbm_u = u3_lmdb_init(log_c, u3_Host.ops_u.siz_i)) ) { fprintf(stderr, "disk: failed to initialize meta-lmdb\r\n"); return c3n; } @@ -842,86 +833,134 @@ _disk_lock(c3_c* pax_c) return paf_c; } -/* u3_disk_acquire(): acquire a lockfile, killing anything that holds it. +/* _disk_acquire(): acquire a lockfile, killing anything that holds it. */ -static void -u3_disk_acquire(c3_c* pax_c) +static c3_i +_disk_acquire(c3_c* pax_c) { - c3_c* paf_c = _disk_lock(pax_c); - c3_w pid_w; - FILE* loq_u; - - if ( NULL != (loq_u = c3_fopen(paf_c, "r")) ) { - if ( 1 != fscanf(loq_u, "%" SCNu32, &pid_w) ) { - u3l_log("lockfile %s is corrupt!", paf_c); - kill(getpid(), SIGTERM); - sleep(1); u3_assert(0); - } - else if (pid_w != getpid()) { - c3_w i_w; + c3_c* paf_c = _disk_lock(pax_c); + c3_y dat_y[12] = {0}; + c3_w pid_w = 0; + c3_i fid_i, ret_i; + + if ( -1 == (fid_i = c3_open(paf_c, O_RDWR|O_CREAT, 0666)) ) { + fprintf(stderr, "disk: failed to open/create lock file\r\n"); + goto fail; + } - int ret = kill(pid_w, SIGTERM); + { + c3_y len_y = 0; + c3_y* buf_y = dat_y; - if ( -1 == ret && errno == EPERM ) { - u3l_log("disk: permission denied when trying to kill process %d!", pid_w); - kill(getpid(), SIGTERM); - sleep(1); u3_assert(0); + do { + c3_zs ret_zs; + + if ( -1 == (ret_zs = read(fid_i, buf_y, 1)) ) { + if ( EINTR == errno ) continue; + + fprintf(stderr, "disk: failed to read lockfile: %s\r\n", + strerror(errno)); + goto fail; } - if ( -1 != ret ) { - u3l_log("disk: stopping process %d, live in %s...", - pid_w, pax_c); + if ( !ret_zs ) break; + else if ( 1 != ret_zs ) { + fprintf(stderr, "disk: strange lockfile read %zd\r\n", ret_zs); + goto fail; + } - for ( i_w = 0; i_w < 16; i_w++ ) { - sleep(1); - if ( -1 == kill(pid_w, SIGTERM) ) { - break; - } - } - if ( 16 == i_w ) { - for ( i_w = 0; i_w < 16; i_w++ ) { - if ( -1 == kill(pid_w, SIGKILL) ) { - break; - } - sleep(1); - } - } - if ( 16 == i_w ) { - u3l_log("disk: process %d seems unkillable!", pid_w); - u3_assert(0); - } - u3l_log("disk: stopped old process %u", pid_w); + len_y++; + buf_y++; + } + while ( len_y < sizeof(dat_y) ); + + + if ( len_y ) { + if ( (1 != sscanf((c3_c*)dat_y, "%" SCNu32 "%n", &pid_w, &ret_i)) + || (0 >= ret_i) + || ('\n' != *(dat_y + ret_i)) ) + { + fprintf(stderr, "disk: lockfile is corrupt\r\n"); } } - fclose(loq_u); - c3_unlink(paf_c); } - if ( NULL == (loq_u = c3_fopen(paf_c, "w")) ) { - u3l_log("disk: unable to open %s: %s", paf_c, strerror(errno)); - u3_assert(0); + { + struct flock lok_u; + memset((void *)&lok_u, 0, sizeof(lok_u)); + lok_u.l_type = F_WRLCK; + lok_u.l_whence = SEEK_SET; + lok_u.l_start = 0; + lok_u.l_len = 1; + + while ( (ret_i = fcntl(fid_i, F_SETLK, &lok_u)) + && (EINTR == (ret_i = errno)) ); + + if ( ret_i ) { + if ( pid_w ) { + fprintf(stderr, "pier: locked by PID %u\r\n", pid_w); + } + else { + fprintf(stderr, "pier: strange: locked by empty lockfile\r\n"); + } + + goto fail; + } } - fprintf(loq_u, "%u\n", getpid()); + ret_i = snprintf((c3_c*)dat_y, sizeof(dat_y), "%u\n", getpid()); + + if ( 0 >= ret_i ) { + fprintf(stderr, "disk: failed to write lockfile\r\n"); + goto fail; + } { - c3_i fid_i = fileno(loq_u); - c3_sync(fid_i); + c3_y len_y = (c3_y)ret_i; + c3_y* buf_y = dat_y; + + do { + c3_zs ret_zs; + + if ( (-1 == (ret_zs = write(fid_i, buf_y, len_y))) + && (EINTR != errno) ) + { + fprintf(stderr, "disk: lockfile write failed %s\r\n", + strerror(errno)); + goto fail; + } + + if ( 0 < ret_zs ) { + len_y -= ret_zs; + buf_y += ret_zs; + } + } + while ( len_y ); + } + + if ( -1 == c3_sync(fid_i) ) { + fprintf(stderr, "disk: failed to sync lockfile: %s\r\n", + strerror(errno)); + goto fail; } - fclose(loq_u); c3_free(paf_c); + return fid_i; + +fail: + kill(getpid(), SIGTERM); + sleep(1); u3_assert(0); } -/* u3_disk_release(): release a lockfile. +/* _disk_release(): release a lockfile. */ static void -u3_disk_release(c3_c* pax_c) +_disk_release(c3_c* pax_c, c3_i fid_i) { c3_c* paf_c = _disk_lock(pax_c); - c3_unlink(paf_c); c3_free(paf_c); + close(fid_i); } /* u3_disk_exit(): close the log. @@ -968,7 +1007,7 @@ u3_disk_exit(u3_disk* log_u) } } - u3_disk_release(log_u->dir_u->pax_c); + _disk_release(log_u->dir_u->pax_c, log_u->lok_i); u3_dire_free(log_u->dir_u); u3_dire_free(log_u->urb_u); @@ -1132,18 +1171,39 @@ u3_disk_epoc_zero(u3_disk* log_u) } // create epoch version file, overwriting any existing file + c3_c epi_c[8193]; c3_c epv_c[8193]; + snprintf(epi_c, sizeof(epv_c), "%s/epoc.tmp", epo_c); snprintf(epv_c, sizeof(epv_c), "%s/epoc.txt", epo_c); - FILE* epv_f = fopen(epv_c, "w"); // XX errors - fprintf(epv_f, "%d", U3E_VERLAT); - fclose(epv_f); + FILE* epv_f = fopen(epi_c, "w"); // XX errors + + if ( !epv_f + || (0 > fprintf(epv_f, "%d", U3E_VERLAT)) + || fflush(epv_f) + || (-1 == c3_sync(fileno(epv_f))) + || fclose(epv_f) + || (-1 == rename(epi_c, epv_c)) ) + { + fprintf(stderr, "disk: write epoc.txt failed %s\r\n", strerror(errno)); + goto fail3; + } // create binary version file, overwriting any existing file + c3_c bii_c[8193]; c3_c biv_c[8193]; - snprintf(biv_c, sizeof(biv_c), "%s/vere.txt", epo_c); - FILE* biv_f = fopen(biv_c, "w"); // XX errors - fprintf(biv_f, URBIT_VERSION); // XX append a sentinel for auto-rollover? - fclose(biv_f); + snprintf(bii_c, sizeof(biv_c), "%s/vere.tmp", epo_c); + snprintf(biv_c, sizeof(epv_c), "%s/vere.txt", epo_c); + FILE* biv_f = fopen(bii_c, "w"); + if ( !biv_f + || (0 > fprintf(biv_f, URBIT_VERSION)) + || fflush(biv_f) + || (-1 == c3_sync(fileno(biv_f))) + || fclose(biv_f) + || (-1 == rename(bii_c, biv_c)) ) + { + fprintf(stderr, "disk: write vere.txt failed %s\r\n", strerror(errno)); + goto fail3; + } if ( -1 == c3_sync(epo_i) ) { // XX fdatasync on linux? fprintf(stderr, "disk: sync epoch dir 0i0 failed: %s\r\n", strerror(errno)); @@ -1214,18 +1274,44 @@ _disk_epoc_roll(u3_disk* log_u, c3_d epo_d) } // create epoch version file, overwriting any existing file + c3_c epi_c[8193]; c3_c epv_c[8193]; + snprintf(epi_c, sizeof(epv_c), "%s/epoc.tmp", epo_c); snprintf(epv_c, sizeof(epv_c), "%s/epoc.txt", epo_c); - FILE* epv_f = fopen(epv_c, "w"); // XX errors - fprintf(epv_f, "%d", U3E_VERLAT); - fclose(epv_f); + FILE* epv_f = fopen(epi_c, "w"); + + if ( !epv_f + || (0 > fprintf(epv_f, "%d", U3E_VERLAT)) + || fflush(epv_f) + || (-1 == c3_sync(fileno(epv_f))) + || fclose(epv_f) + || (-1 == rename(epi_c, epv_c)) ) + { + fprintf(stderr, "disk: write epoc.txt failed %s\r\n", strerror(errno)); + goto fail3; + } // create binary version file, overwriting any existing file + c3_c bii_c[8193]; c3_c biv_c[8193]; - snprintf(biv_c, sizeof(biv_c), "%s/vere.txt", epo_c); - FILE* biv_f = fopen(biv_c, "w"); // XX errors - fprintf(biv_f, URBIT_VERSION); - fclose(biv_f); + snprintf(bii_c, sizeof(biv_c), "%s/vere.tmp", epo_c); + snprintf(biv_c, sizeof(epv_c), "%s/vere.txt", epo_c); + FILE* biv_f = fopen(bii_c, "w"); + if ( !biv_f + || (0 > fprintf(biv_f, URBIT_VERSION)) + || fflush(biv_f) + || (-1 == c3_sync(fileno(biv_f))) + || fclose(biv_f) + || (-1 == rename(bii_c, biv_c)) ) + { + fprintf(stderr, "disk: write vere.txt failed %s\r\n", strerror(errno)); + goto fail3; + } + + if ( -1 == c3_sync(epo_i) ) { // XX fdatasync on linux? + fprintf(stderr, "disk: sync epoch dir 0i0 failed: %s\r\n", strerror(errno)); + goto fail3; + } // get metadata from old log c3_d who_d[2]; @@ -1240,7 +1326,7 @@ _disk_epoc_roll(u3_disk* log_u, c3_d epo_d) log_u->mdb_u = 0; // initialize db of new epoch - if ( 0 == (log_u->mdb_u = u3_lmdb_init(epo_c, siz_i)) ) { + if ( 0 == (log_u->mdb_u = u3_lmdb_init(epo_c, u3_Host.ops_u.siz_i)) ) { fprintf(stderr, "disk: failed to initialize database\r\n"); c3_free(log_u); goto fail3; @@ -1499,7 +1585,7 @@ _disk_migrate(u3_disk* log_u, c3_d eve_d) return c3n; } - if ( 0 == (log_u->mdb_u = u3_lmdb_init(tmp_c, siz_i)) ) { + if ( 0 == (log_u->mdb_u = u3_lmdb_init(tmp_c, u3_Host.ops_u.siz_i)) ) { fprintf(stderr, "disk: failed to initialize database at %s\r\n", tmp_c); return c3n; @@ -1533,7 +1619,7 @@ _disk_migrate(u3_disk* log_u, c3_d eve_d) strerror(errno)); } - if ( 0 == (log_u->mdb_u = u3_lmdb_init(epo_c, siz_i)) ) { + if ( 0 == (log_u->mdb_u = u3_lmdb_init(epo_c, u3_Host.ops_u.siz_i)) ) { fprintf(stderr, "disk: failed to initialize database at %s\r\n", epo_c); return c3n; @@ -1632,7 +1718,9 @@ u3_disk_chop(u3_disk* log_u, c3_d eve_d) u3_disk_epoc_list(log_u, sot_d); if ( len_z <= 2 ) { - fprintf(stderr, "chop: nothing to do, have a great day\r\n"); + fprintf(stderr, "chop: nothing to do, try running roll first\r\n" + "chop: for more info see " + "https://docs.urbit.org/manual/running/vere#chop\r\n"); exit(0); // enjoy } @@ -1714,9 +1802,9 @@ _disk_epoc_load(u3_disk* log_u, c3_d lat_d) return _epoc_gone; } - if ( (1 != sscanf(ver_c, "%" SCNu32 "%n", &ver_w, &car_i)) - && (0 < car_i) - && ('\0' == *(ver_c + car_i)) ) + if ( !( (1 == sscanf(ver_c, "%" SCNu32 "%n", &ver_w, &car_i)) + && (0 < car_i) + && ('\0' == *(ver_c + car_i)) ) ) { fprintf(stderr, "disk: failed to parse epoch version: '%s'\r\n", ver_c); return _epoc_fail; @@ -1734,7 +1822,7 @@ _disk_epoc_load(u3_disk* log_u, c3_d lat_d) snprintf(epo_c, 8192, "%s/0i%" PRIc3_d, log_u->com_u->pax_c, lat_d); // initialize latest epoch's db - if ( 0 == (log_u->mdb_u = u3_lmdb_init(epo_c, siz_i)) ) { + if ( 0 == (log_u->mdb_u = u3_lmdb_init(epo_c, u3_Host.ops_u.siz_i)) ) { fprintf(stderr, "disk: failed to initialize database at %s\r\n", epo_c); return _epoc_fail; @@ -1777,6 +1865,7 @@ u3_disk* u3_disk_init(c3_c* pax_c, u3_disk_cb cb_u) { u3_disk* log_u = c3_calloc(sizeof(*log_u)); + log_u->lok_i = -1; log_u->liv_o = c3n; log_u->ted_o = c3n; log_u->cb_u = cb_u; @@ -1795,7 +1884,7 @@ u3_disk_init(c3_c* pax_c, u3_disk_cb cb_u) // acquire lockfile. // - u3_disk_acquire(pax_c); + log_u->lok_i = _disk_acquire(pax_c); // create/load $pier/.urb // @@ -1878,7 +1967,7 @@ u3_disk_init(c3_c* pax_c, u3_disk_cb cb_u) if ( c3y == exs_o ) { // load the old data.mdb file - if ( 0 == (log_u->mdb_u = u3_lmdb_init(log_c, siz_i)) ) { + if ( 0 == (log_u->mdb_u = u3_lmdb_init(log_c, u3_Host.ops_u.siz_i)) ) { fprintf(stderr, "disk: failed to initialize lmdb\r\n"); c3_free(log_u); return 0; diff --git a/pkg/vere/io/ames.c b/pkg/vere/io/ames.c index eecef8ac75..165b57fd18 100644 --- a/pkg/vere/io/ames.c +++ b/pkg/vere/io/ames.c @@ -2,11 +2,13 @@ #include "vere.h" #include "mdns.h" +#include "io/ames/stun.h" #include "noun.h" #include "ur.h" #include "zlib.h" +#include "natpmp.h" #include @@ -24,6 +26,8 @@ #define QUEUE_MAX 30 // max number of packets in queue +#define _CZAR_GONE UINT32_MAX + typedef enum u3_stun_state { STUN_OFF = 0, STUN_TRYING = 1, @@ -51,29 +55,34 @@ typedef enum u3_stun_state { c3_l sev_l; // instance number ur_cue_test_t* tes_u; // cue-test handle u3_cue_xeno* sil_u; // cue handle - c3_c* dns_c; // domain XX multiple/fallback c3_y ver_y; // protocol version u3p(u3h_root) lax_p; // lane scry cache struct _u3_panc* pan_u; // outbound packet queue, backward - c3_w imp_w[256]; // imperial IPs - time_t imp_t[256]; // imperial IP timestamps - c3_o imp_o[256]; // imperial print status + struct { // + c3_c dom_c[251]; // domain + c3_o dom_o; // have domain + uv_timer_t tim_u; // resolve timer + c3_s pen_s; // pending + c3_w pip_w[256]; // ipv4 + c3_w log_w[256 >> 5]; // log error + } zar_u; // struct { // stun client state: u3_stun_state sat_y; // formal state c3_y tid_y[12]; // last transaction id c3_y dad_y; // sponsoring galaxy u3_lane lan_u; // sponsoring galaxy IP and port uv_timer_t tim_u; // keepalive timer handle - uv_timer_t dns_u; // DNS resolution timer handle - c3_c* dns_c; // sponsoring galaxy fqdn - struct timeval las_u; // XX last sent date (not used?) struct timeval sar_u; // date we started trying to send u3_lane sef_u; // our lane, if we know it c3_o wok_o; // STUN worked, set on first success } sun_u; // + struct { + natpmp_t req_u; // libnatpmp struct for mapping request + uv_poll_t pol_u; // handle waits on libnatpmp socket + uv_timer_t tim_u; // every two hours if mapping succeeds + } nat_u; // libnatpmp stuff for port forwarding c3_o nal_o; // lane cache backcompat flag struct { // config: - c3_o net_o; // can send c3_o see_o; // can scry c3_o fit_o; // filtering active } fig_u; // @@ -175,14 +184,10 @@ typedef enum u3_stun_state { struct _u3_ames* sam_u; // ames backpointer c3_w len_w; // length in bytes c3_y* hun_y; // packet buffer + u3_lane lan_u; // destination/origin lane u3_head hed_u; // head of packet u3_prel pre_u; // packet prelude u3_ptag typ_y; // packet type tag - struct { - u3_lane lan_u; // destination/origin lane - c3_y imp_y; // galaxy (optional) - c3_c* dns_c; // galaxy fqdn (optional) - } rut_u; union { u3_body bod_u; // tagged by PACT_AMES u3_wail wal_u; // tagged by PACT_WAIL @@ -199,7 +204,6 @@ typedef enum u3_stun_state { c3_o for_o; // are we forwarding this? } u3_panc; -#define _str_o(lob_o) ( ( c3y == lob_o ) ? "yes" : "no" ) #define _str_typ(typ_y) ( \ ( PACT_AMES == typ_y ) ? "ames" \ : ( PACT_WAIL == typ_y ) ? "wail" \ @@ -208,79 +212,7 @@ typedef enum u3_stun_state { const c3_c* PATH_PARSER = ";~(pfix fas (most fas (cook crip (star ;~(less fas prn)))))"; -static void -_log_head(u3_head* hed_u) -{ - u3l_log("-- HEADER --"); - u3l_log("is request: %s", _str_o(hed_u->req_o)); - u3l_log("is ames: %s", _str_o(hed_u->sim_o)); - u3l_log("mug: 0x%05x", (hed_u->mug_l &0xfffff)); - u3l_log("protocol version: %u", hed_u->ver_y); - u3l_log("sender class: %u", hed_u->sac_y); - u3l_log("recevr class: %u", hed_u->rac_y); - u3l_log("is relayed: %s", _str_o(hed_u->rel_o)); - u3l_log(""); -} - -static void -_log_prel(u3_prel* pre_u) -{ - u3l_log("-- PRELUDE --"); - u3l_log("sender life: %u", pre_u->sic_y); - u3l_log("receiver life: %u", pre_u->ric_y); - u3l_log("sender: %" PRIu64 "", pre_u->sen_d[0]); - u3l_log("receiver: %" PRIu64" ", pre_u->rec_d[0]); - u3l_log(""); -} - -static void -_log_peep(u3_peep* req_u) -{ - u3l_log("--- REQUEST ---"); - u3l_log("strlen: %u", req_u->len_s); - u3l_log("path: %s", req_u->pat_c); - u3l_log("frag: %u", req_u->fra_w); - u3l_log(""); -} - -static c3_c* -_show_mug_buf(c3_y* buf_y, c3_w len_w) -{ - u3_noun mug = u3r_mug_bytes(buf_y, len_w); - u3_noun cot = u3dc("scot", 'q', mug); - return u3r_string(cot); -} - -static void -_log_meow(u3_meow* mew_u) -{ - c3_c* sig_c = _show_mug_buf(mew_u->sig_y, sizeof(mew_u->sig_y)); - c3_c* dat_c = _show_mug_buf(mew_u->dat_y, mew_u->siz_s); - - u3l_log(" sig=%s" - " num=%u" - " siz=%u" - " dat=%s", - sig_c, - mew_u->num_w, - mew_u->siz_s, - dat_c - ); - - c3_free(sig_c); - c3_free(dat_c); -} - -static void -_log_bytes(c3_y* byt_y, c3_w len_w) -{ - int i; - u3l_log("-- BYTES (%u) --", len_w); - for(i = 0; i < len_w; i++) { - u3l_log("%x", byt_y[i]); - } - u3l_log(""); -} +static c3_o net_o = c3y; // online heuristic to limit verbosity /* _ames_alloc(): libuv buffer allocator. */ @@ -320,7 +252,6 @@ _ames_pact_free(u3_pact* pac_u) u3_pier_bail(u3_king_stub()); } - c3_free(pac_u->rut_u.dns_c); c3_free(pac_u->hun_y); c3_free(pac_u); } @@ -430,34 +361,6 @@ _ames_check_mug(u3_pact* pac_u) ? c3y : c3n); } -static inline c3_s -_ames_sift_short(c3_y buf_y[2]) -{ - return (buf_y[1] << 8 | buf_y[0]); -} - -static inline c3_w -_ames_sift_word(c3_y buf_y[4]) -{ - return (buf_y[3] << 24 | buf_y[2] << 16 | buf_y[1] << 8 | buf_y[0]); -} - -/* _ames_chub_bytes(): c3_y[8] to c3_d -** XX factor out, deduplicate with other conversions -*/ -static inline c3_d -_ames_chub_bytes(c3_y byt_y[8]) -{ - return (c3_d)byt_y[0] - | (c3_d)byt_y[1] << 8 - | (c3_d)byt_y[2] << 16 - | (c3_d)byt_y[3] << 24 - | (c3_d)byt_y[4] << 32 - | (c3_d)byt_y[5] << 40 - | (c3_d)byt_y[6] << 48 - | (c3_d)byt_y[7] << 56; -} - /* _ames_ship_to_chubs(): pack [len_y] bytes into c3_d[2] */ static inline void @@ -466,24 +369,8 @@ _ames_ship_to_chubs(c3_d sip_d[2], c3_y len_y, c3_y* buf_y) c3_y sip_y[16] = {0}; memcpy(sip_y, buf_y, c3_min(16, len_y)); - sip_d[0] = _ames_chub_bytes(sip_y); - sip_d[1] = _ames_chub_bytes(sip_y + 8); -} - -/* _ames_chub_bytes(): c3_d to c3_y[8] -** XX factor out, deduplicate with other conversions -*/ -static inline void -_ames_bytes_chub(c3_y byt_y[8], c3_d num_d) -{ - byt_y[0] = num_d & 0xff; - byt_y[1] = (num_d >> 8) & 0xff; - byt_y[2] = (num_d >> 16) & 0xff; - byt_y[3] = (num_d >> 24) & 0xff; - byt_y[4] = (num_d >> 32) & 0xff; - byt_y[5] = (num_d >> 40) & 0xff; - byt_y[6] = (num_d >> 48) & 0xff; - byt_y[7] = (num_d >> 56) & 0xff; + sip_d[0] = c3_sift_chub(sip_y); + sip_d[1] = c3_sift_chub(sip_y + 8); } /* _ames_ship_of_chubs(): unpack c3_d[2] into [len_y] bytes. @@ -493,8 +380,8 @@ _ames_ship_of_chubs(c3_d sip_d[2], c3_y len_y, c3_y* buf_y) { c3_y sip_y[16] = {0}; - _ames_bytes_chub(sip_y, sip_d[0]); - _ames_bytes_chub(sip_y + 8, sip_d[1]); + c3_etch_chub(sip_y, sip_d[0]); + c3_etch_chub(sip_y + 8, sip_d[1]); memcpy(buf_y, sip_y, c3_min(16, len_y)); } @@ -504,7 +391,7 @@ _ames_ship_of_chubs(c3_d sip_d[2], c3_y len_y, c3_y* buf_y) static void _ames_sift_head(u3_head* hed_u, c3_y buf_y[4]) { - c3_w hed_w = _ames_sift_word(buf_y); + c3_w hed_w = c3_sift_word(buf_y); // first two bits are reserved // @@ -532,7 +419,7 @@ _ames_sift_prel(u3_head* hed_u, if ( c3y == hed_u->rel_o ) { c3_y rag_y[8] = {0}; memcpy(rag_y, buf_y + cur_w, 6); - pre_u->rog_d = _ames_chub_bytes(rag_y); + pre_u->rog_d = c3_sift_chub(rag_y); cur_w += 6; } else { @@ -585,12 +472,12 @@ _fine_sift_wail(u3_pact* pac_u, c3_w cur_w) // parse fragment number // - pac_u->wal_u.pep_u.fra_w = _ames_sift_word(pac_u->hun_y + cur_w); + pac_u->wal_u.pep_u.fra_w = c3_sift_word(pac_u->hun_y + cur_w); cur_w += fra_w; // parse path length field // - len_s = _ames_sift_short(pac_u->hun_y + cur_w); + len_s = c3_sift_short(pac_u->hun_y + cur_w); pac_u->wal_u.pep_u.len_s = len_s; cur_w += len_w; @@ -666,22 +553,6 @@ _fine_sift_meow(u3_meow* mew_u, u3_noun mew) return ret_o; } -static void -_ames_etch_short(c3_y buf_y[2], c3_s sot_s) -{ - buf_y[0] = sot_s & 0xff; - buf_y[1] = (sot_s >> 8) & 0xff; -} - -static void -_ames_etch_word(c3_y buf_y[4], c3_w wod_w) -{ - buf_y[0] = wod_w & 0xff; - buf_y[1] = (wod_w >> 8) & 0xff; - buf_y[2] = (wod_w >> 16) & 0xff; - buf_y[3] = (wod_w >> 24) & 0xff; -} - /* _ames_etch_head(): serialize packet header. */ static void @@ -699,14 +570,14 @@ _ames_etch_head(u3_head* hed_u, c3_y buf_y[4]) ^ ((hed_u->mug_l & 0xfffff) << 11) ^ ((hed_u->rel_o & 0x1) << 31); - _ames_etch_word(buf_y, hed_w); + c3_etch_word(buf_y, hed_w); } static void _ames_etch_origin(c3_d rog_d, c3_y* buf_y) { c3_y rag_y[8] = {0}; - _ames_bytes_chub(rag_y, rog_d); + c3_etch_chub(rag_y, rog_d); memcpy(buf_y, rag_y, 6); } @@ -751,12 +622,12 @@ _fine_etch_peep(u3_peep* pep_u, c3_y* buf_y) // write fragment number // - _ames_etch_word(buf_y + cur_w, pep_u->fra_w); + c3_etch_word(buf_y + cur_w, pep_u->fra_w); cur_w += sizeof(pep_u->fra_w); // write path length // - _ames_etch_short(buf_y + cur_w, pep_u->len_s); + c3_etch_short(buf_y + cur_w, pep_u->len_s); cur_w += sizeof(pep_u->len_s); // write request path @@ -783,7 +654,7 @@ _fine_etch_meow(u3_meow* mew_u, c3_y* buf_y) // write number of fragments // - _ames_etch_word(num_y, mew_u->num_w); + c3_etch_word(num_y, mew_u->num_w); memcpy(buf_y + cur_w, num_y, len_y); if (mew_u->siz_s != 0) { @@ -869,18 +740,17 @@ _ames_send_cb(uv_udp_send_t* req_u, c3_i sas_i) u3_pact* pac_u = (u3_pact*)req_u; u3_ames* sam_u = pac_u->sam_u; - if ( sas_i ) { - u3l_log("ames: send fail_async: %s", uv_strerror(sas_i)); - sam_u->fig_u.net_o = c3n; + if ( !sas_i ) { + net_o = c3y; } - else { - sam_u->fig_u.net_o = c3y; + else if ( c3y == net_o ) { + u3l_log("ames: send fail: %s", uv_strerror(sas_i)); + net_o = c3n; } _ames_pact_free(pac_u); } -#define _fine_send _ames_send /* _ames_send(): send buffer to address on port. */ static void @@ -891,7 +761,7 @@ _ames_send(u3_pact* pac_u) if ( !pac_u->hun_y || !sam_u || !pac_u->len_w - || !pac_u->rut_u.lan_u.por_s ) + || !pac_u->lan_u.por_s ) { u3l_log("ames: _ames_send null"); _ames_pact_free(pac_u); @@ -901,15 +771,11 @@ _ames_send(u3_pact* pac_u) memset(&add_u, 0, sizeof(add_u)); add_u.sin_family = AF_INET; - add_u.sin_addr.s_addr = htonl(pac_u->rut_u.lan_u.pip_w); - add_u.sin_port = htons(pac_u->rut_u.lan_u.por_s); - - //u3l_log("_ames_send %s %u", _str_typ(pac_u->typ_y), - // pac_u->rut_u.lan_u.por_s); + add_u.sin_addr.s_addr = htonl(pac_u->lan_u.pip_w); + add_u.sin_port = htons(pac_u->lan_u.por_s); { uv_buf_t buf_u = uv_buf_init((c3_c*)pac_u->hun_y, pac_u->len_w); - c3_i sas_i = uv_udp_send(&pac_u->snd_u, &sam_u->wax_u, &buf_u, 1, @@ -917,12 +783,7 @@ _ames_send(u3_pact* pac_u) _ames_send_cb); if ( sas_i ) { - if ( c3y == sam_u->fig_u.net_o ) { - u3l_log("ames: send fail_sync: %s", uv_strerror(sas_i)); - sam_u->fig_u.net_o = c3n; - } - - _ames_pact_free(pac_u); + _ames_send_cb(&pac_u->snd_u, sas_i); } } } @@ -1028,201 +889,81 @@ _ames_czar_port(c3_y imp_y) } } -static c3_c* -_ames_czar_dns(c3_y imp_y, c3_c* czar_c) -{ - u3_noun nam = u3dc("scot", 'p', imp_y); - c3_c* nam_c = u3r_string(nam); - c3_w len_w = 3 + strlen(nam_c) + strlen(czar_c); - u3_assert(len_w <= 256); - c3_c* dns_c = c3_malloc(len_w); - - c3_i sas_i = snprintf(dns_c, len_w, "%s.%s.", nam_c + 1, czar_c); - u3_assert(sas_i <= 255); - - c3_free(nam_c); - u3z(nam); - - return dns_c; -} - -/* _ames_czar_gone(): galaxy address resolution failed. -*/ -static void -_ames_czar_gone(u3_ames* sam_u, time_t now, c3_d imp_y, c3_c* dns_c) -{ - if ( c3y == sam_u->imp_o[imp_y] ) { - u3l_log("ames: czar at %s: not found (b)", dns_c); - sam_u->imp_o[imp_y] = c3n; - } - - if ( (0 == sam_u->imp_w[imp_y]) || - (0xffffffff == sam_u->imp_w[imp_y]) ) - { - sam_u->imp_w[imp_y] = 0xffffffff; - } - - // keep existing ip for 5 more minutes - // - sam_u->imp_t[imp_y] = now; -} - -/* _ames_czar_here(): galaxy address resolution succeeded. +/* _ames_czar_str: galaxy name as c3_c[3] */ static void -_ames_czar_here(u3_pact* pac_u, time_t now, struct sockaddr_in* add_u) +_ames_czar_str(c3_c zar_c[3], c3_y imp_y) { - u3_ames* sam_u = pac_u->sam_u; - c3_y imp_y = pac_u->rut_u.imp_y; - c3_w old_w = sam_u->imp_w[imp_y]; - c3_w pip_w = ntohl(add_u->sin_addr.s_addr); - - if ( pip_w != old_w ) { - u3_noun nam = u3dc("scot", c3__if, u3i_word(pip_w)); - c3_c* nam_c = u3r_string(nam); - - u3l_log("ames: czar %s ip %s", pac_u->rut_u.dns_c, nam_c); - - c3_free(nam_c); - u3z(nam); - } - - sam_u->imp_w[imp_y] = pip_w; - sam_u->imp_t[imp_y] = now; - sam_u->imp_o[imp_y] = c3y; - - pac_u->rut_u.lan_u.pip_w = pip_w; + u3_po_to_suffix(imp_y, (c3_y*)zar_c, (c3_y*)zar_c + 1, (c3_y*)zar_c + 2); } -/* _stun_czar_here(): sponsor galaxy address resolution succeeded. +/* _ames_etch_czar: galaxy fqdn */ -static c3_w -_stun_czar_here(u3_ames* sam_u, time_t now, struct sockaddr_in* add_u) +static c3_i +_ames_etch_czar(c3_c dns_c[256], const c3_c* dom_c, c3_y imp_y) { - c3_y imp_y = sam_u->sun_u.dad_y; - c3_w old_w = sam_u->imp_w[imp_y]; - c3_w pip_w = ntohl(add_u->sin_addr.s_addr); - - if ( pip_w != old_w ) { - u3_noun nam = u3dc("scot", c3__if, u3i_word(pip_w)); - c3_c* nam_c = u3r_string(nam); + c3_c* bas_c = dns_c; + c3_w len_w = strlen(dom_c); - u3l_log("stun: czar %s ip %s", sam_u->sun_u.dns_c, nam_c); - - c3_free(nam_c); - u3z(nam); + // name 3, '.' 2, trailing null + // + if ( 250 <= len_w ) { + return -1; } - sam_u->sun_u.lan_u.pip_w = pip_w; - sam_u->imp_w[imp_y] = pip_w; - sam_u->imp_t[imp_y] = now; - sam_u->imp_o[imp_y] = c3y; + _ames_czar_str(dns_c, imp_y); + dns_c += 3; + *dns_c++ = '.'; - return pip_w; -} + memcpy(dns_c, dom_c, len_w); + dns_c += len_w; + *dns_c++ = '.'; -/* _ames_czar_cb(): galaxy address resolution callback. -*/ -static void -_ames_czar_cb(uv_getaddrinfo_t* adr_u, - c3_i sas_i, - struct addrinfo* aif_u) -{ - { - u3_pact* pac_u = (u3_pact*)adr_u->data; - struct addrinfo* rai_u = aif_u; - time_t now = time(0); - - if ( sas_i == 0 ) { - _ames_czar_here(pac_u, now, (struct sockaddr_in *)rai_u->ai_addr); - } else { - _ames_czar_gone(pac_u->sam_u, now, - pac_u->rut_u.imp_y, - pac_u->rut_u.dns_c); - _ames_pact_free(pac_u); - } - } + memset(dns_c, 0, 256 - (dns_c - bas_c)); - c3_free(adr_u); - uv_freeaddrinfo(aif_u); + return 0; } -/* _ames_czar(): galaxy address resolution. +/* _ames_czar_lane: retrieve lane for galaxy if stored. */ -static void -_ames_czar(u3_pact* pac_u) +static c3_o +_ames_czar_lane(u3_ames* sam_u, c3_y imp_y, u3_lane* lan_u) { - u3_ames* sam_u = pac_u->sam_u; - - c3_y imp_y = pac_u->rut_u.imp_y; - - pac_u->rut_u.lan_u.por_s = _ames_czar_port(imp_y); + c3_s por_s = _ames_czar_port(imp_y); + c3_w pip_w; if ( c3n == u3_Host.ops_u.net ) { - pac_u->rut_u.lan_u.pip_w = 0x7f000001; - _ames_send(pac_u); - return; + pip_w = 0x7f000001; } + else { + pip_w = sam_u->zar_u.pip_w[imp_y]; - // if we don't have a galaxy domain, no-op - // - if ( !sam_u->dns_c ) { - u3_noun nam = u3dc("scot", 'p', pac_u->rut_u.imp_y); - c3_c* nam_c = u3r_string(nam); - u3l_log("ames: no galaxy domain for %s, no-op", nam_c); - - c3_free(nam_c); - u3z(nam); - return; - } - - { - c3_w pip_w = sam_u->imp_w[imp_y]; - time_t wen = sam_u->imp_t[imp_y]; - time_t now = time(0); - - // backoff for 5 minutes after failed lookup - // - if ( ( now < wen ) // time shenanigans! - || ( (0xffffffff == pip_w) // sentinal ip address - && ((now - wen) < 300) ) ) - { - _ames_pact_free(pac_u); - return; - } - // cached addresses have a 5 minute TTL - // - else if ( (0 != pip_w) && ((now - wen) < 300) ) { - pac_u->rut_u.lan_u.pip_w = pip_w; - _ames_send(pac_u); - return; + if ( !pip_w ) { + if ( u3C.wag_w & u3o_verbose ) { + u3l_log("ames: czar not resolved"); + } + return c3n; } - else { - pac_u->rut_u.dns_c = _ames_czar_dns(imp_y, sam_u->dns_c); - - { - uv_getaddrinfo_t* adr_u = c3_malloc(sizeof(*adr_u)); - adr_u->data = pac_u; - c3_d imp_y = pac_u->rut_u.imp_y; - c3_c* dns_c = pac_u->rut_u.dns_c; - c3_i sas_i; - - struct addrinfo hints; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_INET; // only IPv4 addresses - - if ( 0 != (sas_i = uv_getaddrinfo(u3L, adr_u, - _ames_czar_cb, - pac_u->rut_u.dns_c, 0, &hints)) ) - { - u3l_log("ames: %s", uv_strerror(sas_i)); - _ames_czar_gone(pac_u->sam_u, now, imp_y, dns_c); - _ames_pact_free(pac_u); - return; - } + else if ( _CZAR_GONE == pip_w ) { + // print only on first send failure + // + c3_w blk_w = imp_y >> 5; + c3_w bit_w = 1 << (imp_y & 31); + + if ( !(sam_u->zar_u.log_w[blk_w] & bit_w) ) { + c3_c dns_c[256]; + u3_assert ( !_ames_etch_czar(dns_c, sam_u->zar_u.dom_c, imp_y) ); + u3l_log("ames: czar at %s: not found (b)", dns_c); + sam_u->zar_u.log_w[blk_w] |= bit_w; } + + return c3n; } } + + lan_u->por_s = por_s; + lan_u->pip_w = pip_w; + return c3y; } /* _fine_get_cache(): get packet list or status from cache. RETAIN @@ -1258,561 +999,242 @@ _fine_put_cache(u3_ames* sam_u, u3_noun pax, c3_w lop_w, u3_noun lis) } } -static void -_stun_stop(u3_ames* sam_u) -{ - switch ( sam_u->sun_u.sat_y ) { - case STUN_OFF: break; // ignore; already stopped - case STUN_TRYING: - case STUN_KEEPALIVE: { - uv_timer_stop(&sam_u->sun_u.tim_u); - uv_timer_stop(&sam_u->sun_u.dns_u); - } break; - default: u3_assert(!"programmer error"); - } - sam_u->sun_u.sat_y = STUN_OFF; -} - -// XX (code reordering?) forward declarations -static void _stun_send_request(u3_ames*); -static void _stun_on_lost(u3_ames* sam_u); -static void _stun_czar(u3_ames* sam_u, c3_d tim_d); -static void _stun_resolve_dns_cb(uv_timer_t* tim_u); -static void _stun_send_request_cb(uv_udp_send_t *req_u, c3_i sas_i); -static void _stun_on_failure(u3_ames* sam_u); -static void _stun_start(u3_ames* sam_u, c3_o fail); -static c3_y* _stun_add_fingerprint(c3_y *message, c3_w index); -static c3_o _stun_find_xor_mapped_address(c3_y* buf_y, c3_w buf_len, u3_lane* lan_u); - -static c3_d -_stun_time_gap(struct timeval start) -{ - struct timeval tim_tv; - gettimeofday(&tim_tv, 0); - u3_noun now = u3_time_in_tv(&tim_tv); - u3_noun den = u3_time_in_tv(&start); - return u3_time_gap_ms(den, now); -} +typedef struct _stun_send { + uv_udp_send_t req_u; // uv udp request handle + c3_y hun_y[0]; // buffer +} _stun_send; -/* _stun_reset(): stun failed. start again using max backoff +/* _stun_send_cb(): stun udp send callback. */ static void -_stun_reset(uv_timer_t* tim_u) -{ - u3_ames* sam_u = (u3_ames*)(tim_u->data); - - _stun_start(sam_u, c3y); -} - -static void -_stun_timer_cb(uv_timer_t* tim_u) +_stun_send_cb(uv_udp_send_t *rep_u, c3_i sas_i) { - c3_w rto = 500; + _stun_send* snd_u = (_stun_send*)rep_u; - u3_ames* sam_u = (u3_ames*)(tim_u->data); - - switch ( sam_u->sun_u.sat_y ) { - case STUN_OFF: { - // ignore; stray timer (although this shouldn't happen) - u3l_log("stun: stray timer STUN_OFF"); - } break; - case STUN_KEEPALIVE: { - sam_u->sun_u.sat_y = STUN_TRYING; - sam_u->sun_u.tim_u.data = sam_u; - gettimeofday(&sam_u->sun_u.sar_u, 0); // set start time to now - uv_timer_start(&sam_u->sun_u.tim_u, _stun_timer_cb, rto, 0); - _stun_send_request(sam_u); - } break; - case STUN_TRYING: { - c3_d gap_d = _stun_time_gap(sam_u->sun_u.sar_u); - c3_d nex_d = (gap_d * 2) + rto - gap_d; - - if ( gap_d >= (39500) ) { - _stun_on_lost(sam_u); - } else if ( gap_d >= (31500) ) { - // wait ~s8 for the last STUN request - uv_timer_start(&sam_u->sun_u.tim_u, _stun_timer_cb, 8000 , 0); - _stun_send_request(sam_u); - } else { - uv_timer_start(&sam_u->sun_u.tim_u, _stun_timer_cb, - (nex_d >= 31500) ? 31500 : nex_d, 0); - _stun_send_request(sam_u); - } - } break; - default: u3_assert(!"programmer error"); - } -} - -typedef struct _u3_stun_send { - uv_udp_send_t req_u; // uv udp request handle - u3_ames* sam_u; // backpointer to driver state - c3_y* hun_y; // buffer - -} u3_stun_send; - -static void -_stun_on_request_fail(u3_ames* sam_u, c3_i sas_i) -{ - u3l_log("stun: send callback fail_async: %s", uv_strerror(sas_i)); - - _stun_on_failure(sam_u); // %kick ping app - - sam_u->sun_u.sat_y = STUN_TRYING; - _stun_timer_cb(&sam_u->sun_u.tim_u); // retry sending the failed request -} - -static void -_stun_send_request_cb(uv_udp_send_t *req_u, c3_i sas_i) -{ - u3_stun_send* snd_u = (u3_stun_send*)req_u; - u3_ames* sam_u = snd_u->sam_u; - - if ( sas_i ) { - _stun_on_request_fail(sam_u, sas_i); + if ( !sas_i ) { + net_o = c3y; } - else { - // XX curently not used - gettimeofday(&sam_u->sun_u.las_u, 0); // overwrite last sent date + else if ( c3y == net_o ) { + u3l_log("stun: send response fail: %s", uv_strerror(sas_i)); + net_o = c3n; } - c3_free(snd_u->hun_y); + c3_free(snd_u); } +/* _stun_on_request(): hear stun request, send response. + */ static void -_stun_send_response_cb(uv_udp_send_t *rep_u, c3_i sas_i) +_stun_on_request(u3_ames* sam_u, + const c3_y* req_y, + const struct sockaddr* adr_u) { - u3_stun_send* snd_u = (u3_stun_send*)rep_u; - if ( sas_i != 0 ) { - u3l_log("stun: _stun_send_response_cb fail_sync: %s", uv_strerror(sas_i)); - } - c3_free(snd_u->hun_y); - c3_free(snd_u); -} + _stun_send* snd_u = c3_malloc(sizeof(*snd_u) + 40); -static void _stun_on_request(u3_ames *sam_u, c3_y* buf_r, - const struct sockaddr* adr_u) -{ struct sockaddr_in* add_u = (struct sockaddr_in*)adr_u; - c3_y *buf_y = c3_calloc(40); - c3_w cookie = 0x2112A442; - - c3_w cur_w = 20; // STUN header is 20 bytes - memcpy(buf_y, buf_r, cur_w); // copy STUN request header - buf_y[0] = 0x01; buf_y[1] = 0x01; // 0x0101 SUCCESS RESPONSE - buf_y[2] = 0x00; buf_y[3] = 0x14; // Length: 20 bytes - - // XOR-MAPPED-ADDRESS - buf_y[cur_w] = 0x00; buf_y[cur_w + 1] = 0x20; // attribute type 0x00020 - buf_y[cur_w + 2] = 0x00; buf_y[cur_w + 3] = 0x08; // STUN attribute length - // extra reserved 0x0 byte - buf_y[cur_w + 5] = 0x01; // family 0x01:IPv4 - - c3_s x_port = htons(ntohs(add_u->sin_port) ^ cookie >> 16); - c3_w x_ip = htonl(ntohl(add_u->sin_addr.s_addr) ^ cookie); - memcpy(buf_y + cur_w + 6, &x_port, 2); // X-Port - memcpy(buf_y + cur_w + 8, &x_ip, 4); // X-IP Addres + u3_lane lan_u = { + .por_s = ntohs(add_u->sin_port), + .pip_w = ntohl(add_u->sin_addr.s_addr) + }; + u3_stun_make_response(req_y, &lan_u, snd_u->hun_y); - // FINGERPRINT - buf_y = _stun_add_fingerprint(buf_y, cur_w + 12); + uv_buf_t buf_u = uv_buf_init((c3_c*)snd_u->hun_y, 40); + c3_i sas_i = uv_udp_send(&snd_u->req_u, &sam_u->wax_u, + &buf_u, 1, adr_u, _stun_send_cb); - uv_buf_t buf_u = uv_buf_init((c3_c*)buf_y, 40); - u3_stun_send* snd_u = c3_calloc(sizeof(*snd_u)); - - snd_u->sam_u = sam_u; - snd_u->hun_y = buf_y; - c3_i sas_i = uv_udp_send( - (uv_udp_send_t*)snd_u, &sam_u->wax_u, &buf_u, 1, - adr_u, _stun_send_response_cb - ); - - if ( sas_i != 0 ) { - u3l_log("stun: send response fail_sync: %s", uv_strerror(sas_i)); - c3_free(buf_y); - c3_free(snd_u); + if ( sas_i ) { + _stun_send_cb(&snd_u->req_u, sas_i); } } +static void +_stun_start(u3_ames* sam_u, c3_w tim_w); + +/* _stun_on_response(): hear stun response from galaxy. + */ static void _stun_on_response(u3_ames* sam_u, c3_y* buf_y, c3_w buf_len) { - u3_stun_state old_y = sam_u->sun_u.sat_y; - u3_lane lan_u; // Ignore STUN responses that dont' have the XOR-MAPPED-ADDRESS attribute - if ( c3n == _stun_find_xor_mapped_address(buf_y, buf_len, &lan_u) ) { + if ( c3n == u3_stun_find_xor_mapped_address(buf_y, buf_len, &lan_u) ) { return; } - u3_noun wir = u3nc(c3__ames, u3_nul); - if (sam_u->sun_u.wok_o == c3n) { - // stop %ping app - u3_noun cad = u3nq(c3__stun, c3__stop, sam_u->sun_u.dad_y, - u3nc(c3n, u3_ames_encode_lane(lan_u))); - u3_ovum *ovo_u = u3_ovum_init(0, c3__ames, wir, cad); - u3_auto_plan(&sam_u->car_u, ovo_u); - sam_u->sun_u.wok_o = c3y; - } - else if ( (sam_u->sun_u.sef_u.por_s != lan_u.por_s) || - (sam_u->sun_u.sef_u.pip_w != lan_u.pip_w) ) + + if ( (sam_u->sun_u.sef_u.por_s != lan_u.por_s) || + (sam_u->sun_u.sef_u.pip_w != lan_u.pip_w) ) { // lane changed + u3_noun wir = u3nc(c3__ames, u3_nul); u3_noun cad = u3nq(c3__stun, c3__once, sam_u->sun_u.dad_y, u3nc(c3n, u3_ames_encode_lane(lan_u))); - u3_ovum *ovo_u = u3_ovum_init(0, c3__ames, wir, cad); - u3_auto_plan(&sam_u->car_u, ovo_u); + u3_auto_plan(&sam_u->car_u, + u3_ovum_init(0, c3__ames, wir, cad)); } - else { - u3z(wir); + else if ( c3n == sam_u->sun_u.wok_o ) { + // stop %ping app + u3_noun wir = u3nc(c3__ames, u3_nul); + u3_noun cad = u3nq(c3__stun, c3__stop, sam_u->sun_u.dad_y, + u3nc(c3n, u3_ames_encode_lane(lan_u))); + u3_auto_plan(&sam_u->car_u, + u3_ovum_init(0, c3__ames, wir, cad)); + sam_u->sun_u.wok_o = c3y; } + sam_u->sun_u.sef_u = lan_u; + // XX should no-op early + // switch ( sam_u->sun_u.sat_y ) { - case STUN_OFF: break; // ignore; stray response - case STUN_KEEPALIVE: break; // ignore; duplicate response - case STUN_TRYING: { - sam_u->sun_u.sat_y = STUN_KEEPALIVE; - if ( ent_getentropy(sam_u->sun_u.tid_y, 12) ) { - u3l_log("stun: getentropy fail: %s", strerror(errno)); - _stun_on_lost(sam_u); - } - else { - uv_timer_start(&sam_u->sun_u.tim_u, _stun_timer_cb, 25*1000, 0); - } - } break; - default: assert("programmer error"); - } -} + case STUN_OFF: break; // ignore; stray response + case STUN_KEEPALIVE: break; // ignore; duplicate response -static void -_stun_on_failure(u3_ames* sam_u) -{ - // only inject event into arvo to %kick ping app on first failure - if (sam_u->sun_u.wok_o == c3y) { - u3_noun wir = u3nc(c3__ames, u3_nul); - u3_noun cad = u3nq(c3__stun, c3__fail, sam_u->sun_u.dad_y, - u3nc(c3n, u3_ames_encode_lane(sam_u->sun_u.sef_u))); - u3_ovum *ovo_u = u3_ovum_init(0, c3__ames, wir, cad); - u3_auto_plan(&sam_u->car_u, ovo_u); - } - sam_u->sun_u.wok_o = c3n; -} + case STUN_TRYING: { + _stun_start(sam_u, 25000); + } break; -static void -_stun_on_lost(u3_ames* sam_u) -{ - _stun_stop(sam_u); - _stun_on_failure(sam_u); - // resolve DNS again, and (re)start STUN - // XX call _stun_start(sam_u, c3y) directly? - uv_timer_start(&sam_u->sun_u.dns_u, _stun_reset, 5*1000, 0); + default: u3_assert(!"programmer error"); + } } +/* _stun_send_request(): send stun request to galaxy lane. + */ static void _stun_send_request(u3_ames* sam_u) { u3_assert( STUN_OFF != sam_u->sun_u.sat_y ); + _stun_send* snd_u = c3_malloc(sizeof(*snd_u) + 28); + + u3_stun_make_request(snd_u->hun_y, sam_u->sun_u.tid_y); + struct sockaddr_in add_u; memset(&add_u, 0, sizeof(add_u)); add_u.sin_family = AF_INET; add_u.sin_addr.s_addr = htonl(sam_u->sun_u.lan_u.pip_w); add_u.sin_port = htons(sam_u->sun_u.lan_u.por_s); - // see STUN RFC 8489 - // https://datatracker.ietf.org/doc/html/rfc8489#section-5 - c3_y *buf_y = c3_calloc(28); - - // STUN message type: "binding request" - buf_y[1] = 0x01; - - // STUN message length: 8 (header and 32-bit FINGERPRINT) - buf_y[2] = 0x00; buf_y[3] = 0x08; - - - // STUN "magic cookie" 0x2112A442 in network byte order - buf_y[4] = 0x21; buf_y[5] = 0x12; buf_y[6] = 0xa4; buf_y[7] = 0x42; - - // STUN "transaction id" - memcpy(buf_y + 8, sam_u->sun_u.tid_y, 12); - - // FINGERPRINT - buf_y = _stun_add_fingerprint(buf_y, 20); - - uv_buf_t buf_u = uv_buf_init((c3_c*)buf_y, 28); - u3_stun_send* snd_u = c3_calloc(sizeof(*snd_u)); - snd_u->sam_u = sam_u; - snd_u->hun_y = buf_y; + uv_buf_t buf_u = uv_buf_init((c3_c*)snd_u->hun_y, 28); + c3_i sas_i = uv_udp_send(&snd_u->req_u, &sam_u->wax_u, &buf_u, 1, + (const struct sockaddr*)&add_u, _stun_send_cb); - c3_i sas_i = uv_udp_send( - (uv_udp_send_t*)snd_u, &sam_u->wax_u, &buf_u, 1, - (const struct sockaddr*)&add_u, _stun_send_request_cb - ); - - if ( sas_i != 0) { - _stun_on_request_fail(sam_u, sas_i); - c3_free(buf_y); - c3_free(snd_u); - } -} - -static void -_stun_czar_cb(uv_getaddrinfo_t* adr_u, - c3_i sas_i, - struct addrinfo* aif_u) -{ - { - u3_ames* sam_u = (u3_ames*)(adr_u->data); - struct addrinfo* rai_u = aif_u; - time_t now = time(0); - - gettimeofday(&sam_u->sun_u.sar_u, 0); // set start time to now - - if (sas_i == 0) { - _stun_czar_here(sam_u, now, (struct sockaddr_in *)rai_u->ai_addr); - if (sam_u->sun_u.sat_y == STUN_OFF) { - sam_u->sun_u.sat_y = STUN_TRYING; - _stun_send_request(sam_u); - uv_timer_start(&sam_u->sun_u.tim_u, _stun_timer_cb, 500, 0); - } - // resolve DNS again in five minutes - uv_timer_start(&sam_u->sun_u.dns_u, _stun_resolve_dns_cb, 5*60*1000, 0); - } else { - u3l_log("stun: _stun_czar_cb request fail_sync: %s", uv_strerror(sas_i)); - _ames_czar_gone(sam_u, now, sam_u->sun_u.dad_y, sam_u->dns_c); - _stun_on_lost(sam_u); - } - } - c3_free(adr_u); - uv_freeaddrinfo(aif_u); -} - -static void -_stun_czar(u3_ames* sam_u, c3_d tim_d) -{ - c3_d imp_y = sam_u->sun_u.dad_y; - sam_u->sun_u.lan_u.por_s = _ames_czar_port(imp_y); - - // Enable STUN using -L - // XX maybe enabled with a flag, for development? - if (c3n == u3_Host.ops_u.net) { - sam_u->sun_u.lan_u.pip_w = 0x7f000001; - sam_u->sun_u.sat_y = STUN_TRYING; - _stun_send_request(sam_u); - - gettimeofday(&sam_u->sun_u.sar_u, 0); - uv_timer_start(&sam_u->sun_u.tim_u, _stun_timer_cb, tim_d, 0); - - return; - } - - - // if we don't have a galaxy domain, no-op - // - if (!sam_u->dns_c) { - u3_noun nam = u3dc("scot", 'p', imp_y); - c3_c *nam_c = u3r_string(nam); - u3l_log("ames: no galaxy domain for %s, no-op", nam_c); - - c3_free(nam_c); - u3z(nam); - return; - } - - { - c3_w pip_w = sam_u->imp_w[imp_y]; - time_t wen = sam_u->imp_t[imp_y]; - time_t now = time(0); - - // XX keep same as ames? - // backoff for 5 minutes after failed lookup - // - if ((now < wen) // time shenanigans! - || ((0xffffffff == pip_w) // sentinal ip address - && ((now - wen) < 300))) { - return; - } - // cached addresses have a 5 minute TTL - // - else if ((0 != pip_w) && ((now - wen) < 300)) { - sam_u->sun_u.sat_y = STUN_TRYING; - sam_u->sun_u.lan_u.pip_w = pip_w; - - _stun_send_request(sam_u); - - gettimeofday(&sam_u->sun_u.sar_u, 0); - uv_timer_start(&sam_u->sun_u.tim_u, _stun_timer_cb, tim_d, 0); - return; - } else { - // call callback right away first time we resolve the sponsor's DNS - sam_u->sun_u.dns_u.data = sam_u; - uv_timer_start(&sam_u->sun_u.dns_u, _stun_resolve_dns_cb, tim_d, 0); - } - } -} - -static void -_stun_start(u3_ames* sam_u, c3_o fail) -{ - if ( ent_getentropy(sam_u->sun_u.tid_y, 12) ) { - u3l_log("stun: getentropy fail: %s", strerror(errno)); - _stun_on_lost(sam_u); - } else { - _stun_czar(sam_u, (fail == c3n) ? 500 : 39500); - } -} - -static void -_stun_resolve_dns_cb(uv_timer_t* tim_u) -{ - u3_ames* sam_u = (u3_ames*)(tim_u->data); - c3_i sas_i; - - c3_y imp_y = sam_u->sun_u.dad_y; - sam_u->sun_u.lan_u.por_s = _ames_czar_port(imp_y); - - if ( !sam_u->sun_u.dns_c ) { - sam_u->sun_u.dns_c = _ames_czar_dns(imp_y, sam_u->dns_c); - } - - { - uv_getaddrinfo_t* adr_u = c3_malloc(sizeof(*adr_u)); - adr_u->data = sam_u; - - struct addrinfo hints; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_INET; // only IPv4 addresses - - if (0 != (sas_i = uv_getaddrinfo(u3L, adr_u, _stun_czar_cb, - sam_u->sun_u.dns_c, 0, &hints))) - { - u3l_log("stun: uv_getaddrinfo failed %s %s", uv_strerror(sas_i), sam_u->sun_u.dns_c); - _ames_czar_gone(sam_u, time(0), sam_u->sun_u.dad_y, sam_u->dns_c); - _stun_on_lost(sam_u); - return; - } - } -} - -static c3_o -_stun_find_xor_mapped_address(c3_y* buf_y, c3_w buf_len, u3_lane* lan_u) -{ - c3_y xor_y[4] = {0x00, 0x20, 0x00, 0x08}; - c3_w cookie = 0x2112A442; - - if (buf_len < 40) { // At least STUN header, XOR-MAPPED-ADDRESS & FINGERPRINT - return c3n; - } - - c3_w i = 20; // start after header - - c3_y* fin_y = memmem(buf_y + i, buf_len - i, xor_y, sizeof(xor_y)); - if ( fin_y != 0 ) { - c3_w cur = (c3_w)(fin_y - buf_y) + sizeof(xor_y); - - if ( (buf_y[cur] != 0x0) && (buf_y[cur+1] != 0x1) ) { - return c3n; - } - - cur += 2; - - c3_s port = htons(_ames_sift_short(buf_y + cur)) ^ cookie >> 16; - c3_w ip = ntohl(htonl(_ames_sift_word(buf_y + cur + 2)) ^ cookie); - - lan_u->por_s = ntohs(port); - lan_u->pip_w = ip; - - if ( u3C.wag_w & u3o_verbose ) { - c3_c ip_str[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, &ip, ip_str, INET_ADDRSTRLEN); - u3l_log("stun: hear ip:port %s:%u", ip_str, port); - } - return c3y; + if ( sas_i ) { + _stun_send_cb(&snd_u->req_u, sas_i); } - return c3n; } -static c3_o -_stun_has_fingerprint(c3_y* buf_y, c3_w buf_len) +/* _stun_reset(): stun failed. start again using max backoff + */ +static void +_stun_reset(uv_timer_t* tim_u) { - c3_y ned_y[4] = {0x80, 0x28, 0x00, 0x04}; - if ( buf_len < 28 ) { // At least STUN header and FINGERPRINT - return c3n; - } - - { - c3_y* fin_y = 0; - c3_w i = 20; // start after the header - - fin_y = memmem(buf_y + i, buf_len - i, ned_y, sizeof(ned_y)); - if ( fin_y != 0 ) { - c3_w len_w = fin_y - buf_y; - // Skip attribute type and length - c3_w fingerprint = _ames_sift_word(fin_y + sizeof(ned_y)); - c3_w init = crc32(0L, Z_NULL, 0); - c3_w crc = htonl(crc32(init, buf_y, len_w) ^ 0x5354554e); - if ((fingerprint == crc) && (fin_y - buf_y + 8) == buf_len) { - return c3y; - } - } + u3_ames* sam_u = (u3_ames*)(tim_u->data); - return c3n; - } + _stun_start(sam_u, 39000); } -static c3_y* -_stun_add_fingerprint(c3_y *message, c3_w index) +/* _stun_on_lost(): stun failed (timeout); capture and reset. + */ +static void +_stun_on_lost(u3_ames* sam_u) { - // Compute FINGERPRINT value as CRC-32 of the STUN message - // up to (but excluding) the FINGERPRINT attribute itself, - // XOR'ed with the 32-bit value 0x5354554e - c3_w init = crc32(0L, Z_NULL, 0); - c3_w crc = htonl(crc32(init, message, index) ^ 0x5354554e); + sam_u->sun_u.sat_y = STUN_OFF; - // STUN attribute type: "FINGERPRINT" - message[index] = 0x80; message[index + 1] = 0x28; - // STUN attribute length: 4 bytes - message[index + 2] = 0x00; message[index + 3] = 0x04; + // only inject event into arvo to %kick ping app on first failure + // + if ( c3y == sam_u->sun_u.wok_o ) { + u3_noun wir = u3nc(c3__ames, u3_nul); + u3_noun cad = u3nq(c3__stun, c3__fail, sam_u->sun_u.dad_y, + u3nc(c3n, u3_ames_encode_lane(sam_u->sun_u.sef_u))); + u3_auto_plan(&sam_u->car_u, + u3_ovum_init(0, c3__ames, wir, cad)); + sam_u->sun_u.wok_o = c3n; + } - memcpy(message + index + 4, &crc, 4); + uv_timer_start(&sam_u->sun_u.tim_u, _stun_reset, 5*1000, 0); +} - return message; +/* _stun_time_gap(): elapsed milliseconds. + */ +static c3_d +_stun_time_gap(struct timeval sar_tv) +{ + struct timeval tim_tv; + gettimeofday(&tim_tv, 0); + u3_noun now = u3_time_in_tv(&tim_tv); + u3_noun den = u3_time_in_tv(&sar_tv); + return u3_time_gap_ms(den, now); } -static c3_o -_stun_is_our_response(c3_y* buf_y, c3_y tid_y[12], c3_w buf_len) +/* _stun_timer_cb(): advance stun state machine. + */ +static void +_stun_timer_cb(uv_timer_t* tim_u) { - c3_w cookie = htonl(0x2112A442); + u3_ames* sam_u = (u3_ames*)(tim_u->data); + c3_w rto_w = 500; - // Expects at least: - // STUN header, 12 byte XOR-MAPPED-ADDRESS and 8 byte FINGERPRINT - if ( (buf_len == 40) && - (buf_y[0] == 0x01 && buf_y[1] == 0x01) && - (memcmp(&cookie, buf_y + 4, 4) == 0) && - (memcmp(tid_y, buf_y + 8, 12) == 0) && - (c3y == _stun_has_fingerprint(buf_y, buf_len)) ) - { - return c3y; + switch ( sam_u->sun_u.sat_y ) { + case STUN_OFF: { + // ignore; stray timer (although this shouldn't happen) + u3l_log("stun: stray timer STUN_OFF"); + } break; + + case STUN_KEEPALIVE: { + u3_lane* lan_u = &(sam_u->sun_u.lan_u); + c3_y imp_y = sam_u->sun_u.dad_y; + + if ( c3n == _ames_czar_lane(sam_u, imp_y, lan_u) ) { + uv_timer_start(&sam_u->sun_u.tim_u, _stun_timer_cb, 25*1000, 0); + } + else { + sam_u->sun_u.sat_y = STUN_TRYING; + gettimeofday(&sam_u->sun_u.sar_u, 0); // set start time to now + uv_timer_start(&sam_u->sun_u.tim_u, _stun_timer_cb, rto_w, 0); + _stun_send_request(sam_u); + } + } break; + + case STUN_TRYING: { + c3_d gap_d = _stun_time_gap(sam_u->sun_u.sar_u); + c3_d nex_d = (gap_d * 2) + rto_w - gap_d; + + if ( gap_d >= 39500 ) { + _stun_on_lost(sam_u); + } + else { + // wait ~s8 for the last STUN request + // + // https://datatracker.ietf.org/doc/html/rfc5389#section-7.2.1 + // + c3_w tim_w = (gap_d >= 31500) ? 8000 : c3_max(nex_d, 31500); + + uv_timer_start(&sam_u->sun_u.tim_u, _stun_timer_cb, tim_w, 0); + _stun_send_request(sam_u); + } + } break; + + default: u3_assert(!"programmer error"); } - return c3n; } -static c3_o -_stun_is_request(c3_y* buf_y, c3_w buf_len) +/* _stun_start(): begin/restart STUN state machine. +*/ +static void +_stun_start(u3_ames* sam_u, c3_w tim_w) { - c3_w cookie = htonl(0x2112A442); - - // Expects at least: - // STUN header and 8 byte FINGERPRINT - if ( (buf_len >= 28) && - (buf_y[0] == 0x0 && buf_y[1] == 0x01) && - (memcmp(&cookie, buf_y + 4, 4) == 0) && - (c3y == _stun_has_fingerprint(buf_y, buf_len)) ) - { - return c3y; + if ( ent_getentropy(sam_u->sun_u.tid_y, 12) ) { + u3l_log("stun: getentropy fail: %s", strerror(errno)); + u3_king_bail(); } - return c3n; + + sam_u->sun_u.sat_y = STUN_KEEPALIVE; + uv_timer_start(&sam_u->sun_u.tim_u, _stun_timer_cb, tim_w, 0); } +/* _ames_is_czar(): [who] is galaxy. +*/ static c3_o _ames_is_czar(u3_noun who) { @@ -1839,17 +1261,71 @@ _ames_ef_saxo(u3_ames* sam_u, u3_noun zad) dad = u3h(daz); u3_noun our = u3i_chubs(2, sam_u->pir_u->who_d); + // if we are a galaxy, don't STUN + // if ( c3y == _ames_is_czar(dad) && c3n == _ames_is_czar(our)) { - // if we are a galaxy, don't STUN - sam_u->sun_u.dad_y = u3r_byte(0, dad); - sam_u->sun_u.wok_o = c3n; - _stun_stop(sam_u); - _stun_start(sam_u, c3n); + sam_u->sun_u.dad_y = (c3_y)dad; + _stun_start(sam_u, 0); } u3z(zad); u3z(daz); u3z(our); } +/* _ames_send_lane(): resolve/decode lane. RETAIN +*/ +static c3_o +_ames_send_lane(u3_ames* sam_u, u3_noun lan, u3_lane* lan_u) +{ + u3_noun tag, val; + + if ( c3n == u3r_cell(lan, &tag, &val) ) { + u3l_log("ames: bad lane; not a cell"); + return c3n; + } + + switch ( tag ) { + case c3y: { // galaxy + if ( val >= 256 ) { + u3l_log("ames: bad galaxy lane: 0x%x", val); + return c3n; + } + return _ames_czar_lane(sam_u, (c3_y)val, lan_u); + } + + case c3n: { // ip:port + u3_lane nal_u = u3_ames_decode_lane(u3k(val)); + + // convert incoming localhost to outgoing localhost + // + // XX this looks like en/de-coding problems ... + // + nal_u.pip_w = ( nal_u.pip_w ) ? nal_u.pip_w : 0x7f000001; + + // if in local-only mode, don't send remote packets + // + if ( (c3n == u3_Host.ops_u.net) && (0x7f000001 != nal_u.pip_w) ) { + return c3n; + } + // if the lane is uninterpretable, silently drop the packet + // + else if ( !nal_u.por_s ) { + if ( u3C.wag_w & u3o_verbose ) { + u3l_log("ames: inscrutable lane"); + } + return c3n; + } + + *lan_u = nal_u; + return c3y; + } + + default: { + u3l_log("ames: bad lane tag"); + return c3n; + } + } +} + /* _ames_ef_send(): send packet to network (v4). */ static void @@ -1861,60 +1337,23 @@ _ames_ef_send(u3_ames* sam_u, u3_noun lan, u3_noun pac) return; } - u3_pact* pac_u = c3_calloc(sizeof(*pac_u)); - pac_u->sam_u = sam_u; - pac_u->len_w = u3r_met(3, pac); - pac_u->hun_y = c3_malloc(pac_u->len_w); - - u3r_bytes(0, pac_u->len_w, pac_u->hun_y, pac); + u3_lane lan_u; - _ames_sift_head(&pac_u->hed_u, pac_u->hun_y); - pac_u->typ_y = _ames_pact_typ(&pac_u->hed_u); + if ( c3y == _ames_send_lane(sam_u, lan, &lan_u) ) { + u3_pact* pac_u = c3_calloc(sizeof(*pac_u)); + pac_u->sam_u = sam_u; + pac_u->lan_u = lan_u; + pac_u->len_w = u3r_met(3, pac); + pac_u->hun_y = c3_malloc(pac_u->len_w); - u3_noun tag, val; - u3x_cell(lan, &tag, &val); - u3_assert( (c3y == tag) || (c3n == tag) ); + u3r_bytes(0, pac_u->len_w, pac_u->hun_y, pac); - // galaxy lane; do DNS lookup and send packet - // - if ( c3y == tag ) { - u3_assert( c3y == u3a_is_cat(val) ); - u3_assert( val < 256 ); + _ames_sift_head(&pac_u->hed_u, pac_u->hun_y); + pac_u->typ_y = _ames_pact_typ(&pac_u->hed_u); - //u3l_log("_ames_ef_send imp %s %u", _str_typ(pac_u->typ_y), val); - pac_u->rut_u.imp_y = val; - _ames_czar(pac_u); + _ames_send(pac_u); } - // non-galaxy lane - // - else { - u3_lane lan_u = u3_ames_decode_lane(u3k(val)); - ////u3l_log("_ames_ef_send low %s %u", _str_typ(pac_u->typ_y), - // lan_u.por_s); - // convert incoming localhost to outgoing localhost - // - lan_u.pip_w = ( 0 == lan_u.pip_w )? 0x7f000001 : lan_u.pip_w; - // if in local-only mode, don't send remote packets - // - if ( (c3n == u3_Host.ops_u.net) && (0x7f000001 != lan_u.pip_w) ) { - _ames_pact_free(pac_u); - } - // if the lane is uninterpretable, silently drop the packet - // - else if ( 0 == lan_u.por_s ) { - if ( u3C.wag_w & u3o_verbose ) { - u3l_log("ames: inscrutable lane"); - } - _ames_pact_free(pac_u); - } - // otherwise, mutate destination and send packet - // - else { - pac_u->rut_u.lan_u = lan_u; - _ames_send(pac_u); - } - } u3z(lan); u3z(pac); } @@ -1924,6 +1363,7 @@ static void _ames_cap_queue(u3_ames* sam_u) { u3_ovum* egg_u = sam_u->car_u.ext_u; + c3_d old_d = sam_u->sat_u.dop_d; while ( egg_u && (QUEUE_MAX < sam_u->car_u.dep_w) ) { u3_ovum* nex_u = egg_u->nex_u; @@ -1940,8 +1380,9 @@ _ames_cap_queue(u3_ames* sam_u) egg_u = nex_u; } - if ( (sam_u->sat_u.dop_d && (0 == (sam_u->sat_u.dop_d % 1000))) - && !(u3C.wag_w & u3o_verbose) ) + if ( !(u3C.wag_w & u3o_verbose) + && (old_d != sam_u->sat_u.dop_d) + && !(sam_u->sat_u.dop_d % 1000) ) { u3l_log("ames: packet dropped (%" PRIu64 " total)", sam_u->sat_u.dop_d); } @@ -2033,13 +1474,13 @@ _ames_send_many(u3_pact* pac_u, u3_noun las, c3_o for_o) u3_noun rec = u3dc("scot", 'p', u3i_chubs(2, pac_u->pre_u.rec_d)); c3_c* sen_c = u3r_string(sen); c3_c* rec_c = u3r_string(rec); - c3_y* pip_y = (c3_y*)&pac_u->rut_u.lan_u.pip_w; + c3_y* pip_y = (c3_y*)&pac_u->lan_u.pip_w; //NOTE ip byte order assumes little-endian u3l_log("ames: forwarding for %s to %s from %d.%d.%d.%d:%d", sen_c, rec_c, pip_y[3], pip_y[2], pip_y[1], pip_y[0], - pac_u->rut_u.lan_u.por_s); + pac_u->lan_u.por_s); c3_free(sen_c); c3_free(rec_c); u3z(sen); u3z(rec); @@ -2099,7 +1540,7 @@ _ames_lane_scry_cb(void* vod_p, u3_noun nun) } _ames_put_packet(sam_u, _ames_pact_to_noun(pac_u), - pac_u->rut_u.lan_u); + pac_u->lan_u); } else { sam_u->sat_u.saw_d = 0; @@ -2376,7 +1817,7 @@ _fine_hear_request(u3_pact* req_u, c3_w cur_w) res_u = c3_calloc(sizeof(*res_u)); res_u->sam_u = req_u->sam_u; res_u->typ_y = PACT_PURR; - res_u->rut_u.lan_u = req_u->rut_u.lan_u; + res_u->lan_u = req_u->lan_u; // copy header, swapping sender and receiver // @@ -2419,14 +1860,6 @@ _fine_hear_request(u3_pact* req_u, c3_w cur_w) _ames_pact_free(req_u); } - // if receiver is a galaxy, note that in res_u - // - if ( res_u->pre_u.rec_d[0] < 256 - && res_u->pre_u.rec_d[1] == 0 ) - { - res_u->rut_u.imp_y = res_u->pre_u.rec_d[0]; - } - // look up request in scry cache // c3_w fra_w = res_u->pur_u.pep_u.fra_w; @@ -2495,7 +1928,7 @@ _fine_hear_response(u3_pact* pac_u, c3_w cur_w) { u3_noun wir = u3nc(c3__fine, u3_nul); u3_noun cad = u3nt(c3__hear, - u3nc(c3n, u3_ames_encode_lane(pac_u->rut_u.lan_u)), + u3nc(c3n, u3_ames_encode_lane(pac_u->lan_u)), u3i_bytes(pac_u->len_w, pac_u->hun_y)); u3_ovum* ovo_u = u3_ovum_init(0, c3__ames, wir, cad); @@ -2518,7 +1951,7 @@ _ames_hear_ames(u3_pact* pac_u, c3_w cur_w) { u3_noun msg = u3i_bytes(pac_u->len_w, pac_u->hun_y); - _ames_put_packet(pac_u->sam_u, msg, pac_u->rut_u.lan_u); + _ames_put_packet(pac_u->sam_u, msg, pac_u->lan_u); _ames_pact_free(pac_u); } } @@ -2538,7 +1971,7 @@ _ames_try_forward(u3_pact* pac_u) c3_w old_w, cur_w; pac_u->hed_u.rel_o = c3y; - pac_u->pre_u.rog_d = u3_ames_lane_to_chub(pac_u->rut_u.lan_u); + pac_u->pre_u.rog_d = u3_ames_lane_to_chub(pac_u->lan_u); old_w = pac_u->len_w; old_y = pac_u->hun_y; @@ -2605,7 +2038,7 @@ _ames_hear(u3_ames* sam_u, pac_u->sam_u = sam_u; pac_u->len_w = len_w; pac_u->hun_y = hun_y; - pac_u->rut_u.lan_u = *lan_u; + pac_u->lan_u = *lan_u; cur_w = 0; // parse the header @@ -2730,21 +2163,22 @@ _ames_recv_cb(uv_udp_t* wax_u, // check ames first, assume that STUN could maybe (not likely) overlap with ames // for next protocol version, have an urbit cookie // - else if (_stun_is_request((c3_y*)buf_u->base, nrd_i) == c3y) { + else if ( c3y == u3_stun_is_request((c3_y*)buf_u->base, nrd_i) ) { _stun_on_request(sam_u, (c3_y *)buf_u->base, adr_u); c3_free(buf_u->base); } - else if (_stun_is_our_response((c3_y*)buf_u->base, sam_u->sun_u.tid_y, nrd_i) - == c3y) { + else if ( c3y == u3_stun_is_our_response((c3_y*)buf_u->base, + sam_u->sun_u.tid_y, nrd_i) ) + { _stun_on_response(sam_u, (c3_y*)buf_u->base, nrd_i); c3_free(buf_u->base); - } else { - u3_ames* sam_u = wax_u->data; + } + else { struct sockaddr_in* add_u = (struct sockaddr_in*)adr_u; - u3_lane lan_u; - - lan_u.por_s = ntohs(add_u->sin_port); - lan_u.pip_w = ntohl(add_u->sin_addr.s_addr); + u3_lane lan_u = { + .por_s = ntohs(add_u->sin_port), + .pip_w = ntohl(add_u->sin_addr.s_addr) + }; // NB: [nrd_i] will never exceed max length from _ames_alloc() // @@ -2752,6 +2186,69 @@ _ames_recv_cb(uv_udp_t* wax_u, } } +static void natpmp_init(uv_timer_t* handle); + +static void +natpmp_cb(uv_poll_t* handle, + c3_i status, + c3_i events) +{ + + if (status != 0) { + return; + } + + u3_ames* sam_u = handle->data; + + natpmpresp_t response; + c3_i err_i = readnatpmpresponseorretry(&sam_u->nat_u.req_u, &response); + if ( NATPMP_TRYAGAIN == err_i ) { + return; + } + + uv_poll_stop(handle); + + if ( 0 != err_i ) { + u3l_log("ames: natpmp error %i", err_i); + uv_poll_stop(&sam_u->nat_u.pol_u); + closenatpmp(&sam_u->nat_u.req_u); + return; + } + + u3l_log("ames: mapped public port %hu to localport %hu lifetime %u", + response.pnu.newportmapping.mappedpublicport, + response.pnu.newportmapping.privateport, + response.pnu.newportmapping.lifetime); + + closenatpmp(&sam_u->nat_u.req_u); + sam_u->nat_u.tim_u.data = sam_u; + uv_timer_start(&sam_u->nat_u.tim_u, natpmp_init, 7200000, 0); +} + +static void +natpmp_init(uv_timer_t *handle) +{ + u3_ames* sam_u = handle->data; + c3_s por_s = sam_u->pir_u->por_s; + + c3_i err_i = initnatpmp(&sam_u->nat_u.req_u, 0, 0); + + if (err_i != 0) { + return; + } + + err_i = uv_poll_init(u3L, &sam_u->nat_u.pol_u, sam_u->nat_u.req_u.s); + + if (err_i != 0) { + return; + } + + sendnewportmappingrequest(&sam_u->nat_u.req_u, NATPMP_PROTOCOL_UDP, por_s, por_s, 7200); + + sam_u->nat_u.pol_u.data = sam_u; + uv_poll_start(&sam_u->nat_u.pol_u, UV_READABLE, natpmp_cb); +} + static void _mdns_dear_bail(u3_ovum* egg_u, u3_noun lud) { @@ -2872,6 +2369,11 @@ _ames_io_start(u3_ames* sam_u) u3z(our); mdns_init(por_s, !sam_u->pir_u->fak_o, our_s, _ames_put_dear, (void *)sam_u); + + if ( c3n == sam_u->pir_u->fak_o ) { + uv_timer_start(&sam_u->nat_u.tim_u, natpmp_init, 0, 0); + } + c3_free(our_s); } @@ -2881,34 +2383,178 @@ _ames_io_start(u3_ames* sam_u) u3z(who); } +typedef struct _czar_resv { + uv_getaddrinfo_t adr_u; + u3_ames* sam_u; + c3_y imp_y; +} _czar_resv; + +/* _ames_czar_gone(): galaxy address resolution failed. +*/ +static void +_ames_czar_gone(u3_ames* sam_u, c3_y imp_y) +{ + c3_w old_w = sam_u->zar_u.pip_w[imp_y]; + + if ( !old_w ) { + sam_u->zar_u.pip_w[imp_y] = _CZAR_GONE; + } +} + +/* _ames_czar_here(): galaxy address resolution succeeded. +*/ +static void +_ames_czar_here(u3_ames* sam_u, c3_y imp_y, c3_w pip_w) +{ + c3_w old_w = sam_u->zar_u.pip_w[imp_y]; + + if ( pip_w != old_w ) { + c3_c dns_c[256]; + u3_assert ( !_ames_etch_czar(dns_c, sam_u->zar_u.dom_c, imp_y) ); + u3l_log("ames: czar %s ip .%d.%d.%d.%d", dns_c, + (pip_w >> 24) & 0xff, + (pip_w >> 16) & 0xff, + (pip_w >> 8) & 0xff, + (pip_w >> 0) & 0xff); + } + + sam_u->zar_u.pip_w[imp_y] = pip_w; + + { + c3_w blk_w = imp_y >> 5; + c3_w bit_w = 1 << (imp_y & 31); + + sam_u->zar_u.log_w[blk_w] &= ~bit_w; + } +} + +/* _ames_czar_cb(): galaxy address resolution callback. +*/ +static void +_ames_czar_cb(uv_getaddrinfo_t* adr_u, + c3_i sas_i, + struct addrinfo* aif_u) +{ + struct addrinfo* rai_u = aif_u; + _czar_resv* res_u = (_czar_resv*)adr_u; + u3_ames* sam_u = res_u->sam_u; + c3_y imp_y = res_u->imp_y; + + while ( rai_u && (AF_INET != rai_u->ai_family) ) { + rai_u = rai_u->ai_next; + } + + if ( rai_u && rai_u->ai_addr ) { + struct sockaddr_in* add_u = (void*)rai_u->ai_addr; + c3_w pip_w = ntohl(add_u->sin_addr.s_addr); + _ames_czar_here(sam_u, imp_y, pip_w); + } + else { + if ( !sas_i ) { + // XX unpossible + u3l_log("ames: czar: strange failure, no error"); + } + else if ( u3C.wag_w & u3o_verbose ) { + u3l_log("ames: czar fail: %s", uv_strerror(sas_i)); + } + + _ames_czar_gone(sam_u, imp_y); + } + + sam_u->zar_u.pen_s--; + + uv_freeaddrinfo(aif_u); + c3_free(res_u); +} + +/* _ames_czar(): single galaxy address resolution. +*/ +static void +_ames_czar(u3_ames* sam_u, const c3_c* dom_c, c3_y imp_y) +{ + struct addrinfo hin_u = { .ai_family = AF_INET }; + uv_getaddrinfo_t* adr_u; + _czar_resv* res_u; + c3_c dns_c[256]; + c3_i sas_i; + + u3_assert ( !_ames_etch_czar(dns_c, dom_c, imp_y) ); + + res_u = c3_malloc(sizeof(*res_u)); + res_u->sam_u = sam_u; + res_u->imp_y = imp_y; + + adr_u = &(res_u->adr_u); + sas_i = uv_getaddrinfo(u3L, adr_u, _ames_czar_cb, dns_c, 0, &hin_u); + + if ( sas_i ) { + _ames_czar_cb(adr_u, sas_i, NULL); + } +} + +/* _ames_czar_all(): galaxy address resolution. +*/ +static void +_ames_czar_all(uv_timer_t* tim_u) +{ + u3_ames* sam_u = tim_u->data; + + // requests still pending + if ( sam_u->zar_u.pen_s ) { + uv_timer_start(&sam_u->zar_u.tim_u, _ames_czar_all, 30*1000, 0); + return; + } + + sam_u->zar_u.pen_s = 256; + + for ( c3_w i_w = 0; i_w < 256; i_w++ ) { + _ames_czar(sam_u, sam_u->zar_u.dom_c, (c3_y)i_w); + } + + uv_timer_start(&sam_u->zar_u.tim_u, _ames_czar_all, 300*1000, 0); +} + /* _ames_ef_turf(): initialize ames I/O on domain(s). */ static void _ames_ef_turf(u3_ames* sam_u, u3_noun tuf) { if ( u3_nul != tuf ) { - // XX save all for fallback, not just first - // - u3_noun hot = u3k(u3h(tuf)); + c3_c dom_c[sizeof(sam_u->zar_u.dom_c)]; + u3_noun hot = u3h(tuf); c3_w len_w = u3_mcut_host(0, 0, u3k(hot)); - sam_u->dns_c = c3_malloc(1 + len_w); - u3_mcut_host(sam_u->dns_c, 0, hot); - sam_u->dns_c[len_w] = 0; - - if ( 250 <= len_w ) { + if ( len_w >= sizeof(dom_c) ) { // >250 // 3 char for the galaxy (e.g. zod) and two dots - u3l_log("ames: galaxy domain too big %s len=%u", sam_u->dns_c, len_w); + u3l_log("ames: galaxy domain too big (len=%u)", len_w); + u3m_p("hot", hot); u3_pier_bail(u3_king_stub()); } - // XX invalidate sam_u->imp_w &c ? - c3_free(sam_u->sun_u.dns_c); - sam_u->sun_u.dns_c = 0; + u3_mcut_host(dom_c, 0, u3k(hot)); + memset(dom_c + len_w, 0, sizeof(dom_c) - len_w); + + if ( 0 != memcmp(sam_u->zar_u.dom_c, dom_c, sizeof(dom_c)) ) { + memcpy(sam_u->zar_u.dom_c, dom_c, sizeof(dom_c)); + memset(sam_u->zar_u.pip_w, 0, sizeof(sam_u->zar_u.pip_w)); + sam_u->zar_u.dom_o = c3y; + _ames_czar_all(&(sam_u->zar_u.tim_u)); + } + + // XX save all for fallback, not just first + // + if ( u3_nul != u3t(tuf) ) { + u3l_log("ames: turf: ignoring additional domains"); + u3m_p("second", u3h(u3t(tuf))); + + if ( u3_nul != u3t(u3t(tuf)) ) { + u3m_p("third", u3h(u3t(u3t(tuf)))); + } + } u3z(tuf); } - else if ( (c3n == sam_u->pir_u->fak_o) && (0 == sam_u->dns_c) ) { + else if ( (c3n == sam_u->pir_u->fak_o) && (c3n == sam_u->zar_u.dom_o) ) { u3l_log("ames: turf: no domains"); } @@ -3111,8 +2757,13 @@ _ames_io_exit(u3_auto* car_u) { u3_ames* sam_u = (u3_ames*)car_u; uv_close(&sam_u->had_u, _ames_exit_cb); - uv_close((uv_handle_t*)&sam_u->sun_u.dns_u, 0); uv_close((uv_handle_t*)&sam_u->sun_u.tim_u, 0); + uv_close((uv_handle_t*)&sam_u->nat_u.tim_u, 0); + + uv_handle_type handle = uv_handle_get_type((uv_handle_t *)&sam_u->nat_u.pol_u); + if ( UV_UNKNOWN_HANDLE != handle) { + uv_close((uv_handle_t*)&sam_u->nat_u.pol_u, 0); + } } /* _ames_io_info(): produce status info. @@ -3131,8 +2782,9 @@ _ames_io_info(u3_auto* car_u) return u3i_list( u3_pier_mase("filtering", sam_u->fig_u.fit_o), - u3_pier_mase("can-send", sam_u->fig_u.net_o), + u3_pier_mase("can-send", net_o), u3_pier_mase("can-scry", sam_u->fig_u.see_o), + u3_pier_mase("stun-working", sam_u->sun_u.wok_o), u3_pier_mase("scry-cache", u3i_word(u3h_wyt(sam_u->fin_s.sac_p))), u3_pier_mase("scry-cache-size", u3i_word(sac_w)), u3_pier_mase("lane-cache", u3i_word(u3h_wyt(sam_u->lax_p))), @@ -3175,11 +2827,13 @@ _ames_io_slog(u3_auto* car_u) // u3l_log(" config:"); u3l_log(" filtering: %s", FLAG(sam_u->fig_u.fit_o)); - u3l_log(" can send: %s", FLAG(sam_u->fig_u.net_o)); + u3l_log(" can send: %s", FLAG(net_o)); u3l_log(" can scry: %s", FLAG(sam_u->fig_u.see_o)); - u3l_log(" caches:"); - u3l_log(" cached lanes: %u, %u B", u3h_wyt(sam_u->lax_p), lax_w); - u3l_log(" cached meows: %u, %u B", u3h_wyt(sam_u->fin_s.sac_p), sac_w); + u3l_log(" stun:"); + u3l_log(" working: %s", FLAG(sam_u->sun_u.wok_o)); + u3l_log(" caches:"); + u3l_log(" cached lanes: %u, %u B", u3h_wyt(sam_u->lax_p), lax_w); + u3l_log(" cached meows: %u, %u B", u3h_wyt(sam_u->fin_s.sac_p), sac_w); u3l_log(" counters:"); u3l_log(" dropped: %" PRIu64, sam_u->sat_u.dop_d); u3l_log(" forwards dropped: %" PRIu64, sam_u->sat_u.fod_d); @@ -3205,15 +2859,20 @@ u3_ames_io_init(u3_pier* pir_u) u3_ames* sam_u = c3_calloc(sizeof(*sam_u)); sam_u->pir_u = pir_u; sam_u->nal_o = c3n; - sam_u->fig_u.net_o = c3y; sam_u->fig_u.see_o = c3y; sam_u->fig_u.fit_o = c3n; + sam_u->sun_u.wok_o = c3n; + sam_u->zar_u.dom_o = c3n; + + uv_timer_init(u3L, &sam_u->zar_u.tim_u); + sam_u->zar_u.tim_u.data = sam_u; - // initialize STUN timers - uv_timer_init(u3L, &sam_u->sun_u.dns_u); uv_timer_init(u3L, &sam_u->sun_u.tim_u); sam_u->sun_u.tim_u.data = sam_u; - sam_u->sun_u.dns_u.data = sam_u; + + // initialize libnatpmp + sam_u->nat_u.tim_u.data = sam_u; + uv_timer_init(u3L, &sam_u->nat_u.tim_u); // enable forwarding on galaxies only u3_noun who = u3i_chubs(2, sam_u->pir_u->who_d); diff --git a/pkg/vere/io/ames/stun.c b/pkg/vere/io/ames/stun.c new file mode 100644 index 0000000000..8bdad69742 --- /dev/null +++ b/pkg/vere/io/ames/stun.c @@ -0,0 +1,189 @@ +#include "vere.h" +#include "zlib.h" + +static c3_y* +_stun_add_fingerprint(c3_y *message, c3_w index) +{ + // Compute FINGERPRINT value as CRC-32 of the STUN message + // up to (but excluding) the FINGERPRINT attribute itself, + // XOR'ed with the 32-bit value 0x5354554e + c3_w init = crc32(0L, Z_NULL, 0); + c3_w crc = htonl(crc32(init, message, index) ^ 0x5354554e); + + // STUN attribute type: "FINGERPRINT" + message[index] = 0x80; message[index + 1] = 0x28; + // STUN attribute length: 4 bytes + message[index + 2] = 0x00; message[index + 3] = 0x04; + + memcpy(message + index + 4, &crc, 4); + + return message; +} + +static c3_o +_stun_has_fingerprint(c3_y* buf_y, c3_w len_w) +{ + c3_y ned_y[4] = {0x80, 0x28, 0x00, 0x04}; + if ( len_w < 28 ) { // At least STUN header and FINGERPRINT + return c3n; + } + + { + c3_y* fin_y = 0; + c3_w i = 20; // start after the header + + fin_y = memmem(buf_y + i, len_w - i, ned_y, sizeof(ned_y)); + if ( fin_y != 0 ) { + c3_w lin_w = fin_y - buf_y; + // Skip attribute type and length + c3_w fingerprint = c3_sift_word(fin_y + sizeof(ned_y)); + c3_w init = crc32(0L, Z_NULL, 0); + c3_w crc = htonl(crc32(init, buf_y, lin_w) ^ 0x5354554e); + if ((fingerprint == crc) && (fin_y - buf_y + 8) == len_w) { + return c3y; + } + } + + return c3n; + } +} + +/* u3_stun_is_request(): buffer is a stun request. +*/ +c3_o +u3_stun_is_request(c3_y* buf_y, c3_w len_w) +{ + c3_w cookie = htonl(0x2112A442); + + // Expects at least: + // STUN header and 8 byte FINGERPRINT + if ( (len_w >= 28) && + (buf_y[0] == 0x0 && buf_y[1] == 0x01) && + (memcmp(&cookie, buf_y + 4, 4) == 0) && + (c3y == _stun_has_fingerprint(buf_y, len_w)) ) + { + return c3y; + } + return c3n; +} + +/* u3_stun_is_our_response(): buffer is a response to our request. +*/ +c3_o +u3_stun_is_our_response(c3_y* buf_y, c3_y tid_y[12], c3_w len_w) +{ + c3_w cookie = htonl(0x2112A442); + + // Expects at least: + // STUN header, 12 byte XOR-MAPPED-ADDRESS and 8 byte FINGERPRINT + if ( (len_w == 40) && + (buf_y[0] == 0x01 && buf_y[1] == 0x01) && + (memcmp(&cookie, buf_y + 4, 4) == 0) && + (memcmp(tid_y, buf_y + 8, 12) == 0) && + (c3y == _stun_has_fingerprint(buf_y, len_w)) ) + { + return c3y; + } + return c3n; +} + +/* u3_stun_make_request(): serialize stun request. +*/ +void +u3_stun_make_request(c3_y buf_y[28], c3_y tid_y[12]) +{ + // see STUN RFC 8489 + // https://datatracker.ietf.org/doc/html/rfc8489#section-5 + memset(buf_y, 0, 28); + + // STUN message type: "binding request" + buf_y[1] = 0x01; + + // STUN message length: 8 (header and 32-bit FINGERPRINT) + buf_y[2] = 0x00; buf_y[3] = 0x08; + + // STUN "magic cookie" 0x2112A442 in network byte order + buf_y[4] = 0x21; buf_y[5] = 0x12; buf_y[6] = 0xa4; buf_y[7] = 0x42; + + // STUN "transaction id" + memcpy(buf_y + 8, tid_y, 12); + + // FINGERPRINT + _stun_add_fingerprint(buf_y, 20); +} + +/* u3_stun_make_response(): serialize stun response from request. +*/ +void +u3_stun_make_response(const c3_y req_y[20], + u3_lane* lan_u, + c3_y buf_y[40]) +{ + c3_w cok_w = 0x2112A442; + c3_w cur_w = 20; + + // XX hardcoded to match the requests we produce + // + memcpy(buf_y, req_y, cur_w); + buf_y[0] = 0x01; buf_y[1] = 0x01; // 0x0101 SUCCESS RESPONSE + buf_y[2] = 0x00; buf_y[3] = 0x14; // Length: 20 bytes + + memset(buf_y + cur_w, 0, cur_w); + + // XOR-MAPPED-ADDRESS + buf_y[cur_w + 0] = 0x00; // + buf_y[cur_w + 1] = 0x20; // attribute type 0x00020 + buf_y[cur_w + 2] = 0x00; // + buf_y[cur_w + 3] = 0x08; // STUN attribute length + buf_y[cur_w + 4] = 0x00; // extra reserved 0x0 byte + buf_y[cur_w + 5] = 0x01; // family 0x01:IPv4 + + c3_s por_s = htons(lan_u->por_s ^ (cok_w >> 16)); + c3_w pip_w = htonl(lan_u->pip_w ^ cok_w); + + memcpy(buf_y + cur_w + 6, &por_s, 2); // X-Port + memcpy(buf_y + cur_w + 8, &pip_w, 4); // X-IP Addres + + // FINGERPRINT + _stun_add_fingerprint(buf_y, cur_w + 12); +} + +/* u3_stun_find_xor_mapped_address(): extract lane from response. +*/ +c3_o +u3_stun_find_xor_mapped_address(c3_y* buf_y, + c3_w len_w, + u3_lane* lan_u) +{ + c3_y xor_y[4] = {0x00, 0x20, 0x00, 0x08}; + c3_w cookie = 0x2112A442; + + if ( len_w < 40 ) { // At least STUN header, XOR-MAPPED-ADDRESS & FINGERPRINT + return c3n; + } + + c3_w i = 20; // start after header + + c3_y* fin_y = memmem(buf_y + i, len_w - i, xor_y, sizeof(xor_y)); + if ( fin_y != 0 ) { + c3_w cur = (c3_w)(fin_y - buf_y) + sizeof(xor_y); + + if ( (buf_y[cur] != 0x0) && (buf_y[cur+1] != 0x1) ) { + return c3n; + } + + cur += 2; + + lan_u->por_s = ntohs(c3_sift_short(buf_y + cur)) ^ (cookie >> 16); + lan_u->pip_w = ntohl(c3_sift_word(buf_y + cur + 2)) ^ cookie; + + if ( u3C.wag_w & u3o_verbose ) { + c3_w nip_w = htonl(lan_u->pip_w); + c3_c nip_c[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &nip_w, nip_c, INET_ADDRSTRLEN); + u3l_log("stun: hear ip:port %s:%u", nip_c, lan_u->por_s); + } + return c3y; + } + return c3n; +} diff --git a/pkg/vere/io/ames/stun.h b/pkg/vere/io/ames/stun.h new file mode 100644 index 0000000000..0fcaff6466 --- /dev/null +++ b/pkg/vere/io/ames/stun.h @@ -0,0 +1,35 @@ +#include "vere.h" + +#ifndef U3_STUN_H +#define U3_STUN_H + + /* u3_stun_is_request(): buffer is a stun request. + */ + c3_o + u3_stun_is_request(c3_y* buf_y, c3_w len_w); + + /* u3_stun_is_our_response(): buffer is a response to our request. + */ + c3_o + u3_stun_is_our_response(c3_y* buf_y, c3_y tid_y[12], c3_w len_w); + + /* u3_stun_make_request(): serialize stun request. + */ + void + u3_stun_make_request(c3_y buf_y[28], c3_y tid_y[12]); + + /* u3_stun_make_response(): serialize stun response from request. + */ + void + u3_stun_make_response(const c3_y req_y[20], + u3_lane* lan_u, + c3_y buf_y[40]); + + /* u3_stun_find_xor_mapped_address(): extract lane from response. + */ + c3_o + u3_stun_find_xor_mapped_address(c3_y* buf_y, + c3_w len_w, + u3_lane* lan_u); + +#endif /* ifndef U3_STUN_H */ diff --git a/pkg/vere/io/behn.c b/pkg/vere/io/behn.c index fc3ea413dc..0937c50f25 100644 --- a/pkg/vere/io/behn.c +++ b/pkg/vere/io/behn.c @@ -50,9 +50,14 @@ _behn_wake_bail(u3_ovum* egg_u, u3_noun lud) u3l_log("behn: timer failed; queue blocked"); - // XX review, add flag to continue? - // - u3_pier_bail(car_u->pir_u); + if ( c3n == u3_Host.ops_u.beb ) { + u3_pier_bail(car_u->pir_u); + } + else { + u3l_log("vere: warning: continuing without timers"); + u3z(lud); + u3_ovum_free(egg_u); + } } } diff --git a/pkg/vere/io/http.c b/pkg/vere/io/http.c index 6fb607ad5c..63812a4792 100644 --- a/pkg/vere/io/http.c +++ b/pkg/vere/io/http.c @@ -111,6 +111,8 @@ typedef struct _u3_httd { u3p(u3h_root) nax_p; // scry->noun cache } u3_httd; +static u3_weak _http_rec_to_httq(h2o_req_t* rec_u); +static u3_hreq* _http_req_prepare(h2o_req_t* rec_u, u3_hreq* (*new_f)(u3_hcon*, h2o_req_t*)); static void _http_serv_free(u3_http* htp_u); static void _http_serv_start_all(u3_httd* htd_u); static void _http_form_free(u3_httd* htd_u); @@ -673,7 +675,19 @@ _http_cache_respond(u3_hreq* req_u, u3_noun nun) { u3_httd* htd_u = req_u->hon_u->htp_u->htd_u; if ( u3_nul == nun ) { - h2o_send_error_404(rec_u, "Not Found", "not found", 0); + u3_weak req = _http_rec_to_httq(rec_u); + if ( u3_none == req ) { + if ( (u3C.wag_w & u3o_verbose) ) { + u3l_log("strange %.*s request", (c3_i)rec_u->method.len, + rec_u->method.base); + } + c3_c* msg_c = "bad request"; + h2o_send_error_generic(rec_u, 400, msg_c, msg_c, 0); + } + else { + u3_hreq* req_u = _http_req_prepare(rec_u, _http_req_new); + _http_req_dispatch(req_u, req); + } } else if ( u3_none == u3r_at(7, nun) ) { h2o_send_error_500(rec_u, "Internal Server Error", "scry failed", 0); diff --git a/pkg/vere/king.c b/pkg/vere/king.c index 7446ae7199..915ce7262f 100644 --- a/pkg/vere/king.c +++ b/pkg/vere/king.c @@ -90,6 +90,8 @@ void _king_doom(u3_noun doom); void _king_fake(u3_noun ship, u3_noun pill, u3_noun path); void _king_pier(u3_noun pier); +static u3_noun _king_get_atom(c3_c* url_c); + /* _king_defy_fate(): invalid fate */ void @@ -158,6 +160,49 @@ _king_boot(u3_noun bul) u3z(bul); } +/* _king_prop(): events from prop arguments +*/ +u3_noun +_king_prop() +{ + u3_noun mor = u3_nul; + while ( 0 != u3_Host.ops_u.vex_u ) { + u3_even* vex_u = u3_Host.ops_u.vex_u; + switch ( vex_u->kin_i ) { + case 1: { // file + u3_atom jam = u3m_file(vex_u->loc_c); + mor = u3nc(u3ke_cue(jam), mor); + } break; + + case 2: { // url + u3l_log("boot: downloading prop %s", vex_u->loc_c); + u3_atom jam = _king_get_atom(vex_u->loc_c); + mor = u3nc(u3ke_cue(jam), mor); + } break; + + case 3: { // name + //NOTE this implementation limits us to max 213 char prop names + c3_c url_c[256]; + snprintf(url_c, 255, + //TODO should maybe respect ops_u.url_c + "https://bootstrap.urbit.org/props/" URBIT_VERSION "/%s.jam", + vex_u->loc_c); + u3l_log("boot: downloading prop %s", url_c); + u3_atom jam = _king_get_atom(url_c); + mor = u3nc(u3ke_cue(jam), mor); + } break; + + default: { + u3l_log("invalid prop source %d", vex_u->kin_i); + exit(1); + } + } + + u3_Host.ops_u.vex_u = vex_u->pre_u; + } + return mor; +} + /* _king_fake(): boot with fake keys */ void @@ -166,7 +211,8 @@ _king_fake(u3_noun ship, u3_noun pill, u3_noun path) // XX link properly // u3_noun vent = u3nc(c3__fake, u3k(ship)); - u3K.pir_u = u3_pier_boot(sag_w, ship, vent, pill, path, u3_none); + u3K.pir_u = u3_pier_boot(sag_w, ship, vent, pill, path, + u3_none, _king_prop()); } /* _king_come(): mine a comet under star (unit) @@ -201,7 +247,8 @@ _king_dawn(u3_noun feed, u3_noun pill, u3_noun path) u3_noun vent = u3_dawn_vent(u3k(ship), u3k(feed)); // XX link properly // - u3K.pir_u = u3_pier_boot(sag_w, u3k(ship), vent, pill, path, feed); + u3K.pir_u = u3_pier_boot(sag_w, u3k(ship), vent, pill, path, + feed, _king_prop()); // disable ivory slog printfs // @@ -251,6 +298,7 @@ _king_curl_bytes(c3_c* url_c, c3_w* len_w, c3_y** hun_y, c3_t veb_t) CURL *cul_u; CURLcode res_i; long cod_i; + c3_y try_y = 0; uv_buf_t buf_u = uv_buf_init(c3_malloc(1), 0); if ( !(cul_u = curl_easy_init()) ) { @@ -263,29 +311,34 @@ _king_curl_bytes(c3_c* url_c, c3_w* len_w, c3_y** hun_y, c3_t veb_t) curl_easy_setopt(cul_u, CURLOPT_WRITEFUNCTION, _king_curl_alloc); curl_easy_setopt(cul_u, CURLOPT_WRITEDATA, (void*)&buf_u); - res_i = curl_easy_perform(cul_u); - curl_easy_getinfo(cul_u, CURLINFO_RESPONSE_CODE, &cod_i); + while ( 5 > try_y ) { + sleep(try_y++); + res_i = curl_easy_perform(cul_u); + curl_easy_getinfo(cul_u, CURLINFO_RESPONSE_CODE, &cod_i); - // XX retry? - // - if ( CURLE_OK != res_i ) { - if ( veb_t ) { - u3l_log("curl: failed %s: %s", url_c, curl_easy_strerror(res_i)); + if ( CURLE_OK != res_i ) { + if ( veb_t ) { + u3l_log("curl: failed to fetch %s: %s", + url_c, curl_easy_strerror(res_i)); + } + ret_i = -1; } - ret_i = -1; - } - if ( 300 <= cod_i ) { - if ( veb_t ) { - u3l_log("curl: error %s: HTTP %ld", url_c, cod_i); + else if ( 300 <= cod_i ) { + if ( veb_t ) { + u3l_log("curl: error fetching %s: HTTP %ld", url_c, cod_i); + } + ret_i = -2; + } + else { + *len_w = buf_u.len; + *hun_y = (c3_y*)buf_u.base; + ret_i = 0; + break; } - ret_i = -2; } curl_easy_cleanup(cul_u); - *len_w = buf_u.len; - *hun_y = (c3_y*)buf_u.base; - return ret_i; } @@ -376,6 +429,7 @@ u3_king_next(c3_c* pac_c, c3_c** out_c) } // skip printfs on failed requests (/next is usually not present) + //REVIEW new retry logic means this case will take longer. make retries optional? // if ( _king_curl_bytes(url_c, &len_w, &hun_y, 0) ) { c3_free(url_c); @@ -1444,7 +1498,7 @@ _king_copy_vere(c3_c* pac_c, c3_c* ver_c, c3_c* arc_c, c3_t lin_t) if ( ret_i ) { fprintf(stderr, "vere: copy %s -> %s failed: %s\r\n", - bin_c, u3_Host.dem_c, strerror(errno)); + u3_Host.dem_c, bin_c, strerror(errno)); c3_free(bin_c); return -1; } diff --git a/pkg/vere/main.c b/pkg/vere/main.c index ec98561ea7..12c64d8ecd 100644 --- a/pkg/vere/main.c +++ b/pkg/vere/main.c @@ -18,6 +18,8 @@ #include "db/lmdb.h" #include "getopt.h" #include "libgen.h" +#include "pthread.h" +#include "spawn.h" #include "ca_bundle.h" #include "pace.h" @@ -177,6 +179,7 @@ _main_init(void) u3_Host.ops_u.rep = c3n; u3_Host.ops_u.eph = c3n; u3_Host.ops_u.tos = c3n; + u3_Host.ops_u.beb = c3n; u3_Host.ops_u.tem = c3n; u3_Host.ops_u.tex = c3n; u3_Host.ops_u.tra = c3n; @@ -192,6 +195,14 @@ _main_init(void) u3_Host.ops_u.lut_y = 31; /* aka 2G */ u3_Host.ops_u.lom_y = 31; + u3_Host.ops_u.siz_i = +#if (defined(U3_CPU_aarch64) && defined(U3_OS_linux)) + // 500 GiB is as large as musl on aarch64 wants to allow + 0x7d00000000; +#else + 0x10000000000; +#endif + u3C.eph_c = 0; u3C.tos_w = 0; } @@ -218,6 +229,19 @@ _main_pier_run(c3_c* bin_c) return dir_c; } +/* _main_add_prop(): add a boot prop to u3_Host.ops_u.vex_u. +*/ +u3_even* +_main_add_prop(c3_i kin_i, c3_c* loc_c) +{ + u3_even* nex_u = c3_calloc(sizeof(*nex_u)); + nex_u->kin_i = kin_i; + nex_u->loc_c = loc_c; + nex_u->pre_u = u3_Host.ops_u.vex_u; + u3_Host.ops_u.vex_u = nex_u; + return nex_u; +} + /* _main_getopt(): extract option map from command line. */ static u3_noun @@ -273,11 +297,18 @@ _main_getopt(c3_i argc, c3_c** argv) { "scry-into", required_argument, NULL, 'Y' }, { "scry-format", required_argument, NULL, 'Z' }, // - { "urth-loom", required_argument, NULL, 5 }, - { "no-demand", no_argument, NULL, 6 }, - { "swap", no_argument, NULL, 7 }, - { "swap-to", required_argument, NULL, 8 }, - { "toss", required_argument, NULL, 9 }, + { "prop-file", required_argument, NULL, 1 }, + { "prop-url", required_argument, NULL, 2 }, + { "prop-name", required_argument, NULL, 3 }, + // + { "urth-loom", required_argument, NULL, 5 }, + { "no-demand", no_argument, NULL, 6 }, + { "swap", no_argument, NULL, 7 }, + { "swap-to", required_argument, NULL, 8 }, + { "toss", required_argument, NULL, 9 }, + { "behn-allow-blocked", no_argument, NULL, 10 }, + { "serf-bin", required_argument, NULL, 11 }, + { "lmdb-map-size", required_argument, NULL, 12 }, // { NULL, 0, NULL, 0 }, }; @@ -287,6 +318,10 @@ _main_getopt(c3_i argc, c3_c** argv) lop_u, &lid_i)) ) { switch ( ch_i ) { + case 1: case 2: case 3: { // prop-* + _main_add_prop(ch_i, strdup(optarg)); + break; + } case 5: { // urth-loom if (_main_readw_loom("urth-loom", &u3_Host.ops_u.lut_y)) { return c3n; @@ -313,6 +348,21 @@ _main_getopt(c3_i argc, c3_c** argv) } break; } + case 10: { // behn-allow-blocked + u3_Host.ops_u.beb = c3y; + break; + } + case 11: { // serf-bin + u3_Host.wrk_c = strdup(optarg); + break; + } + case 12: { // lmdb-map-size + if ( 1 != sscanf(optarg, "%" SCNuMAX, &u3_Host.ops_u.siz_i) ) { + return c3n; + } + + break; + } // special args // case c3__loom: { @@ -603,6 +653,7 @@ _main_getopt(c3_i argc, c3_c** argv) if ( hyphen_c ) { *hyphen_c = '\0'; } + //TODO use brass pill from b.u.org/props/etc eventually c3_i res_i = asprintf(&u3_Host.ops_u.url_c, "https://bootstrap.urbit.org/urbit-v%s.pill", version_c); @@ -628,6 +679,18 @@ _main_getopt(c3_i argc, c3_c** argv) } } + if ( u3_Host.ops_u.vex_u != 0 ) { + struct stat s; + u3_even* vex_u = u3_Host.ops_u.vex_u; + while ( vex_u != 0 ) { + if ( vex_u->kin_i == 1 && stat(vex_u->loc_c, &s) != 0 ) { + fprintf(stderr, "events file %s not found\n", vex_u->loc_c); + return c3n; + } + vex_u = vex_u->pre_u; + } + } + struct sockaddr_in t; if ( u3_Host.ops_u.bin_c != 0 && inet_pton(AF_INET, u3_Host.ops_u.bin_c, &t.sin_addr) == 0 ) { fprintf(stderr, "-b invalid IP address\n"); @@ -642,6 +705,15 @@ _main_getopt(c3_i argc, c3_c** argv) } } + //TODO split up "default distribution" packages eventually + // // if we're not in lite mode, include the default props + // // + // if ( u3_Host.ops_u.lit == c3n ) { + // _main_add_prop(3, "landscape"); + // _main_add_prop(3, "webterm"); + // _main_add_prop(3, "groups"); + // } + return c3y; } @@ -803,6 +875,9 @@ u3_ve_usage(c3_i argc, c3_c** argv) " --no-dock Skip binary \"docking\" on boot\n", " --swap Use an explicit ephemeral (swap-like) file\n", " --swap-to FILE Specify ephemeral file location\n", + " --prop-file FILE Add a prop into the boot sequence\n" + " --prop-url URL Download a prop into the boot sequence\n", + " --prop-name NAME Download a prop from bootstrap.urbit.org\n", "\n", "Development Usage:\n", " To create a development ship, use a fakezod:\n", @@ -861,6 +936,14 @@ report(void) LIBCURL_VERSION_PATCH); } +/* _stop_exit_fore(): exit before. +*/ +static void +_stop_exit_fore(c3_i int_i) +{ + kill(getpid(), SIGTERM); +} + /* _stop_exit(): exit immediately. */ static void @@ -1088,7 +1171,6 @@ _cw_serf_commence(c3_i argc, c3_c* argv[]) fprintf(stderr, "serf: missing args\n"); exit(1); } - // XX use named arguments and getopt c3_d eve_d = 0; @@ -1518,10 +1600,11 @@ _cw_info(c3_i argc, c3_c* argv[]) c3_w arg_w; static struct option lop_u[] = { - { "loom", required_argument, NULL, c3__loom }, - { "no-demand", no_argument, NULL, 6 }, - { "swap", no_argument, NULL, 7 }, - { "swap-to", required_argument, NULL, 8 }, + { "loom", required_argument, NULL, c3__loom }, + { "no-demand", no_argument, NULL, 6 }, + { "swap", no_argument, NULL, 7 }, + { "swap-to", required_argument, NULL, 8 }, + { "lmdb-map-size", required_argument, NULL, 9 }, { NULL, 0, NULL, 0 } }; @@ -1552,6 +1635,13 @@ _cw_info(c3_i argc, c3_c* argv[]) break; } + case 9: { // lmdb-map-size + if ( 1 != sscanf(optarg, "%" SCNuMAX, &u3_Host.ops_u.siz_i) ) { + exit(1); + } + break; + } + case '?': { fprintf(stderr, "invalid argument\r\n"); exit(1); @@ -1693,10 +1783,11 @@ _cw_cram(c3_i argc, c3_c* argv[]) c3_w arg_w; static struct option lop_u[] = { - { "loom", required_argument, NULL, c3__loom }, - { "no-demand", no_argument, NULL, 6 }, - { "swap", no_argument, NULL, 7 }, - { "swap-to", required_argument, NULL, 8 }, + { "loom", required_argument, NULL, c3__loom }, + { "no-demand", no_argument, NULL, 6 }, + { "swap", no_argument, NULL, 7 }, + { "swap-to", required_argument, NULL, 8 }, + { "lmdb-map-size", required_argument, NULL, 9 }, { NULL, 0, NULL, 0 } }; @@ -1727,6 +1818,13 @@ _cw_cram(c3_i argc, c3_c* argv[]) break; } + case 9: { // lmdb-map-size + if ( 1 != sscanf(optarg, "%" SCNuMAX, &u3_Host.ops_u.siz_i) ) { + exit(1); + } + break; + } + case '?': { fprintf(stderr, "invalid argument\r\n"); exit(1); @@ -1789,11 +1887,12 @@ _cw_queu(c3_i argc, c3_c* argv[]) c3_c* roc_c = 0; static struct option lop_u[] = { - { "loom", required_argument, NULL, c3__loom }, - { "no-demand", no_argument, NULL, 6 }, - { "swap", no_argument, NULL, 7 }, - { "swap-to", required_argument, NULL, 8 }, - { "replay-from", required_argument, NULL, 'r' }, + { "loom", required_argument, NULL, c3__loom }, + { "no-demand", no_argument, NULL, 6 }, + { "swap", no_argument, NULL, 7 }, + { "swap-to", required_argument, NULL, 8 }, + { "lmdb-map-size", required_argument, NULL, 9 }, + { "replay-from", required_argument, NULL, 'r' }, { NULL, 0, NULL, 0 } }; @@ -1824,6 +1923,13 @@ _cw_queu(c3_i argc, c3_c* argv[]) break; } + case 9: { // lmdb-map-size + if ( 1 != sscanf(optarg, "%" SCNuMAX, &u3_Host.ops_u.siz_i) ) { + exit(1); + } + break; + } + case 'r': { roc_c = strdup(optarg); } break; @@ -1897,11 +2003,12 @@ _cw_meld(c3_i argc, c3_c* argv[]) c3_w arg_w; static struct option lop_u[] = { - { "loom", required_argument, NULL, c3__loom }, - { "no-demand", no_argument, NULL, 6 }, - { "swap", no_argument, NULL, 7 }, - { "swap-to", required_argument, NULL, 8 }, - { "gc-early", no_argument, NULL, 9 }, + { "loom", required_argument, NULL, c3__loom }, + { "no-demand", no_argument, NULL, 6 }, + { "swap", no_argument, NULL, 7 }, + { "swap-to", required_argument, NULL, 8 }, + { "gc-early", no_argument, NULL, 9 }, + { "lmdb-map-size", required_argument, NULL, 10 }, { NULL, 0, NULL, 0 } }; @@ -1937,6 +2044,13 @@ _cw_meld(c3_i argc, c3_c* argv[]) break; } + case 10: { // lmdb-map-size + if ( 1 != sscanf(optarg, "%" SCNuMAX, &u3_Host.ops_u.siz_i) ) { + exit(1); + } + break; + } + case '?': { fprintf(stderr, "invalid argument\r\n"); exit(1); @@ -2065,11 +2179,12 @@ _cw_pack(c3_i argc, c3_c* argv[]) c3_w arg_w; static struct option lop_u[] = { - { "loom", required_argument, NULL, c3__loom }, - { "no-demand", no_argument, NULL, 6 }, - { "swap", no_argument, NULL, 7 }, - { "swap-to", required_argument, NULL, 8 }, - { "gc-early", no_argument, NULL, 9 }, + { "loom", required_argument, NULL, c3__loom }, + { "no-demand", no_argument, NULL, 6 }, + { "swap", no_argument, NULL, 7 }, + { "swap-to", required_argument, NULL, 8 }, + { "gc-early", no_argument, NULL, 9 }, + { "lmdb-map-size", required_argument, NULL, 10 }, { NULL, 0, NULL, 0 } }; @@ -2105,6 +2220,13 @@ _cw_pack(c3_i argc, c3_c* argv[]) break; } + case 10: { // lmdb-map-size + if ( 1 != sscanf(optarg, "%" SCNuMAX, &u3_Host.ops_u.siz_i) ) { + exit(1); + } + break; + } + case '?': { fprintf(stderr, "invalid argument\r\n"); exit(1); @@ -2188,10 +2310,7 @@ _cw_play_snap(u3_disk* log_u) static void _cw_play_exit(c3_i int_i) { - // explicit fprintf to avoid allocation in u3l_log - // - fprintf(stderr, "\r\n[received keyboard stop signal, exiting]\r\n"); - raise(SIGINT); + kill(getpid(), SIGINT); } /* _cw_play_impl(): replay events, but better. @@ -2264,6 +2383,136 @@ _cw_play_impl(c3_d eve_d, c3_d sap_d, c3_o mel_o, c3_o sof_o, c3_o ful_o) return pay_d; } +/* _cw_play_fork_heed(): wait for EOF on STDIN or until canceled. +*/ +static void* +_cw_play_fork_heed(void* arg) { + // XX + c3_c buf[1]; + c3_zs red; + + sigset_t set; + + sigemptyset(&set); + sigaddset(&set, SIGINT); + sigaddset(&set, SIGTERM); + sigaddset(&set, SIGTSTP); + if ( 0 != pthread_sigmask(SIG_BLOCK, &set, NULL) ) { + fprintf(stderr, "play: watcher failed to block sigs: %s\r\n", strerror(errno)); + exit(1); + } + + do { + pthread_testcancel(); + red = read(STDIN_FILENO, buf, sizeof(buf)); + if ( 0 == red ) { + fprintf(stderr, "play: god save the king! committing sudoku...\r\n"); + kill(getpid(), SIGINT); + return NULL; + } + } while ( 0 < red ); + + return NULL; +} + +/* _cw_play_fork_exit(): exit callback for uv_spawn. +*/ +void +_cw_play_fork_exit(uv_process_t* req_u, c3_ds sat_d, c3_i tem_i) { + if ( sat_d || tem_i ) { + fprintf(stderr, "play: failed: %" PRId64 " signal: %d\r\n", sat_d, tem_i); + exit(1); + } + uv_close((uv_handle_t*)req_u, NULL); +} + +/* _cw_play_fork(): spawn a subprocess for event replay. +*/ +static c3_i +_cw_play_fork(c3_d eve_d, c3_d sap_d, c3_o mel_o, c3_o sof_o, c3_o ful_o) +{ + c3_c *argv[12] = {0}; + c3_c eve_c[21] = {0}; + c3_c sap_c[21] = {0}; + c3_c lom_c[3] = {0}; + c3_i ret_i; + + ret_i = snprintf(eve_c, sizeof(eve_c), "%" PRIu64, eve_d); + u3_assert( ret_i && ret_i < sizeof(eve_c) ); + ret_i = snprintf(sap_c, sizeof(sap_c), "%" PRIu64, sap_d); + u3_assert( ret_i && ret_i < sizeof(sap_c) ); + ret_i = snprintf(lom_c, sizeof(lom_c), "%u", u3_Host.ops_u.lom_y); + u3_assert( ret_i && ret_i < sizeof(lom_c) ); + + { + c3_z i_z = 0; + c3_i run_i = 0; + + c3_c* run_c = _main_pier_run(u3_Host.wrk_c); + if ( run_c ) { + c3_free(run_c); + run_i = 1; + } + + argv[i_z++] = u3_Host.wrk_c; + argv[i_z++] = "play"; + argv[i_z++] = "--loom"; + argv[i_z++] = lom_c; + argv[i_z++] = "--replay-to"; + argv[i_z++] = eve_c; + argv[i_z++] = "--snap-at"; + argv[i_z++] = sap_c; + + if _(mel_o) { + argv[i_z++] = "--auto-meld"; + } + if _(sof_o) { + argv[i_z++] = "--soft-mugs"; + } + if _(ful_o) { + argv[i_z++] = "--full"; + } + if ( !run_i ) { + argv[i_z++] = u3_Host.dir_c; + } + + argv[i_z] = NULL; + u3_assert( i_z < sizeof(argv) ); + } + + // use uv_spawn to fork a new serf process and call its play subcommand + // + u3L = uv_default_loop(); + + uv_pipe_t stdin_pipe; + uv_pipe_init(u3L, &stdin_pipe, 0); + + uv_process_t child_req = {0}; + uv_process_options_t options = {0}; + uv_stdio_container_t stdio[3]; + stdio[0].data.stream = (uv_stream_t*) &stdin_pipe; + stdio[1].data.fd = STDOUT_FILENO; + stdio[2].data.fd = STDERR_FILENO; + stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; // stdin + stdio[1].flags = UV_INHERIT_FD; // stdout + stdio[2].flags = UV_INHERIT_FD; // stderr + options.stdio_count = 3; + options.stdio = stdio; + options.file = argv[0]; + options.args = argv; + options.exit_cb = (uv_exit_cb)_cw_play_fork_exit; + + c3_i sat_i; + if ( 0 != (sat_i = uv_spawn(u3L, &child_req, &options)) ) { + fprintf(stderr, "play: uv_spawn: %s\r\n", uv_strerror(sat_i)); + return 1; + } + + signal(SIGINT, SIG_IGN); + + return uv_run(u3L, UV_RUN_DEFAULT); +} + /* _cw_play(): replay events, but better. */ static void @@ -2356,9 +2605,12 @@ _cw_play(c3_i argc, c3_c* argv[]) exit(1); } - if ( !_cw_play_impl(eve_d, sap_d, mel_o, sof_o, ful_o) ) { - fprintf(stderr, "mars: nothing to do!\r\n"); - } + pthread_t ted; + pthread_create(&ted, NULL, _cw_play_fork_heed, NULL); + + _cw_play_impl(eve_d, sap_d, mel_o, sof_o, ful_o); + + pthread_cancel(ted); } /* _cw_prep(): prepare for upgrade @@ -2446,10 +2698,11 @@ _cw_chop(c3_i argc, c3_c* argv[]) c3_w arg_w; static struct option lop_u[] = { - { "loom", required_argument, NULL, c3__loom }, - { "no-demand", no_argument, NULL, 6 }, - { "swap", no_argument, NULL, 7 }, - { "swap-to", required_argument, NULL, 8 }, + { "loom", required_argument, NULL, c3__loom }, + { "no-demand", no_argument, NULL, 6 }, + { "swap", no_argument, NULL, 7 }, + { "swap-to", required_argument, NULL, 8 }, + { "lmdb-map-size", required_argument, NULL, 9 }, { NULL, 0, NULL, 0 } }; @@ -2480,6 +2733,13 @@ _cw_chop(c3_i argc, c3_c* argv[]) break; } + case 9: { // lmdb-map-size + if ( 1 != sscanf(optarg, "%" SCNuMAX, &u3_Host.ops_u.siz_i) ) { + exit(1); + } + break; + } + case '?': { fprintf(stderr, "invalid argument\r\n"); exit(1); @@ -2527,7 +2787,8 @@ _cw_roll(c3_i argc, c3_c* argv[]) c3_w arg_w; static struct option lop_u[] = { - { "loom", required_argument, NULL, c3__loom }, + { "loom", required_argument, NULL, c3__loom }, + { "lmdb-map-size", required_argument, NULL, 6 }, { NULL, 0, NULL, 0 } }; @@ -2535,6 +2796,13 @@ _cw_roll(c3_i argc, c3_c* argv[]) while ( -1 != (ch_i=getopt_long(argc, argv, "", lop_u, &lid_i)) ) { switch ( ch_i ) { + case 6: { // lmdb-map-size + if ( 1 != sscanf(optarg, "%" SCNuMAX, &u3_Host.ops_u.siz_i) ) { + exit(1); + } + break; + } + case c3__loom: { if (_main_readw_loom("loom", &u3_Host.ops_u.lom_y)) { exit(1); @@ -2883,6 +3151,10 @@ main(c3_i argc, _main_init(); +#if defined(U3_OS_osx) + darwin_register_mach_exception_handler(); +#endif + c3_c* bin_c = strdup(argv[0]); // parse for subcommands @@ -2911,8 +3183,6 @@ main(c3_i argc, _main_self_path(); - // XX add argument - // if ( !u3_Host.wrk_c ) { u3_Host.wrk_c = bin_c; } @@ -2964,7 +3234,7 @@ main(c3_i argc, // // Configured here using signal() so as to be immediately available. // - signal(SIGTSTP, _stop_exit); + signal(SIGTSTP, _stop_exit_fore); printf("~\n"); // printf("welcome.\n"); @@ -3055,7 +3325,12 @@ main(c3_i argc, // we need the current snapshot's latest event number to // validate whether we can execute disk migration if ( u3_Host.ops_u.nuu == c3n ) { - _cw_play_impl(0, 0, c3n, c3n, c3n); + c3_i sat_i = _cw_play_fork(0, 0, c3n, c3n, c3n); + if ( sat_i ) { + fprintf(stderr, "play: replay failed\r\n"); + exit(sat_i); + } + signal(SIGTSTP, _stop_exit); // XX unmap loom, else parts of the snapshot could be left in memory } diff --git a/pkg/vere/mdns.c b/pkg/vere/mdns.c index 0875489625..80a2b3ac9a 100644 --- a/pkg/vere/mdns.c +++ b/pkg/vere/mdns.c @@ -61,7 +61,7 @@ static void resolve_cb(DNSServiceRef sref, payload->sref = sref; payload->port = port; - char *start = name; + const char *start = name; if (strncmp(name, "fake-", 4) == 0) { payload->fake = 1; start = name + 5; diff --git a/pkg/vere/pier.c b/pkg/vere/pier.c index 7514e17155..2894b9bf84 100644 --- a/pkg/vere/pier.c +++ b/pkg/vere/pier.c @@ -1821,7 +1821,8 @@ _pier_boot_make(u3_noun who, u3_noun wyr, u3_noun ven, u3_noun pil, - u3_weak fed) + u3_weak fed, + u3_noun mor) { u3_boot bot_u = _pier_pill_parse(pil); // transfer @@ -1873,7 +1874,52 @@ _pier_boot_make(u3_noun who, bot_u.use = u3nc(u3nc(wir, cad), bot_u.use); } + // prepend & append additional boot enhancements to the userspace sequence + // + // +$ prop [%prop meta tier (list ovum)] + // +$ meta term + // +$ tier ?(%fore %hind) :: before or after userspace + // + { + u3_noun mos = mor; + u3_noun pre = u3_nul; + u3_noun aft = u3_nul; + while ( u3_nul != mos ) { + u3_noun mot = u3h(mos); + + switch ( u3h(mot) ) { + case c3__prop: { + u3_noun ter, met, ves; + + if ( c3n == u3r_trel(u3t(mot), &met, &ter, &ves) ) { + u3m_p("invalid prop", u3t(mot)); + break; + } + + if ( c3__fore == ter ) { + u3m_p("prop: fore", met); + pre = u3kb_weld(pre, u3k(ves)); + } + else if ( c3__hind == ter ) { + u3m_p("prop: hind", met); + aft = u3kb_weld(aft, u3k(ves)); + } + else { + u3m_p("unrecognized prop tier", ter); + } + } break; + + default: u3m_p("unrecognized boot sequence enhancement", u3h(mot)); + } + + mos = u3t(mos); + } + + bot_u.use = u3kb_weld(pre, u3kb_weld(bot_u.use, aft)); // transfer + } + u3z(fed); + u3z(mor); return bot_u; } @@ -1884,7 +1930,8 @@ _pier_boot_plan(u3_pier* pir_u, u3_noun who, u3_noun ven, u3_noun pil, - u3_weak fed) + u3_weak fed, + u3_noun mor) { u3_boot bot_u; { @@ -1892,7 +1939,7 @@ _pier_boot_plan(u3_pier* pir_u, pir_u->fak_o = ( c3__fake == u3h(ven) ) ? c3y : c3n; u3r_chubs(0, 2, pir_u->who_d, who); - bot_u = _pier_boot_make(who, _pier_wyrd_card(pir_u), ven, pil, fed); + bot_u = _pier_boot_make(who, _pier_wyrd_card(pir_u), ven, pil, fed, mor); pir_u->lif_w = u3qb_lent(bot_u.bot); } @@ -1976,7 +2023,8 @@ u3_pier_boot(c3_w wag_w, // config flags u3_noun ven, // boot event u3_noun pil, // type-of/path-to pill u3_noun pax, // path to pier - u3_weak fed) // extra private keys + u3_weak fed, // extra private keys + u3_noun mor) // extra boot sequence props { u3_pier* pir_u; @@ -1988,7 +2036,7 @@ u3_pier_boot(c3_w wag_w, // config flags // XX must be called from on_lord_live // - if ( c3n == _pier_boot_plan(pir_u, who, ven, pil, fed) ) { + if ( c3n == _pier_boot_plan(pir_u, who, ven, pil, fed, mor) ) { fprintf(stderr, "pier: boot plan fail\r\n"); // XX dispose // diff --git a/pkg/vere/platform/darwin/mach.c b/pkg/vere/platform/darwin/mach.c new file mode 100644 index 0000000000..9775882d2e --- /dev/null +++ b/pkg/vere/platform/darwin/mach.c @@ -0,0 +1,28 @@ +#include "log.h" + +#include +#include +#include +#include + + +// lldb does not listen to BSD signals, it only listens to Mach exceptions. +// The Mach exception EXC_BAD_ACCESS corresponds to SIGSEGV, but lldb has +// problems converting between them because of a longstanding macOS kernel bug. +// This means that without this workaround we cannot debug our binaries with +// lldb. The first segfault we hit causes an infinite loop in lldb no matter +// how many times you try to continue. This workaround is implemented in projects +// such as the Go runtime. +// See https://bugs.llvm.org/show_bug.cgi?id=22868#c1 for more details. + +void darwin_register_mach_exception_handler() { + kern_return_t kr = task_set_exception_ports( + mach_task_self(), + EXC_MASK_BAD_ACCESS, // SIGSEGV + MACH_PORT_NULL, + EXCEPTION_STATE_IDENTITY, + MACHINE_THREAD_STATE); + if ( KERN_SUCCESS != kr) { + u3l_log("mach: unable to register exception handler"); + } +} diff --git a/pkg/vere/vere.h b/pkg/vere/vere.h index bb3ac9df80..dab95c5080 100644 --- a/pkg/vere/vere.h +++ b/pkg/vere/vere.h @@ -258,6 +258,14 @@ struct _u3_auto* car_u; // driver hack } u3_utty; + /* u3_even: boot event specifier + */ + typedef struct _u3_even { + c3_i kin_i; + c3_c* loc_c; + struct _u3_even* pre_u; + } u3_even; + /* u3_opts: command line configuration. */ typedef struct _u3_opts { @@ -309,6 +317,10 @@ c3_o map; // --no-demand (reversed) c3_o eph; // --swap, use ephemeral file c3_o tos; // --toss, discard ephemeral + u3_even* vex_u; // --prop-*, boot enhancements + + c3_o beb; // --behn-allow-blocked + c3_z siz_i; // --lmdb-map-size } u3_opts; /* u3_host: entire host. @@ -541,6 +553,7 @@ u3_dire* dir_u; // main pier directory u3_dire* urb_u; // urbit system data u3_dire* com_u; // log directory + c3_i lok_i; // lockfile c3_o liv_o; // live c3_w ver_w; // version (see version.h) void* mdb_u; // lmdb env of current epoch @@ -1428,7 +1441,8 @@ u3_noun ven, // boot event u3_noun pil, // type-of/path-to pill u3_noun pax, // path to pier - u3_weak fed); // extra private keys + u3_weak fed, // extra private keys + u3_noun mor); // extra boot seq props /* u3_pier_stay(): restart the pier. */ @@ -1545,6 +1559,13 @@ void u3_daemon_init(); +#if defined(U3_OS_osx) + /* darwin_register_mach_exception_handler(): make lldb work + */ + void + darwin_register_mach_exception_handler(); +#endif + /* u3_write_fd(): retry interrupts, continue partial writes, assert errors. */ void