/* * Copyright (c) 2003-2005 Sendmail, Inc. and its suppliers. * All rights reserved. * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the sendmail distribution. */ #include "sm/generic.h" SM_RCSID("@(#)$Id: reverse.c,v 1.53 2007/11/05 05:50:13 ca Exp $") #include "sm/error.h" #include "sm/assert.h" #include "sm/io.h" #include "sm/net.h" #include "sm/rcb.h" #include "sm/mta.h" #include "sm/net.h" #include "smar.h" #include "reverse.h" #include "log.h" #include "sm/reccom.h" /* Reverse lookup caller should ask for: - one host name only - all host names - client_resolve results: 1. hostname 2. client_resolve: a. reverse lookup failed: TEMP/PERM b. forward lookup failed: TEMP/PERM c. forward does not match original address requires 5 bits how to map that to a single value? MATCH forward matches original address NOMATCH none of the forward lookups matches original address TEMP_PTR temporary error while resolving PTR record PERM_PTR permanent error while resolving PTR record TEMP_A temporary error while resolving A record PERM_A permanent error while resolving A record Note: resolving an A record may fail but the result can still be OK because there can be multiple A records one of which matches the original address. can be "condensed" to: MATCH forward matches original address NOMATCH none of the forward lookups matches original address TEMP_PTR temporary error while resolving PTR record TEMP_A temporary error while resolving A record lookup a PTR record (gethostbyaddr() isn't thread safe) this can return a list of hostnames for each host in list lookup A records if one A record matches original query: OK note: if caller only asks for one hostname and client_resolve then the list of hosts does not need to be stored. this algorithm is almost the same as the one for MX records. can we reuse it (that is, put some parameters into the original one)? simple approach: make a copy and some modifications... but that's harder to maintain. */ /* ** SMAR_RVRS_NEW -- Create a SMAR RVRS context ** ** Parameters: ** smar_ctx -- SMAR context ** smar_clt_ctx -- SMAR client context ** psmar_rvrs -- (pointer to) SMAR RVRS context (output) ** ** Returns: ** usual sm_error code ** ** Last code review: 2004-03-22 16:39:32 ** Last code change: */ sm_ret_T smar_rvrs_new(smar_ctx_P smar_ctx, smar_clt_ctx_P smar_clt_ctx, smar_rvrs_P *psmar_rvrs) { smar_rvrs_P smar_rvrs; SM_REQUIRE(psmar_rvrs != NULL); smar_rvrs = (smar_rvrs_P) sm_zalloc(sizeof(*smar_rvrs)); if (NULL == smar_rvrs) return sm_error_temp(SM_EM_AR, ENOMEM); smar_rvrs->arv_smar_ctx = smar_ctx; smar_rvrs->arv_smar_clt_ctx = smar_clt_ctx; *psmar_rvrs = smar_rvrs; return SM_SUCCESS; } /* ** SMAR_RVRS_FREE -- Free a SMAR RVRS context ** ** Parameters: ** smar_rvrs -- SMAR RVRS context ** ** Returns: ** usual sm_error code ** ** Last code review: 2004-03-22 16:39:46 ** Last code change: */ sm_ret_T smar_rvrs_free(smar_rvrs_P smar_rvrs) { if (NULL == smar_rvrs) return SM_SUCCESS; SMAR_LEV_DPRINTF(4, (SMAR_DEBFP, "sev=DBG, func=smar_rvrs_free, hostname=%#C\n", smar_rvrs->arv_hostname)); SM_STR_FREE(smar_rvrs->arv_pa); SM_CSTR_FREE(smar_rvrs->arv_hostname); if (smar_rvrs->arv_res != NULL) { uint i; smar_rdns_P smar_rdns; for (i = 0; i < smar_rvrs->arv_A_qsent; i++) { smar_rdns = &(smar_rvrs->arv_res[i]); #if SMAR_ARRDNS_A SM_FREE(smar_rdns->arrdns_a); #endif SM_CSTR_FREE(smar_rdns->arrdns_name); } sm_free(smar_rvrs->arv_res); } if (smar_rvrs->arv_rcbe != NULL) { sm_rcbe_free(smar_rvrs->arv_rcbe); smar_rvrs->arv_rcbe = NULL; } sm_free_size(smar_rvrs, sizeof(*smar_rvrs)); return SM_SUCCESS; } /* ** SMAR_RVRS_flags2ret -- convert flags to return value ** ** Parameters: ** smar_rvrs -- SMAR RVRS context ** ** Returns: ** none. ** ** Side Effects: ** sets smar_rvrs->arv_ret ** ** Last code review: 2004-03-22 16:42:46 ** Last code change: */ static void smar_rvrs_flags2ret(smar_rvrs_P smar_rvrs) { if (SMARV_IS_FLAG(smar_rvrs, SMARV_FL_MATCH)) smar_rvrs->arv_ret = SM_RVRS_MATCH; else if (SMARV_IS_FLAG(smar_rvrs, SMARV_FL_TMPPTR)) smar_rvrs->arv_ret = SM_RVRS_TEMP_PTR; else if (SMARV_IS_FLAG(smar_rvrs, SMARV_FL_TMPA)) smar_rvrs->arv_ret = SM_RVRS_TEMP_A; else /* SMARV_FL_PRMPTR and SMARV_FL_PRMA are "no match" too */ smar_rvrs->arv_ret = SM_RVRS_NOMATCH; return; } #if SMAR_RVRS_ALL /* ** SMAR_RVRS_RE_ALL -- Complete reply for a reverse resolution request ** ** Parameters: ** smar_rvrs -- reverse resolution context ** ** Returns: ** usual sm_error code ** ** Last code review: ** Last code change: */ static sm_ret_T smar_rvrs_re_all(smar_rvrs_P smar_rvrs) { sm_rcb_P rcb; int i; sm_ret_T ret; smar_rdns_P smar_rdns; rcb = &smar_rvrs->arv_rcbe->rcbe_rcb; /* First: send basic data and number of entries */ SMAR_LEV_DPRINTF(4, (SMAR_DEBFP, "sev=DBG, func=smar_rvrs_re_all, entries=%d, sm_rcb_len=%d, sm_rcb_rw=%d\n", smar_rvrs->arv_n_a, rcb->sm_rcb_len, rcb->sm_rcb_rw)); ret = sm_rcb_putv(rcb, RCB_PUTV_FIRST, SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT, SM_RCBV_BUF, RT_A2S_ID, smar_rvrs->arv_seid, SMTP_RCPTID_SIZE, SM_RCBV_INT, RT_A2S_N_HOSTN, smar_rvrs->arv_A_qsent, SM_RCBV_END); SMAR_LEV_DPRINTF(4, (SMAR_DEBFP, "sev=DBG, func=smar_rvrs_re_all, n_a=%d, n_ptr=%d, putv=%x\n", smar_rvrs->arv_n_a, smar_rvrs->arv_A_qsent, ret)); if (sm_is_err(ret)) goto error; /* Now go through all MX entries */ /* Note: smar_rvrs->arv_A_qsent > 0 iff smar_rvrs->arv_res != NULL */ for (i = 0; i < smar_rvrs->arv_A_qsent; i++) { smar_rdns = &(smar_rvrs->arv_res[i]); SMAR_LEV_DPRINTF(4, (SMAR_DEBFP, "sev=DBG, func=smar_rvrs_re_all, i=%d, smar_rdns=%p\n", i, smar_rdns)); #if 0 /* no reply for this A record? */ if (0 == smar_rdns->arrdns_n_a) continue; #endif /* 0 */ ret = sm_rcb_putv(rcb, RCB_PUTV_NONE, SM_RCBV_INT, RT_A2S_RVRS_NAME, smar_rdns->arrdns_name, SM_RCBV_END); SMAR_LEV_DPRINTF(4, (SMAR_DEBFP, "sev=DBG, func=smar_rvrs_re_all, i=%d, putv=%x\n", i, ret)); } return ret; error: /* cleanup? */ return ret; } #endif /* SMAR_RVRS_ALL */ /* ** SMAR_RVRS_RE -- Send a reply for a reverse resolution query ** callback function to return result ** ** Parameters: ** smar_rvrs -- reverse resolution context ** ctx -- unused ** ** Returns: ** usual sm_error code ** ** Last code review: 2004-03-22 16:45:51 ** Last code change: */ sm_ret_T smar_rvrs_re(smar_rvrs_P smar_rvrs, void *ctx) { sm_rcb_P rcb; sm_ret_T ret; rcb = &smar_rvrs->arv_rcbe->rcbe_rcb; SM_REQUIRE(smar_rvrs->arv_ret != 0); ret = sm_rcb_putv(rcb, RCB_PUTV_FIRST, SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT, SM_RCBV_BUF, RT_A2S_SEID, smar_rvrs->arv_seid, SMTP_STID_SIZE, SM_RCBV_INT, RT_A2S_RVRS_ST, smar_rvrs->arv_ret, SM_RCBV_END); if (sm_is_success(ret) && smar_rvrs->arv_hostname != NULL) { ret = sm_rcb_putv(rcb, RCB_PUTV_NONE, SM_RCBV_CSTR, RT_A2S_RVRS_NAME, smar_rvrs->arv_hostname, SM_RCBV_END); } return ret; } /* ** SMAR_RVRS_CB -- Callback function for DNS resolver ** ** Parameters: ** dns_res -- DNS resolver result ** ctx -- context: smar_rvrs ** ** Returns: ** usual sm_error code ** ** Used as callback for dns_req_add() by smar_rvrs_rslv() and by itself. ** ** This function needs to keep track of the number of open requests. ** Only when it received all answers it must send them back (in an RCB) ** to the client. Unfortunately there are a lot of error cases to handle. ** Nevertheless, the function requires that all outstanding requests ** are answered (even if the DNS resolver encounters an error), there ** is no timeout in here (can't be: it's a callback function). ** ** Last code review: ** Last code change: ** ** XXX split this into two functions? 2004-05-04 ** one for responses to PTR queries ** one for responses to A queries ** -> code duplication? vs code complexity */ static sm_ret_T smar_rvrs_cb(dns_res_P dns_res, void *ctx) { dns_type_T type; sm_ret_T ret, dnsres, res; uint n_entries, i; size_t n; /* SMAR_RVRS_ALL */ int r; uint8_t flags; smar_rvrs_P smar_rvrs; dns_rese_P dns_rese; sm_cstr_P q; smar_ctx_P smar_ctx; dns_mgr_ctx_P dns_mgr_ctx; smar_clt_ctx_P smar_clt_ctx; #define SM_INCR_A_RCVD 0x01 #define SM_INCR_PTR_RCVD 0x02 #define SM_RCB_SENT 0x04 #define SM_CB_INVOKED 0x08 #define SM_NOSEND 0x10 #define SM_NOFREE 0x20 /* SM_REQUIRE(dns_res != NULL); */ /* SM_REQUIRE(ctx != NULL); */ if (NULL == dns_res || NULL == ctx) { SMAR_LEV_DPRINTF(0, (SMAR_DEBFP, "sev=ERROR, func=smar_rvrs_cb, dns_res=%p, ctx=%p\n", dns_res, ctx)); /* XXX Oops... how to do logging if there is no context? */ sm_log_write(NULL, AR_LCAT_RESOLVER, AR_LMOD_RVRS_CB, SM_LOG_CRIT, 0, "sev=CRIT, func=smar_rvrs_cb, dns_res=%p, ctx=%p" , dns_res, ctx); return SM_FAILURE; } smar_rvrs = (smar_rvrs_P) ctx; smar_clt_ctx = smar_rvrs->arv_smar_clt_ctx; SM_IS_SMAR_CLT_CTX(smar_clt_ctx); smar_ctx = smar_rvrs->arv_smar_ctx; SM_IS_SMAR_CTX(smar_ctx); flags = 0; SMAR_LEV_DPRINTF(6, (SMAR_DEBFP, "sev=DBG, func=smar_rvrs_cb, dns_res=%p, ctx=%p\n", dns_res, ctx)); /* ** Check whether SMAR is shutting down: if yes: don't ** do anything with the replies since the tasks are invalid. */ r = pthread_mutex_lock(&smar_ctx->smar_mutex); SM_LOCK_OK(r); if (r != 0) { /* LOG; XXX What to do in this error case? */ sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RVRS_CB, SM_LOG_CRIT, 1, "sev=CRIT, func=smar_rvrs_cb, lock=%d", r); return SM_FAILURE; } if (smar_is_stop(smar_ctx)) { sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RVRS_CB, SM_LOG_INFO, 10, "sev=INFO, func=smar_rvrs_cb, status=shutting_down"); r = pthread_mutex_unlock(&smar_ctx->smar_mutex); return SM_SUCCESS; } dns_mgr_ctx = smar_ctx->smar_dns_mgr_ctx; SM_REQUIRE(dns_mgr_ctx != NULL); /* ** smar_rvrs can be free()d as soon as ** smar_rvrs->arv_cbf is invoked, hence store ** required data in a local variable. ** ** Should this be an assertion? The caller should set NOSEND ** if rcb(e) isn't valid. */ SM_REQUIRE(SMARV_IS_FLAG(smar_rvrs, SMARV_FL_NOSEND) || smar_rvrs->arv_rcbe != NULL); #if 0 if (NULL == smar_rvrs->arv_rcbe || SMARV_IS_FLAG(smar_rvrs, SMARV_FL_NOSEND)) { SMARV_SET_FLAG(smar_rvrs, SMARV_FL_NOSEND); SM_SET_FLAG(flags, SM_NOSEND); } else { rcb = &smar_rvrs->arv_rcbe->rcbe_rcb; if (NULL == rcb) { SMARV_SET_FLAG(smar_rvrs, SMARV_FL_NOSEND); SM_SET_FLAG(flags, SM_NOSEND); } } #endif /* 0 */ if (SMARV_IS_FLAG(smar_rvrs, SMARV_FL_NOSEND)) SM_SET_FLAG(flags, SM_NOSEND); if (SMARV_IS_FLAG(smar_rvrs, SMARV_FL_NOFREE)) SM_SET_FLAG(flags, SM_NOFREE); ret = SM_SUCCESS; dnsres = dns_res->dnsres_ret; type = dns_res->dnsres_qtype; n_entries = dns_res->dnsres_entries; if (T_A == dns_res->dnsres_qtype) { smar_rvrs->arv_A_rrcvd++; SM_SET_FLAG(flags, SM_INCR_A_RCVD); } else if (T_PTR == dns_res->dnsres_qtype) { smar_rvrs->arv_PTR_rrcvd++; SM_SET_FLAG(flags, SM_INCR_PTR_RCVD); } SMAR_LEV_DPRINTF(4, (SMAR_DEBFP, "sev=DBG, func=smar_rvrs_cb, pa=%S, dnsres=%x, type=%x, flags=%x, entries=%d\n", smar_rvrs->arv_pa, dnsres, type, smar_rvrs->arv_flags, n_entries)); if (sm_is_err(dnsres)) { sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RVRS_CB, SM_LOG_INFO, 10, "sev=INFO, func=smar_rvrs_cb, pa=%S, type=%d, error=%m" , smar_rvrs->arv_pa, type, dnsres); /* ** This needs to deal with errors for A records of PTR records, ** i.e., partial results! */ /* XXX shouldn't be necessary anymore */ if ((T_A == type || SMARV_IS_FLAG(smar_rvrs, SMARV_FL_A4A)) && !SM_IS_FLAG(flags, SM_INCR_A_RCVD)) { /* count it as a reply */ smar_rvrs->arv_A_rrcvd++; SM_SET_FLAG(flags, SM_INCR_A_RCVD); } switch (dnsres) { case DNSR_TEMP: case DNSR_TIMEOUT: /* change error code? */ if (SMARV_IS_FLAG(smar_rvrs, SMARV_FL_A4A)) SMARV_SET_FLAG(smar_rvrs, SMARV_FL_TMPA); else SMARV_SET_FLAG(smar_rvrs, SMARV_FL_TMPPTR); break; case DNSR_REFUSED: sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RVRS_CB, SM_LOG_CRIT, 6, "sev=CRIT, func=smar_rvrs_cb, pa=%#S, type=%d, error=%m", smar_rvrs->arv_pa, type, dnsres); case DNSR_NOTFOUND: case DNSR_PERM: case DNSR_NO_DATA: case DNSR_MXINVALID: case DNSR_PTRINVALID: case DNSR_CNINVALID: case sm_error_perm(SM_EM_DNS, EINVAL): /* change error code? */ if (SMARV_IS_FLAG(smar_rvrs, SMARV_FL_A4A)) SMARV_SET_FLAG(smar_rvrs, SMARV_FL_PRMA); else SMARV_SET_FLAG(smar_rvrs, SMARV_FL_PRMPTR); break; default: sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RVRS_CB, SM_LOG_ERROR, 0, "sev=ERROR, func=smar_rvrs_cb, unknown_error=%d" , dns_res->dnsres_ret); /* Which error to return to caller? */ ret = dnsres; /* sm_error_perm(SM_EM_AR, EINVAL); */ goto error; } /* ** Error out iff XXX ???? CHECK !!! ** this query was for a PTR record or ** this query was for an A record ** and all records have been received ** and (there was a temporary error or ** there was only one (failed) query) ** and !(the first result is valid) */ sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RVRS_CB, SM_LOG_INFO, 10, "sev=INFO, func=smar_rvrs_cb, pa=%S, type=%d, flags=%x, error=%m, arv_A_rrcvd=%d, arv_A_qsent=%d", smar_rvrs->arv_pa, type, smar_rvrs->arv_flags, dnsres, smar_rvrs->arv_A_qsent, smar_rvrs->arv_A_rrcvd); if ((SMARV_IS_FLAG(smar_rvrs, SMARV_FL_A4PTR) && !SMARV_IS_FLAG(smar_rvrs, SMARV_FL_A4A)) || (smar_rvrs->arv_A_qsent == smar_rvrs->arv_A_rrcvd && SMARV_IS_FLAG(smar_rvrs, SMARV_FL_A4A) && (SMARV_IS_FLAG(smar_rvrs, SMARV_FL_TMPA) || smar_rvrs->arv_A_qsent == 1) ) ) { smar_rvrs_flags2ret(smar_rvrs); ret = smar_rvrs->arv_cbf(smar_rvrs, smar_rvrs->arv_cb_ctx); SM_SET_FLAG(flags, SM_CB_INVOKED); if (sm_is_err(ret)) goto error; if (!SM_IS_FLAG(flags, SM_NOSEND)) { ret = sm_rcbcom_endrep(&smar_clt_ctx->smac_com_ctx, smar_clt_ctx->smac_com_ctx.rcbcom_tsk, false, &smar_rvrs->arv_rcbe); SM_SET_FLAG(flags, SM_RCB_SENT); if (sm_is_err(ret)) goto error; } } /* XXX Cleanup? */ /* "else" for the rest ... */ /* fall through: dnsres != SM_SUCCESS -> reply part below */ } for (dns_rese = DRESL_FIRST(dns_res), i = 0; SM_SUCCESS == dnsres /* just a hack to avoid else/goto */ && dns_rese != DRESL_END(dns_res) && i < n_entries; dns_rese = DRESL_NEXT(dns_rese), i++) { type = dns_rese->dnsrese_type; SMAR_LEV_DPRINTF(4, (SMAR_DEBFP, "sev=DBG, func=smar_rvrs_cb, type=%d, name=%.256C, entry=%d/%d\n", type, dns_rese->dnsrese_name, i, n_entries)); if (T_PTR == type) { SMAR_LEV_DPRINTF(4, (SMAR_DEBFP, "sev=DBG, func=smar_rvrs_cb, type=PTR, entries=%d\n", n_entries)); SMARV_SET_FLAG(smar_rvrs, SMARV_FL_GOTPTR); if (NULL == smar_rvrs->arv_res) { /* count PTR replies first in a separate loop?? */ n = n_entries * sizeof(*(smar_rvrs->arv_res)); if (n < n_entries || n < sizeof(*(smar_rvrs->arv_res))) { ret = sm_error_perm(SM_EM_AR, SM_E_OVFLW_SC); goto error; /* more cleanup?? */ } smar_rvrs->arv_res = (smar_rdns_P) sm_zalloc(n); if (NULL == smar_rvrs->arv_res) { ret = sm_error_perm(SM_EM_AR, ENOMEM); goto error; /* XXX more cleanup?? */ } } q = dns_rese->dnsrese_val.dnsresu_name; SMAR_LEV_DPRINTF(1, (SMAR_DEBFP, "sev=DBG, func=smar_rvrs_cb, pa=%S, PTR=%#C, i=%u\n", smar_rvrs->arv_pa, q, i)); smar_rvrs->arv_res[i].arrdns_name = SM_CSTR_DUP(q); #if SMAR_ARRDNS_A smar_rvrs->arv_res[i].arrdns_n_a = -1; smar_rvrs->arv_res[i].arrdns_a = NULL; #endif SMAR_LEV_DPRINTF(1, (SMAR_DEBFP, "sev=DBG, func=smar_rvrs_cb, w=1, name0=%#C\n", smar_rvrs->arv_res[0].arrdns_name)); ret = dns_req_add(dns_mgr_ctx, q, T_A, 0u, smar_rvrs_cb, ctx); if (sm_is_err(ret)) break; /* XXX need more cleanup here? */ ++smar_rvrs->arv_A_qsent; SMAR_LEV_DPRINTF(1, (SMAR_DEBFP, "sev=DBG, func=smar_rvrs_cb, pa=%S, PTR=%#C, dns_req_add=%x\n", smar_rvrs->arv_pa, q, ret)); SMARV_SET_FLAG(smar_rvrs, SMARV_FL_A4A); } else if (T_A == type) { SMARV_SET_FLAG(smar_rvrs, SMARV_FL_GOTA); if (n_entries > 0) { smar_rdns_P smar_rdns; uint j; SMAR_LEV_DPRINTF(4, (SMAR_DEBFP, "sev=DBG, func=smar_rvrs_cb, nA_entries=%d, nPTR=%d\n", n_entries, smar_rvrs->arv_A_qsent)); /* Find matching entry in list of PTR records */ for (j = 0; j < smar_rvrs->arv_A_qsent; j++) { q = smar_rvrs->arv_res[j].arrdns_name; if (q != NULL && sm_strcaseeq( (const char *) sm_cstr_data(q), (const char *) sm_cstr_data(dns_res->dnsres_query))) break; sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RVRS_CB, SM_LOG_INFO, 12, "sev=INFO, func=smar_rvrs_cb, q=%#C, PTR=%#C, j=%u" , q, dns_res->dnsres_query, j); } if (j >= smar_rvrs->arv_A_qsent) { sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RVRS_CB, SM_LOG_ERROR, 0, "sev=ERROR, func=smar_rvrs_cb, PTR=%.256s, status=cannot_find_PTR, entries=%d", sm_cstr_data(dns_res->dnsres_query), smar_rvrs->arv_A_qsent); ret = sm_error_perm(SM_EM_AR, SM_E_NOTFOUND); goto error; /* XXX more cleanup! better handling */ } smar_rdns = &(smar_rvrs->arv_res[j]); #if SMAR_ARRDNS_A n = n_entries * sizeof(*(smar_rdns->arrdns_a)); smar_rdns->arrdns_a = (ipv4_T *) sm_zalloc(n); if (NULL == smar_rdns->arrdns_a) { ret = sm_error_perm(SM_EM_AR, ENOMEM); goto error; /* XXX need more cleanup here? */ } smar_rdns->arrdns_n_a = n_entries; #endif smar_rvrs->arv_n_a += n_entries; #if 1 sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RVRS_CB, SM_LOG_INFO, 13, "sev=INFO, func=smar_rvrs_cb, j=%u, i=%u, pa=%S, ip=%A", j, i, smar_rvrs->arv_pa, dns_rese->dnsrese_val.dnsresu_a); #endif /* 1 */ #if SMAR_ARRDNS_A smar_rdns->arrdns_a[i] = dns_rese->dnsrese_val.dnsresu_a; #endif /* found a match? */ if (dns_rese->dnsrese_val.dnsresu_a == smar_rvrs->arv_ipv4) { SMARV_SET_FLAG(smar_rvrs, SMARV_FL_MATCH); if (NULL == smar_rvrs->arv_hostname) { smar_rvrs->arv_hostname = SM_CSTR_DUP(smar_rvrs->arv_res[j].arrdns_name); } } } #if 0 sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RVRS_CB, SM_LOG_INFO, 13, "sev=INFO, func=smar_rvrs_cb, pa=%S, ip=%A", smar_rvrs->arv_pa, smar_rvrs->arv_ipv4); #endif sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RVRS_CB, SM_LOG_INFO, 11, "sev=INFO, func=smar_rvrs_cb, pa=%S, type=%d, arv_A_rrcvd=%d, arv_A_qsent=%d, hostname=%.256C", smar_rvrs->arv_pa, type, smar_rvrs->arv_A_qsent, smar_rvrs->arv_A_rrcvd, smar_rvrs->arv_hostname); } else if (T_CNAME == type) { SMAR_LEV_DPRINTF(4, (SMAR_DEBFP, "sev=DBG, func=smar_rvrs_cb, type=CNAME, value=%.256C\n", dns_rese->dnsrese_val.dnsresu_name)); if (T_PTR == dns_res->dnsres_qtype) { if (SMARV_IS_FLAG(smar_rvrs, SMARV_FL_C_PTR)) { /* don't want two CNAMEs */ SMARV_SET_FLAG(smar_rvrs, SMARV_FL_C_PTR_L); continue; /* XXX set error code? */ } else SMARV_SET_FLAG(smar_rvrs, SMARV_FL_C_PTR); } else if (T_A == dns_res->dnsres_qtype) { if (SMARV_IS_FLAG(smar_rvrs, SMARV_FL_C_A)) { /* don't want two CNAMEs */ SMARV_SET_FLAG(smar_rvrs, SMARV_FL_C_A_L); continue; /* XXX set error code? */ } else SMARV_SET_FLAG(smar_rvrs, SMARV_FL_C_A); } if (T_PTR == dns_res->dnsres_qtype || T_A == dns_res->dnsres_qtype) { q = dns_rese->dnsrese_val.dnsresu_name; ret = dns_req_add(dns_mgr_ctx, q, dns_res->dnsres_qtype, 0u, smar_rvrs_cb, ctx); if (sm_is_err(ret)) { SMARV_SET_FLAG(smar_rvrs, SMARV_FL_TMPPTR); /* XXX more cleanup? */ } else ++smar_rvrs->arv_PTR_qsent; } } #if 0 else { /* XXX other T_*: ignore for now! */ } #endif /* 0 */ } /* Got all entries? */ if (!SM_IS_FLAG(flags, SM_CB_INVOKED) && smar_rvrs->arv_A_qsent == smar_rvrs->arv_A_rrcvd && smar_rvrs->arv_PTR_qsent == smar_rvrs->arv_PTR_rrcvd) { if (!SM_IS_FLAG(flags, SM_CB_INVOKED)) { smar_rvrs_flags2ret(smar_rvrs); #if SMAR_RVRS_ALL ret = smar_rvrs_re_all(smar_rvrs); #else ret = smar_rvrs->arv_cbf(smar_rvrs, smar_rvrs->arv_cb_ctx); #endif SM_SET_FLAG(flags, SM_CB_INVOKED); if (sm_is_err(ret)) goto error; /* XXX need more cleanup here? */ } if (!SM_IS_FLAG(flags, SM_RCB_SENT) && !SM_IS_FLAG(flags, SM_NOSEND)) { ret = sm_rcbcom_endrep(&smar_clt_ctx->smac_com_ctx, smar_clt_ctx->smac_com_ctx.rcbcom_tsk, false, &smar_rvrs->arv_rcbe); SM_SET_FLAG(flags, SM_RCB_SENT); if (sm_is_err(ret)) goto error; } if (!SM_IS_FLAG(flags, SM_NOFREE)) { (void) smar_rvrs_free(smar_rvrs); smar_rvrs = NULL; } } r = pthread_mutex_unlock(&smar_ctx->smar_mutex); return ret; error: /* XXX Cleanup? */ sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RVRS_CB, SM_LOG_ERROR, 0, "sev=ERROR, func=smar_rvrs_cb, ret=%x", ret); if (SM_IS_FLAG(flags, SM_CB_INVOKED)) goto errunl; if (0 == smar_rvrs->arv_A_rrcvd) goto errunl; if (T_A == type && !SM_IS_FLAG(flags, SM_INCR_A_RCVD)) { smar_rvrs->arv_A_rrcvd++; SM_SET_FLAG(flags, SM_INCR_A_RCVD); } if (smar_rvrs->arv_A_qsent == smar_rvrs->arv_A_rrcvd) { sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RVRS_CB, SM_LOG_ERR, 1, "sev=ERROR, func=smar_rvrs_cb, pa=%S, type=%d, error=%m, A_records_rcvd=%d, A_queries_sent=%d, free=%d" , smar_rvrs->arv_pa, type, ret , smar_rvrs->arv_A_qsent, smar_rvrs->arv_A_rrcvd , SMARV_IS_FLAG(smar_rvrs, SMARV_FL_NOFREE)); if (!SM_IS_FLAG(flags, SM_CB_INVOKED)) { smar_rvrs_flags2ret(smar_rvrs); #if SMAR_RVRS_ALL res = smar_rvrs_re_all(smar_rvrs); #else res = smar_rvrs->arv_cbf(smar_rvrs, smar_rvrs->arv_cb_ctx); #endif SM_SET_FLAG(flags, SM_CB_INVOKED); #if 0 if (sm_is_err(res)) /* oops? */; #endif } if (!SM_IS_FLAG(flags, SM_RCB_SENT) && !SM_IS_FLAG(flags, SM_NOSEND)) { res = sm_rcbcom_endrep(&smar_clt_ctx->smac_com_ctx, smar_clt_ctx->smac_com_ctx.rcbcom_tsk, false, &smar_rvrs->arv_rcbe); SM_SET_FLAG(flags, SM_RCB_SENT); } if (!SM_IS_FLAG(flags, SM_NOFREE)) { res = smar_rvrs_free(smar_rvrs); smar_rvrs = NULL; } } errunl: r = pthread_mutex_unlock(&smar_ctx->smar_mutex); return ret; } /* ** SMAR_RVRS_RSLV -- Reverse resolve IPv4 address ** ** Parameters: ** smar_ctx -- SMAR context ** smar_rvrs -- context for reverse resolution ** timeout -- timeout (if 0: use default) ** ** Returns: ** usual sm_error code ** ** Last code review: 2004-03-22 17:05:10; see comments ** Last code change: */ sm_ret_T smar_rvrs_rslv(smar_ctx_P smar_ctx, smar_rvrs_P smar_rvrs, uint timeout) { sm_ret_T ret; dns_mgr_ctx_P dns_mgr_ctx; sm_cstr_P q; SM_IS_SMAR_CTX(smar_ctx); ret = SM_SUCCESS; q = NULL; dns_mgr_ctx = smar_ctx->smar_dns_mgr_ctx; /* strlen("255.") * 4 + strlen(".in-addr.arpa") */ smar_rvrs->arv_pa = sm_str_new(NULL, 32, 40); /* XXX */ ret = sm_inet_ipv42arpastr(smar_rvrs->arv_ipv4, smar_rvrs->arv_pa); if (sm_is_err(ret)) goto error; q = sm_cstr_scpyn0((const uchar *) sm_str_getdata(smar_rvrs->arv_pa), sm_str_getlen(smar_rvrs->arv_pa)); if (NULL == q) { ret = sm_error_perm(SM_EM_AR, ENOMEM); goto error; } /* ** Set flag before passing smar_rvrs to dns_req_add() ** because smar_rvrs_cb() will free() smar_rvrs. */ SMARV_SET_FLAG(smar_rvrs, SMARV_FL_A4PTR); smar_rvrs->arv_PTR_qsent = 1; ret = dns_req_add(dns_mgr_ctx, q, T_PTR, timeout, smar_rvrs_cb, smar_rvrs); /* XXX check return? */ /* ** query has been "copied" by dns_req_add(). ** Maybe we should let dns_req_add() just "own" the query ** instead of "copying" it? */ SM_CSTR_FREE(q); return ret; error: sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RESOLVER, SM_LOG_ERROR, 0, "sev=ERROR, func=smar_rvrs_rslv, ret=%x", ret); return ret; }