From 12e95630b1acf07d40aed071cdfc4a6cc3c5ae24 Mon Sep 17 00:00:00 2001 From: "Else, Someone" Date: Wed, 17 Sep 2025 15:51:59 +0300 Subject: [PATCH] examples,pkgs,pkgs.ch-proxy: init --- default.nix | 57 ++++++++++++ pkgs/ch-proxy/meson.build | 3 + pkgs/ch-proxy/package.nix | 26 ++++++ pkgs/ch-proxy/proxy.c | 176 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 262 insertions(+) create mode 100644 default.nix create mode 100644 pkgs/ch-proxy/meson.build create mode 100644 pkgs/ch-proxy/package.nix create mode 100644 pkgs/ch-proxy/proxy.c diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..6e70d81 --- /dev/null +++ b/default.nix @@ -0,0 +1,57 @@ +let + pkgs = import { }; + inherit (pkgs) lib; + dirToAttrs = + root: patterns: f: + lib.listToAttrs ( + lib.concatMap ( + dirent: + let + fname = dirent.name; + typ = dirent.value; + fpath = root + "/${fname}"; + doMatch = + pat: + let + match = pat fpath fname typ; + value = f match fpath typ; + in + if match == null then [ ] else [ (lib.nameValuePair match value) ]; + in + (lib.take 1 (lib.concatMap (doMatch) patterns)) + ) (lib.attrsToList (builtins.readDir root)) + ); +in +{ + examples = + dirToAttrs ./examples + [ + ( + path: fname: _: + lib.strings.removeSuffix ".nix" fname + ) + ] + ( + name: fpath: _: + import { modules = [ fpath ]; } + ); + pkgs = lib.makeScope pkgs.newScope ( + self: + dirToAttrs ./pkgs + [ + ( + path: fname: _: + lib.strings.removeSuffix ".nix" fname + ) + ] + ( + name: fpath: typ: + if typ == "regular" then + self.callPackage fpath { } + else if typ == "directory" && builtins.pathExists (fpath + "/package.nix") then + self.callPackage (fpath + "/package.nix") { } + else + null + ) + ); +} diff --git a/pkgs/ch-proxy/meson.build b/pkgs/ch-proxy/meson.build new file mode 100644 index 0000000..e3976a2 --- /dev/null +++ b/pkgs/ch-proxy/meson.build @@ -0,0 +1,3 @@ +project('ch-proxy', 'c') + +executable('ch-proxy', 'proxy.c', install: true) diff --git a/pkgs/ch-proxy/package.nix b/pkgs/ch-proxy/package.nix new file mode 100644 index 0000000..ba34db0 --- /dev/null +++ b/pkgs/ch-proxy/package.nix @@ -0,0 +1,26 @@ +{ + lib, + stdenv, + meson, + ninja, +}: + +stdenv.mkDerivation { + pname = "ch-proxy"; + version = "0.0.0"; + nativeBuildInputs = [ + meson + ninja + ]; + src = + let + fs = lib.fileset; + in + fs.toSource { + fileset = fs.unions [ + ./proxy.c + ./meson.build + ]; + root = ./.; + }; +} diff --git a/pkgs/ch-proxy/proxy.c b/pkgs/ch-proxy/proxy.c new file mode 100644 index 0000000..74f4f32 --- /dev/null +++ b/pkgs/ch-proxy/proxy.c @@ -0,0 +1,176 @@ +#include +#include +#include +#include +#include + +#include +#include + +struct msghdr mk_msghdr(); +int ch_connect(const char*, const char*); +ssize_t send_fd(int, int); + +#define _WRITE_CONFIRM(fd, buf, buflen) {if (write((fd), (buf), (buflen)) != (buflen)) { perror("ch-proxy/write/partial write"); exit(EXIT_FAILURE); }} + +void print_usage() { + fprintf(stderr, "%s", + "Usage:\n" + "\tch-proxy uvm/$USER_VM_NAME [PORT]\n" + "\tch-proxy uuvm/$VM_NAME [PORT]\n" + "\tch-proxy vsock-mux%$PATH [PORT]\n"); +} + +char *extract_vsock_mux(const char *host_string) { + const char PREFIX[] = "vsock-mux%"; + const ssize_t PREFIX_LEN = sizeof(PREFIX) - 1; + if (strncmp(host_string, PREFIX, PREFIX_LEN) == 0) { + return strdup(&host_string[PREFIX_LEN]); + } + return NULL; +} + +char *extract_uvm(const char *host_string) { + const char PREFIX[] = "uuvm/"; + const ssize_t PREFIX_LEN = sizeof(PREFIX) - 1; + + const char *home = getenv("HOME") != NULL ? getenv("HOME") : "./."; + + if (strncmp(host_string, PREFIX, PREFIX_LEN) != 0) { + return NULL; + } + + char *result; + if (asprintf(&result, "%s/uvms/%s/CONNECT.sock", home, &host_string[PREFIX_LEN]) == -1) { + perror("ch-proxy/extract_uvm"); + exit(EXIT_FAILURE); + } + + return result; +} + +char *extract_muvm(const char *host_string) { + const char PREFIX[] = "uvm/"; + const ssize_t PREFIX_LEN = sizeof(PREFIX) - 1; + + + if (strncmp(host_string, PREFIX, PREFIX_LEN) != 0) { + return NULL; + } + + char *result; + if (asprintf(&result, "/var/lib/microvms/%s/CONNECT.sock", &host_string[PREFIX_LEN]) == -1) { + perror("ch-proxy/extract_muvm"); + exit(EXIT_FAILURE); + } + + return result; +} + +int main(int argc, char** argv) { + if (!(2 <= argc && argc <= 3)) { + fprintf(stderr, "%s: Wrong # of arguments: %d\n", argv[0], argc); + print_usage(); + return 1; + } + + const char PORT_DEFAULT[] = "22"; + + const char *ssh_host = argv[1]; + const char *port_string = argc == 3 ? argv[2] : PORT_DEFAULT; + + char *path_un; + if ((path_un = extract_uvm(ssh_host)) != NULL) { + } else if ((path_un = extract_muvm(ssh_host)) != NULL) { + } else if ((path_un = extract_vsock_mux(ssh_host)) != NULL) { + } else { + fprintf(stderr, "ch-proxy/main: unexpected host stirng format: %s\n", ssh_host); + print_usage(); + return EXIT_FAILURE; + } + + const int s = ch_connect(path_un, port_string); + if (s == -1) { + perror("ssh-vsock-proxy/main/ch_connect"); + return EXIT_FAILURE; + }; + + if (send_fd(1, s) == -1) { + perror("ssh-vsock-proxy/main/send_fd"); + return EXIT_FAILURE; + } + return 0; +} + +struct msghdr mk_msghdr() { + struct msghdr msg; + memset(&msg, 0, sizeof(msg)); + + return msg; +} + +int ch_connect(const char *path, const char *port) { + int s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s == -1) { + perror("ch-proxy/ch_connect/bind"); + return -1; + } + + struct sockaddr_un a; + a.sun_family = AF_UNIX; + strcpy(a.sun_path, path); + + if (connect(s, (struct sockaddr*) &a, sizeof(a)) == -1) { + char *err_msg; + if (asprintf(&err_msg, "ch-proxy/ch_connect/connect(\"%s\")", path) == -1) { + perror("ch-proxy/ch_connect/while printing connect() error"); + exit(EXIT_FAILURE); + } + perror(err_msg); + exit(EXIT_FAILURE); + } + + const char CMD[] = "CONNECT "; + const int CMD_LEN = sizeof(CMD) - 1; + + _WRITE_CONFIRM(s, CMD, CMD_LEN); + _WRITE_CONFIRM(s, port, strlen(port)); + _WRITE_CONFIRM(s, "\n", 1); + + return s; +} + +ssize_t send_fd(int dst_fd, int fd) { + struct msghdr msg = mk_msghdr(); + + /* openssh expects to receive a dummy length=1 iovec? */ + char ch; + struct iovec vec; + vec.iov_base = &ch; + vec.iov_len = 1; + msg.msg_iov = &vec; + msg.msg_iovlen = 1; + + union { + struct cmsghdr align; + char buf[CMSG_SPACE(sizeof(int))]; + } u; + + msg.msg_control = u.buf; + msg.msg_controllen = sizeof(u.buf); + + struct cmsghdr *cmptr; + cmptr = CMSG_FIRSTHDR(&msg); + + if (cmptr == NULL) { + fprintf(stderr, "ch-proxy/send_fd/CMSG_FIRSTHDR: failed to initialize msg_control\n"); + exit(EXIT_FAILURE); + } + + cmptr->cmsg_len = CMSG_LEN(sizeof(int)); + cmptr->cmsg_level = SOL_SOCKET; + cmptr->cmsg_type = SCM_RIGHTS; + *((int*) CMSG_DATA(cmptr)) = fd; + + return (sendmsg(dst_fd, &msg, 0)); +}