+/* called from multi when a sub transfer, e.g. doh probe, is done.
+ * Parse the response and set the results in the `async` context
+ * of master, using the id from the probe's CURL_EZM_DOH_PROBE
+ * meta data. */
+static void doh_probe_done(struct Curl_easy *doh,
+ struct Curl_easy *master, CURLcode result)
+{
+ struct Curl_resolv_async *async = NULL;
+ struct doh_probes *dohp = NULL;
+ struct doh_request *doh_req = NULL;
+ struct Curl_addrinfo **pdest_ai;
+ struct dohentry de;
+ int slot, httpcode;
+
+ de_init(&de);
+ doh_req = Curl_meta_get(doh, CURL_EZM_DOH_PROBE);
+ if(!doh_req) {
+ /* transfer `doh` is not a DoH probe. */
+ DEBUGASSERT(0);
+ goto out;
+ }
+
+ async = Curl_async_get(master, doh_req->resolv_id);
+ if(!async) {
+ CURL_TRC_DNS(master, "[%u] ignoring outdated DoH response",
+ doh_req->resolv_id);
+ goto out;
+ }
+ dohp = async->doh;
+
+ for(slot = 0; slot < DOH_SLOT_COUNT; ++slot) {
+ if(dohp->probe_mid[slot] == doh->mid)
+ break;
+ }
+ /* We really should have found the slot where to store the response */
+ if(slot >= DOH_SLOT_COUNT) {
+ failf(master, "DoH: unknown sub request done");
+ DEBUGASSERT(0);
+ goto out;
+ }
+
+ async->queries_ongoing--;
+ dohp = async->doh;
+ httpcode = doh->info.httpcode;
+ switch(slot) {
+ case DOH_SLOT_IPV4:
+ async->dns_responses |= CURL_DNSQ_A;
+ break;
+#ifdef USE_IPV6
+ case DOH_SLOT_IPV6:
+ async->dns_responses |= CURL_DNSQ_AAAA;
+ break;
+#endif
+#ifdef USE_HTTPSRR
+ case DOH_SLOT_HTTPS_RR:
+ async->dns_responses |= CURL_DNSQ_HTTPS;
+ break;
+#endif
+ default:
+ DEBUGASSERT(0);
+ break;
+ }
+
+ if(result) {
+ dohp->probe_rc[slot] = DOH_HTTP_FAILED;
+ infof(doh, "[DoH] [%s] error: %s",
+ doh_type2name(doh_req->dnstype), curl_easy_strerror(result));
+ goto out;
+ }
+ else if((httpcode < 200) || (httpcode >= 300)) {
+ dohp->probe_rc[slot] = DOH_HTTP_FAILED;
+ infof(doh, "[DoH] [%s] error: HTTP status %d",
+ doh_type2name(doh_req->dnstype), httpcode);
+ goto out;
+ }
+
+ dohp->probe_rc[slot] = doh_resp_decode(curlx_dyn_uptr(&doh_req->resp_body),
+ curlx_dyn_len(&doh_req->resp_body),
+ doh_req->dnstype, &de);
+ if(dohp->probe_rc[slot]) {
+#ifdef USE_HTTPSRR
+ if((dohp->probe_rc[slot] == DOH_NO_CONTENT) &&
+ (doh_req->dnstype == CURL_DNS_TYPE_HTTPS)) {
+ dohp->probe_rc[slot] = DOH_DNS_NXDOMAIN;
+ }
+#endif
+ infof(doh, "[DoH] [%s] error decoding response: %s",
+ doh_type2name(doh_req->dnstype),
+ doh_strerror(dohp->probe_rc[slot]));
+ goto out;
+ }
+
+ if(doh_req->dnstype == CURL_DNS_TYPE_A)
+ pdest_ai = &async->ai_A;
+ else if(doh_req->dnstype == CURL_DNS_TYPE_AAAA)
+ pdest_ai = &async->ai_AAAA;
+ else
+ pdest_ai = NULL;
+
+ if(pdest_ai && de.numaddr) {
+ if(*pdest_ai) {
+ Curl_freeaddrinfo(*pdest_ai);
+ *pdest_ai = NULL;
+ }
+ result = doh2ai(&de, async->peer->hostname, async->peer->port, pdest_ai);
+ if(result) { /* hard failure on our side, fail completely */
+ infof(doh, "[DoH] [%s] error creating addrinfo: %s",
+ doh_type2name(doh_req->dnstype), curl_easy_strerror(result));
+ dohp->probe_rc[slot] = DOH_OOM;
+ async->result = result;
+ }
+ }
+#ifdef USE_HTTPSRR
+ else if((doh_req->dnstype == CURL_DNS_TYPE_HTTPS) && de.numhttps_rrs) {
+ CURL_TRC_DNS(doh, "[HTTPS] got %d records", de.numhttps_rrs);
+ result = doh_resp_decode_httpsrr(doh, de.https_rrs->val,
+ de.https_rrs->len, &async->httpsrr);
+ if(result) {
+ dohp->probe_rc[slot] = DOH_HTTP_FAILED;
+ infof(doh, "[DoH] error decoding HTTPS RR: %s",
+ curl_easy_strerror(result));
+ goto out;
+ }
+ }
+#endif /* USE_HTTPSRR */
+
+ /* DoH request complete, run master to act on results */
+ infof(doh, "DoH request complete, %u to go", async->queries_ongoing);
+
+out:
+ Curl_multi_mark_dirty(master);
+ de_cleanup(&de);
+ Curl_meta_remove(doh, CURL_EZM_DOH_PROBE);
+}
+