1 /*
2 * dns.c -- handles:
3 * DNS resolve calls and events
4 * provides the code used by the bot if the DNS module is not loaded
5 * DNS Tcl commands
6 */
7 /*
8 * Written by Fabian Knittel <fknittel@gmx.de>
9 *
10 * Copyright (C) 1999 - 2021 Eggheads Development Team
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 */
26
27 #include "main.h"
28 #include <netdb.h>
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 #include "dns.h"
33 #ifdef EGG_TDNS
34 #include <errno.h>
35 #include <pthread.h>
36 #else
37 #include <setjmp.h>
38 #endif
39
40 extern struct dcc_t *dcc;
41 extern int dcc_total;
42 extern time_t now;
43 extern Tcl_Interp *interp;
44 #ifdef EGG_TDNS
45 struct dns_thread_node *dns_thread_head;
46 extern int pref_af;
47 #else
48 extern int resolve_timeout;
49 extern sigjmp_buf alarmret;
50 #endif
51
52 devent_t *dns_events = NULL;
53
54
55 /*
56 * DCC functions
57 */
58
dcc_dnswait(int idx,char * buf,int len)59 static void dcc_dnswait(int idx, char *buf, int len)
60 {
61 /* Ignore anything now. */
62 }
63
eof_dcc_dnswait(int idx)64 static void eof_dcc_dnswait(int idx)
65 {
66 putlog(LOG_MISC, "*", "Lost connection while resolving hostname [%s/%d]",
67 iptostr(&dcc[idx].sockname.addr.sa), dcc[idx].port);
68 killsock(dcc[idx].sock);
69 lostdcc(idx);
70 }
71
display_dcc_dnswait(int idx,char * buf)72 static void display_dcc_dnswait(int idx, char *buf)
73 {
74 sprintf(buf, "dns waited %lis", (long) (now - dcc[idx].timeval));
75 }
76
expmem_dcc_dnswait(void * x)77 static int expmem_dcc_dnswait(void *x)
78 {
79 struct dns_info *p = (struct dns_info *) x;
80 int size = 0;
81
82 if (p) {
83 size = sizeof(struct dns_info);
84 if (p->host)
85 size += strlen(p->host) + 1;
86 if (p->cbuf)
87 size += strlen(p->cbuf) + 1;
88 }
89 return size;
90 }
91
kill_dcc_dnswait(int idx,void * x)92 static void kill_dcc_dnswait(int idx, void *x)
93 {
94 struct dns_info *p = (struct dns_info *) x;
95
96 if (p) {
97 if (p->host)
98 nfree(p->host);
99 if (p->cbuf)
100 nfree(p->cbuf);
101 nfree(p);
102 }
103 }
104
105 struct dcc_table DCC_DNSWAIT = {
106 "DNSWAIT",
107 DCT_VALIDIDX,
108 eof_dcc_dnswait,
109 dcc_dnswait,
110 0,
111 0,
112 display_dcc_dnswait,
113 expmem_dcc_dnswait,
114 kill_dcc_dnswait,
115 0
116 };
117
118
119 /*
120 * DCC events
121 */
122
123 /* Walk through every dcc entry and look for waiting DNS requests
124 * of RES_HOSTBYIP for our IP address.
125 */
dns_dcchostbyip(sockname_t * ip,char * hostn,int ok,void * other)126 static void dns_dcchostbyip(sockname_t *ip, char *hostn, int ok, void *other)
127 {
128 int idx;
129
130 for (idx = 0; idx < dcc_total; idx++) {
131 if ((dcc[idx].type == &DCC_DNSWAIT) &&
132 (dcc[idx].u.dns->dns_type == RES_HOSTBYIP) && (
133 #ifdef IPV6
134 (ip->family == AF_INET6 &&
135 IN6_ARE_ADDR_EQUAL(&dcc[idx].u.dns->ip->addr.s6.sin6_addr,
136 &ip->addr.s6.sin6_addr)) ||
137 (ip->family == AF_INET &&
138 #endif
139 (dcc[idx].u.dns->ip->addr.s4.sin_addr.s_addr ==
140 ip->addr.s4.sin_addr.s_addr)))
141 #ifdef IPV6
142 )
143 #endif
144 {
145 if (dcc[idx].u.dns->host)
146 nfree(dcc[idx].u.dns->host);
147 dcc[idx].u.dns->host = get_data_ptr(strlen(hostn) + 1);
148 strcpy(dcc[idx].u.dns->host, hostn);
149 if (ok)
150 dcc[idx].u.dns->dns_success(idx);
151 else
152 dcc[idx].u.dns->dns_failure(idx);
153 }
154 }
155 }
156
157 /* Walk through every dcc entry and look for waiting DNS requests
158 * of RES_IPBYHOST for our hostname.
159 */
dns_dccipbyhost(sockname_t * ip,char * hostn,int ok,void * other)160 static void dns_dccipbyhost(sockname_t *ip, char *hostn, int ok, void *other)
161 {
162 int idx;
163
164 for (idx = 0; idx < dcc_total; idx++) {
165 if ((dcc[idx].type == &DCC_DNSWAIT) &&
166 (dcc[idx].u.dns->dns_type == RES_IPBYHOST) &&
167 !strcasecmp(dcc[idx].u.dns->host, hostn)) {
168 if (ok) {
169 if (dcc[idx].u.dns->ip)
170 memcpy(dcc[idx].u.dns->ip, ip, sizeof(sockname_t));
171 else
172 memcpy(&dcc[idx].sockname, ip, sizeof(sockname_t));
173 dcc[idx].u.dns->dns_success(idx);
174 } else
175 dcc[idx].u.dns->dns_failure(idx);
176 }
177 }
178 }
179
dns_dccexpmem(void * other)180 static int dns_dccexpmem(void *other)
181 {
182 return 0;
183 }
184
185 devent_type DNS_DCCEVENT_HOSTBYIP = {
186 "DCCEVENT_HOSTBYIP",
187 dns_dccexpmem,
188 dns_dcchostbyip
189 };
190
191 devent_type DNS_DCCEVENT_IPBYHOST = {
192 "DCCEVENT_IPBYHOST",
193 dns_dccexpmem,
194 dns_dccipbyhost
195 };
196
dcc_dnsipbyhost(char * hostn)197 void dcc_dnsipbyhost(char *hostn)
198 {
199 devent_t *de;
200
201 for (de = dns_events; de; de = de->next) {
202 if (de->type && (de->type == &DNS_DCCEVENT_IPBYHOST) &&
203 (de->lookup == RES_IPBYHOST)) {
204 if (de->res_data.hostname &&
205 !strcasecmp(de->res_data.hostname, hostn))
206 /* No need to add anymore. */
207 return;
208 }
209 }
210
211 de = nmalloc(sizeof(devent_t));
212 egg_bzero(de, sizeof(devent_t));
213
214 /* Link into list. */
215 de->next = dns_events;
216 dns_events = de;
217
218 de->type = &DNS_DCCEVENT_IPBYHOST;
219 de->lookup = RES_IPBYHOST;
220 de->res_data.hostname = nmalloc(strlen(hostn) + 1);
221 strcpy(de->res_data.hostname, hostn);
222
223 /* Send request. */
224 dns_ipbyhost(hostn);
225 }
226
dcc_dnshostbyip(sockname_t * ip)227 void dcc_dnshostbyip(sockname_t *ip)
228 {
229 devent_t *de;
230
231 for (de = dns_events; de; de = de->next) {
232 if (de->type && (de->type == &DNS_DCCEVENT_HOSTBYIP) &&
233 (de->lookup == RES_HOSTBYIP)) {
234 if (de->res_data.ip_addr == ip)
235 /* No need to add anymore. */
236 return;
237 }
238 }
239
240 de = nmalloc(sizeof(devent_t));
241 egg_bzero(de, sizeof(devent_t));
242
243 /* Link into list. */
244 de->next = dns_events;
245 dns_events = de;
246
247 de->type = &DNS_DCCEVENT_HOSTBYIP;
248 de->lookup = RES_HOSTBYIP;
249 de->res_data.ip_addr = ip;
250
251 /* Send request. */
252 dns_hostbyip(ip);
253 }
254
255
256 /*
257 * Tcl events
258 */
259
dns_tcl_iporhostres(sockname_t * ip,char * hostn,int ok,void * other)260 static void dns_tcl_iporhostres(sockname_t *ip, char *hostn, int ok, void *other)
261 {
262 devent_tclinfo_t *tclinfo = (devent_tclinfo_t *) other;
263 Tcl_DString list;
264
265 Tcl_DStringInit(&list);
266 Tcl_DStringAppendElement(&list, tclinfo->proc);
267 Tcl_DStringAppendElement(&list, iptostr(&ip->addr.sa));
268 Tcl_DStringAppendElement(&list, hostn);
269 Tcl_DStringAppendElement(&list, ok ? "1" : "0");
270
271 if (tclinfo->paras) {
272 EGG_CONST char *argv[2];
273 char *output;
274
275 argv[0] = Tcl_DStringValue(&list);
276 argv[1] = tclinfo->paras;
277 output = Tcl_Concat(2, argv);
278
279 if (Tcl_Eval(interp, output) == TCL_ERROR) {
280 putlog(LOG_MISC, "*", DCC_TCLERROR, tclinfo->proc, tcl_resultstring());
281 Tcl_BackgroundError(interp);
282 }
283 Tcl_Free(output);
284 } else if (Tcl_Eval(interp, Tcl_DStringValue(&list)) == TCL_ERROR) {
285 putlog(LOG_MISC, "*", DCC_TCLERROR, tclinfo->proc, tcl_resultstring());
286 Tcl_BackgroundError(interp);
287 }
288
289 Tcl_DStringFree(&list);
290
291 nfree(tclinfo->proc);
292 if (tclinfo->paras)
293 nfree(tclinfo->paras);
294 nfree(tclinfo);
295 }
296
dns_tclexpmem(void * other)297 static int dns_tclexpmem(void *other)
298 {
299 devent_tclinfo_t *tclinfo = (devent_tclinfo_t *) other;
300 int l = 0;
301
302 if (tclinfo) {
303 l = sizeof(devent_tclinfo_t);
304 if (tclinfo->proc)
305 l += strlen(tclinfo->proc) + 1;
306 if (tclinfo->paras)
307 l += strlen(tclinfo->paras) + 1;
308 }
309 return l;
310 }
311
312 devent_type DNS_TCLEVENT_HOSTBYIP = {
313 "TCLEVENT_HOSTBYIP",
314 dns_tclexpmem,
315 dns_tcl_iporhostres
316 };
317
318 devent_type DNS_TCLEVENT_IPBYHOST = {
319 "TCLEVENT_IPBYHOST",
320 dns_tclexpmem,
321 dns_tcl_iporhostres
322 };
323
tcl_dnsipbyhost(char * hostn,char * proc,char * paras)324 static void tcl_dnsipbyhost(char *hostn, char *proc, char *paras)
325 {
326 devent_t *de;
327 devent_tclinfo_t *tclinfo;
328
329 de = nmalloc(sizeof(devent_t));
330 egg_bzero(de, sizeof(devent_t));
331
332 /* Link into list. */
333 de->next = dns_events;
334 dns_events = de;
335
336 de->type = &DNS_TCLEVENT_IPBYHOST;
337 de->lookup = RES_IPBYHOST;
338 de->res_data.hostname = nmalloc(strlen(hostn) + 1);
339 strcpy(de->res_data.hostname, hostn);
340
341 /* Store additional data. */
342 tclinfo = nmalloc(sizeof(devent_tclinfo_t));
343 tclinfo->proc = nmalloc(strlen(proc) + 1);
344 strcpy(tclinfo->proc, proc);
345 if (paras) {
346 tclinfo->paras = nmalloc(strlen(paras) + 1);
347 strcpy(tclinfo->paras, paras);
348 } else
349 tclinfo->paras = NULL;
350 de->other = tclinfo;
351
352 /* Send request. */
353 dns_ipbyhost(hostn);
354 }
355
tcl_dnshostbyip(sockname_t * ip,char * proc,char * paras)356 static void tcl_dnshostbyip(sockname_t *ip, char *proc, char *paras)
357 {
358 devent_t *de;
359 devent_tclinfo_t *tclinfo;
360
361 de = nmalloc(sizeof(devent_t));
362 egg_bzero(de, sizeof(devent_t));
363
364 /* Link into list. */
365 de->next = dns_events;
366 dns_events = de;
367
368 de->type = &DNS_TCLEVENT_HOSTBYIP;
369 de->lookup = RES_HOSTBYIP;
370 de->res_data.ip_addr = ip;
371
372 /* Store additional data. */
373 tclinfo = nmalloc(sizeof(devent_tclinfo_t));
374 tclinfo->proc = nmalloc(strlen(proc) + 1);
375 strcpy(tclinfo->proc, proc);
376 memcpy(&tclinfo->sockname, ip, sizeof(sockname_t));
377 de->res_data.ip_addr = &tclinfo->sockname;
378 if (paras) {
379 tclinfo->paras = nmalloc(strlen(paras) + 1);
380 strcpy(tclinfo->paras, paras);
381 } else
382 tclinfo->paras = NULL;
383 de->other = tclinfo;
384
385 /* Send request. */
386 dns_hostbyip(ip);
387 }
388
389
390 /*
391 * Event functions
392 */
393
dnsevent_expmem(void)394 static int dnsevent_expmem(void)
395 {
396 devent_t *de;
397 int tot = 0;
398
399 for (de = dns_events; de; de = de->next) {
400 tot += sizeof(devent_t);
401 if ((de->lookup == RES_IPBYHOST) && de->res_data.hostname)
402 tot += strlen(de->res_data.hostname) + 1;
403 if (de->type && de->type->expmem)
404 tot += de->type->expmem(de->other);
405 }
406 return tot;
407 }
408
call_hostbyip(sockname_t * ip,char * hostn,int ok)409 void call_hostbyip(sockname_t *ip, char *hostn, int ok)
410 {
411 devent_t *de = dns_events, *ode = NULL, *nde = NULL;
412
413 while (de) {
414 nde = de->next;
415 if ((de->lookup == RES_HOSTBYIP) && (
416 #ifdef IPV6
417 (ip->family == AF_INET6 &&
418 IN6_ARE_ADDR_EQUAL(&de->res_data.ip_addr->addr.s6.sin6_addr,
419 &ip->addr.s6.sin6_addr)) ||
420 (ip->family == AF_INET &&
421 #endif
422 (de->res_data.ip_addr->addr.s4.sin_addr.s_addr ==
423 ip->addr.s4.sin_addr.s_addr)))
424 #ifdef IPV6
425 )
426 #endif
427 {
428 /* A memcmp() could have perfectly done it .. */
429 /* Remove the event from the list here, to avoid conflicts if one of
430 * the event handlers re-adds another event. */
431 if (ode)
432 ode->next = de->next;
433 else
434 dns_events = de->next;
435
436 if (de->type && de->type->event)
437 de->type->event(ip, hostn, ok, de->other);
438 else
439 putlog(LOG_MISC, "*", "(!) Unknown DNS event type found: %s",
440 (de->type && de->type->name) ? de->type->name : "<empty>");
441 nfree(de);
442 de = ode;
443 }
444 ode = de;
445 de = nde;
446 }
447 }
448
call_ipbyhost(char * hostn,sockname_t * ip,int ok)449 void call_ipbyhost(char *hostn, sockname_t *ip, int ok)
450 {
451 devent_t *de = dns_events, *ode = NULL, *nde = NULL;
452
453 while (de) {
454 nde = de->next;
455 if ((de->lookup == RES_IPBYHOST) && (!de->res_data.hostname ||
456 !strcasecmp(de->res_data.hostname, hostn))) {
457 /* Remove the event from the list here, to avoid conflicts if one of
458 * the event handlers re-adds another event. */
459 if (ode)
460 ode->next = de->next;
461 else
462 dns_events = de->next;
463
464 if (de->type && de->type->event)
465 de->type->event(ip, hostn, ok, de->other);
466 else
467 putlog(LOG_MISC, "*", "(!) Unknown DNS event type found: %s",
468 (de->type && de->type->name) ? de->type->name : "<empty>");
469
470 if (de->res_data.hostname)
471 nfree(de->res_data.hostname);
472 nfree(de);
473 de = ode;
474 }
475 ode = de;
476 de = nde;
477 }
478 }
479
480 #ifdef EGG_TDNS
481 /* The following 2 threads work like this: a libc resolver function is called,
482 * that blocks the thread and returns the result or after timeout. The default
483 * is RES_TIMEOUT, which is generally 5, the allowed maximum is RES_MAXRETRANS
484 * (see <resolv.h>). The result is written to the threads dns_thread_node. There
485 * is 1 node per thread in a linked list, which is MT-safe. One end of the pipe
486 * is closed and the thread is ended by return. The other end will make
487 * eggdrops mainloop select() return, read the result from the dns_thread_node
488 * and call call_hostbyip() or call_ipbyhost(). No signal or tcl thread problem.
489 */
490
thread_dns_hostbyip(void * arg)491 void *thread_dns_hostbyip(void *arg)
492 {
493 struct dns_thread_node *dtn = (struct dns_thread_node *) arg;
494 sockname_t *addr = &dtn->addr;
495 int i = 0; /* make codacy happy */
496
497 i = getnameinfo((const struct sockaddr *) &addr->addr.sa, addr->addrlen,
498 dtn->host, sizeof dtn->host, NULL, 0, 0);
499 if (i) {
500 #ifdef IPV6
501 if (addr->family == AF_INET6)
502 inet_ntop(AF_INET6, &addr->addr.s6.sin6_addr, dtn->host, sizeof dtn->host);
503 else
504 #endif
505 inet_ntop(AF_INET, &addr->addr.s4.sin_addr.s_addr, dtn->host, sizeof dtn->host);
506 }
507 dtn->ok = !i;
508 close(dtn->fildes[1]);
509 return NULL;
510 }
511
thread_dns_ipbyhost(void * arg)512 void *thread_dns_ipbyhost(void *arg)
513 {
514 struct dns_thread_node *dtn = (struct dns_thread_node *) arg;
515 struct addrinfo *res0, *res;
516 int i;
517 sockname_t *addr = &dtn->addr;
518
519 i = getaddrinfo(dtn->host, NULL, NULL, &res0);
520 memset(addr, 0, sizeof *addr);
521 if (!i) {
522 #ifdef IPV6
523 for (res = res0; res; res = res->ai_next) {
524 if (res == res0 || res->ai_family == (pref_af ? AF_INET6 : AF_INET)) {
525 addr->family = res->ai_family;
526 addr->addrlen = res->ai_addrlen;
527 memcpy(&addr->addr.sa, res->ai_addr, res->ai_addrlen);
528 if (res->ai_family == (pref_af ? AF_INET6 : AF_INET))
529 break;
530 }
531 }
532 #else
533 i = 1;
534 for (res = res0; res; res = res->ai_next) {
535 if (res->ai_family == AF_INET) {
536 addr->family = res->ai_family;
537 addr->addrlen = res->ai_addrlen;
538 memcpy(&addr->addr.sa, res->ai_addr, res->ai_addrlen);
539 i = 0;
540 break;
541 }
542 }
543 #endif
544 freeaddrinfo(res0);
545 }
546 dtn->ok = !i;
547 close(dtn->fildes[1]);
548 return NULL;
549 }
550
core_dns_hostbyip(sockname_t * addr)551 void core_dns_hostbyip(sockname_t *addr)
552 {
553 struct dns_thread_node *dtn = nmalloc(sizeof(struct dns_thread_node));
554 pthread_t thread; /* only used by pthread_create(), no need to save */
555
556 if (pipe(dtn->fildes) < 0) {
557 putlog(LOG_MISC, "*", "core_dns_hostbyip(): pipe(): error: %s", strerror(errno));
558 call_hostbyip(addr, iptostr(&addr->addr.sa), 0);
559 nfree(dtn);
560 return;
561 }
562 memcpy(&dtn->addr, addr, sizeof *addr);
563 if (pthread_create(&thread, NULL, thread_dns_hostbyip, (void *) dtn)) {
564 putlog(LOG_MISC, "*", "core_dns_hostbyip(): pthread_create(): error = %s", strerror(errno));
565 call_hostbyip(addr, iptostr(&addr->addr.sa), 0);
566 close(dtn->fildes[0]);
567 close(dtn->fildes[1]);
568 nfree(dtn);
569 return;
570 }
571 dtn->type = DTN_TYPE_HOSTBYIP;
572 dtn->next = dns_thread_head->next;
573 dns_thread_head->next = dtn;
574 }
575
core_dns_ipbyhost(char * host)576 void core_dns_ipbyhost(char *host)
577 {
578 sockname_t addr;
579 struct dns_thread_node *dtn;
580 pthread_t thread; /* only used by pthread_create(), no need to save */
581
582 /* if addr is ip instead of host */
583 if (setsockname(&addr, host, 0, 0) != AF_UNSPEC) {
584 call_ipbyhost(host, &addr, 1);
585 return;
586 }
587 dtn = nmalloc(sizeof(struct dns_thread_node));
588 if (pipe(dtn->fildes) < 0) {
589 putlog(LOG_MISC, "*", "core_dns_ipbyhost(): pipe(): error: %s", strerror(errno));
590 call_ipbyhost(host, &addr, 0);
591 nfree(dtn);
592 return;
593 }
594 dtn->next = dns_thread_head->next;
595 dns_thread_head->next = dtn;
596 strlcpy(dtn->host, host, sizeof dtn->host);
597 if (pthread_create(&thread, NULL, thread_dns_ipbyhost, (void *) dtn)) {
598 putlog(LOG_MISC, "*", "core_dns_ipbyhost(): pthread_create(): error = %s", strerror(errno));
599 call_ipbyhost(host, &addr, 0);
600 close(dtn->fildes[0]);
601 close(dtn->fildes[1]);
602 dns_thread_head->next = dtn->next;
603 nfree(dtn);
604 return;
605 }
606 dtn->type = DTN_TYPE_IPBYHOST;
607 }
608 #else /* EGG_TDNS */
609 /*
610 * Async DNS emulation functions
611 */
core_dns_hostbyip(sockname_t * addr)612 void core_dns_hostbyip(sockname_t *addr)
613 {
614 char host[256] = "";
615 volatile int i = 1;
616
617 if (addr->family == AF_INET) {
618 if (!sigsetjmp(alarmret, 1)) {
619 alarm(resolve_timeout);
620 i = getnameinfo((const struct sockaddr *) &addr->addr.s4,
621 sizeof (struct sockaddr_in), host, sizeof host, NULL, 0, 0);
622 alarm(0);
623 if (i)
624 debug1("dns: getnameinfo(): error = %s", gai_strerror(i));
625 }
626 if (i)
627 inet_ntop(AF_INET, &addr->addr.s4.sin_addr.s_addr, host, sizeof host);
628 #ifdef IPV6
629 } else {
630 if (!sigsetjmp(alarmret, 1)) {
631 alarm(resolve_timeout);
632 i = getnameinfo((const struct sockaddr *) &addr->addr.s6,
633 sizeof (struct sockaddr_in6), host, sizeof host, NULL, 0, 0);
634 alarm(0);
635 if (i)
636 debug1("dns: getnameinfo(): error = %s", gai_strerror(i));
637 }
638 if (i)
639 inet_ntop(AF_INET6, &addr->addr.s6.sin6_addr, host, sizeof host);
640 }
641 #else
642 }
643 #endif
644 call_hostbyip(addr, host, !i);
645 }
646
core_dns_ipbyhost(char * host)647 void core_dns_ipbyhost(char *host)
648 {
649 sockname_t name;
650
651 if (setsockname(&name, host, 0, 1) == AF_UNSPEC)
652 call_ipbyhost(host, &name, 0);
653 else
654 call_ipbyhost(host, &name, 1);
655 }
656 #endif /* EGG_TDNS */
657
658 /*
659 * Misc functions
660 */
661
expmem_dns(void)662 int expmem_dns(void)
663 {
664 return dnsevent_expmem();
665 }
666
667
668 /*
669 * Tcl functions
670 */
671
672 /* dnslookup <ip-address> <proc> */
673 static int tcl_dnslookup STDVAR
674 {
675 sockname_t addr;
676 Tcl_DString paras;
677
678 if (argc < 3) {
679 Tcl_AppendResult(irp, "wrong # args: should be \"", argv[0],
680 " ip-address/hostname proc ?args...?\"", NULL);
681 return TCL_ERROR;
682 }
683
684 Tcl_DStringInit(¶s);
685 if (argc > 3) {
686 int p;
687
688 for (p = 3; p < argc; p++)
689 Tcl_DStringAppendElement(¶s, argv[p]);
690 }
691
692 if (setsockname(&addr, argv[1], 0, 0) != AF_UNSPEC)
693 tcl_dnshostbyip(&addr, argv[2], Tcl_DStringValue(¶s));
694 else {
695 if (strlen(argv[1]) > 255) {
696 Tcl_AppendResult(irp, "hostname too long. max 255 chars.", NULL);
697 return TCL_ERROR;
698 }
699 tcl_dnsipbyhost(argv[1], argv[2], Tcl_DStringValue(¶s));
700 }
701
702 Tcl_DStringFree(¶s);
703 return TCL_OK;
704 }
705
706 tcl_cmds tcldns_cmds[] = {
707 {"dnslookup", tcl_dnslookup},
708 {NULL, NULL}
709 };
710