/* Fallback to 'addr' */ void *_ruli_srv_answer_fallback_addr(ruli_srv_t *srv_qry) { ruli_host_t *fall_qry; #ifdef RULI_SRV_DEBUG { char txt_dname_buf[RULI_LIMIT_DNAME_TEXT_BUFSZ]; int txt_dname_len; int result; result = ruli_dname_decode(txt_dname_buf, RULI_LIMIT_DNAME_TEXT_BUFSZ, &txt_dname_len, srv_qry->srv_domain, srv_qry->srv_domain_len); assert(!result); fprintf(stderr, "DEBUG: %s: %s(): %d: address query: " "domain=%s domain_len=%d fallback=%d\n", __FILE__, __PRETTY_FUNCTION__, __LINE__, txt_dname_buf, txt_dname_len, !!(srv_qry->srv_options & RULI_RES_OPT_SRV_NOFALL)); } #endif /* Has the user disabled fallback query? */ if (srv_qry->srv_options & RULI_RES_OPT_SRV_NOFALL) return query_done(srv_qry, RULI_SRV_CODE_OK); /* * Allocate space for fallback query */ fall_qry = (ruli_host_t *) \ ruli_malloc(sizeof(*fall_qry)); if (!fall_qry) return query_done(srv_qry, RULI_SRV_CODE_FALL_OTHER); /* * Initialize fallback query arguments */ fall_qry->host_resolver = srv_qry->srv_resolver; fall_qry->host_on_answer = on_fallback_answer; fall_qry->host_on_answer_arg = srv_qry; fall_qry->host_domain = srv_qry->srv_domain; fall_qry->host_domain_len = srv_qry->srv_domain_len; fall_qry->host_options = srv_qry->srv_options; fall_qry->host_max_cname_depth = RULI_LIMIT_CNAME_DEPTH; /* * Submit fallback query */ if (ruli_host_query_submit(fall_qry)) { ruli_free(fall_qry); return query_done(srv_qry, RULI_SRV_CODE_FALL_OTHER); } /* Wait query answer */ return OOP_CONTINUE; }
int main(int argc, const char *const *argv) { struct timeval *tv, tvbuf; adns_query qu; void *qun_v; adns_answer *answer; int r, maxfd; fd_set readfds, writefds, exceptfds; const char *arg; ensure_adns_init(); while ((arg= *++argv)) process_optarg(arg,&argv,0); if (!ov_pipe && !ads) usageerr("no domains given, and -f/--pipe not used; try --help"); for (;;) { for (;;) { qu= ov_asynch ? 0 : outstanding.head ? outstanding.head->qu : 0; r= adns_check(ads,&qu,&answer,&qun_v); if ((r == EAGAIN) || (r == EWOULDBLOCK)) break; if (r == ESRCH) { if (!ov_pipe) goto x_quit; else break; } assert(!r); query_done(qun_v,answer); } maxfd= 0; FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds); if (ov_pipe) { maxfd= 1; FD_SET(0,&readfds); } tv= 0; adns_beforeselect(ads, &maxfd, &readfds,&writefds,&exceptfds, &tv,&tvbuf,0); ADNS_CLEAR_ERRNO; r= select(maxfd, &readfds,&writefds,&exceptfds, tv); ADNS_CAPTURE_ERRNO; if (r == -1) { if (errno == EINTR) continue; sysfail("select",errno); } adns_afterselect(ads, maxfd, &readfds,&writefds,&exceptfds, 0); if (ov_pipe && FD_ISSET(0,&readfds)) read_stdin(); } x_quit: if (fclose(stdout)) outerr(); quitnow(rcode); }
/* finish fallback query */ static void *fall_query_done(ruli_srv_t *srv_qry, int srv_result_code, ruli_host_t *fall_qry) { #ifdef RULI_SRV_DEBUG fprintf(stderr, "DEBUG: %s: %s(): %d: id=%d query_result=%s [%d]\n", __FILE__, __PRETTY_FUNCTION__, __LINE__, fall_qry->host_query.query_id, srv_result_code ? "FAIL" : "SUCCESS", srv_result_code); #endif f_query_done(fall_qry); return query_done(srv_qry, srv_result_code); }
/* finish walk query */ static void *walk_query_done(walk_t *walk, int srv_result_code) { ruli_srv_t *srv_qry = walk->srv_query; #ifdef RULI_SRV_DEBUG fprintf(stderr, "DEBUG: %s: %s(): %d: id=%d query_result=%s [%d]\n", __FILE__, __PRETTY_FUNCTION__, __LINE__, walk->walk_query.host_query.query_id, srv_result_code ? "FAIL" : "SUCCESS", srv_result_code); #endif w_query_done(walk); return query_done(srv_qry, srv_result_code); }
static void *on_srv_answer(ruli_res_query_t *qry, void *arg) { ruli_srv_t *srv_qry = (ruli_srv_t *) arg; int result; assert(qry->answer_code != RULI_SRV_CODE_VOID); /* * Underlying query failed? */ if (qry->answer_code == RULI_CODE_TIMEOUT) return query_done(srv_qry, RULI_SRV_CODE_ALARM); if (qry->answer_code) return query_done(srv_qry, RULI_SRV_CODE_QUERY_FAILED); #ifdef RULI_SRV_DEBUG { ruli_msg_header_t msg_hdr; msg_hdr = qry->answer_header; fprintf(stderr, "DEBUG: on_srv_answer(): underlying query succeded: " "id=%d rcode=%d qd=%d an=%d ns=%d ar=%d " "answer_buf_size=%d answer_msg_len=%d\n", msg_hdr.id, msg_hdr.rcode, msg_hdr.qdcount, msg_hdr.ancount, msg_hdr.nscount, msg_hdr.arcount, qry->answer_buf_size, qry->answer_msg_len); } #endif /* * Parse answer for SRV records */ assert(sizeof(ruli_uint8_t) == sizeof(char)); result = ruli_parse_message(&srv_qry->parse, &qry->answer_header, (ruli_uint8_t *) qry->answer_buf, qry->answer_msg_len); if (result) return query_done(srv_qry, RULI_SRV_CODE_PARSE_FAILED); /* * Check reply code and answer count */ if ((qry->answer_header.rcode != RULI_RCODE_NOERROR) || (qry->answer_header.ancount < 1)) { #ifdef RULI_SRV_DEBUG fprintf(stderr, "DEBUG: on_srv_answer(): SRV query failed\n"); #endif /* Fallback query */ return srv_qry->fallback(srv_qry); } /* * NOERROR && (ancount > 0) */ assert(qry->answer_header.rcode == RULI_RCODE_NOERROR); assert(qry->answer_header.ancount > 0); /* * Processing of SRV answer: * * Step 1/6: One SRV RR with target == '.' ? * Step 2/6: Parse rdata portion of all SRV RRs * Step 3/6: Sort SRV RRs by priority * Step 4/6: Select SRV RRs by random weighted order * Step 5/6: Build list of srv answers by inspecting additional section * Step 6/6: Launch queries to fill missing addresses, if any */ /* * Step 1/6: One SRV RR with target == '.' ? */ if (qry->answer_header.ancount == 1) { ruli_list_t *an_list = &srv_qry->parse.answer_list; if (ruli_list_size(an_list) == 1) { ruli_rr_t *rr = (ruli_rr_t *) ruli_list_top(an_list); if (rr->qclass == RULI_RR_CLASS_IN) { if (rr->type == RULI_RR_TYPE_SRV) { ruli_srv_rdata_t srv_rdata; if (ruli_parse_rr_srv(&srv_rdata, rr->rdata, rr->rdlength)) return query_done(srv_qry, RULI_SRV_CODE_PARSE_FAILED); /* target == '.' ? */ if (*srv_rdata.target == '\0') { /* Sanity test */ if (srv_rdata.target_len != 1) return query_done(srv_qry, RULI_SRV_CODE_PARSE_FAILED); /* * ruli_srv.c: target=='.': Owner match? */ assert(sizeof(ruli_uint8_t) == sizeof(char)); #ifdef RULI_SRV_DEBUG show_dname("on_srv_answer(): target=='.': qdomain", (const char *) qry->full_dname, qry->full_dname_len); #endif if (ruli_dname_compare(rr->owner, (ruli_uint8_t *) qry->answer_buf, qry->answer_msg_len, (ruli_uint8_t *) qry->full_dname, qry->full_dname_len)) return query_done(srv_qry, RULI_SRV_CODE_PARSE_FAILED); return query_done(srv_qry, RULI_SRV_CODE_UNAVAILABLE); } } } } } /* One SRV RR with target == '.' ? */ /* * Step 2/6: Parse rdata portion of all SRV RRs */ { ruli_list_t *an_list = &srv_qry->parse.answer_list; int an_list_size = ruli_list_size(an_list); int i; for (i = 0; i < an_list_size; ++i) { ruli_rr_t *rr = (ruli_rr_t *) ruli_list_get(an_list, i); ruli_srv_rdata_t *srv_rdata; if (rr->qclass != RULI_RR_CLASS_IN) continue; if (rr->type != RULI_RR_TYPE_SRV) continue; #ifdef RULI_SRV_DEBUG fprintf(stderr, "DEBUG: on_srv_answer(): considering SRV-RR owner: %d of %d\n", (i + 1), an_list_size); #endif if (ruli_dname_compare(rr->owner, (ruli_uint8_t *) qry->answer_buf, qry->answer_msg_len, (ruli_uint8_t *) qry->full_dname, qry->full_dname_len)) return query_done(srv_qry, RULI_SRV_CODE_PARSE_FAILED); #ifdef RULI_SRV_DEBUG fprintf(stderr, "DEBUG: on_srv_answer(): SRV-RR owner OK: %d of %d\n", (i + 1), an_list_size); #endif srv_rdata = (ruli_srv_rdata_t *) ruli_malloc(sizeof(ruli_srv_rdata_t)); if (!srv_rdata) return query_done(srv_qry, RULI_SRV_CODE_MALLOC); if (ruli_list_push(&srv_qry->rr_srv_list, srv_rdata)) { ruli_free(srv_rdata); return query_done(srv_qry, RULI_SRV_CODE_LIST); } if (ruli_parse_rr_srv(srv_rdata, rr->rdata, rr->rdlength)) return query_done(srv_qry, RULI_SRV_CODE_PARSE_FAILED); } } #ifdef RULI_SRV_DEBUG { int i; ruli_list_t *list = &srv_qry->rr_srv_list; fflush(stdout); for (i = 0; i < ruli_list_size(list); ++i) { ruli_srv_rdata_t *srv_rdata = \ (ruli_srv_rdata_t *) ruli_list_get(list, i); fflush(stderr); fprintf(stderr, "DEBUG: on_srv_answer(): SRV RR: " "priority=%d weight=%d port=%d\n", srv_rdata->priority, srv_rdata->weight, srv_rdata->port); fflush(stderr); } } #endif /* * Step 3/6: Sort SRV RRs by priority */ { ruli_list_t *src_list = &srv_qry->rr_srv_list; int src_list_size = ruli_list_size(src_list); int j; /* * Handle every RR based on priority (higher priority first) */ for (j = 0; j < src_list_size; ++j) { ruli_srv_rdata_t *srv_rdata = \ (ruli_srv_rdata_t *) ruli_list_get(src_list, j); ruli_list_t *dst_list = &srv_qry->pri_srv_list; int dst_list_size = ruli_list_size(dst_list); int i; assert(srv_rdata); /* * Find a lower-or-equal priority */ for (i = 0; i < dst_list_size; ++i) { ruli_srv_rdata_t *rd = (ruli_srv_rdata_t *) ruli_list_get(dst_list, i); if (srv_rdata->priority < rd->priority) continue; /* * For this priority, put 0-weight-elements at tail */ if (srv_rdata->weight == 0) { /* * Find begin of next priority and insert there */ for (; i < dst_list_size; ++i) { ruli_srv_rdata_t *s_rd = \ (ruli_srv_rdata_t *) ruli_list_get(dst_list, i); if (srv_rdata->priority != s_rd->priority) break; } /* for */ if (i == dst_list_size) break; /* Insert at tail (of this priority) */ } if (ruli_list_insert_at(dst_list, i, srv_rdata)) return query_done(srv_qry, RULI_SRV_CODE_LIST); srv_rdata = 0; /* mark as handled */ break; } /* for */ /* If not handled yet, insert at tail */ if (srv_rdata) if (ruli_list_push(dst_list, srv_rdata)) return query_done(srv_qry, RULI_SRV_CODE_LIST); } /* while */ } #ifdef RULI_SRV_DEBUG { int i; ruli_list_t *list = &srv_qry->pri_srv_list; fflush(stdout); for (i = 0; i < ruli_list_size(list); ++i) { ruli_srv_rdata_t *srv_rdata = \ (ruli_srv_rdata_t *) ruli_list_get(list, i); fflush(stderr); fprintf(stderr, "DEBUG: on_srv_answer(): priority SRV RR: " "priority=%d weight=%d port=%d\n", srv_rdata->priority, srv_rdata->weight, srv_rdata->port); fflush(stderr); } } #endif /* * Step 4/6: Select SRV RRs by random weighted order */ { ruli_list_t *src_list = &srv_qry->pri_srv_list; ruli_list_t *dst_list = &srv_qry->wei_srv_list; /* * Iterate over every priority */ for (;;) { int src_list_size = ruli_list_size(src_list); ruli_uint16_t curr_priority; int priority_weight_sum; int curr; int i; int rnd; int run_sum; ruli_srv_rdata_t *srv_rd; if (src_list_size < 1) break; /* * Get current priority */ curr = src_list_size - 1; srv_rd = (ruli_srv_rdata_t *) ruli_list_get(src_list, curr); curr_priority = srv_rd->priority; /* * Accumulate weight sum for priority */ priority_weight_sum = 0; for (i = curr; i >= 0; --i) { ruli_srv_rdata_t *rd = (ruli_srv_rdata_t *) ruli_list_get(src_list, i); if (curr_priority != rd->priority) break; priority_weight_sum += rd->weight; } /* for */ /* * Pick random number: 0..priority_weight_sum */ rnd = ruli_rand_next(&srv_qry->srv_resolver->rand_ctx, 0, priority_weight_sum); /* * Select least running sum */ run_sum = 0; for (i = curr; ; --i) { ruli_srv_rdata_t *rd; assert(i >= 0); rd = (ruli_srv_rdata_t *) ruli_list_get(src_list, i); run_sum += rd->weight; if (run_sum < rnd) continue; /* * Move from src_list to dst_list * (Both lists are only referential) */ ruli_list_shift_at(src_list, i); if (ruli_list_push(dst_list, rd)) return query_done(srv_qry, RULI_SRV_CODE_LIST); break; } /* for */ } /* for */ } #ifdef RULI_SRV_DEBUG { int i; ruli_list_t *list = &srv_qry->wei_srv_list; fflush(stdout); for (i = 0; i < ruli_list_size(list); ++i) { ruli_srv_rdata_t *srv_rdata = \ (ruli_srv_rdata_t *) ruli_list_get(list, i); fflush(stderr); fprintf(stderr, "DEBUG: on_srv_answer(): weight SRV RR: " "priority=%d weight=%d port=%d\n", srv_rdata->priority, srv_rdata->weight, srv_rdata->port); fflush(stderr); } } #endif /* * Step 5/6: Build list of srv answers by inspecting additional section */ { ruli_list_t *src_list = &srv_qry->wei_srv_list; ruli_list_t *dst_list = &srv_qry->answer_srv_list; int src_list_size = ruli_list_size(src_list); int i; assert(ruli_list_size(dst_list) == 0); #ifdef RULI_SRV_DEBUG { fflush(stdout); fprintf(stderr, "DEBUG: %s: %s(): %d: " "BEFORE addit inspection: " "srv_rr_list_size=%d srv_answer_list_size=%d\n", __FILE__, __PRETTY_FUNCTION__, __LINE__, src_list_size, ruli_list_size(&srv_qry->answer_srv_list)); fflush(stderr); } #endif /* * Build answer records inspecting additional section */ /* Scan all targets */ for (i = 0; i < src_list_size; ++i) { ruli_srv_rdata_t *rd = (ruli_srv_rdata_t *) ruli_list_get(src_list, i); ruli_srv_entry_t *srv_entry; /* Create SRV entry and append it to list */ srv_entry =_ruli_srv_list_new_entry(dst_list, (const char *) rd->target, rd->target_len, rd->priority, rd->weight, rd->port); if (!srv_entry) return query_done(srv_qry, RULI_SRV_CODE_MALLOC); /* * Look up target address(es) in additional section */ { ruli_list_t *ad_list = &srv_qry->parse.additional_list; int ad_list_size = ruli_list_size(ad_list); int j; /* Scan additional section */ for (j = 0; j < ad_list_size; ++j) { ruli_rr_t *rr = (ruli_rr_t *) ruli_list_get(ad_list, j); ruli_addr_t *addr; if (rr->qclass != RULI_RR_CLASS_IN) continue; if (!ruli_rr_type_is_address(srv_qry->srv_options, rr->type)) continue; /* Compare SRV target against additional record owner */ if (ruli_dname_compare(rr->owner, (ruli_uint8_t *) qry->answer_buf, qry->answer_msg_len, (ruli_uint8_t *) srv_entry->target, srv_entry->target_len)) continue; /* Allocate space for address */ addr = (ruli_addr_t *) ruli_malloc(sizeof(*addr)); if (!addr) return query_done(srv_qry, RULI_SRV_CODE_MALLOC); /* Save space */ if (ruli_list_push(&srv_entry->addr_list, addr)) { ruli_free(addr); return query_done(srv_qry, RULI_SRV_CODE_LIST); } /* Write address into space */ ruli_parse_addr_rr(addr, rr, srv_qry->srv_options); } /* for */ } } /* for */ #ifdef RULI_SRV_DEBUG { fflush(stdout); fprintf(stderr, "DEBUG: %s: %s(): %d: " "AFTER addit inspection: " "srv_rr_list_size=%d srv_answer_list_size=%d\n", __FILE__, __PRETTY_FUNCTION__, __LINE__, src_list_size, ruli_list_size(&srv_qry->answer_srv_list)); fflush(stderr); } #endif assert(ruli_list_size(dst_list) == src_list_size); } /* Build list of srv answers by inspecting additional section */ #ifdef RULI_SRV_DEBUG { int i; ruli_list_t *list = &srv_qry->answer_srv_list; fflush(stdout); for (i = 0; i < ruli_list_size(list); ++i) { ruli_srv_entry_t *srv_entry = \ (ruli_srv_entry_t *) ruli_list_get(list, i); fprintf(stderr, "DEBUG: on_srv_answer(): answer SRV RR: " "priority=%d weight=%d port=%d\n", srv_entry->priority, srv_entry->weight, srv_entry->port); } fflush(stderr); } #endif /* * Step 6/6: Launch queries to fill missing addresses, if any */ assert(srv_qry->under.walk_index == -1); srv_qry->under.walk_index = 0; return _ruli_srv_answer_walk(srv_qry); }
/* Search missing addresses for targets. We make serialized queries so we don't need to keep more than one query reference (walk_query). */ void *_ruli_srv_answer_walk(ruli_srv_t *srv_qry) { ruli_list_t *srv_list = &srv_qry->answer_srv_list; int srv_list_size = ruli_list_size(srv_list); /* Have the user disabled walk query? */ if (srv_qry->srv_options & RULI_RES_OPT_SRV_NOWALK) return query_done(srv_qry, RULI_SRV_CODE_OK); /* * Scan SRV answer targets, considering address lists */ for (; srv_qry->under.walk_index < srv_list_size; ++srv_qry->under.walk_index) { ruli_srv_entry_t *entry = \ (ruli_srv_entry_t *) ruli_list_get(srv_list, srv_qry->under.walk_index); ruli_list_t *addr_list = &entry->addr_list; walk_t *walk_qry; /* If this target already has address(es), skip it */ if (find_addr(addr_list, srv_qry->srv_options)) continue; #ifdef RULI_SRV_DEBUG { char target_txt[RULI_LIMIT_DNAME_TEXT_BUFSZ]; int target_txt_len; int result; result = ruli_dname_decode(target_txt, RULI_LIMIT_DNAME_TEXT_BUFSZ, &target_txt_len, entry->target, entry->target_len); assert(!result); fprintf(stderr, "DEBUG: _ruli_srv_answer_walk(): " "missing target=%s walk_index=%d\n", target_txt, srv_qry->under.walk_index); } #endif /* * Allocate space for auxiliary walk query */ walk_qry = \ (walk_t *) ruli_malloc(sizeof(*walk_qry)); if (!walk_qry) return query_done(srv_qry, RULI_SRV_CODE_WALK_OTHER); walk_qry->srv_query = srv_qry; /* * Initialize walk query arguments */ walk_qry->walk_query.host_resolver = srv_qry->srv_resolver; walk_qry->walk_query.host_on_answer = on_walk_answer; walk_qry->walk_query.host_on_answer_arg = walk_qry; walk_qry->walk_query.host_domain = entry->target; walk_qry->walk_query.host_domain_len = entry->target_len; walk_qry->walk_query.host_options = srv_qry->srv_options; /* RFC 2782 states CNAME aren't valid SRV targets */ walk_qry->walk_query.host_max_cname_depth = (srv_qry->srv_options & RULI_RES_OPT_SRV_CNAME) ? RULI_LIMIT_CNAME_DEPTH : 0; /* * Submit walk query */ if (ruli_host_query_submit(&walk_qry->walk_query)) { ruli_free(walk_qry); return query_done(srv_qry, RULI_SRV_CODE_WALK_QUERY); } /* Wait answer */ return OOP_CONTINUE; } /* for */ /* * All targets scanned, we're done */ return query_done(srv_qry, RULI_SRV_CODE_OK); }