mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-08-31 22:45:39 +00:00
RTT support
This commit is contained in:
@@ -92,6 +92,8 @@ typedef struct query {
|
|||||||
unsigned int magic;
|
unsigned int magic;
|
||||||
fetchctx_t * fctx;
|
fetchctx_t * fctx;
|
||||||
dns_dispatch_t * dispatch;
|
dns_dispatch_t * dispatch;
|
||||||
|
dns_adbaddrinfo_t * addrinfo;
|
||||||
|
isc_time_t start;
|
||||||
/* Locked by fctx lock. */
|
/* Locked by fctx lock. */
|
||||||
dns_messageid_t id;
|
dns_messageid_t id;
|
||||||
dns_dispentry_t * dispentry; /* XXX name */
|
dns_dispentry_t * dispentry; /* XXX name */
|
||||||
@@ -138,9 +140,10 @@ struct fetchctx {
|
|||||||
dns_message_t * qmessage;
|
dns_message_t * qmessage;
|
||||||
dns_message_t * rmessage;
|
dns_message_t * rmessage;
|
||||||
ISC_LIST(resquery_t) queries;
|
ISC_LIST(resquery_t) queries;
|
||||||
ISC_LIST(dns_adbfind_t) finds;
|
dns_adbfindlist_t finds;
|
||||||
dns_adbfind_t * find;
|
dns_adbfind_t * find;
|
||||||
unsigned int pending;
|
unsigned int pending;
|
||||||
|
unsigned int restarts;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define FCTX_MAGIC 0x46212121U /* F!!! */
|
#define FCTX_MAGIC 0x46212121U /* F!!! */
|
||||||
@@ -246,17 +249,42 @@ fctx_stoptimer(fetchctx_t *fctx) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
fctx_cancelquery(resquery_t **queryp, dns_dispatchevent_t **deventp) {
|
fctx_cancelquery(resquery_t **queryp, dns_dispatchevent_t **deventp,
|
||||||
|
isc_time_t *finish)
|
||||||
|
{
|
||||||
fetchctx_t *fctx;
|
fetchctx_t *fctx;
|
||||||
resquery_t *query;
|
resquery_t *query;
|
||||||
|
unsigned int rtt;
|
||||||
|
isc_time_t now;
|
||||||
|
unsigned int factor;
|
||||||
|
|
||||||
query = *queryp;
|
query = *queryp;
|
||||||
fctx = query->fctx;
|
fctx = query->fctx;
|
||||||
|
|
||||||
FCTXTRACE("cancelquery");
|
FCTXTRACE("cancelquery");
|
||||||
|
|
||||||
|
if (finish != NULL) {
|
||||||
|
rtt = (unsigned int)isc_time_microdiff(finish, &query->start);
|
||||||
|
factor = 0;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* We don't have an RTT for this query. Maybe the packet
|
||||||
|
* was lost, or maybe this server is very slow. We don't
|
||||||
|
* know. Increase the RTT.
|
||||||
|
*/
|
||||||
|
rtt = query->addrinfo->srtt + (100000 * fctx->restarts);
|
||||||
|
if (rtt > 10000000)
|
||||||
|
rtt = 10000000;
|
||||||
|
/*
|
||||||
|
* We set 'factor' to 1, so that we will replace the current
|
||||||
|
* RTT.
|
||||||
|
*/
|
||||||
|
factor = 1;
|
||||||
|
}
|
||||||
|
dns_adb_adjustsrtt(fctx->res->view->adb, query->addrinfo, rtt, factor);
|
||||||
|
|
||||||
|
|
||||||
dns_dispatch_removeresponse(query->dispatch, &query->dispentry,
|
dns_dispatch_removeresponse(query->dispatch, &query->dispentry,
|
||||||
deventp);
|
deventp);
|
||||||
ISC_LIST_UNLINK(fctx->queries, query, link);
|
ISC_LIST_UNLINK(fctx->queries, query, link);
|
||||||
@@ -277,7 +305,7 @@ fctx_cancelqueries(fetchctx_t *fctx) {
|
|||||||
query != NULL;
|
query != NULL;
|
||||||
query = next_query) {
|
query = next_query) {
|
||||||
next_query = ISC_LIST_NEXT(query, link);
|
next_query = ISC_LIST_NEXT(query, link);
|
||||||
fctx_cancelquery(&query, NULL);
|
fctx_cancelquery(&query, NULL, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,6 +313,8 @@ static void
|
|||||||
fctx_cleanupfinds(fetchctx_t *fctx) {
|
fctx_cleanupfinds(fetchctx_t *fctx) {
|
||||||
dns_adbfind_t *find, *next_find;
|
dns_adbfind_t *find, *next_find;
|
||||||
|
|
||||||
|
REQUIRE(ISC_LIST_EMPTY(fctx->queries));
|
||||||
|
|
||||||
for (find = ISC_LIST_HEAD(fctx->finds);
|
for (find = ISC_LIST_HEAD(fctx->finds);
|
||||||
find != NULL;
|
find != NULL;
|
||||||
find = next_find) {
|
find = next_find) {
|
||||||
@@ -298,8 +328,8 @@ fctx_cleanupfinds(fetchctx_t *fctx) {
|
|||||||
static inline void
|
static inline void
|
||||||
fctx_stopeverything(fetchctx_t *fctx) {
|
fctx_stopeverything(fetchctx_t *fctx) {
|
||||||
FCTXTRACE("stopeverything");
|
FCTXTRACE("stopeverything");
|
||||||
fctx_cleanupfinds(fctx);
|
|
||||||
fctx_cancelqueries(fctx);
|
fctx_cancelqueries(fctx);
|
||||||
|
fctx_cleanupfinds(fctx);
|
||||||
fctx_stoptimer(fctx);
|
fctx_stoptimer(fctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -364,13 +394,13 @@ resquery_senddone(isc_task_t *task, isc_event_t *event) {
|
|||||||
(void)task;
|
(void)task;
|
||||||
|
|
||||||
if (sevent->result != ISC_R_SUCCESS)
|
if (sevent->result != ISC_R_SUCCESS)
|
||||||
fctx_cancelquery(&query, NULL);
|
fctx_cancelquery(&query, NULL, NULL);
|
||||||
|
|
||||||
isc_event_free(&event);
|
isc_event_free(&event);
|
||||||
}
|
}
|
||||||
|
|
||||||
static isc_result_t
|
static isc_result_t
|
||||||
fctx_sendquery(fetchctx_t *fctx, isc_sockaddr_t *address) {
|
fctx_sendquery(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo) {
|
||||||
resquery_t *query;
|
resquery_t *query;
|
||||||
isc_result_t result;
|
isc_result_t result;
|
||||||
dns_rdataset_t *qrdataset;
|
dns_rdataset_t *qrdataset;
|
||||||
@@ -400,10 +430,20 @@ fctx_sendquery(fetchctx_t *fctx, isc_sockaddr_t *address) {
|
|||||||
goto cleanup_temps;
|
goto cleanup_temps;
|
||||||
|
|
||||||
query = isc_mem_get(res->mctx, sizeof *query);
|
query = isc_mem_get(res->mctx, sizeof *query);
|
||||||
if (query == NULL)
|
if (query == NULL) {
|
||||||
return (ISC_R_NOMEMORY);
|
result = ISC_R_NOMEMORY;
|
||||||
|
goto cleanup_temps;
|
||||||
|
}
|
||||||
isc_buffer_init(&query->buffer, query->data, sizeof query->data,
|
isc_buffer_init(&query->buffer, query->data, sizeof query->data,
|
||||||
ISC_BUFFERTYPE_BINARY);
|
ISC_BUFFERTYPE_BINARY);
|
||||||
|
/*
|
||||||
|
* Note that the caller MUST guarantee that 'addrinfo' will remain
|
||||||
|
* valid until this query is canceled.
|
||||||
|
*/
|
||||||
|
query->addrinfo = addrinfo;
|
||||||
|
result = isc_time_now(&query->start);
|
||||||
|
if (result != ISC_R_SUCCESS)
|
||||||
|
goto cleanup_query;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If this is a TCP query, then we need to make a socket and
|
* If this is a TCP query, then we need to make a socket and
|
||||||
@@ -417,7 +457,7 @@ fctx_sendquery(fetchctx_t *fctx, isc_sockaddr_t *address) {
|
|||||||
result = DNS_R_NOTIMPLEMENTED;
|
result = DNS_R_NOTIMPLEMENTED;
|
||||||
goto cleanup_query;
|
goto cleanup_query;
|
||||||
} else {
|
} else {
|
||||||
switch (isc_sockaddr_pf(address)) {
|
switch (isc_sockaddr_pf(addrinfo->sockaddr)) {
|
||||||
case AF_INET:
|
case AF_INET:
|
||||||
query->dispatch = res->dispatch4;
|
query->dispatch = res->dispatch4;
|
||||||
break;
|
break;
|
||||||
@@ -443,7 +483,7 @@ fctx_sendquery(fetchctx_t *fctx, isc_sockaddr_t *address) {
|
|||||||
*/
|
*/
|
||||||
query->dispentry = NULL;
|
query->dispentry = NULL;
|
||||||
result = dns_dispatch_addresponse(query->dispatch,
|
result = dns_dispatch_addresponse(query->dispatch,
|
||||||
address,
|
addrinfo->sockaddr,
|
||||||
task,
|
task,
|
||||||
resquery_response,
|
resquery_response,
|
||||||
query,
|
query,
|
||||||
@@ -513,7 +553,7 @@ fctx_sendquery(fetchctx_t *fctx, isc_sockaddr_t *address) {
|
|||||||
isc_buffer_used(&query->buffer, &r);
|
isc_buffer_used(&query->buffer, &r);
|
||||||
result = isc_socket_sendto(dns_dispatch_getsocket(query->dispatch),
|
result = isc_socket_sendto(dns_dispatch_getsocket(query->dispatch),
|
||||||
&r, task, resquery_senddone,
|
&r, task, resquery_senddone,
|
||||||
query, address);
|
query, addrinfo->sockaddr);
|
||||||
if (result != ISC_R_SUCCESS)
|
if (result != ISC_R_SUCCESS)
|
||||||
goto cleanup_message;
|
goto cleanup_message;
|
||||||
|
|
||||||
@@ -612,6 +652,61 @@ fctx_finddone(isc_task_t *task, isc_event_t *event) {
|
|||||||
empty_bucket(res);
|
empty_bucket(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sort_adbfind(dns_adbfind_t *find) {
|
||||||
|
dns_adbaddrinfo_t *best, *curr;
|
||||||
|
dns_adbaddrinfolist_t sorted;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Lame N^2 bubble sort.
|
||||||
|
*/
|
||||||
|
|
||||||
|
ISC_LIST_INIT(sorted);
|
||||||
|
while (!ISC_LIST_EMPTY(find->list)) {
|
||||||
|
best = ISC_LIST_HEAD(find->list);
|
||||||
|
curr = ISC_LIST_NEXT(best, publink);
|
||||||
|
while (curr != NULL) {
|
||||||
|
if (curr->srtt < best->srtt)
|
||||||
|
best = curr;
|
||||||
|
curr = ISC_LIST_NEXT(curr, publink);
|
||||||
|
}
|
||||||
|
ISC_LIST_UNLINK(find->list, best, publink);
|
||||||
|
ISC_LIST_APPEND(sorted, best, publink);
|
||||||
|
}
|
||||||
|
find->list = sorted;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sort_finds(fetchctx_t *fctx) {
|
||||||
|
dns_adbfind_t *best, *curr;
|
||||||
|
dns_adbfindlist_t sorted;
|
||||||
|
dns_adbaddrinfo_t *addrinfo, *bestaddrinfo;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Lame N^2 bubble sort.
|
||||||
|
*/
|
||||||
|
|
||||||
|
ISC_LIST_INIT(sorted);
|
||||||
|
while (!ISC_LIST_EMPTY(fctx->finds)) {
|
||||||
|
best = ISC_LIST_HEAD(fctx->finds);
|
||||||
|
bestaddrinfo = ISC_LIST_HEAD(best->list);
|
||||||
|
INSIST(bestaddrinfo != NULL);
|
||||||
|
curr = ISC_LIST_NEXT(best, publink);
|
||||||
|
while (curr != NULL) {
|
||||||
|
addrinfo = ISC_LIST_HEAD(curr->list);
|
||||||
|
INSIST(addrinfo != NULL);
|
||||||
|
if (addrinfo->srtt < bestaddrinfo->srtt) {
|
||||||
|
best = curr;
|
||||||
|
bestaddrinfo = addrinfo;
|
||||||
|
}
|
||||||
|
curr = ISC_LIST_NEXT(curr, publink);
|
||||||
|
}
|
||||||
|
ISC_LIST_UNLINK(fctx->finds, best, publink);
|
||||||
|
ISC_LIST_APPEND(sorted, best, publink);
|
||||||
|
}
|
||||||
|
fctx->finds = sorted;
|
||||||
|
}
|
||||||
|
|
||||||
static isc_result_t
|
static isc_result_t
|
||||||
fctx_getaddresses(fetchctx_t *fctx) {
|
fctx_getaddresses(fetchctx_t *fctx) {
|
||||||
dns_rdata_t rdata;
|
dns_rdata_t rdata;
|
||||||
@@ -625,6 +720,13 @@ fctx_getaddresses(fetchctx_t *fctx) {
|
|||||||
|
|
||||||
FCTXTRACE("getaddresses");
|
FCTXTRACE("getaddresses");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Don't pound on remote servers.
|
||||||
|
*/
|
||||||
|
fctx->restarts++;
|
||||||
|
if (fctx->restarts > 10)
|
||||||
|
return (DNS_R_SERVFAIL);
|
||||||
|
|
||||||
res = fctx->res;
|
res = fctx->res;
|
||||||
options = DNS_ADBFIND_WANTEVENT|DNS_ADBFIND_EMPTYEVENT;
|
options = DNS_ADBFIND_WANTEVENT|DNS_ADBFIND_EMPTYEVENT;
|
||||||
if (res->dispatch4 != NULL)
|
if (res->dispatch4 != NULL)
|
||||||
@@ -635,7 +737,7 @@ fctx_getaddresses(fetchctx_t *fctx) {
|
|||||||
if (result != ISC_R_SUCCESS)
|
if (result != ISC_R_SUCCESS)
|
||||||
return (result);
|
return (result);
|
||||||
|
|
||||||
fctx_cleanupfinds(fctx);
|
INSIST(ISC_LIST_EMPTY(fctx->finds));
|
||||||
|
|
||||||
result = dns_rdataset_first(&fctx->nameservers);
|
result = dns_rdataset_first(&fctx->nameservers);
|
||||||
while (result == ISC_R_SUCCESS) {
|
while (result == ISC_R_SUCCESS) {
|
||||||
@@ -647,7 +749,7 @@ fctx_getaddresses(fetchctx_t *fctx) {
|
|||||||
dns_name_init(&name, NULL);
|
dns_name_init(&name, NULL);
|
||||||
dns_name_fromregion(&name, &r);
|
dns_name_fromregion(&name, &r);
|
||||||
/*
|
/*
|
||||||
* XXXRTH If this name is the same as QNAME, remember
|
* XXXRTH If this name is the same as QNAME, remember to
|
||||||
* skip it, and remember that we did so so we can
|
* skip it, and remember that we did so so we can
|
||||||
* use an ancestor QDOMAIN if we find no addresses.
|
* use an ancestor QDOMAIN if we find no addresses.
|
||||||
*/
|
*/
|
||||||
@@ -668,9 +770,7 @@ fctx_getaddresses(fetchctx_t *fctx) {
|
|||||||
* name.
|
* name.
|
||||||
*/
|
*/
|
||||||
INSIST((find->options & DNS_ADBFIND_WANTEVENT) == 0);
|
INSIST((find->options & DNS_ADBFIND_WANTEVENT) == 0);
|
||||||
/*
|
sort_adbfind(find);
|
||||||
* XXXRTH Sort.
|
|
||||||
*/
|
|
||||||
ISC_LIST_APPEND(fctx->finds, find, publink);
|
ISC_LIST_APPEND(fctx->finds, find, publink);
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
@@ -713,11 +813,7 @@ fctx_getaddresses(fetchctx_t *fctx) {
|
|||||||
* We've found some addresses. We might still be looking
|
* We've found some addresses. We might still be looking
|
||||||
* for more addresses.
|
* for more addresses.
|
||||||
*/
|
*/
|
||||||
|
sort_finds(fctx);
|
||||||
/*
|
|
||||||
* XXXRTH Sort.
|
|
||||||
*/
|
|
||||||
|
|
||||||
result = ISC_R_SUCCESS;
|
result = ISC_R_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -732,7 +828,6 @@ static inline dns_adbaddrinfo_t *
|
|||||||
fctx_nextaddress(fetchctx_t *fctx) {
|
fctx_nextaddress(fetchctx_t *fctx) {
|
||||||
dns_adbfind_t *find;
|
dns_adbfind_t *find;
|
||||||
dns_adbaddrinfo_t *addrinfo;
|
dns_adbaddrinfo_t *addrinfo;
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return the next untried address, if any.
|
* Return the next untried address, if any.
|
||||||
@@ -755,8 +850,6 @@ fctx_nextaddress(fetchctx_t *fctx) {
|
|||||||
*/
|
*/
|
||||||
addrinfo = NULL;
|
addrinfo = NULL;
|
||||||
while (find != fctx->find) {
|
while (find != fctx->find) {
|
||||||
count++;
|
|
||||||
INSIST(count < 1000);
|
|
||||||
for (addrinfo = ISC_LIST_HEAD(find->list);
|
for (addrinfo = ISC_LIST_HEAD(find->list);
|
||||||
addrinfo != NULL;
|
addrinfo != NULL;
|
||||||
addrinfo = ISC_LIST_NEXT(addrinfo, publink)) {
|
addrinfo = ISC_LIST_NEXT(addrinfo, publink)) {
|
||||||
@@ -774,6 +867,10 @@ fctx_nextaddress(fetchctx_t *fctx) {
|
|||||||
|
|
||||||
fctx->find = find;
|
fctx->find = find;
|
||||||
|
|
||||||
|
/* XXX */
|
||||||
|
if (addrinfo != NULL)
|
||||||
|
printf("RTT = %u\n", addrinfo->srtt);
|
||||||
|
|
||||||
return (addrinfo);
|
return (addrinfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -796,6 +893,7 @@ fctx_try(fetchctx_t *fctx) {
|
|||||||
* We have no more addresses. Start over.
|
* We have no more addresses. Start over.
|
||||||
*/
|
*/
|
||||||
fctx_cancelqueries(fctx);
|
fctx_cancelqueries(fctx);
|
||||||
|
fctx_cleanupfinds(fctx);
|
||||||
result = fctx_getaddresses(fctx);
|
result = fctx_getaddresses(fctx);
|
||||||
if (result == DNS_R_WAIT) {
|
if (result == DNS_R_WAIT) {
|
||||||
/*
|
/*
|
||||||
@@ -826,7 +924,7 @@ fctx_try(fetchctx_t *fctx) {
|
|||||||
* just send a single query.
|
* just send a single query.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
result = fctx_sendquery(fctx, addrinfo->sockaddr);
|
result = fctx_sendquery(fctx, addrinfo);
|
||||||
if (result != ISC_R_SUCCESS)
|
if (result != ISC_R_SUCCESS)
|
||||||
fctx_done(fctx, result);
|
fctx_done(fctx, result);
|
||||||
}
|
}
|
||||||
@@ -1083,6 +1181,7 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type,
|
|||||||
ISC_LIST_INIT(fctx->finds);
|
ISC_LIST_INIT(fctx->finds);
|
||||||
fctx->find = NULL;
|
fctx->find = NULL;
|
||||||
fctx->pending = 0;
|
fctx->pending = 0;
|
||||||
|
fctx->restarts = 0;
|
||||||
fctx->attributes = 0;
|
fctx->attributes = 0;
|
||||||
|
|
||||||
fctx->qmessage = NULL;
|
fctx->qmessage = NULL;
|
||||||
@@ -2265,6 +2364,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
|
|||||||
dns_name_t *fname;
|
dns_name_t *fname;
|
||||||
dns_fixedname_t foundname;
|
dns_fixedname_t foundname;
|
||||||
isc_stdtime_t now;
|
isc_stdtime_t now;
|
||||||
|
isc_time_t tnow, *finish;
|
||||||
|
|
||||||
REQUIRE(VALID_QUERY(query));
|
REQUIRE(VALID_QUERY(query));
|
||||||
fctx = query->fctx;
|
fctx = query->fctx;
|
||||||
@@ -2280,11 +2380,22 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
|
|||||||
broken_server = ISC_FALSE;
|
broken_server = ISC_FALSE;
|
||||||
get_nameservers = ISC_FALSE;
|
get_nameservers = ISC_FALSE;
|
||||||
covers = 0;
|
covers = 0;
|
||||||
|
finish = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XXXRTH We should really get the current time just once. We
|
||||||
|
* need a routine to convert from an isc_time_t to an
|
||||||
|
* isc_stdtime_t.
|
||||||
|
*/
|
||||||
|
result = isc_time_now(&tnow);
|
||||||
|
if (result != ISC_R_SUCCESS)
|
||||||
|
goto done;
|
||||||
|
finish = &tnow;
|
||||||
result = isc_stdtime_get(&now);
|
result = isc_stdtime_get(&now);
|
||||||
if (result != ISC_R_SUCCESS)
|
if (result != ISC_R_SUCCESS)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
|
|
||||||
message = fctx->rmessage;
|
message = fctx->rmessage;
|
||||||
message->querytsig = query->tsig;
|
message->querytsig = query->tsig;
|
||||||
message->tsigkey = query->tsigkey;
|
message->tsigkey = query->tsigkey;
|
||||||
@@ -2429,14 +2540,10 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
|
|||||||
|
|
||||||
done:
|
done:
|
||||||
/*
|
/*
|
||||||
* Give the event back to the dispatcher.
|
* Cancel the query.
|
||||||
*/
|
*/
|
||||||
dns_dispatch_freeevent(query->dispatch, query->dispentry, &devent);
|
fctx_cancelquery(&query, &devent, finish);
|
||||||
|
|
||||||
/*
|
|
||||||
* XXXRTH Record round-trip statistics here. Note that 'result'
|
|
||||||
* MUST NOT be changed by this recording process.
|
|
||||||
*/
|
|
||||||
if (keep_trying) {
|
if (keep_trying) {
|
||||||
if (result == DNS_R_FORMERR)
|
if (result == DNS_R_FORMERR)
|
||||||
broken_server = ISC_TRUE;
|
broken_server = ISC_TRUE;
|
||||||
@@ -2496,6 +2603,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
|
|||||||
fctx_done(fctx, DNS_R_SERVFAIL);
|
fctx_done(fctx, DNS_R_SERVFAIL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
fctx_cancelqueries(fctx);
|
||||||
fctx_cleanupfinds(fctx);
|
fctx_cleanupfinds(fctx);
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
|
Reference in New Issue
Block a user