/** * @return 成功した場合は NETDB_SUCCESS. */ static int DnsResolver_lookupTxtData(DnsResolver *self, int rrtype, const char *domain, DnsTxtResponse **resp) { int query_stat = DnsResolver_query(self, domain, rrtype); if (NETDB_SUCCESS != query_stat) { return query_stat; } // end if size_t msg_count = ns_msg_count(self->msghanlde, ns_s_an); DnsTxtResponse *respobj = (DnsTxtResponse *) malloc(sizeof(DnsTxtResponse) + msg_count * sizeof(char *)); if (NULL == respobj) { return DnsResolver_setError(self, NETDB_INTERNAL); } // end if memset(respobj, 0, sizeof(DnsTxtResponse) + msg_count * sizeof(char *)); respobj->num = 0; for (size_t n = 0; n < msg_count; ++n) { ns_rr rr; int parse_stat = ns_parserr(&self->msghanlde, ns_s_an, n, &rr); if (0 != parse_stat) { goto formerr; } // end if if (ns_t_txt != ns_rr_type(rr)) { continue; } // end if respobj->data[respobj->num] = (char *) malloc(ns_rr_rdlen(rr)); // RDLEN を越えることはない. if (NULL == respobj->data[respobj->num]) { goto noresource; } // end if const unsigned char *rdata = ns_rr_rdata(rr); const unsigned char *rdata_tail = ns_rr_rdata(rr) + ns_rr_rdlen(rr); char *bufp = respobj->data[respobj->num]; while (rdata < rdata_tail) { // 長さフィールドが RDLEN の中に収まっているか確認する if (rdata_tail < rdata + (*rdata) + 1) { free(respobj->data[respobj->num]); goto formerr; } // end if memcpy(bufp, rdata + 1, *rdata); bufp += (size_t) *rdata; rdata += (size_t) *rdata + 1; } // end while *bufp = '\0'; // 扱いやすいように NULL 終端させる ++(respobj->num); } // end for if (0 == respobj->num) { goto formerr; } // end if *resp = respobj; return NETDB_SUCCESS; formerr: DnsTxtResponse_free(respobj); return DnsResolver_setError(self, NO_RECOVERY); noresource: DnsTxtResponse_free(respobj); return DnsResolver_setError(self, NETDB_INTERNAL); } // end function : DnsResolver_lookupTxtData
void DnsSpfResponse_free(DnsSpfResponse *self) { DnsTxtResponse_free(self); } // end function : DnsSpfResponse_free
/** * @return DSTAT_OK for success, otherwise status code that indicates error. * @error DSTAT_INFO_ADSP_NOT_EXIST ADSP record have not found * @error DSTAT_PERMFAIL_MULTIPLE_ADSP_RECORD multiple ADSP records are found * @error DSTAT_TMPERR_DNS_ERROR_RESPONSE DNS lookup error (received error response) * @error DSTAT_SYSERR_DNS_LOOKUP_FAILURE DNS lookup error (failed to lookup itself) * @error DSTAT_SYSERR_IMPLERROR obvious implementation error * @error DSTAT_SYSERR_NORESOURCE memory allocation error */ static DkimAdsp * DkimAdsp_query(const DkimPolicyBase *policy, DnsResolver *resolver, const char *domain, DkimStatus *dstat) { assert(NULL != resolver); assert(NULL != domain); // lookup ADSP record DnsTxtResponse *txt_rr = NULL; dns_stat_t txtquery_stat = DnsResolver_lookupTxt(resolver, domain, &txt_rr); switch (txtquery_stat) { case DNS_STAT_NOERROR:; // a TXT RR is found /* * [RFC5617] 4.3. * If the result of this query is a NOERROR response (rcode=0 in * [RFC1035]) with an answer that is a single record that is a valid * ADSP record, use that record, and the algorithm terminates. */ if (0 == DnsTxtResponse_size(txt_rr)) { // no TXT records are found DnsTxtResponse_free(txt_rr); SETDEREF(dstat, DSTAT_INFO_ADSP_NOT_EXIST); break; } else if (1 < DnsTxtResponse_size(txt_rr)) { // multiple TXT records are found DnsTxtResponse_free(txt_rr); SETDEREF(dstat, DSTAT_PERMFAIL_MULTIPLE_ADSP_RECORD); break; } // end if // only one TXT record is found, and now, try to parse as ADSP record DkimStatus build_stat; const char *txtrecord = DnsTxtResponse_data(txt_rr, 0); DkimAdsp *self = DkimAdsp_build(policy, txtrecord, &build_stat); if (NULL != self) { // parsed as a valid ADSP record DnsTxtResponse_free(txt_rr); SETDEREF(dstat, DSTAT_OK); return self; } else if (DSTAT_ISCRITERR(build_stat)) { // propagate system errors as-is DkimLogSysError (policy, "System error has occurred while parsing ADSP record: domain=%s, err=%s, record=%s", domain, DKIM_strerror(build_stat), NNSTR(txtrecord)); SETDEREF(dstat, build_stat); } else if (DSTAT_ISPERMFAIL(build_stat)) { /* * treat syntax errors on ADSP record as DNS NODATA response * * [RFC5617] 4.1. * Records not in compliance with that syntax * or the syntax of individual tags described in Section 4.3 MUST be * ignored (considered equivalent to a NODATA result) for purposes of * ADSP, although they MAY cause the logging of warning messages via an * appropriate system logging mechanism. */ DkimLogDebug(policy, "ADSP record candidate discarded: domain=%s, err=%s, record=%s", domain, DKIM_strerror(build_stat), NNSTR(txtrecord)); SETDEREF(dstat, DSTAT_INFO_ADSP_NOT_EXIST); } else { DkimLogNotice(policy, "DkimAdsp_build failed: domain=%s, err=%s, record=%s", domain, DKIM_strerror(build_stat), NNSTR(txtrecord)); SETDEREF(dstat, DSTAT_INFO_ADSP_NOT_EXIST); } // end if // a TXT RR is not a valid ADSP record DnsTxtResponse_free(txt_rr); break; case DNS_STAT_NXDOMAIN: case DNS_STAT_NODATA: /* * no TXT (and ADSP) records are found * * [RFC5617] 4.3. * If the result of the query is NXDOMAIN or NOERROR with zero * records, there is no ADSP record. If the result of the query * contains more than one record, or a record that is not a valid * ADSP record, the ADSP result is undefined. */ DkimLogDebug(policy, "No ADSP record is found on DNS: domain=%s", domain); SETDEREF(dstat, DSTAT_INFO_ADSP_NOT_EXIST); break; case DNS_STAT_FORMERR: case DNS_STAT_SERVFAIL: case DNS_STAT_NOTIMPL: case DNS_STAT_REFUSED: case DNS_STAT_YXDOMAIN: case DNS_STAT_YXRRSET: case DNS_STAT_NXRRSET: case DNS_STAT_NOTAUTH: case DNS_STAT_NOTZONE: case DNS_STAT_RESERVED11: case DNS_STAT_RESERVED12: case DNS_STAT_RESERVED13: case DNS_STAT_RESERVED14: case DNS_STAT_RESERVED15: /* * [RFC5617] 4.3. * If a query results in a "SERVFAIL" error response (rcode=2 in * [RFC1035]), the algorithm terminates without returning a result; * possible actions include queuing the message or returning an SMTP * error indicating a temporary failure. */ DkimLogInfo(policy, "DNS error on ADSP record look-up: domain=%s, type=txt, err=%s", domain, DnsResolver_getErrorString(resolver)); SETDEREF(dstat, DSTAT_TMPERR_DNS_ERROR_RESPONSE); break; case DNS_STAT_SYSTEM: case DNS_STAT_RESOLVER: case DNS_STAT_RESOLVER_INTERNAL: DkimLogSysError(policy, "error occurred during DNS lookup: domain=%s, type=txt, err=%s", domain, DnsResolver_getErrorString(resolver)); SETDEREF(dstat, DSTAT_SYSERR_DNS_LOOKUP_FAILURE); break; case DNS_STAT_NOMEMORY: DkimLogNoResource(policy); SETDEREF(dstat, DSTAT_SYSERR_NORESOURCE); break; case DNS_STAT_BADREQUEST: default: DkimLogImplError(policy, "DnsResolver_lookupTxt returns unexpected value: value=0x%x, domain=%s, type=txt", txtquery_stat, domain); SETDEREF(dstat, DSTAT_SYSERR_IMPLERROR); break; } // end switch return NULL; } // end function: DkimAdsp_query