diff options
| author | Anders Betts <anders.betts@gmail.com> | 2026-09-17 21:26:20 +0200 |
|---|---|---|
| committer | Anders Betts <anders.betts@gmail.com> | 2026-09-17 21:26:20 +0200 |
| commit | ed1c15929d2eb2dbc6432986c26661bf1549964a (patch) | |
| tree | 4f04934edef686b19d16e1bb2f79bb5c15e142de /clients/bokfctl.c | |
| parent | 380195f7cd5e57acf2c1cf2bc41069e6b0b979ed (diff) | |
| download | bokf-d8175a8f76bc5eefa2524f2b245cb8a5b45864ba.tar.gz bokf-d8175a8f76bc5eefa2524f2b245cb8a5b45864ba.zip | |
Add native TLS transport, TLS clients and lego cert sidecarv0.1.1
- bokfd: optional TLS listener (OpenSSL), certificate reload on change
- clients: tls:host:port targets with chain and host verification
- compose: port 8788 and an INWX/lego renewal sidecar
- Makefile: header dependency tracking (-MMD -MP)
Diffstat (limited to 'clients/bokfctl.c')
| -rw-r--r-- | clients/bokfctl.c | 236 |
1 files changed, 32 insertions, 204 deletions
diff --git a/clients/bokfctl.c b/clients/bokfctl.c index c541fec..1f1372c 100644 --- a/clients/bokfctl.c +++ b/clients/bokfctl.c @@ -1,168 +1,19 @@ -#include <errno.h> -#include <netdb.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sys/socket.h> -#include <sys/un.h> -#include <unistd.h> +#include "client.h" #include "util.h" #include "version.h" #include "yyjson.h" -static ssize_t write_all(int fd, const char *buf, size_t len) -{ - size_t off = 0; - while (off < len) { - ssize_t w = write(fd, buf + off, len - off); - if (w < 0) { - if (errno == EINTR) - continue; - return -1; - } - off += (size_t)w; - } - return (ssize_t)off; -} - -static char *read_line_fd(int fd) -{ - struct buf b; - buf_init(&b); - char chunk[4096]; - for (;;) { - ssize_t r = read(fd, chunk, sizeof chunk); - if (r < 0) { - if (errno == EINTR) - continue; - buf_free(&b); - return NULL; - } - if (r == 0) - break; - unsigned char *nl = memchr(chunk, '\n', (size_t)r); - if (nl) { - buf_append(&b, chunk, (size_t)(nl - (unsigned char *)chunk)); - break; - } - buf_append(&b, chunk, (size_t)r); - } - char *out = xmalloc(b.len + 1); - memcpy(out, b.p ? (char *)b.p : "", b.len); - out[b.len] = '\0'; - buf_free(&b); - return out; -} - -static int tcp_connect_addr(const char *addrport) -{ - char host[256] = "127.0.0.1"; - char port[16] = "8787"; - const char *colon = strrchr(addrport, ':'); - if (colon) { - size_t hl = (size_t)(colon - addrport); - if (hl < sizeof host) { - memcpy(host, addrport, hl); - host[hl] = '\0'; - } - snprintf(port, sizeof port, "%s", colon + 1); - } else { - snprintf(port, sizeof port, "%s", addrport); - } - struct addrinfo hints, *res = NULL; - memset(&hints, 0, sizeof hints); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - if (getaddrinfo(host, port, &hints, &res) != 0) - return -1; - int fd = -1; - for (struct addrinfo *ai = res; ai; ai = ai->ai_next) { - fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); - if (fd < 0) - continue; - if (connect(fd, ai->ai_addr, ai->ai_addrlen) == 0) - break; - close(fd); - fd = -1; - } - freeaddrinfo(res); - return fd; -} - -static int connect_target(const char *target) -{ - if (strncmp(target, "tcp:", 4) == 0) - return tcp_connect_addr(target + 4); - struct sockaddr_un sa; - memset(&sa, 0, sizeof sa); - sa.sun_family = AF_UNIX; - if (strlen(target) >= sizeof sa.sun_path) { - errno = ENAMETOOLONG; - return -1; - } - snprintf(sa.sun_path, sizeof sa.sun_path, "%s", target); - int fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (fd < 0) - return -1; - if (connect(fd, (struct sockaddr *)&sa, sizeof sa) != 0) { - close(fd); - return -1; - } - return fd; -} - -static char *make_request(const char *cmd, const char *session, int64_t org, - const char *args_json, const char *id) -{ - yyjson_doc *adoc = NULL; - if (args_json) { - adoc = yyjson_read(args_json, strlen(args_json), 0); - if (!adoc || !yyjson_is_obj(yyjson_doc_get_root(adoc))) { - yyjson_doc_free(adoc); - return NULL; - } - } - yyjson_mut_doc *d = yyjson_mut_doc_new(NULL); - yyjson_mut_val *o = yyjson_mut_obj(d); - yyjson_mut_doc_set_root(d, o); - yyjson_mut_obj_add_int(d, o, "v", 1); - yyjson_mut_obj_add_str(d, o, "id", id ? id : "cli"); - yyjson_mut_obj_add_str(d, o, "cmd", cmd); - if (session) - yyjson_mut_obj_add_str(d, o, "session", session); - if (org > 0) - yyjson_mut_obj_add_int(d, o, "org", org); - if (adoc) { - yyjson_mut_val *args = yyjson_val_mut_copy(d, yyjson_doc_get_root(adoc)); - yyjson_mut_obj_add_val(d, o, "args", args); - yyjson_doc_free(adoc); - } - char *s = yyjson_mut_write(d, 0, NULL); - yyjson_mut_doc_free(d); - return s; -} - -static char *make_login_args(const char *user, const char *password) -{ - yyjson_mut_doc *d = yyjson_mut_doc_new(NULL); - yyjson_mut_val *o = yyjson_mut_obj(d); - yyjson_mut_doc_set_root(d, o); - yyjson_mut_obj_add_str(d, o, "method", "password"); - yyjson_mut_obj_add_str(d, o, "username", user); - yyjson_mut_obj_add_str(d, o, "password", password); - char *s = yyjson_mut_write(d, 0, NULL); - yyjson_mut_doc_free(d); - return s; -} - static void usage(void) { fprintf(stderr, "usage: bokfctl [options] <cmd> [args-json]\n" "\n" "options:\n" - " --socket TARGET unix socket path or tcp:host:port\n" + " --socket TARGET unix path, tcp:host:port or tls:host:port\n" " (env BOKFD_SOCKET, default /run/bokfd/bokfd.sock)\n" " --user NAME login user (env BOKFD_USER)\n" " --password PW login password (env BOKFD_PASSWORD)\n" @@ -175,6 +26,12 @@ static void usage(void) " bokfctl describe\n"); } +static int is_local_cmd(const char *cmd) +{ + return strcmp(cmd, "health") == 0 || strcmp(cmd, "meta") == 0 || + strcmp(cmd, "session.open") == 0; +} + int main(int argc, char **argv) { const char *target = getenv("BOKFD_SOCKET"); @@ -245,90 +102,61 @@ int main(int argc, char **argv) return 2; } - int fd = connect_target(target); - if (fd < 0) { + struct client_conn conn; + if (client_connect(target, &conn) != 0) { fprintf(stderr, "bokfctl: cannot connect to %s: %s\n", target, - strerror(errno)); + client_last_error()); return 2; } - char session[128] = ""; - if (strcmp(cmd, "health") != 0 && strcmp(cmd, "meta") != 0 && - strcmp(cmd, "session.open") != 0) { + char *session = NULL; + if (!is_local_cmd(cmd)) { if (!user || !password) { fprintf(stderr, "bokfctl: set BOKFD_USER and BOKFD_PASSWORD (or --user/--password) to log in\n"); - close(fd); + client_close(&conn); return 2; } - char *largs = make_login_args(user, password); - char *lreq = make_request("session.open", NULL, 0, largs, "login"); - free(largs); - if (!lreq || write_all(fd, lreq, strlen(lreq)) < 0 || - write_all(fd, "\n", 1) < 0) { - fprintf(stderr, "bokfctl: send failed\n"); - free(lreq); - close(fd); - return 2; - } - free(lreq); - char *lresp = read_line_fd(fd); - if (!lresp) { - fprintf(stderr, "bokfctl: no response\n"); - close(fd); - return 2; - } - yyjson_doc *ld = yyjson_read(lresp, strlen(lresp), 0); - int ok = ld && yyjson_is_obj(yyjson_doc_get_root(ld)) && - yyjson_get_bool(yyjson_obj_get(yyjson_doc_get_root(ld), "ok")); - const char *sid = NULL; - if (ok) { - yyjson_val *r = yyjson_obj_get(yyjson_doc_get_root(ld), "result"); - yyjson_val *s = r ? yyjson_obj_get(r, "session") : NULL; - if (s && yyjson_is_str(s)) - sid = yyjson_get_str(s); - } - if (!ok || !sid) { - fprintf(stderr, "%s\n", lresp); - yyjson_doc_free(ld); - free(lresp); - close(fd); - return 1; + char *lerr = NULL; + if (client_login(&conn, user, password, &session, &lerr) != 0) { + fprintf(stderr, "%s\n", lerr ? lerr : "login failed"); + int rc = lerr && lerr[0] == '{' ? 1 : 2; + free(lerr); + client_close(&conn); + return rc; } - snprintf(session, sizeof session, "%s", sid); - yyjson_doc_free(ld); - free(lresp); } char *req = NULL; if (strcmp(cmd, "raw") == 0) { if (!args_json) { fprintf(stderr, "bokfctl: raw requires a full request JSON\n"); - close(fd); + client_close(&conn); return 2; } req = xstrdup(args_json); } else { - req = make_request(cmd, session[0] ? session : NULL, org, args_json, - "cli"); + req = client_make_request(cmd, session, org, args_json, "cli"); if (!req) { fprintf(stderr, "bokfctl: args must be a JSON object\n"); - close(fd); + free(session); + client_close(&conn); return 2; } } - if (write_all(fd, req, strlen(req)) < 0 || write_all(fd, "\n", 1) < 0) { - fprintf(stderr, "bokfctl: send failed\n"); + free(session); + if (client_send_line(&conn, req) != 0) { + fprintf(stderr, "bokfctl: send failed: %s\n", client_last_error()); free(req); - close(fd); + client_close(&conn); return 2; } free(req); - char *resp = read_line_fd(fd); - close(fd); + char *resp = client_read_line(&conn); + client_close(&conn); if (!resp) { - fprintf(stderr, "bokfctl: no response\n"); + fprintf(stderr, "bokfctl: no response: %s\n", client_last_error()); return 2; } yyjson_doc *rd = yyjson_read(resp, strlen(resp), 0); |
