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(&paras);
685   if (argc > 3) {
686     int p;
687 
688     for (p = 3; p < argc; p++)
689       Tcl_DStringAppendElement(&paras, argv[p]);
690   }
691 
692   if (setsockname(&addr, argv[1], 0, 0) != AF_UNSPEC)
693     tcl_dnshostbyip(&addr, argv[2], Tcl_DStringValue(&paras));
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(&paras));
700   }
701 
702   Tcl_DStringFree(&paras);
703   return TCL_OK;
704 }
705 
706 tcl_cmds tcldns_cmds[] = {
707   {"dnslookup", tcl_dnslookup},
708   {NULL,                 NULL}
709 };
710