1 |
|
|
/* |
2 |
|
|
* Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC") |
3 |
|
|
* Copyright (C) 2000-2003 Internet Software Consortium. |
4 |
|
|
* |
5 |
|
|
* Permission to use, copy, modify, and/or distribute this software for any |
6 |
|
|
* purpose with or without fee is hereby granted, provided that the above |
7 |
|
|
* copyright notice and this permission notice appear in all copies. |
8 |
|
|
* |
9 |
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH |
10 |
|
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY |
11 |
|
|
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, |
12 |
|
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM |
13 |
|
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE |
14 |
|
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
15 |
|
|
* PERFORMANCE OF THIS SOFTWARE. |
16 |
|
|
*/ |
17 |
|
|
|
18 |
|
|
/* $ISC: nslookup.c,v 1.101.18.15 2007/08/28 07:19:55 tbox Exp $ */ |
19 |
|
|
|
20 |
|
|
#include <config.h> |
21 |
|
|
|
22 |
|
|
#include <stdlib.h> |
23 |
|
|
#include <unistd.h> |
24 |
|
|
|
25 |
|
|
#include <arpa/nameser.h> |
26 |
|
|
|
27 |
|
|
#include <isc/app.h> |
28 |
|
|
#include <isc/buffer.h> |
29 |
|
|
#include <isc/commandline.h> |
30 |
|
|
#include <isc/event.h> |
31 |
|
|
#include <isc/parseint.h> |
32 |
|
|
#include <isc/string.h> |
33 |
|
|
#include <isc/timer.h> |
34 |
|
|
#include <isc/util.h> |
35 |
|
|
#include <isc/task.h> |
36 |
|
|
#include <isc/netaddr.h> |
37 |
|
|
|
38 |
|
|
#include <dns/message.h> |
39 |
|
|
#include <dns/name.h> |
40 |
|
|
#include <dns/fixedname.h> |
41 |
|
|
#include <dns/rdata.h> |
42 |
|
|
#include <dns/rdataclass.h> |
43 |
|
|
#include <dns/rdataset.h> |
44 |
|
|
#include <dns/rdatastruct.h> |
45 |
|
|
#include <dns/rdatatype.h> |
46 |
|
|
#include <dns/byaddr.h> |
47 |
|
|
|
48 |
|
|
#include <dig/dig.h> |
49 |
|
|
|
50 |
|
|
static isc_boolean_t short_form = ISC_TRUE, |
51 |
|
|
tcpmode = ISC_FALSE, |
52 |
|
|
identify = ISC_FALSE, stats = ISC_TRUE, |
53 |
|
|
comments = ISC_TRUE, section_question = ISC_TRUE, |
54 |
|
|
section_answer = ISC_TRUE, section_authority = ISC_TRUE, |
55 |
|
|
section_additional = ISC_TRUE, recurse = ISC_TRUE, |
56 |
|
|
aaonly = ISC_FALSE, nofail = ISC_TRUE; |
57 |
|
|
|
58 |
|
|
static isc_boolean_t in_use = ISC_FALSE; |
59 |
|
|
static char defclass[MXRD] = "IN"; |
60 |
|
|
static char deftype[MXRD] = "A"; |
61 |
|
|
static isc_event_t *global_event = NULL; |
62 |
|
|
|
63 |
|
|
static char domainopt[DNS_NAME_MAXTEXT]; |
64 |
|
|
|
65 |
|
|
static const char *rcodetext[] = { |
66 |
|
|
"NOERROR", |
67 |
|
|
"FORMERR", |
68 |
|
|
"SERVFAIL", |
69 |
|
|
"NXDOMAIN", |
70 |
|
|
"NOTIMP", |
71 |
|
|
"REFUSED", |
72 |
|
|
"YXDOMAIN", |
73 |
|
|
"YXRRSET", |
74 |
|
|
"NXRRSET", |
75 |
|
|
"NOTAUTH", |
76 |
|
|
"NOTZONE", |
77 |
|
|
"RESERVED11", |
78 |
|
|
"RESERVED12", |
79 |
|
|
"RESERVED13", |
80 |
|
|
"RESERVED14", |
81 |
|
|
"RESERVED15", |
82 |
|
|
"BADVERS" |
83 |
|
|
}; |
84 |
|
|
|
85 |
|
|
static const char *rtypetext[] = { |
86 |
|
|
"rtype_0 = ", /* 0 */ |
87 |
|
|
"internet address = ", /* 1 */ |
88 |
|
|
"nameserver = ", /* 2 */ |
89 |
|
|
"md = ", /* 3 */ |
90 |
|
|
"mf = ", /* 4 */ |
91 |
|
|
"canonical name = ", /* 5 */ |
92 |
|
|
"soa = ", /* 6 */ |
93 |
|
|
"mb = ", /* 7 */ |
94 |
|
|
"mg = ", /* 8 */ |
95 |
|
|
"mr = ", /* 9 */ |
96 |
|
|
"rtype_10 = ", /* 10 */ |
97 |
|
|
"protocol = ", /* 11 */ |
98 |
|
|
"name = ", /* 12 */ |
99 |
|
|
"hinfo = ", /* 13 */ |
100 |
|
|
"minfo = ", /* 14 */ |
101 |
|
|
"mail exchanger = ", /* 15 */ |
102 |
|
|
"text = ", /* 16 */ |
103 |
|
|
"rp = ", /* 17 */ |
104 |
|
|
"afsdb = ", /* 18 */ |
105 |
|
|
"x25 address = ", /* 19 */ |
106 |
|
|
"isdn address = ", /* 20 */ |
107 |
|
|
"rt = ", /* 21 */ |
108 |
|
|
"nsap = ", /* 22 */ |
109 |
|
|
"nsap_ptr = ", /* 23 */ |
110 |
|
|
"signature = ", /* 24 */ |
111 |
|
|
"key = ", /* 25 */ |
112 |
|
|
"px = ", /* 26 */ |
113 |
|
|
"gpos = ", /* 27 */ |
114 |
|
|
"has AAAA address ", /* 28 */ |
115 |
|
|
"loc = ", /* 29 */ |
116 |
|
|
"next = ", /* 30 */ |
117 |
|
|
"rtype_31 = ", /* 31 */ |
118 |
|
|
"rtype_32 = ", /* 32 */ |
119 |
|
|
"service = ", /* 33 */ |
120 |
|
|
"rtype_34 = ", /* 34 */ |
121 |
|
|
"naptr = ", /* 35 */ |
122 |
|
|
"kx = ", /* 36 */ |
123 |
|
|
"cert = ", /* 37 */ |
124 |
|
|
"v6 address = ", /* 38 */ |
125 |
|
|
"dname = ", /* 39 */ |
126 |
|
|
"rtype_40 = ", /* 40 */ |
127 |
|
|
"optional = " /* 41 */ |
128 |
|
|
}; |
129 |
|
|
|
130 |
|
|
#define N_KNOWN_RRTYPES (sizeof(rtypetext) / sizeof(rtypetext[0])) |
131 |
|
|
|
132 |
|
|
static void flush_lookup_list(void); |
133 |
|
|
static void getinput(isc_task_t *task, isc_event_t *event); |
134 |
|
|
|
135 |
|
|
void |
136 |
|
|
dighost_shutdown(void) { |
137 |
|
|
isc_event_t *event = global_event; |
138 |
|
|
|
139 |
|
|
flush_lookup_list(); |
140 |
|
|
debug("dighost_shutdown()"); |
141 |
|
|
|
142 |
|
|
if (!in_use) { |
143 |
|
|
isc_app_shutdown(); |
144 |
|
|
return; |
145 |
|
|
} |
146 |
|
|
|
147 |
|
|
isc_task_send(global_task, &event); |
148 |
|
|
} |
149 |
|
|
|
150 |
|
|
static void |
151 |
|
|
printsoa(dns_rdata_t *rdata) { |
152 |
|
|
dns_rdata_soa_t soa; |
153 |
|
|
isc_result_t result; |
154 |
|
|
char namebuf[DNS_NAME_FORMATSIZE]; |
155 |
|
|
|
156 |
|
|
result = dns_rdata_tostruct(rdata, &soa, NULL); |
157 |
|
|
check_result(result, "dns_rdata_tostruct"); |
158 |
|
|
|
159 |
|
|
dns_name_format(&soa.origin, namebuf, sizeof(namebuf)); |
160 |
|
|
printf("\torigin = %s\n", namebuf); |
161 |
|
|
dns_name_format(&soa.contact, namebuf, sizeof(namebuf)); |
162 |
|
|
printf("\tmail addr = %s\n", namebuf); |
163 |
|
|
printf("\tserial = %u\n", soa.serial); |
164 |
|
|
printf("\trefresh = %u\n", soa.refresh); |
165 |
|
|
printf("\tretry = %u\n", soa.retry); |
166 |
|
|
printf("\texpire = %u\n", soa.expire); |
167 |
|
|
printf("\tminimum = %u\n", soa.minimum); |
168 |
|
|
dns_rdata_freestruct(&soa); |
169 |
|
|
} |
170 |
|
|
|
171 |
|
|
static void |
172 |
|
|
printa(dns_rdata_t *rdata) { |
173 |
|
|
isc_result_t result; |
174 |
|
|
char text[sizeof("255.255.255.255")]; |
175 |
|
|
isc_buffer_t b; |
176 |
|
|
|
177 |
|
|
isc_buffer_init(&b, text, sizeof(text)); |
178 |
|
|
result = dns_rdata_totext(rdata, NULL, &b); |
179 |
|
|
check_result(result, "dns_rdata_totext"); |
180 |
|
|
printf("Address: %.*s\n", (int)isc_buffer_usedlength(&b), |
181 |
|
|
(char *)isc_buffer_base(&b)); |
182 |
|
|
} |
183 |
|
|
#ifdef DIG_SIGCHASE |
184 |
|
|
/* Just for compatibility : not use in host program */ |
185 |
|
|
isc_result_t |
186 |
|
|
printrdataset(dns_name_t *owner_name, dns_rdataset_t *rdataset, |
187 |
|
|
isc_buffer_t *target) |
188 |
|
|
{ |
189 |
|
|
UNUSED(owner_name); |
190 |
|
|
UNUSED(rdataset); |
191 |
|
|
UNUSED(target); |
192 |
|
|
return(ISC_FALSE); |
193 |
|
|
} |
194 |
|
|
#endif |
195 |
|
|
static void |
196 |
|
|
printrdata(dns_rdata_t *rdata) { |
197 |
|
|
isc_result_t result; |
198 |
|
|
isc_buffer_t *b = NULL; |
199 |
|
|
unsigned int size = 1024; |
200 |
|
|
isc_boolean_t done = ISC_FALSE; |
201 |
|
|
|
202 |
|
|
if (rdata->type < N_KNOWN_RRTYPES) |
203 |
|
|
printf("%s", rtypetext[rdata->type]); |
204 |
|
|
else |
205 |
|
|
printf("rdata_%d = ", rdata->type); |
206 |
|
|
|
207 |
|
|
while (!done) { |
208 |
|
|
result = isc_buffer_allocate(mctx, &b, size); |
209 |
|
|
if (result != ISC_R_SUCCESS) |
210 |
|
|
check_result(result, "isc_buffer_allocate"); |
211 |
|
|
result = dns_rdata_totext(rdata, NULL, b); |
212 |
|
|
if (result == ISC_R_SUCCESS) { |
213 |
|
|
printf("%.*s\n", (int)isc_buffer_usedlength(b), |
214 |
|
|
(char *)isc_buffer_base(b)); |
215 |
|
|
done = ISC_TRUE; |
216 |
|
|
} else if (result != ISC_R_NOSPACE) |
217 |
|
|
check_result(result, "dns_rdata_totext"); |
218 |
|
|
isc_buffer_free(&b); |
219 |
|
|
size *= 2; |
220 |
|
|
} |
221 |
|
|
} |
222 |
|
|
|
223 |
|
|
static isc_result_t |
224 |
|
|
printsection(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers, |
225 |
|
|
dns_section_t section) { |
226 |
|
|
isc_result_t result, loopresult; |
227 |
|
|
dns_name_t *name; |
228 |
|
|
dns_rdataset_t *rdataset = NULL; |
229 |
|
|
dns_rdata_t rdata = DNS_RDATA_INIT; |
230 |
|
|
char namebuf[DNS_NAME_FORMATSIZE]; |
231 |
|
|
|
232 |
|
|
UNUSED(query); |
233 |
|
|
UNUSED(headers); |
234 |
|
|
|
235 |
|
|
debug("printsection()"); |
236 |
|
|
|
237 |
|
|
result = dns_message_firstname(msg, section); |
238 |
|
|
if (result == ISC_R_NOMORE) |
239 |
|
|
return (ISC_R_SUCCESS); |
240 |
|
|
else if (result != ISC_R_SUCCESS) |
241 |
|
|
return (result); |
242 |
|
|
for (;;) { |
243 |
|
|
name = NULL; |
244 |
|
|
dns_message_currentname(msg, section, |
245 |
|
|
&name); |
246 |
|
|
for (rdataset = ISC_LIST_HEAD(name->list); |
247 |
|
|
rdataset != NULL; |
248 |
|
|
rdataset = ISC_LIST_NEXT(rdataset, link)) { |
249 |
|
|
loopresult = dns_rdataset_first(rdataset); |
250 |
|
|
while (loopresult == ISC_R_SUCCESS) { |
251 |
|
|
dns_rdataset_current(rdataset, &rdata); |
252 |
|
|
switch (rdata.type) { |
253 |
|
|
case dns_rdatatype_a: |
254 |
|
|
if (section != DNS_SECTION_ANSWER) |
255 |
|
|
goto def_short_section; |
256 |
|
|
dns_name_format(name, namebuf, |
257 |
|
|
sizeof(namebuf)); |
258 |
|
|
printf("Name:\t%s\n", namebuf); |
259 |
|
|
printa(&rdata); |
260 |
|
|
break; |
261 |
|
|
case dns_rdatatype_soa: |
262 |
|
|
dns_name_format(name, namebuf, |
263 |
|
|
sizeof(namebuf)); |
264 |
|
|
printf("%s\n", namebuf); |
265 |
|
|
printsoa(&rdata); |
266 |
|
|
break; |
267 |
|
|
default: |
268 |
|
|
def_short_section: |
269 |
|
|
dns_name_format(name, namebuf, |
270 |
|
|
sizeof(namebuf)); |
271 |
|
|
printf("%s\t", namebuf); |
272 |
|
|
printrdata(&rdata); |
273 |
|
|
break; |
274 |
|
|
} |
275 |
|
|
dns_rdata_reset(&rdata); |
276 |
|
|
loopresult = dns_rdataset_next(rdataset); |
277 |
|
|
} |
278 |
|
|
} |
279 |
|
|
result = dns_message_nextname(msg, section); |
280 |
|
|
if (result == ISC_R_NOMORE) |
281 |
|
|
break; |
282 |
|
|
else if (result != ISC_R_SUCCESS) { |
283 |
|
|
return (result); |
284 |
|
|
} |
285 |
|
|
} |
286 |
|
|
return (ISC_R_SUCCESS); |
287 |
|
|
} |
288 |
|
|
|
289 |
|
|
static isc_result_t |
290 |
|
|
detailsection(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers, |
291 |
|
|
dns_section_t section) { |
292 |
|
|
isc_result_t result, loopresult; |
293 |
|
|
dns_name_t *name; |
294 |
|
|
dns_rdataset_t *rdataset = NULL; |
295 |
|
|
dns_rdata_t rdata = DNS_RDATA_INIT; |
296 |
|
|
char namebuf[DNS_NAME_FORMATSIZE]; |
297 |
|
|
|
298 |
|
|
UNUSED(query); |
299 |
|
|
|
300 |
|
|
debug("detailsection()"); |
301 |
|
|
|
302 |
|
|
if (headers) { |
303 |
|
|
switch (section) { |
304 |
|
|
case DNS_SECTION_QUESTION: |
305 |
|
|
puts(" QUESTIONS:"); |
306 |
|
|
break; |
307 |
|
|
case DNS_SECTION_ANSWER: |
308 |
|
|
puts(" ANSWERS:"); |
309 |
|
|
break; |
310 |
|
|
case DNS_SECTION_AUTHORITY: |
311 |
|
|
puts(" AUTHORITY RECORDS:"); |
312 |
|
|
break; |
313 |
|
|
case DNS_SECTION_ADDITIONAL: |
314 |
|
|
puts(" ADDITIONAL RECORDS:"); |
315 |
|
|
break; |
316 |
|
|
} |
317 |
|
|
} |
318 |
|
|
|
319 |
|
|
result = dns_message_firstname(msg, section); |
320 |
|
|
if (result == ISC_R_NOMORE) |
321 |
|
|
return (ISC_R_SUCCESS); |
322 |
|
|
else if (result != ISC_R_SUCCESS) |
323 |
|
|
return (result); |
324 |
|
|
for (;;) { |
325 |
|
|
name = NULL; |
326 |
|
|
dns_message_currentname(msg, section, |
327 |
|
|
&name); |
328 |
|
|
for (rdataset = ISC_LIST_HEAD(name->list); |
329 |
|
|
rdataset != NULL; |
330 |
|
|
rdataset = ISC_LIST_NEXT(rdataset, link)) { |
331 |
|
|
if (section == DNS_SECTION_QUESTION) { |
332 |
|
|
dns_name_format(name, namebuf, |
333 |
|
|
sizeof(namebuf)); |
334 |
|
|
printf("\t%s, ", namebuf); |
335 |
|
|
dns_rdatatype_format(rdataset->type, |
336 |
|
|
namebuf, |
337 |
|
|
sizeof(namebuf)); |
338 |
|
|
printf("type = %s, ", namebuf); |
339 |
|
|
dns_rdataclass_format(rdataset->rdclass, |
340 |
|
|
namebuf, |
341 |
|
|
sizeof(namebuf)); |
342 |
|
|
printf("class = %s\n", namebuf); |
343 |
|
|
} |
344 |
|
|
loopresult = dns_rdataset_first(rdataset); |
345 |
|
|
while (loopresult == ISC_R_SUCCESS) { |
346 |
|
|
dns_rdataset_current(rdataset, &rdata); |
347 |
|
|
|
348 |
|
|
dns_name_format(name, namebuf, |
349 |
|
|
sizeof(namebuf)); |
350 |
|
|
printf(" -> %s\n", namebuf); |
351 |
|
|
|
352 |
|
|
switch (rdata.type) { |
353 |
|
|
case dns_rdatatype_soa: |
354 |
|
|
printsoa(&rdata); |
355 |
|
|
break; |
356 |
|
|
default: |
357 |
|
|
printf("\t"); |
358 |
|
|
printrdata(&rdata); |
359 |
|
|
} |
360 |
|
|
dns_rdata_reset(&rdata); |
361 |
|
|
loopresult = dns_rdataset_next(rdataset); |
362 |
|
|
} |
363 |
|
|
} |
364 |
|
|
result = dns_message_nextname(msg, section); |
365 |
|
|
if (result == ISC_R_NOMORE) |
366 |
|
|
break; |
367 |
|
|
else if (result != ISC_R_SUCCESS) { |
368 |
|
|
return (result); |
369 |
|
|
} |
370 |
|
|
} |
371 |
|
|
return (ISC_R_SUCCESS); |
372 |
|
|
} |
373 |
|
|
|
374 |
|
|
void |
375 |
|
|
received(int bytes, isc_sockaddr_t *from, dig_query_t *query) |
376 |
|
|
{ |
377 |
|
|
UNUSED(bytes); |
378 |
|
|
UNUSED(from); |
379 |
|
|
UNUSED(query); |
380 |
|
|
} |
381 |
|
|
|
382 |
|
|
void |
383 |
|
|
trying(char *frm, dig_lookup_t *lookup) { |
384 |
|
|
UNUSED(frm); |
385 |
|
|
UNUSED(lookup); |
386 |
|
|
|
387 |
|
|
} |
388 |
|
|
|
389 |
|
|
isc_result_t |
390 |
|
|
printmessage(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers) { |
391 |
|
|
char servtext[ISC_SOCKADDR_FORMATSIZE]; |
392 |
|
|
|
393 |
|
|
debug("printmessage()"); |
394 |
|
|
|
395 |
|
|
isc_sockaddr_format(&query->sockaddr, servtext, sizeof(servtext)); |
396 |
|
|
printf("Server:\t\t%s\n", query->userarg); |
397 |
|
|
printf("Address:\t%s\n", servtext); |
398 |
|
|
|
399 |
|
|
puts(""); |
400 |
|
|
|
401 |
|
|
if (!short_form) { |
402 |
|
|
isc_boolean_t headers = ISC_TRUE; |
403 |
|
|
puts("------------"); |
404 |
|
|
/* detailheader(query, msg);*/ |
405 |
|
|
detailsection(query, msg, headers, DNS_SECTION_QUESTION); |
406 |
|
|
detailsection(query, msg, headers, DNS_SECTION_ANSWER); |
407 |
|
|
detailsection(query, msg, headers, DNS_SECTION_AUTHORITY); |
408 |
|
|
detailsection(query, msg, headers, DNS_SECTION_ADDITIONAL); |
409 |
|
|
puts("------------"); |
410 |
|
|
} |
411 |
|
|
|
412 |
|
|
if (msg->rcode != 0) { |
413 |
|
|
char nametext[DNS_NAME_FORMATSIZE]; |
414 |
|
|
dns_name_format(query->lookup->name, |
415 |
|
|
nametext, sizeof(nametext)); |
416 |
|
|
printf("** server can't find %s: %s\n", |
417 |
|
|
(msg->rcode != dns_rcode_nxdomain) ? nametext : |
418 |
|
|
query->lookup->textname, rcodetext[msg->rcode]); |
419 |
|
|
debug("returning with rcode == 0"); |
420 |
|
|
return (ISC_R_SUCCESS); |
421 |
|
|
} |
422 |
|
|
|
423 |
|
|
if ((msg->flags & DNS_MESSAGEFLAG_AA) == 0) |
424 |
|
|
puts("Non-authoritative answer:"); |
425 |
|
|
if (!ISC_LIST_EMPTY(msg->sections[DNS_SECTION_ANSWER])) |
426 |
|
|
printsection(query, msg, headers, DNS_SECTION_ANSWER); |
427 |
|
|
else |
428 |
|
|
printf("*** Can't find %s: No answer\n", |
429 |
|
|
query->lookup->textname); |
430 |
|
|
|
431 |
|
|
if (((msg->flags & DNS_MESSAGEFLAG_AA) == 0) && |
432 |
|
|
(query->lookup->rdtype != dns_rdatatype_a)) { |
433 |
|
|
puts("\nAuthoritative answers can be found from:"); |
434 |
|
|
printsection(query, msg, headers, |
435 |
|
|
DNS_SECTION_AUTHORITY); |
436 |
|
|
printsection(query, msg, headers, |
437 |
|
|
DNS_SECTION_ADDITIONAL); |
438 |
|
|
} |
439 |
|
|
return (ISC_R_SUCCESS); |
440 |
|
|
} |
441 |
|
|
|
442 |
|
|
static void |
443 |
|
|
show_settings(isc_boolean_t full, isc_boolean_t serv_only) { |
444 |
|
|
dig_server_t *srv; |
445 |
|
|
isc_sockaddr_t sockaddr; |
446 |
|
|
dig_searchlist_t *listent; |
447 |
|
|
in_port_t servport; |
448 |
|
|
|
449 |
|
|
servport = (port != 0) ? port : NAMESERVER_PORT; |
450 |
|
|
|
451 |
|
|
srv = ISC_LIST_HEAD(server_list); |
452 |
|
|
|
453 |
|
|
while (srv != NULL) { |
454 |
|
|
char sockstr[ISC_SOCKADDR_FORMATSIZE]; |
455 |
|
|
|
456 |
|
|
get_address(srv->servername, servport, &sockaddr); |
457 |
|
|
isc_sockaddr_format(&sockaddr, sockstr, sizeof(sockstr)); |
458 |
|
|
printf("Default server: %s\nAddress: %s\n", |
459 |
|
|
srv->userarg, sockstr); |
460 |
|
|
if (!full) |
461 |
|
|
return; |
462 |
|
|
srv = ISC_LIST_NEXT(srv, link); |
463 |
|
|
} |
464 |
|
|
if (serv_only) |
465 |
|
|
return; |
466 |
|
|
printf("\nSet options:\n"); |
467 |
|
|
printf(" %s\t\t\t%s\t\t%s\n", |
468 |
|
|
tcpmode ? "vc" : "novc", |
469 |
|
|
short_form ? "nodebug" : "debug", |
470 |
|
|
debugging ? "d2" : "nod2"); |
471 |
|
|
printf(" %s\t\t%s\n", |
472 |
|
|
usesearch ? "search" : "nosearch", |
473 |
|
|
recurse ? "recurse" : "norecurse"); |
474 |
|
|
printf(" timeout = %d\t\tretry = %d\tport = %d\n", |
475 |
|
|
timeout, tries, servport); |
476 |
|
|
printf(" querytype = %-8s\tclass = %s\n", deftype, defclass); |
477 |
|
|
printf(" srchlist = "); |
478 |
|
|
for (listent = ISC_LIST_HEAD(search_list); |
479 |
|
|
listent != NULL; |
480 |
|
|
listent = ISC_LIST_NEXT(listent, link)) { |
481 |
|
|
printf("%s", listent->origin); |
482 |
|
|
if (ISC_LIST_NEXT(listent, link) != NULL) |
483 |
|
|
printf("/"); |
484 |
|
|
} |
485 |
|
|
printf("\n"); |
486 |
|
|
} |
487 |
|
|
|
488 |
|
|
static isc_boolean_t |
489 |
|
|
testtype(char *typetext) { |
490 |
|
|
isc_result_t result; |
491 |
|
|
isc_textregion_t tr; |
492 |
|
|
dns_rdatatype_t rdtype; |
493 |
|
|
|
494 |
|
|
tr.base = typetext; |
495 |
|
|
tr.length = strlen(typetext); |
496 |
|
|
result = dns_rdatatype_fromtext(&rdtype, &tr); |
497 |
|
|
if (result == ISC_R_SUCCESS) |
498 |
|
|
return (ISC_TRUE); |
499 |
|
|
else { |
500 |
|
|
printf("unknown query type: %s\n", typetext); |
501 |
|
|
return (ISC_FALSE); |
502 |
|
|
} |
503 |
|
|
} |
504 |
|
|
|
505 |
|
|
static isc_boolean_t |
506 |
|
|
testclass(char *typetext) { |
507 |
|
|
isc_result_t result; |
508 |
|
|
isc_textregion_t tr; |
509 |
|
|
dns_rdataclass_t rdclass; |
510 |
|
|
|
511 |
|
|
tr.base = typetext; |
512 |
|
|
tr.length = strlen(typetext); |
513 |
|
|
result = dns_rdataclass_fromtext(&rdclass, &tr); |
514 |
|
|
if (result == ISC_R_SUCCESS) |
515 |
|
|
return (ISC_TRUE); |
516 |
|
|
else { |
517 |
|
|
printf("unknown query class: %s\n", typetext); |
518 |
|
|
return (ISC_FALSE); |
519 |
|
|
} |
520 |
|
|
} |
521 |
|
|
|
522 |
|
|
static void |
523 |
|
|
safecpy(char *dest, char *src, int size) { |
524 |
|
|
strlcpy(dest, src, size); |
525 |
|
|
} |
526 |
|
|
|
527 |
|
|
static isc_result_t |
528 |
|
|
parse_uint(isc_uint32_t *uip, const char *value, isc_uint32_t max, |
529 |
|
|
const char *desc) { |
530 |
|
|
isc_uint32_t n; |
531 |
|
|
isc_result_t result = isc_parse_uint32(&n, value, 10); |
532 |
|
|
if (result == ISC_R_SUCCESS && n > max) |
533 |
|
|
result = ISC_R_RANGE; |
534 |
|
|
if (result != ISC_R_SUCCESS) { |
535 |
|
|
printf("invalid %s '%s': %s\n", desc, |
536 |
|
|
value, isc_result_totext(result)); |
537 |
|
|
return result; |
538 |
|
|
} |
539 |
|
|
*uip = n; |
540 |
|
|
return (ISC_R_SUCCESS); |
541 |
|
|
} |
542 |
|
|
|
543 |
|
|
static void |
544 |
|
|
set_port(const char *value) { |
545 |
|
|
isc_uint32_t n; |
546 |
|
|
isc_result_t result = parse_uint(&n, value, 65535, "port"); |
547 |
|
|
if (result == ISC_R_SUCCESS) |
548 |
|
|
port = 0; /* (isc_uint16_t) n;*/ |
549 |
|
|
printf("The port command has been disabled.\n"); |
550 |
|
|
} |
551 |
|
|
|
552 |
|
|
static void |
553 |
|
|
set_timeout(const char *value) { |
554 |
|
|
isc_uint32_t n; |
555 |
|
|
isc_result_t result = parse_uint(&n, value, UINT_MAX, "timeout"); |
556 |
|
|
if (result == ISC_R_SUCCESS) |
557 |
|
|
timeout = n; |
558 |
|
|
} |
559 |
|
|
|
560 |
|
|
static void |
561 |
|
|
set_tries(const char *value) { |
562 |
|
|
isc_uint32_t n; |
563 |
|
|
isc_result_t result = parse_uint(&n, value, INT_MAX, "tries"); |
564 |
|
|
if (result == ISC_R_SUCCESS) |
565 |
|
|
tries = n; |
566 |
|
|
} |
567 |
|
|
|
568 |
|
|
static void |
569 |
|
|
setoption(char *opt) { |
570 |
|
|
if (strncasecmp(opt, "all", 4) == 0) { |
571 |
|
|
show_settings(ISC_TRUE, ISC_FALSE); |
572 |
|
|
} else if (strncasecmp(opt, "class=", 6) == 0) { |
573 |
|
|
if (testclass(&opt[6])) |
574 |
|
|
safecpy(defclass, &opt[6], sizeof(defclass)); |
575 |
|
|
} else if (strncasecmp(opt, "cl=", 3) == 0) { |
576 |
|
|
if (testclass(&opt[3])) |
577 |
|
|
safecpy(defclass, &opt[3], sizeof(defclass)); |
578 |
|
|
} else if (strncasecmp(opt, "type=", 5) == 0) { |
579 |
|
|
if (testtype(&opt[5])) |
580 |
|
|
safecpy(deftype, &opt[5], sizeof(deftype)); |
581 |
|
|
} else if (strncasecmp(opt, "ty=", 3) == 0) { |
582 |
|
|
if (testtype(&opt[3])) |
583 |
|
|
safecpy(deftype, &opt[3], sizeof(deftype)); |
584 |
|
|
} else if (strncasecmp(opt, "querytype=", 10) == 0) { |
585 |
|
|
if (testtype(&opt[10])) |
586 |
|
|
safecpy(deftype, &opt[10], sizeof(deftype)); |
587 |
|
|
} else if (strncasecmp(opt, "query=", 6) == 0) { |
588 |
|
|
if (testtype(&opt[6])) |
589 |
|
|
safecpy(deftype, &opt[6], sizeof(deftype)); |
590 |
|
|
} else if (strncasecmp(opt, "qu=", 3) == 0) { |
591 |
|
|
if (testtype(&opt[3])) |
592 |
|
|
safecpy(deftype, &opt[3], sizeof(deftype)); |
593 |
|
|
} else if (strncasecmp(opt, "q=", 2) == 0) { |
594 |
|
|
if (testtype(&opt[2])) |
595 |
|
|
safecpy(deftype, &opt[2], sizeof(deftype)); |
596 |
|
|
} else if (strncasecmp(opt, "domain=", 7) == 0) { |
597 |
|
|
safecpy(domainopt, &opt[7], sizeof(domainopt)); |
598 |
|
|
set_search_domain(domainopt); |
599 |
|
|
usesearch = ISC_TRUE; |
600 |
|
|
} else if (strncasecmp(opt, "do=", 3) == 0) { |
601 |
|
|
safecpy(domainopt, &opt[3], sizeof(domainopt)); |
602 |
|
|
set_search_domain(domainopt); |
603 |
|
|
usesearch = ISC_TRUE; |
604 |
|
|
} else if (strncasecmp(opt, "port=", 5) == 0) { |
605 |
|
|
set_port(&opt[5]); |
606 |
|
|
} else if (strncasecmp(opt, "po=", 3) == 0) { |
607 |
|
|
set_port(&opt[3]); |
608 |
|
|
} else if (strncasecmp(opt, "timeout=", 8) == 0) { |
609 |
|
|
set_timeout(&opt[8]); |
610 |
|
|
} else if (strncasecmp(opt, "t=", 2) == 0) { |
611 |
|
|
set_timeout(&opt[2]); |
612 |
|
|
} else if (strncasecmp(opt, "rec", 3) == 0) { |
613 |
|
|
recurse = ISC_TRUE; |
614 |
|
|
} else if (strncasecmp(opt, "norec", 5) == 0) { |
615 |
|
|
recurse = ISC_FALSE; |
616 |
|
|
} else if (strncasecmp(opt, "retry=", 6) == 0) { |
617 |
|
|
set_tries(&opt[6]); |
618 |
|
|
} else if (strncasecmp(opt, "ret=", 4) == 0) { |
619 |
|
|
set_tries(&opt[4]); |
620 |
|
|
} else if (strncasecmp(opt, "def", 3) == 0) { |
621 |
|
|
usesearch = ISC_TRUE; |
622 |
|
|
} else if (strncasecmp(opt, "nodef", 5) == 0) { |
623 |
|
|
usesearch = ISC_FALSE; |
624 |
|
|
} else if (strncasecmp(opt, "vc", 3) == 0) { |
625 |
|
|
tcpmode = ISC_TRUE; |
626 |
|
|
} else if (strncasecmp(opt, "novc", 5) == 0) { |
627 |
|
|
tcpmode = ISC_FALSE; |
628 |
|
|
} else if (strncasecmp(opt, "deb", 3) == 0) { |
629 |
|
|
short_form = ISC_FALSE; |
630 |
|
|
showsearch = ISC_TRUE; |
631 |
|
|
} else if (strncasecmp(opt, "nodeb", 5) == 0) { |
632 |
|
|
short_form = ISC_TRUE; |
633 |
|
|
showsearch = ISC_FALSE; |
634 |
|
|
} else if (strncasecmp(opt, "d2", 2) == 0) { |
635 |
|
|
debugging = ISC_TRUE; |
636 |
|
|
} else if (strncasecmp(opt, "nod2", 4) == 0) { |
637 |
|
|
debugging = ISC_FALSE; |
638 |
|
|
} else if (strncasecmp(opt, "search", 3) == 0) { |
639 |
|
|
usesearch = ISC_TRUE; |
640 |
|
|
} else if (strncasecmp(opt, "nosearch", 5) == 0) { |
641 |
|
|
usesearch = ISC_FALSE; |
642 |
|
|
} else if (strncasecmp(opt, "sil", 3) == 0) { |
643 |
|
|
/* deprecation_msg = ISC_FALSE; */ |
644 |
|
|
} else if (strncasecmp(opt, "fail", 3) == 0) { |
645 |
|
|
nofail=ISC_FALSE; |
646 |
|
|
} else if (strncasecmp(opt, "nofail", 3) == 0) { |
647 |
|
|
nofail=ISC_TRUE; |
648 |
|
|
} else { |
649 |
|
|
printf("*** Invalid option: %s\n", opt); |
650 |
|
|
} |
651 |
|
|
} |
652 |
|
|
|
653 |
|
|
static void |
654 |
|
|
addlookup(char *opt) { |
655 |
|
|
dig_lookup_t *lookup; |
656 |
|
|
isc_result_t result; |
657 |
|
|
isc_textregion_t tr; |
658 |
|
|
dns_rdatatype_t rdtype; |
659 |
|
|
dns_rdataclass_t rdclass; |
660 |
|
|
char store[MXNAME]; |
661 |
|
|
|
662 |
|
|
debug("addlookup()"); |
663 |
|
|
tr.base = deftype; |
664 |
|
|
tr.length = strlen(deftype); |
665 |
|
|
result = dns_rdatatype_fromtext(&rdtype, &tr); |
666 |
|
|
if (result != ISC_R_SUCCESS) { |
667 |
|
|
printf("unknown query type: %s\n", deftype); |
668 |
|
|
rdclass = dns_rdatatype_a; |
669 |
|
|
} |
670 |
|
|
tr.base = defclass; |
671 |
|
|
tr.length = strlen(defclass); |
672 |
|
|
result = dns_rdataclass_fromtext(&rdclass, &tr); |
673 |
|
|
if (result != ISC_R_SUCCESS) { |
674 |
|
|
printf("unknown query class: %s\n", defclass); |
675 |
|
|
rdclass = dns_rdataclass_in; |
676 |
|
|
} |
677 |
|
|
lookup = make_empty_lookup(); |
678 |
|
|
if (get_reverse(store, sizeof(store), opt, lookup->ip6_int, ISC_TRUE) |
679 |
|
|
== ISC_R_SUCCESS) { |
680 |
|
|
safecpy(lookup->textname, store, sizeof(lookup->textname)); |
681 |
|
|
lookup->rdtype = dns_rdatatype_ptr; |
682 |
|
|
lookup->rdtypeset = ISC_TRUE; |
683 |
|
|
} else { |
684 |
|
|
safecpy(lookup->textname, opt, sizeof(lookup->textname)); |
685 |
|
|
lookup->rdtype = rdtype; |
686 |
|
|
lookup->rdtypeset = ISC_TRUE; |
687 |
|
|
} |
688 |
|
|
lookup->rdclass = rdclass; |
689 |
|
|
lookup->rdclassset = ISC_TRUE; |
690 |
|
|
lookup->trace = ISC_FALSE; |
691 |
|
|
lookup->trace_root = lookup->trace; |
692 |
|
|
lookup->ns_search_only = ISC_FALSE; |
693 |
|
|
lookup->identify = identify; |
694 |
|
|
lookup->recurse = recurse; |
695 |
|
|
lookup->aaonly = aaonly; |
696 |
|
|
lookup->retries = tries; |
697 |
|
|
lookup->udpsize = 0; |
698 |
|
|
lookup->comments = comments; |
699 |
|
|
lookup->tcp_mode = tcpmode; |
700 |
|
|
lookup->stats = stats; |
701 |
|
|
lookup->section_question = section_question; |
702 |
|
|
lookup->section_answer = section_answer; |
703 |
|
|
lookup->section_authority = section_authority; |
704 |
|
|
lookup->section_additional = section_additional; |
705 |
|
|
lookup->new_search = ISC_TRUE; |
706 |
|
|
if (nofail) |
707 |
|
|
lookup->servfail_stops = ISC_FALSE; |
708 |
|
|
ISC_LIST_INIT(lookup->q); |
709 |
|
|
ISC_LINK_INIT(lookup, link); |
710 |
|
|
ISC_LIST_APPEND(lookup_list, lookup, link); |
711 |
|
|
lookup->origin = NULL; |
712 |
|
|
ISC_LIST_INIT(lookup->my_server_list); |
713 |
|
|
debug("looking up %s", lookup->textname); |
714 |
|
|
} |
715 |
|
|
|
716 |
|
|
static void |
717 |
|
|
get_next_command(void) { |
718 |
|
|
char *buf; |
719 |
|
|
char *ptr, *arg; |
720 |
|
|
char *input; |
721 |
|
|
|
722 |
|
|
fflush(stdout); |
723 |
|
|
buf = isc_mem_allocate(mctx, COMMSIZE); |
724 |
|
|
if (buf == NULL) |
725 |
|
|
fatal("memory allocation failure"); |
726 |
|
|
fputs("> ", stdout); |
727 |
|
|
fflush(stdout); |
728 |
|
|
isc_app_block(); |
729 |
|
|
ptr = fgets(buf, COMMSIZE, stdin); |
730 |
|
|
isc_app_unblock(); |
731 |
|
|
if (ptr == NULL) { |
732 |
|
|
in_use = ISC_FALSE; |
733 |
|
|
goto cleanup; |
734 |
|
|
} |
735 |
|
|
input = buf; |
736 |
|
|
ptr = next_token(&input, " \t\r\n"); |
737 |
|
|
if (ptr == NULL) |
738 |
|
|
goto cleanup; |
739 |
|
|
arg = next_token(&input, " \t\r\n"); |
740 |
|
|
if ((strcasecmp(ptr, "set") == 0) && |
741 |
|
|
(arg != NULL)) |
742 |
|
|
setoption(arg); |
743 |
|
|
else if ((strcasecmp(ptr, "server") == 0) || |
744 |
|
|
(strcasecmp(ptr, "lserver") == 0)) { |
745 |
|
|
isc_app_block(); |
746 |
|
|
set_nameserver(arg); |
747 |
|
|
check_ra = ISC_FALSE; |
748 |
|
|
isc_app_unblock(); |
749 |
|
|
show_settings(ISC_TRUE, ISC_TRUE); |
750 |
|
|
} else if (strcasecmp(ptr, "exit") == 0) { |
751 |
|
|
in_use = ISC_FALSE; |
752 |
|
|
goto cleanup; |
753 |
|
|
} else if (strcasecmp(ptr, "help") == 0 || |
754 |
|
|
strcasecmp(ptr, "?") == 0) { |
755 |
|
|
printf("The '%s' command is not yet implemented.\n", ptr); |
756 |
|
|
goto cleanup; |
757 |
|
|
} else if (strcasecmp(ptr, "finger") == 0 || |
758 |
|
|
strcasecmp(ptr, "root") == 0 || |
759 |
|
|
strcasecmp(ptr, "ls") == 0 || |
760 |
|
|
strcasecmp(ptr, "view") == 0) { |
761 |
|
|
printf("The '%s' command is not implemented.\n", ptr); |
762 |
|
|
goto cleanup; |
763 |
|
|
} else |
764 |
|
|
addlookup(ptr); |
765 |
|
|
cleanup: |
766 |
|
|
isc_mem_free(mctx, buf); |
767 |
|
|
} |
768 |
|
|
|
769 |
|
|
static void |
770 |
|
|
parse_args(int argc, char **argv) { |
771 |
|
|
isc_boolean_t have_lookup = ISC_FALSE; |
772 |
|
|
|
773 |
|
|
usesearch = ISC_TRUE; |
774 |
|
|
for (argc--, argv++; argc > 0; argc--, argv++) { |
775 |
|
|
debug("main parsing %s", argv[0]); |
776 |
|
|
if (argv[0][0] == '-') { |
777 |
|
|
if (argv[0][1] != 0) |
778 |
|
|
setoption(&argv[0][1]); |
779 |
|
|
else |
780 |
|
|
have_lookup = ISC_TRUE; |
781 |
|
|
} else { |
782 |
|
|
if (!have_lookup) { |
783 |
|
|
have_lookup = ISC_TRUE; |
784 |
|
|
in_use = ISC_TRUE; |
785 |
|
|
addlookup(argv[0]); |
786 |
|
|
} else { |
787 |
|
|
set_nameserver(argv[0]); |
788 |
|
|
check_ra = ISC_FALSE; |
789 |
|
|
} |
790 |
|
|
} |
791 |
|
|
} |
792 |
|
|
} |
793 |
|
|
|
794 |
|
|
static void |
795 |
|
|
flush_lookup_list(void) { |
796 |
|
|
dig_lookup_t *l, *lp; |
797 |
|
|
dig_query_t *q, *qp; |
798 |
|
|
dig_server_t *s, *sp; |
799 |
|
|
|
800 |
|
|
lookup_counter = 0; |
801 |
|
|
l = ISC_LIST_HEAD(lookup_list); |
802 |
|
|
while (l != NULL) { |
803 |
|
|
q = ISC_LIST_HEAD(l->q); |
804 |
|
|
while (q != NULL) { |
805 |
|
|
if (q->sock != NULL) { |
806 |
|
|
isc_socket_cancel(q->sock, NULL, |
807 |
|
|
ISC_SOCKCANCEL_ALL); |
808 |
|
|
isc_socket_detach(&q->sock); |
809 |
|
|
} |
810 |
|
|
if (ISC_LINK_LINKED(&q->recvbuf, link)) |
811 |
|
|
ISC_LIST_DEQUEUE(q->recvlist, &q->recvbuf, |
812 |
|
|
link); |
813 |
|
|
if (ISC_LINK_LINKED(&q->lengthbuf, link)) |
814 |
|
|
ISC_LIST_DEQUEUE(q->lengthlist, &q->lengthbuf, |
815 |
|
|
link); |
816 |
|
|
isc_buffer_invalidate(&q->recvbuf); |
817 |
|
|
isc_buffer_invalidate(&q->lengthbuf); |
818 |
|
|
qp = q; |
819 |
|
|
q = ISC_LIST_NEXT(q, link); |
820 |
|
|
ISC_LIST_DEQUEUE(l->q, qp, link); |
821 |
|
|
isc_mem_free(mctx, qp); |
822 |
|
|
} |
823 |
|
|
s = ISC_LIST_HEAD(l->my_server_list); |
824 |
|
|
while (s != NULL) { |
825 |
|
|
sp = s; |
826 |
|
|
s = ISC_LIST_NEXT(s, link); |
827 |
|
|
ISC_LIST_DEQUEUE(l->my_server_list, sp, link); |
828 |
|
|
isc_mem_free(mctx, sp); |
829 |
|
|
|
830 |
|
|
} |
831 |
|
|
if (l->sendmsg != NULL) |
832 |
|
|
dns_message_destroy(&l->sendmsg); |
833 |
|
|
if (l->timer != NULL) |
834 |
|
|
isc_timer_detach(&l->timer); |
835 |
|
|
lp = l; |
836 |
|
|
l = ISC_LIST_NEXT(l, link); |
837 |
|
|
ISC_LIST_DEQUEUE(lookup_list, lp, link); |
838 |
|
|
isc_mem_free(mctx, lp); |
839 |
|
|
} |
840 |
|
|
} |
841 |
|
|
|
842 |
|
|
static void |
843 |
|
|
getinput(isc_task_t *task, isc_event_t *event) { |
844 |
|
|
UNUSED(task); |
845 |
|
|
if (global_event == NULL) |
846 |
|
|
global_event = event; |
847 |
|
|
while (in_use) { |
848 |
|
|
get_next_command(); |
849 |
|
|
if (ISC_LIST_HEAD(lookup_list) != NULL) { |
850 |
|
|
start_lookup(); |
851 |
|
|
return; |
852 |
|
|
} |
853 |
|
|
} |
854 |
|
|
isc_app_shutdown(); |
855 |
|
|
} |
856 |
|
|
|
857 |
|
|
int |
858 |
|
|
main(int argc, char **argv) { |
859 |
|
|
isc_result_t result; |
860 |
|
|
|
861 |
|
|
ISC_LIST_INIT(lookup_list); |
862 |
|
|
ISC_LIST_INIT(server_list); |
863 |
|
|
ISC_LIST_INIT(search_list); |
864 |
|
|
|
865 |
|
|
check_ra = ISC_TRUE; |
866 |
|
|
|
867 |
|
|
result = isc_app_start(); |
868 |
|
|
check_result(result, "isc_app_start"); |
869 |
|
|
|
870 |
|
|
if (pledge("stdio rpath dns wpath cpath", NULL) == -1) { |
871 |
|
|
perror("pledge"); |
872 |
|
|
exit(1); |
873 |
|
|
} |
874 |
|
|
|
875 |
|
|
setup_libs(); |
876 |
|
|
progname = argv[0]; |
877 |
|
|
|
878 |
|
|
if (pledge("stdio dns rpath wpath cpath", NULL) == -1) { |
879 |
|
|
perror("pledge"); |
880 |
|
|
exit(1); |
881 |
|
|
} |
882 |
|
|
|
883 |
|
|
parse_args(argc, argv); |
884 |
|
|
|
885 |
|
|
setup_system(); |
886 |
|
|
if (domainopt[0] != '\0') |
887 |
|
|
set_search_domain(domainopt); |
888 |
|
|
if (in_use) |
889 |
|
|
result = isc_app_onrun(mctx, global_task, onrun_callback, |
890 |
|
|
NULL); |
891 |
|
|
else |
892 |
|
|
result = isc_app_onrun(mctx, global_task, getinput, NULL); |
893 |
|
|
check_result(result, "isc_app_onrun"); |
894 |
|
|
in_use = ISC_TF(!in_use); |
895 |
|
|
|
896 |
|
|
(void)isc_app_run(); |
897 |
|
|
|
898 |
|
|
puts(""); |
899 |
|
|
debug("done, and starting to shut down"); |
900 |
|
|
if (global_event != NULL) |
901 |
|
|
isc_event_free(&global_event); |
902 |
|
|
cancel_all(); |
903 |
|
|
destroy_libs(); |
904 |
|
|
isc_app_finish(); |
905 |
|
|
|
906 |
|
|
return (0); |
907 |
|
|
} |