?? resolver.c
字號(hào):
PJ_ASSERT_ON_FAIL(resolver, return); pj_mutex_lock(resolver->mutex); pj_timer_heap_poll(resolver->timer, NULL); pj_mutex_unlock(resolver->mutex); pj_ioqueue_poll(resolver->ioqueue, timeout);}/* Get one query node from the free node, if any, or allocate * a new one. */static pj_dns_async_query *alloc_qnode(pj_dns_resolver *resolver, unsigned options, void *user_data, pj_dns_callback *cb){ pj_dns_async_query *q; /* Merge query options with resolver options */ options |= resolver->settings.options; if (!pj_list_empty(&resolver->query_free_nodes)) { q = resolver->query_free_nodes.next; pj_list_erase(q); pj_bzero(q, sizeof(*q)); } else { q = pj_pool_zalloc(resolver->pool, sizeof(*q)); } /* Init query */ q->resolver = resolver; q->options = options; q->user_data = user_data; q->cb = cb; pj_list_init(&q->child_head); return q;}/* * Transmit query. */static pj_status_t transmit_query(pj_dns_resolver *resolver, pj_dns_async_query *q){ unsigned pkt_size; unsigned i, server_cnt; unsigned servers[PJ_DNS_RESOLVER_MAX_NS]; pj_time_val now; pj_str_t name; pj_time_val delay; pj_status_t status; /* Create DNS query packet */ pkt_size = sizeof(resolver->udp_tx_pkt); name = pj_str(q->key.name); status = pj_dns_make_query(resolver->udp_tx_pkt, &pkt_size, q->id, q->key.qtype, &name); if (status != PJ_SUCCESS) { return status; } /* Select which nameserver(s) to send requests to. */ server_cnt = PJ_ARRAY_SIZE(servers); status = select_nameservers(resolver, &server_cnt, servers); if (status != PJ_SUCCESS) { return status; } if (server_cnt == 0) { return PJLIB_UTIL_EDNSNOWORKINGNS; } /* Start retransmit/timeout timer for the query */ pj_assert(q->timer_entry.id == 0); q->timer_entry.id = 1; q->timer_entry.user_data = q; q->timer_entry.cb = &on_timeout; delay.sec = 0; delay.msec = resolver->settings.qretr_delay; pj_time_val_normalize(&delay); status = pj_timer_heap_schedule(resolver->timer, &q->timer_entry, &delay); if (status != PJ_SUCCESS) { return status; } /* Get current time. */ pj_gettimeofday(&now); /* Send the packet to name servers */ for (i=0; i<server_cnt; ++i) { pj_ssize_t sent = (pj_ssize_t) pkt_size; struct nameserver *ns = &resolver->ns[servers[i]]; pj_sock_sendto(resolver->udp_sock, resolver->udp_tx_pkt, &sent, 0, &resolver->ns[servers[i]].addr, sizeof(pj_sockaddr_in)); PJ_LOG(4,(resolver->name.ptr, "%s %d bytes to NS %d (%s:%d): DNS %s query for %s", (q->transmit_cnt==0? "Transmitting":"Re-transmitting"), (int)sent, servers[i], pj_inet_ntoa(ns->addr.sin_addr), (int)pj_ntohs(ns->addr.sin_port), pj_dns_get_type_name(q->key.qtype), q->key.name)); if (ns->q_id == 0) { ns->q_id = q->id; ns->sent_time = now; } } ++q->transmit_cnt; return PJ_SUCCESS;}/* * Initialize resource key for hash table lookup. */static void init_res_key(struct res_key *key, int type, const pj_str_t *name){ unsigned i, len; char *dst = key->name; const char *src = name->ptr; pj_bzero(key, sizeof(struct res_key)); key->qtype = (pj_uint16_t)type; len = name->slen; if (len > PJ_MAX_HOSTNAME) len = PJ_MAX_HOSTNAME; /* Copy key, in lowercase */ for (i=0; i<len; ++i) { *dst++ = (char)pj_tolower(*src++); }}/* * Create and start asynchronous DNS query for a single resource. */PJ_DEF(pj_status_t) pj_dns_resolver_start_query( pj_dns_resolver *resolver, const pj_str_t *name, int type, unsigned options, pj_dns_callback *cb, void *user_data, pj_dns_async_query **p_query){ pj_time_val now; struct res_key key; struct cached_res *cache; pj_dns_async_query *q; pj_uint32_t hval; pj_status_t status = PJ_SUCCESS; /* Validate arguments */ PJ_ASSERT_RETURN(resolver && name && type, PJ_EINVAL); /* Check name is not too long. */ PJ_ASSERT_RETURN(name->slen>0 && name->slen < PJ_MAX_HOSTNAME, PJ_ENAMETOOLONG); /* Check type */ PJ_ASSERT_RETURN(type > 0 && type < 0xFFFF, PJ_EINVAL); if (p_query) *p_query = NULL; /* Build resource key for looking up hash tables */ init_res_key(&key, type, name); /* Start working with the resolver */ pj_mutex_lock(resolver->mutex); /* Get current time. */ pj_gettimeofday(&now); /* First, check if we have cached response for the specified name/type, * and the cached entry has not expired. */ hval = 0; cache = pj_hash_get(resolver->hrescache, &key, sizeof(key), &hval); if (cache) { /* We've found a cached entry. */ /* Check for expiration */ if (PJ_TIME_VAL_GT(cache->expiry_time, now)) { /* Log */ PJ_LOG(5,(resolver->name.ptr, "Picked up DNS %s record for %.*s from cache, ttl=%d", pj_dns_get_type_name(type), (int)name->slen, name->ptr, (int)(cache->expiry_time.sec - now.sec))); /* Map DNS Rcode in the response into PJLIB status name space */ status = PJ_DNS_GET_RCODE(cache->pkt->hdr.flags); status = PJ_STATUS_FROM_DNS_RCODE(status); /* This cached response is still valid. Just return this * response to caller. */ if (cb) { (*cb)(user_data, status, cache->pkt); } /* Done. No host resolution is necessary */ /* Must return PJ_SUCCESS */ status = PJ_SUCCESS; goto on_return; } /* At this point, we have a cached entry, but this entry has expired. * Remove this entry from the cached list. */ pj_hash_set(NULL, resolver->hrescache, &key, sizeof(key), 0, NULL); /* Store the entry into free nodes */ pj_list_push_back(&resolver->res_free_nodes, cache); /* Must continue with creating a query now */ } /* Next, check if we have pending query on the same resource */ q = pj_hash_get(resolver->hquerybyres, &key, sizeof(key), NULL); if (q) { /* Yes, there's another pending query to the same key. * Just create a new child query and add this query to * pending query's child queries. */ pj_dns_async_query *nq; nq = alloc_qnode(resolver, options, user_data, cb); pj_list_push_back(&q->child_head, nq); /* Done. This child query will be notified once the "parent" * query completes. */ status = PJ_SUCCESS; goto on_return; } /* There's no pending query to the same key, initiate a new one. */ q = alloc_qnode(resolver, options, user_data, cb); /* Save the ID and key */ q->id = resolver->last_id++; if (resolver->last_id == 0) resolver->last_id = 1; pj_memcpy(&q->key, &key, sizeof(struct res_key)); /* Send the query */ status = transmit_query(resolver, q); if (status != PJ_SUCCESS) { pj_list_push_back(&resolver->query_free_nodes, q); goto on_return; } /* Add query entry to the hash tables */ pj_hash_set_np(resolver->hquerybyid, &q->id, sizeof(q->id), 0, q->hbufid, q); pj_hash_set_np(resolver->hquerybyres, &q->key, sizeof(q->key), 0, q->hbufkey, q); if (p_query) *p_query = q;on_return: pj_mutex_unlock(resolver->mutex); return status;}/* * Cancel a pending query. */PJ_DEF(pj_status_t) pj_dns_resolver_cancel_query(pj_dns_async_query *query, pj_bool_t notify){ pj_dns_callback *cb; PJ_ASSERT_RETURN(query, PJ_EINVAL); pj_mutex_lock(query->resolver->mutex); cb = query->cb; query->cb = NULL; if (notify) (*cb)(query->user_data, PJ_ECANCELLED, NULL); pj_mutex_unlock(query->resolver->mutex); return PJ_SUCCESS;}/* Set nameserver state */static void set_nameserver_state(pj_dns_resolver *resolver, unsigned index, enum ns_state state, const pj_time_val *now){ struct nameserver *ns = &resolver->ns[index]; ns->state = state; ns->state_expiry = *now; if (state == STATE_PROBING) ns->state_expiry.sec += ((PJ_DNS_RESOLVER_QUERY_RETRANSMIT_COUNT + 2) * PJ_DNS_RESOLVER_QUERY_RETRANSMIT_DELAY) / 1000; else if (state == STATE_ACTIVE) ns->state_expiry.sec += PJ_DNS_RESOLVER_GOOD_NS_TTL; else ns->state_expiry.sec += PJ_DNS_RESOLVER_BAD_NS_TTL;}/* Select which nameserver(s) to use. Note this may return multiple * name servers. The algorithm to select which nameservers to be * sent the request to is as follows: * - select the first nameserver that is known to be good for the * last PJ_DNS_RESOLVER_GOOD_NS_TTL interval. * - for all NSes, if last_known_good >= PJ_DNS_RESOLVER_GOOD_NS_TTL, * include the NS to re-check again that the server is still good, * unless the NS is known to be bad in the last PJ_DNS_RESOLVER_BAD_NS_TTL * interval. * - for all NSes, if last_known_bad >= PJ_DNS_RESOLVER_BAD_NS_TTL, * also include the NS to re-check again that the server is still bad. */static pj_status_t select_nameservers(pj_dns_resolver *resolver, unsigned *count, unsigned servers[]){ unsigned i, max_count=*count; int min; pj_time_val now; pj_assert(max_count > 0); *count = 0; servers[0] = 0xFFFF; /* Check that nameservers are configured. */ if (resolver->ns_count == 0) return PJLIB_UTIL_EDNSNONS; pj_gettimeofday(&now); /* Select one Active nameserver with best response time. */ for (min=-1, i=0; i<resolver->ns_count; ++i) { struct nameserver *ns = &resolver->ns[i]; if (ns->state != STATE_ACTIVE) continue; if (min == -1) min = i; else if (PJ_TIME_VAL_LT(ns->rt_delay, resolver->ns[min].rt_delay)) min = i; } if (min != -1) { servers[0] = min; ++(*count); } /* Scan nameservers. */ for (i=0; i<resolver->ns_count && *count < max_count; ++i) { struct nameserver *ns = &resolver->ns[i]; if (PJ_TIME_VAL_LTE(ns->state_expiry, now)) { if (ns->state == STATE_PROBING) { set_nameserver_state(resolver, i, STATE_BAD, &now); } else { set_nameserver_state(resolver, i, STATE_PROBING, &now); if ((int)i != min) { servers[*count] = i; ++(*count); } } } else if (ns->state == STATE_PROBING && (int)i != min) { servers[*count] = i; ++(*count); } } return PJ_SUCCESS;}/* Update name server status */static void report_nameserver_status(pj_dns_resolver *resolver, const pj_sockaddr_in *ns_addr, const pj_dns_parsed_packet *pkt){ unsigned i; int rcode; pj_uint32_t q_id; pj_time_val now; pj_bool_t is_good; /* Only mark nameserver as "bad" if it returned non-parseable response or * it returned the following status codes */ if (pkt) { rcode = PJ_DNS_GET_RCODE(pkt->hdr.flags); q_id = pkt->hdr.id; } else { rcode = 0; q_id = (pj_uint32_t)-1; } if (!pkt || rcode == PJ_DNS_RCODE_SERVFAIL || rcode == PJ_DNS_RCODE_REFUSED || rcode == PJ_DNS_RCODE_NOTAUTH) { is_good = PJ_FALSE; } else { is_good = PJ_TRUE; } /* Mark time */ pj_gettimeofday(&now); /* Recheck all nameservers. */ for (i=0; i<resolver->ns_count; ++i) { struct nameserver *ns = &resolver->ns[i]; if (ns->addr.sin_addr.s_addr == ns_addr->sin_addr.s_addr && ns->addr.sin_port == ns_addr->sin_port && ns->addr.sin_family == ns_addr->sin_family) { if (q_id == ns->q_id) { /* Calculate response time */ pj_time_val rt = now; PJ_TIME_VAL_SUB(rt, ns->sent_time); ns->rt_delay = rt; ns->q_id = 0; } set_nameserver_state(resolver, i, (is_good ? STATE_ACTIVE : STATE_BAD), &now); break; } }}/* Update response cache */static void update_res_cache(pj_dns_resolver *resolver, const struct res_key *key, pj_status_t status, pj_bool_t set_expiry, const pj_dns_parsed_packet *pkt){ struct cached_res *cache; pj_pool_t *res_pool; pj_uint32_t hval=0, ttl; PJ_USE_EXCEPTION; /* If status is unsuccessful, clear the same entry from the cache */
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -