forked from lei.chen/myxce
feat(config-repl): wire do_one_config_fetch() — full SSL roundtrip
Replaces -ENOSYS stub with: - connect_ssl_raw() helper (non-blocking connect + poll + SSL handshake, checks ha_is_primary() between iterations for early promotion exit) - xce_replconfig_req_ssl() to send OP_replConfig request - xce_replconfig_reply_ssl_decode() to receive and decode reply - SHA-256 integrity verify (retry on mismatch with -EBADMSG) - conf_save_config() atomic disk write to workdir/name/conf/ - load_primary_config() to reload cluster_config from newly saved file Standby can now fetch ClusterConfig from primary on startup.
This commit is contained in:
parent
4ef2089fd4
commit
2aae45f046
1 changed files with 213 additions and 11 deletions
|
|
@ -5,12 +5,17 @@
|
|||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <netdb.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
|
|
@ -666,22 +671,219 @@ int handle_config_request(cluster_t *c, endpoint_t *ep, xce_request_t *req)
|
|||
/* ── fetch_config_from_primary helpers ─────────────────────── */
|
||||
|
||||
/*
|
||||
* do_one_config_fetch: attempt one network fetch of config from the primary.
|
||||
* connect_ssl_raw: open a non-blocking TCP connection to host:port, then
|
||||
* perform a TLS handshake using ssl_ctx.
|
||||
*
|
||||
* The full wire protocol (capnp encode/decode over a standalone SSL socket)
|
||||
* depends on the typemap shims being complete (Task 11). Until then this
|
||||
* function returns -ENOSYS so that fetch_config_from_primary() falls back to
|
||||
* the locally saved config.
|
||||
* The poll loop checks ha_is_primary(ha) between iterations: if this node
|
||||
* is promoted to primary while waiting, the function returns -ECANCELED so
|
||||
* the caller can switch to the primary path immediately.
|
||||
*
|
||||
* On success: *out_ssl and *out_sock are set; caller owns them and must call
|
||||
* SSL_shutdown(ssl) + SSL_free(ssl) + close(sock).
|
||||
* On failure: nothing is left open; returns negative errno.
|
||||
*/
|
||||
static int connect_ssl_raw(SSL_CTX *ssl_ctx, const char *host, uint32_t port,
|
||||
int poll_timeout_ms, const ha_state_t *ha,
|
||||
SSL **out_ssl, int *out_sock)
|
||||
{
|
||||
struct addrinfo hints, *res = NULL;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
char port_str[16];
|
||||
snprintf(port_str, sizeof(port_str), "%u", (unsigned)port);
|
||||
if (getaddrinfo(host, port_str, &hints, &res) != 0)
|
||||
return -ENONET;
|
||||
|
||||
int sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
||||
if (sock < 0) {
|
||||
int e = errno;
|
||||
freeaddrinfo(res);
|
||||
return -e;
|
||||
}
|
||||
|
||||
/* Non-blocking connect */
|
||||
int flags = fcntl(sock, F_GETFL, 0);
|
||||
fcntl(sock, F_SETFL, flags | O_NONBLOCK);
|
||||
connect(sock, res->ai_addr, res->ai_addrlen);
|
||||
freeaddrinfo(res);
|
||||
res = NULL;
|
||||
|
||||
/* Poll loop: check ha_is_primary between iterations */
|
||||
while (!ha_is_primary(ha)) {
|
||||
struct pollfd pfd = { .fd = sock, .events = POLLOUT | POLLERR | POLLHUP };
|
||||
int pr = poll(&pfd, 1, poll_timeout_ms);
|
||||
if (pr < 0 && errno == EINTR)
|
||||
continue;
|
||||
if (pr == 0) {
|
||||
close(sock);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
if (pfd.revents & (POLLERR | POLLHUP)) {
|
||||
close(sock);
|
||||
return -ECONNREFUSED;
|
||||
}
|
||||
if (pfd.revents & POLLOUT)
|
||||
break;
|
||||
}
|
||||
if (ha_is_primary(ha)) {
|
||||
close(sock);
|
||||
return -ECANCELED;
|
||||
}
|
||||
|
||||
/* Verify connect succeeded */
|
||||
{
|
||||
int err = 0;
|
||||
socklen_t el = sizeof(err);
|
||||
getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &el);
|
||||
if (err != 0) {
|
||||
close(sock);
|
||||
return -err;
|
||||
}
|
||||
}
|
||||
|
||||
/* Restore blocking for SSL handshake */
|
||||
fcntl(sock, F_SETFL, flags & ~O_NONBLOCK);
|
||||
|
||||
SSL *ssl = SSL_new(ssl_ctx);
|
||||
if (!ssl) {
|
||||
close(sock);
|
||||
return -ENOMEM;
|
||||
}
|
||||
SSL_set_fd(ssl, sock);
|
||||
|
||||
int sr;
|
||||
do {
|
||||
sr = SSL_connect(ssl);
|
||||
if (sr <= 0) {
|
||||
int e = SSL_get_error(ssl, sr);
|
||||
if (e != SSL_ERROR_WANT_READ && e != SSL_ERROR_WANT_WRITE)
|
||||
break;
|
||||
}
|
||||
} while (sr <= 0 && !ha_is_primary(ha));
|
||||
|
||||
if (sr <= 0 || ha_is_primary(ha)) {
|
||||
SSL_free(ssl);
|
||||
close(sock);
|
||||
return -ECONNRESET;
|
||||
}
|
||||
|
||||
*out_ssl = ssl;
|
||||
*out_sock = sock;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* do_one_config_fetch: attempt one network fetch of the ClusterConfig from
|
||||
* the primary controller over a standalone SSL socket.
|
||||
*
|
||||
* Steps:
|
||||
* 1. Open non-blocking TCP + SSL handshake via connect_ssl_raw()
|
||||
* 2. Send OP_replConfig request with our current version tag
|
||||
* 3. Decode the reply
|
||||
* 4. If up_to_date: nothing to save — return 0
|
||||
* 5. Verify SHA-256 integrity of received bytes
|
||||
* 6. Save to disk via conf_save_config()
|
||||
* 7. Reload cluster_config via load_primary_config()
|
||||
* 8. Log success and clean up
|
||||
*
|
||||
* Returns 0 on success (config up-to-date or freshly saved+loaded).
|
||||
* Returns -ECANCELED if promoted to primary during the attempt (caller should
|
||||
* switch to primary path). Returns other negative errno on transient failure
|
||||
* (caller will retry with backoff).
|
||||
*/
|
||||
static int do_one_config_fetch(cluster_t *c, const char *host, uint32_t port,
|
||||
uint64_t our_tag)
|
||||
{
|
||||
(void)c;
|
||||
(void)host;
|
||||
(void)port;
|
||||
(void)our_tag;
|
||||
/* Task 11: wire full capnp encode/decode over standalone SSL socket */
|
||||
return -ENOSYS;
|
||||
SSL *ssl = NULL;
|
||||
int sock = -1;
|
||||
|
||||
/* 1. Connect + SSL handshake */
|
||||
int rc = connect_ssl_raw(
|
||||
(SSL_CTX *)c->ha.ssl_ctx, host, port,
|
||||
(int)c->ha.config.probe_connect_timeout_ms,
|
||||
&c->ha, &ssl, &sock);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
/* 2. Send OP_replConfig request */
|
||||
rc = xce_replconfig_req_ssl(ssl, our_tag);
|
||||
if (rc != 0) {
|
||||
SSL_shutdown(ssl);
|
||||
SSL_free(ssl);
|
||||
close(sock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* 3. Decode reply */
|
||||
int up_to_date = 0;
|
||||
uint64_t new_tag = 0;
|
||||
uint8_t *sha256 = NULL;
|
||||
uint8_t *config_bytes = NULL;
|
||||
size_t config_len = 0;
|
||||
|
||||
rc = xce_replconfig_reply_ssl_decode(ssl, &up_to_date, &new_tag,
|
||||
&sha256, &config_bytes, &config_len);
|
||||
if (rc != 0) {
|
||||
SSL_shutdown(ssl);
|
||||
SSL_free(ssl);
|
||||
close(sock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* 4. Up-to-date fast path: our tag matches primary — nothing to save */
|
||||
if (up_to_date) {
|
||||
SSL_shutdown(ssl);
|
||||
SSL_free(ssl);
|
||||
close(sock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 5. Verify SHA-256 integrity */
|
||||
uint8_t computed[32];
|
||||
SHA256(config_bytes, config_len, computed);
|
||||
if (memcmp(computed, sha256, 32) != 0) {
|
||||
ERROR("HA: config fetch SHA-256 mismatch from primary — retrying");
|
||||
free(sha256);
|
||||
free(config_bytes);
|
||||
SSL_shutdown(ssl);
|
||||
SSL_free(ssl);
|
||||
close(sock);
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
/* 6. Save to disk atomically */
|
||||
rc = conf_save_config(c->myconf, config_bytes, config_len, sha256);
|
||||
free(sha256);
|
||||
free(config_bytes);
|
||||
sha256 = NULL;
|
||||
config_bytes = NULL;
|
||||
if (rc != 0) {
|
||||
SSL_shutdown(ssl);
|
||||
SSL_free(ssl);
|
||||
close(sock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* 7. Reload cluster_config from the newly saved file */
|
||||
rc = load_primary_config(c);
|
||||
if (rc != 0) {
|
||||
SSL_shutdown(ssl);
|
||||
SSL_free(ssl);
|
||||
close(sock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* 8. Log success */
|
||||
INFO("HA: config received from primary (tag=%016" PRIx64 ")",
|
||||
c->myconf->config_version_tag);
|
||||
|
||||
/* Clean up SSL and socket */
|
||||
SSL_shutdown(ssl);
|
||||
SSL_free(ssl);
|
||||
close(sock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ── fetch_config_from_primary ──────────────────── */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue