diff --git a/Makefile b/Makefile index b72189e98..064fed3af 100644 --- a/Makefile +++ b/Makefile @@ -643,7 +643,7 @@ ifneq ($(USE_OPENSSL:0=),) OPTIONS_OBJS += src/ssl_sock.o src/ssl_ckch.o src/ssl_ocsp.o src/ssl_crtlist.o \ src/ssl_sample.o src/cfgparse-ssl.o src/ssl_gencert.o \ src/ssl_utils.o src/jwt.o src/ssl_clienthello.o src/jws.o src/acme.o \ - src/ssl_trace.o src/jwe.o + src/acme_resolvers.o src/ssl_trace.o src/jwe.o endif ifneq ($(USE_ENGINE:0=),) diff --git a/include/haproxy/acme-t.h b/include/haproxy/acme-t.h index e93b617c7..8e828904e 100644 --- a/include/haproxy/acme-t.h +++ b/include/haproxy/acme-t.h @@ -2,9 +2,12 @@ #ifndef _ACME_T_H_ #define _ACME_T_H_ +#include #include #include +#if defined(HAVE_ACME) + #define ACME_RETRY 5 /* acme section configuration */ @@ -13,6 +16,8 @@ struct acme_cfg { int linenum; /* config linenum */ char *name; /* section name */ int reuse_key; /* do we need to renew the private key */ + int dns_check; /* enable DNS resolution to verify TXT record before challenge */ + unsigned int dns_delay; /* delay in seconds before re-triggering DNS resolution (default: 300) */ char *directory; /* directory URL */ char *map; /* storage for tokens + thumbprint */ struct { @@ -40,6 +45,9 @@ enum acme_st { ACME_NEWACCOUNT, ACME_NEWORDER, ACME_AUTH, + ACME_RSLV_WAIT, + ACME_RSLV_TRIGGER, + ACME_RSLV_READY, ACME_CHALLENGE, ACME_CHKCHALLENGE, ACME_FINALIZE, @@ -59,6 +67,7 @@ struct acme_auth { struct ist chall; /* challenge URI */ struct ist token; /* token */ int validated; /* already validated */ + struct acme_rslv *rslv; /* acme dns-01 resolver */ int ready; /* is the challenge ready ? */ void *next; }; @@ -85,6 +94,7 @@ struct acme_ctx { X509_REQ *req; struct ist finalize; struct ist certificate; + unsigned int dnstasks; /* number of DNS tasks running for this ctx */ struct task *task; struct ebmb_node node; char name[VAR_ARRAY]; @@ -102,4 +112,6 @@ struct acme_ctx { #define ACME_VERB_ADVANCED 4 #define ACME_VERB_COMPLETE 5 +#endif /* ! HAVE_ACME */ + #endif diff --git a/include/haproxy/acme_resolvers-t.h b/include/haproxy/acme_resolvers-t.h new file mode 100644 index 000000000..dde061210 --- /dev/null +++ b/include/haproxy/acme_resolvers-t.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#ifndef _HAPROXY_ACME_RESOLVERS_T_H +#define _HAPROXY_ACME_RESOLVERS_T_H + +#include +#include + +struct dns_counters; + +/* TXT records for dns-01 */ + +struct acme_rslv { + enum obj_type obj_type; /* OBJ_TYPE_ACME_RSLV */ + unsigned int *dnstasks; /* number of running DNS resolution for the same acme_task */ + char *hostname_dn; + int hostname_dn_len; + struct resolvers *resolvers; + struct resolv_requester *requester; + int result; /* RSLV_STATUS_* — NONE until done */ + int error_code; /* RSLV_RESP_* from the error callback */ + struct task *acme_task; /* ACME task to wake on completion, or NULL */ + struct ist txt; /* first TXT record found */ + int (*success_cb)(struct resolv_requester *, struct dns_counters *); + int (*error_cb)(struct resolv_requester *, int); +}; + +#endif /* _HAPROXY_ACME_RESOLVERS_T_H */ diff --git a/include/haproxy/acme_resolvers.h b/include/haproxy/acme_resolvers.h new file mode 100644 index 000000000..fb3d7dff5 --- /dev/null +++ b/include/haproxy/acme_resolvers.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#ifndef _HAPROXY_ACME_RESOLVERS_H +#define _HAPROXY_ACME_RESOLVERS_H + +#include + +#if defined(HAVE_ACME) + +#include +#include +#include + +struct acme_rslv *acme_rslv_start(struct acme_auth *auth, unsigned int *dnstasks, char **errmsg); +void acme_rslv_free(struct acme_rslv *rslv); + +#endif + +#endif /* _HAPROXY_ACME_RESOLVERS_H */ diff --git a/include/haproxy/obj_type-t.h b/include/haproxy/obj_type-t.h index fd232b347..f57fbf01a 100644 --- a/include/haproxy/obj_type-t.h +++ b/include/haproxy/obj_type-t.h @@ -47,6 +47,7 @@ enum obj_type { OBJ_TYPE_DGRAM, /* object is a struct quic_dgram */ #endif OBJ_TYPE_HATERM, /* object is a struct hstream */ + OBJ_TYPE_ACME_RSLV, /* object is a struct acme_rslv */ OBJ_TYPE_ENTRIES /* last one : number of entries */ } __attribute__((packed)) ; diff --git a/include/haproxy/obj_type.h b/include/haproxy/obj_type.h index bd850b969..d10a32969 100644 --- a/include/haproxy/obj_type.h +++ b/include/haproxy/obj_type.h @@ -22,6 +22,7 @@ #ifndef _HAPROXY_OBJ_TYPE_H #define _HAPROXY_OBJ_TYPE_H +#include #include #include #include @@ -45,17 +46,18 @@ static inline enum obj_type obj_type(const enum obj_type *t) static inline const char *obj_type_name(const enum obj_type *t) { switch (obj_type(t)) { - case OBJ_TYPE_NONE: return "NONE"; - case OBJ_TYPE_LISTENER: return "LISTENER"; - case OBJ_TYPE_PROXY: return "PROXY"; - case OBJ_TYPE_SERVER: return "SERVER"; - case OBJ_TYPE_APPLET: return "APPLET"; - case OBJ_TYPE_APPCTX: return "APPCTX"; - case OBJ_TYPE_CONN: return "CONN"; - case OBJ_TYPE_SRVRQ: return "SRVRQ"; - case OBJ_TYPE_SC: return "SC"; - case OBJ_TYPE_STREAM: return "STREAM"; - case OBJ_TYPE_CHECK: return "CHECK"; + case OBJ_TYPE_NONE: return "NONE"; + case OBJ_TYPE_LISTENER: return "LISTENER"; + case OBJ_TYPE_PROXY: return "PROXY"; + case OBJ_TYPE_SERVER: return "SERVER"; + case OBJ_TYPE_APPLET: return "APPLET"; + case OBJ_TYPE_APPCTX: return "APPCTX"; + case OBJ_TYPE_CONN: return "CONN"; + case OBJ_TYPE_SRVRQ: return "SRVRQ"; + case OBJ_TYPE_SC: return "SC"; + case OBJ_TYPE_STREAM: return "STREAM"; + case OBJ_TYPE_CHECK: return "CHECK"; + case OBJ_TYPE_ACME_RSLV: return "ACME_RSLV"; #ifdef USE_QUIC case OBJ_TYPE_DGRAM: return "DGRAM"; #endif @@ -203,6 +205,18 @@ static inline struct hstream *objt_hstream(enum obj_type *t) return __objt_hstream(t); } +static inline struct acme_rslv *__objt_acme_rslv(enum obj_type *t) +{ + return container_of(t, struct acme_rslv, obj_type); +} + +static inline struct acme_rslv *objt_acme_rslv(enum obj_type *t) +{ + if (!t || *t != OBJ_TYPE_ACME_RSLV) + return NULL; + return __objt_acme_rslv(t); +} + #ifdef USE_QUIC static inline struct quic_dgram *__objt_dgram(enum obj_type *t) { @@ -220,17 +234,18 @@ static inline struct quic_dgram *objt_dgram(enum obj_type *t) static inline void *obj_base_ptr(enum obj_type *t) { switch (obj_type(t)) { - case OBJ_TYPE_NONE: return NULL; - case OBJ_TYPE_LISTENER: return __objt_listener(t); - case OBJ_TYPE_PROXY: return __objt_proxy(t); - case OBJ_TYPE_SERVER: return __objt_server(t); - case OBJ_TYPE_APPLET: return __objt_applet(t); - case OBJ_TYPE_APPCTX: return __objt_appctx(t); - case OBJ_TYPE_CONN: return __objt_conn(t); - case OBJ_TYPE_SRVRQ: return __objt_resolv_srvrq(t); - case OBJ_TYPE_SC: return __objt_sc(t); - case OBJ_TYPE_STREAM: return __objt_stream(t); - case OBJ_TYPE_CHECK: return __objt_check(t); + case OBJ_TYPE_NONE: return NULL; + case OBJ_TYPE_LISTENER: return __objt_listener(t); + case OBJ_TYPE_PROXY: return __objt_proxy(t); + case OBJ_TYPE_SERVER: return __objt_server(t); + case OBJ_TYPE_APPLET: return __objt_applet(t); + case OBJ_TYPE_APPCTX: return __objt_appctx(t); + case OBJ_TYPE_CONN: return __objt_conn(t); + case OBJ_TYPE_SRVRQ: return __objt_resolv_srvrq(t); + case OBJ_TYPE_SC: return __objt_sc(t); + case OBJ_TYPE_STREAM: return __objt_stream(t); + case OBJ_TYPE_CHECK: return __objt_check(t); + case OBJ_TYPE_ACME_RSLV: return __objt_acme_rslv(t); #ifdef USE_QUIC case OBJ_TYPE_DGRAM: return __objt_dgram(t); #endif diff --git a/src/acme.c b/src/acme.c index 3d762af48..a21c326a0 100644 --- a/src/acme.c +++ b/src/acme.c @@ -4,6 +4,7 @@ * Implements the ACMEv2 RFC 8555 protocol */ +#include "haproxy/ticks.h" #include #include #include @@ -14,6 +15,7 @@ #include +#include #include #include #include @@ -24,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -118,6 +121,9 @@ static void acme_trace(enum trace_level level, uint64_t mask, const struct trace case ACME_NEWACCOUNT: chunk_appendf(&trace_buf, "ACME_NEWACCOUNT"); break; case ACME_NEWORDER: chunk_appendf(&trace_buf, "ACME_NEWORDER"); break; case ACME_AUTH: chunk_appendf(&trace_buf, "ACME_AUTH"); break; + case ACME_RSLV_WAIT: chunk_appendf(&trace_buf, "ACME_RSLV_WAIT"); break; + case ACME_RSLV_TRIGGER: chunk_appendf(&trace_buf, "ACME_RSLV_TRIGGER"); break; + case ACME_RSLV_READY: chunk_appendf(&trace_buf, "ACME_RSLV_READY"); break; case ACME_CHALLENGE: chunk_appendf(&trace_buf, "ACME_CHALLENGE"); break; case ACME_CHKCHALLENGE: chunk_appendf(&trace_buf, "ACME_CHKCHALLENGE"); break; case ACME_FINALIZE: chunk_appendf(&trace_buf, "ACME_FINALIZE"); break; @@ -191,6 +197,7 @@ struct acme_cfg *new_acme_cfg(const char *name) ret->linenum = 0; ret->challenge = strdup("http-01"); /* default value */ + ret->dns_delay = 300; /* default DNS re-trigger delay in seconds */ /* The default generated keys are EC-384 */ ret->key.type = EVP_PKEY_EC; @@ -437,6 +444,49 @@ static int cfg_parse_acme_kws(char **args, int section_type, struct proxy *curpx ha_alert("parsing [%s:%d]: out of memory.\n", file, linenum); goto out; } + } else if (strcmp(args[0], "dns-check") == 0) { + if (!*args[1]) { + ha_alert("parsing [%s:%d]: keyword '%s' in '%s' section requires an argument\n", file, linenum, args[0], cursection); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + if (alertif_too_many_args(1, file, linenum, args, &err_code)) + goto out; + + if (strcmp(args[1], "on") == 0) { + cur_acme->dns_check = 1; + } else if (strcmp(args[1], "off") == 0) { + cur_acme->dns_check = 0; + } else { + err_code |= ERR_ALERT | ERR_FATAL; + ha_alert("parsing [%s:%d]: keyword '%s' in '%s' section requires either the 'on' or 'off' parameter\n", file, linenum, args[0], cursection); + goto out; + } + } else if (strcmp(args[0], "dns-delay") == 0) { + const char *res; + + if (!*args[1]) { + ha_alert("parsing [%s:%d]: keyword '%s' in '%s' section requires an argument\n", file, linenum, args[0], cursection); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + if (alertif_too_many_args(1, file, linenum, args, &err_code)) + goto out; + + res = parse_time_err(args[1], &cur_acme->dns_delay, TIME_UNIT_S); + if (res == PARSE_TIME_OVER) { + ha_alert("parsing [%s:%d]: timer overflow in argument <%s> to '%s'\n", file, linenum, args[1], args[0]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } else if (res == PARSE_TIME_UNDER) { + ha_alert("parsing [%s:%d]: timer underflow in argument <%s> to '%s'\n", file, linenum, args[1], args[0]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } else if (res) { + ha_alert("parsing [%s:%d]: unexpected character '%c' in argument to '%s'\n", file, linenum, *res, args[0]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } } else if (strcmp(args[0], "reuse-key") == 0) { if (!*args[1]) { ha_alert("parsing [%s:%d]: keyword '%s' in '%s' section requires an argument\n", file, linenum, args[0], cursection); @@ -841,6 +891,8 @@ static struct cfg_kw_list cfg_kws_acme = {ILH, { { CFG_ACME, "curves", cfg_parse_acme_cfg_key }, { CFG_ACME, "map", cfg_parse_acme_kws }, { CFG_ACME, "reuse-key", cfg_parse_acme_kws }, + { CFG_ACME, "dns-check", cfg_parse_acme_kws }, + { CFG_ACME, "dns-delay", cfg_parse_acme_kws }, { CFG_ACME, "acme-vars", cfg_parse_acme_vars_provider }, { CFG_ACME, "provider-name", cfg_parse_acme_vars_provider }, { CFG_GLOBAL, "acme.scheduler", cfg_parse_global_acme_sched }, @@ -879,6 +931,7 @@ static void acme_ctx_destroy(struct acme_ctx *ctx) istfree(&auth->chall); istfree(&auth->token); istfree(&auth->dns); + acme_rslv_free(auth->rslv); next = auth->next; free(auth); auth = next; @@ -2272,13 +2325,115 @@ re: goto retry; } if ((ctx->next_auth = ctx->next_auth->next) == NULL) { - st = ACME_CHALLENGE; + if (strcasecmp(ctx->cfg->challenge, "dns-01") == 0 && ctx->cfg->dns_check) + st = ACME_RSLV_WAIT; + else + st = ACME_CHALLENGE; ctx->next_auth = ctx->auths; } /* call with next auth or do the challenge step */ goto nextreq; } break; + case ACME_RSLV_WAIT: { + /* wait dns-delay */ + st = ACME_RSLV_TRIGGER; + ctx->http_state = ACME_HTTP_REQ; + ctx->state = st; + send_log(NULL, LOG_NOTICE, "acme: %s: dns-01: triggering the resolution in %ds\n", + ctx->store->path, ctx->cfg->dns_delay); + + task->expire = tick_add(now_ms, ctx->cfg->dns_delay * 1000); + return task; + } + break; + case ACME_RSLV_TRIGGER: { + struct acme_auth *auth; + + /* if it was trigger by the CLI, still wait dns_delay if + * not everything is ready, or skip and to to + * ACME_CHALLENGE */ + if (!(state & TASK_WOKEN_TIMER)) { + int all_ready = 1; + + for (auth = ctx->auths; auth != NULL; auth = auth->next) { + if (auth->ready) + continue; + all_ready = 0; + } + if (all_ready) { + st = ACME_CHALLENGE; + ctx->http_state = ACME_HTTP_REQ; + ctx->state = st; + goto nextreq; + } else { + return task; + } + } + + /* on timer expiry, re-trigger resolution for non-ready auths */ + for (auth = ctx->auths; auth != NULL; auth = auth->next) { + if (auth->ready) + continue; + + HA_ATOMIC_INC(&ctx->dnstasks); + + auth->rslv = acme_rslv_start(auth, &ctx->dnstasks, &errmsg); + if (!auth->rslv) + goto abort; + auth->rslv->acme_task = task; + } + st = ACME_RSLV_READY; + goto wait; + } + break; + case ACME_RSLV_READY: { + struct acme_auth *auth; + int all_ready = 1; + + /* if triggered by the CLI, wait for the DNS tasks to + * finish + */ + if (HA_ATOMIC_LOAD(&ctx->dnstasks) != 0) + goto wait; + + /* triggered by the latest DNS task */ + for (auth = ctx->auths; auth != NULL; auth = auth->next) { + if (auth->ready) + continue; + if (auth->rslv->result != RSLV_STATUS_VALID) { + send_log(NULL, LOG_NOTICE, "acme: %s: dns-01: Couldn't get the TXT record for \"_acme-challenge.%.*s\", expected \"%.*s\" (status=%d)\n", + ctx->store->path, (int)auth->dns.len, auth->dns.ptr, + (int)auth->token.len, auth->token.ptr, + auth->rslv->result); + all_ready = 0; + } else { + if (isteq(auth->rslv->txt, auth->token)) { + auth->ready = 1; + } else { + send_log(NULL, LOG_NOTICE, "acme: %s: dns-01: TXT record mismatch for \"_acme-challenge.%.*s\": expected \"%.*s\", got \"%.*s\"\n", + ctx->store->path, (int)auth->dns.len, auth->dns.ptr, + (int)auth->token.len, auth->token.ptr, + (int)auth->rslv->txt.len, auth->rslv->txt.ptr); + all_ready = 0; + } + } + acme_rslv_free(auth->rslv); + auth->rslv = NULL; + } + if (all_ready) { + st = ACME_CHALLENGE; + ctx->next_auth = ctx->auths; + goto nextreq; + } + + /* not all ready yet, retry after dns-delay */ + st = ACME_RSLV_WAIT; + ctx->http_state = ACME_HTTP_REQ; + ctx->state = st; + goto nextreq; + } + break; case ACME_CHALLENGE: if (http_st == ACME_HTTP_REQ) { /* if challenge is already validated we skip this stage */ diff --git a/src/acme_resolvers.c b/src/acme_resolvers.c new file mode 100644 index 000000000..65d03a36f --- /dev/null +++ b/src/acme_resolvers.c @@ -0,0 +1,166 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* + * Implements the DNS resolution pre-check for dns-01 + */ + +#include + +#if defined(HAVE_ACME) + +#include +#include + + +#include +#include +#include +#include +#include + +/* success callback, copy the TXT string to rslv->txt */ +static int acme_rslv_success_cb(struct resolv_requester *req, struct dns_counters *counters) +{ + struct acme_rslv *rslv = objt_acme_rslv(req->owner); + struct resolv_resolution *res; + struct eb32_node *eb32; + struct resolv_answer_item *item; + + if (!rslv) + return 1; + + rslv->result = RSLV_STATUS_INVALID; + + res = req->resolution; + if (!res) + goto done; + + /* XXX: must fail on multiple TXT entries for the same dn */ + + /* copy the data from the response tree */ + for (eb32 = eb32_first(&res->response.answer_tree); eb32 != NULL; eb32 = eb32_next(eb32)) { + item = eb32_entry(eb32, typeof(*item), link); + /* only handle 1 entry */ + if (item->type == DNS_RTYPE_TXT) { + int len = item->data_len; + + if (len > DNS_MAX_NAME_SIZE) + len = DNS_MAX_NAME_SIZE; + rslv->txt = istdup(ist2(item->data.target, len)); + break; + } + } + + rslv->result = RSLV_STATUS_VALID; +done: + /* if there's no other DNS task for this acme task, wake up acme_task */ + if (HA_ATOMIC_SUB_FETCH(rslv->dnstasks, 1) == 0) { + if (rslv->acme_task) + task_wakeup(rslv->acme_task, TASK_WOKEN_MSG); + } + return 1; +} + +/* error callback, set the error code to rslv->result */ +static int acme_rslv_error_cb(struct resolv_requester *req, int error_code) +{ + struct acme_rslv *rslv = objt_acme_rslv(req->owner); + + if (!rslv) + return 0; + + rslv->result = error_code; + if (HA_ATOMIC_SUB_FETCH(rslv->dnstasks, 1) == 0) { + if (rslv->acme_task) + task_wakeup(rslv->acme_task, TASK_WOKEN_MSG); + } + + return 0; +} + +/* unlink from the resolver and free the acme_rslv */ +void acme_rslv_free(struct acme_rslv *rslv) +{ + if (!rslv) + return; + if (rslv->requester) + resolv_unlink_resolution(rslv->requester); + free(rslv->hostname_dn); + istfree(&rslv->txt); + free(rslv); +} + +struct acme_rslv *acme_rslv_start(struct acme_auth *auth, unsigned int *dnstasks, char **errmsg) +{ + struct acme_rslv *rslv = NULL; + struct resolvers *resolvers; + char hostname[DNS_MAX_NAME_SIZE + 1]; + char dn[DNS_MAX_NAME_SIZE + 1]; + int hostname_len; + int dn_len; + + /* XXX: allow to change the resolvers section to use */ + resolvers = find_resolvers_by_id("default"); + if (!resolvers) { + memprintf(errmsg, "couldn't find the \"default\" resolvers section!\n"); + goto error; + } + + /* dns-01 TXT record lives at _acme-challenge. */ + hostname_len = snprintf(hostname, sizeof(hostname), "_acme-challenge.%.*s", + (int)auth->dns.len, auth->dns.ptr); + if (hostname_len < 0 || hostname_len >= (int)sizeof(hostname)) { + memprintf(errmsg, "hostname \"_acme-challenge.%.*s\" too long!\n", (int)auth->dns.len, auth->dns.ptr); + goto error; + } + + dn_len = resolv_str_to_dn_label(hostname, hostname_len, dn, sizeof(dn)); + if (dn_len <= 0) { + memprintf(errmsg, "couldn't convert hostname \"_acme-challenge.%.*s\" into dn label\n", (int)auth->dns.len, auth->dns.ptr); + goto error; + } + + rslv = calloc(1, sizeof(*rslv)); + if (!rslv) { + memprintf(errmsg, "Could not allocate memory\n"); + goto error; + } + + rslv->obj_type = OBJ_TYPE_ACME_RSLV; + rslv->resolvers = resolvers; + rslv->hostname_dn = strdup(dn); + rslv->hostname_dn_len = dn_len; + rslv->result = RSLV_STATUS_NONE; + rslv->success_cb = acme_rslv_success_cb; + rslv->error_cb = acme_rslv_error_cb; + rslv->dnstasks = dnstasks; + + if (!rslv->hostname_dn) { + memprintf(errmsg, "Could not allocate memory\n"); + goto error; + } + + if (resolv_link_resolution(rslv, OBJ_TYPE_ACME_RSLV, 0) < 0) { + memprintf(errmsg, "Could not create resolution task for \"%.*s\"\n", hostname_len, hostname); + goto error; + } + + resolv_trigger_resolution(rslv->requester); + + return rslv; + +error: + if (rslv) + free(rslv->hostname_dn); + free(rslv); + return NULL; +} + +#endif /* HAVE_ACME */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + */ diff --git a/src/resolvers.c b/src/resolvers.c index 4cc13179d..1b17b11e0 100644 --- a/src/resolvers.c +++ b/src/resolvers.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -2170,6 +2171,25 @@ int resolv_link_resolution(void *requester, int requester_type, int requester_lo ? DNS_RTYPE_A : DNS_RTYPE_AAAA; break; +#if defined(HAVE_ACME) + case OBJ_TYPE_ACME_RSLV: { + struct acme_rslv *acme_rslv = (struct acme_rslv *)requester; + + req = resolv_get_requester(&acme_rslv->requester, + &acme_rslv->obj_type, + acme_rslv->success_cb, + acme_rslv->error_cb); + if (!req) + goto err; + + hostname_dn = &acme_rslv->hostname_dn; + hostname_dn_len = acme_rslv->hostname_dn_len; + resolvers = acme_rslv->resolvers; + + query_type = DNS_RTYPE_TXT; + break; + } +#endif default: goto err; } @@ -2557,6 +2577,11 @@ struct task *process_resolvers(struct task *t, void *context, unsigned int state /* Always perform the resolution */ must_run = 1; break; + case OBJ_TYPE_ACME_RSLV: + /* Always perform the resolution */ + must_run = 1; + break; + default: break; }