int DnsResolver_lookupMx(DnsResolver *self, const char *domain, DnsMxResponse **resp) { int query_stat = DnsResolver_query(self, domain, ns_t_mx); if (NETDB_SUCCESS != query_stat) { return query_stat; } // end if size_t msg_count = ns_msg_count(self->msghanlde, ns_s_an); DnsMxResponse *respobj = (DnsMxResponse *) malloc(sizeof(DnsMxResponse) + msg_count * sizeof(struct mxentry *)); if (NULL == respobj) { return DnsResolver_setError(self, NETDB_INTERNAL); } // end if memset(respobj, 0, sizeof(DnsMxResponse) + msg_count * sizeof(struct mxentry *)); 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_mx != ns_rr_type(rr)) { continue; } // end if const unsigned char *rdata = ns_rr_rdata(rr); if (ns_rr_rdlen(rr) < NS_INT16SZ) { goto formerr; } // end if int preference = ns_get16(rdata); rdata += NS_INT16SZ; // NOTE: NS_MAXDNAME で十分なのかイマイチ確証が持てないが, bind8 の dig コマンドの実装でもこの値を使っていたのでいいのではないか. char dnamebuf[NS_MAXDNAME]; int dnamelen = ns_name_uncompress(self->msgbuf, self->msgbuf + self->msglen, rdata, dnamebuf, sizeof(dnamebuf)); if (NS_INT16SZ + dnamelen != ns_rr_rdlen(rr)) { goto formerr; } // end if // TODO: dnamebuf は NULL 終端が保証されているのか? size_t domainlen = strlen(dnamebuf); respobj->exchange[respobj->num] = (struct mxentry *) malloc(sizeof(struct mxentry) + sizeof(char[domainlen + 1])); if (NULL == respobj->exchange[respobj->num]) { goto noresource; } // end if respobj->exchange[respobj->num]->preference = preference; if (domainlen + 1 <= strlcpy(respobj->exchange[respobj->num]->domain, dnamebuf, domainlen + 1)) { abort(); } // end if ++(respobj->num); } // end for if (0 == respobj->num) { goto formerr; } // end if *resp = respobj; return NETDB_SUCCESS; formerr: DnsMxResponse_free(respobj); return DnsResolver_setError(self, NO_RECOVERY); noresource: DnsMxResponse_free(respobj); return DnsResolver_setError(self, NETDB_INTERNAL); } // end function : DnsResolver_lookupMx
/** * Check whether a given Author Domain is within scope for ADSP. * @return DSTAT_OK for success, otherwise status code that indicates error. * @error DSTAT_INFO_ADSP_NXDOMAIN Author Domain does not exist (NXDOMAIN) * @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 */ static DkimStatus DkimAdsp_checkDomainScope(const DkimPolicyBase *policy, DnsResolver *resolver, const char *domain) { assert(NULL != resolver); assert(NULL != domain); /* * [RFC5617] 4.3. * The host MUST perform a DNS query for a record corresponding to * the Author Domain (with no prefix). The type of the query can be * of any type, since this step is only to determine if the domain * itself exists in DNS. This query MAY be done in parallel with the * query to fetch the named ADSP Record. If the result of this query * is that the Author Domain does not exist in the DNS (often called * an NXDOMAIN error, rcode=3 in [RFC1035]), the algorithm MUST * terminate with an error indicating that the domain is out of * scope. Note that a result with rcode=0 but no records (often * called NODATA) is not the same as NXDOMAIN. * * NON-NORMATIVE DISCUSSION: Any resource record type could be * used for this query since the existence of a resource record of * any type will prevent an NXDOMAIN error. MX is a reasonable * choice for this purpose because this record type is thought to * be the most common for domains used in email, and will * therefore produce a result that can be more readily cached than * a negative result. */ DnsMxResponse *mx_rr = NULL; dns_stat_t mxquery_stat = DnsResolver_lookupMx(resolver, domain, &mx_rr); switch (mxquery_stat) { case DNS_STAT_NOERROR: DnsMxResponse_free(mx_rr); // fall through case DNS_STAT_NODATA: return DSTAT_OK; case DNS_STAT_NXDOMAIN: DkimLogPermFail(policy, "The author domain does not exist: domain=%s, type=mx, err=%s", domain, DnsResolver_getErrorString(resolver)); return DSTAT_INFO_ADSP_NXDOMAIN; 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: DkimLogPermFail(policy, "DNS error on checking author domain existence: domain=%s, type=mx, err=%s", domain, DnsResolver_getErrorString(resolver)); return DSTAT_TMPERR_DNS_ERROR_RESPONSE; case DNS_STAT_SYSTEM: case DNS_STAT_RESOLVER: case DNS_STAT_RESOLVER_INTERNAL: DkimLogSysError(policy, "error occurred during DNS lookup: domain=%s, type=mx, err=%s", domain, DnsResolver_getErrorString(resolver)); return DSTAT_SYSERR_DNS_LOOKUP_FAILURE; case DNS_STAT_NOMEMORY: DkimLogNoResource(policy); return DSTAT_SYSERR_NORESOURCE; case DNS_STAT_BADREQUEST: default: DkimLogImplError(policy, "DnsResolver_lookupMx returns unexpected value: value=0x%x, domain=%s, type=mx", mxquery_stat, domain); return DSTAT_SYSERR_IMPLERROR; } // end switch } // end function: DkimAdsp_checkDomainScope