1 /*
2  * dns.c -- part of dns.mod
3  *   domain lookup glue code for eggdrop
4  *
5  * Written by Fabian Knittel <fknittel@gmx.de>
6  */
7 /*
8  * Copyright (C) 1999 - 2021 Eggheads Development Team
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24 
25 #include "src/mod/module.h"
26 
27 #ifndef EGG_TDNS
28 #define MODULE_NAME "dns"
29 
30 #include "dns.h"
31 
32 static void dns_event_success(struct resolve *rp, int type);
33 static void dns_event_failure(struct resolve *rp, int type);
34 
35 static Function *global = NULL;
36 
37 static int dns_maxsends = 4;
38 static int dns_retrydelay = 3;
39 static int dns_cache = 86400;
40 static int dns_negcache = 600;
41 
42 static char dns_servers[144] = "";
43 
44 #include "coredns.c"
45 
46 
47 /*
48  *    DNS event related code
49  */
dns_event_success(struct resolve * rp,int type)50 static void dns_event_success(struct resolve *rp, int type)
51 {
52   if (!rp)
53     return;
54 
55   if (type == T_PTR) {
56     debug2("DNS resolved %s to %s", iptostr(&rp->sockname.addr.sa),
57            rp->hostn);
58     call_hostbyip(&rp->sockname, rp->hostn, 1);
59   } else if (type == T_A) {
60     debug2("DNS resolved %s to %s", rp->hostn,
61            iptostr(&rp->sockname.addr.sa));
62     call_ipbyhost(rp->hostn, &rp->sockname, 1);
63   }
64 }
65 
dns_event_failure(struct resolve * rp,int type)66 static void dns_event_failure(struct resolve *rp, int type)
67 {
68   if (!rp)
69     return;
70 
71   if (type == T_PTR) {
72     static char s[UHOSTLEN];
73 
74     strcpy(s, iptostr(&rp->sockname.addr.sa));
75     debug1("DNS resolve failed for %s", s);
76     call_hostbyip(&rp->sockname, s, 0);
77   } else if (type == T_A) {
78     debug1("DNS resolve failed for %s", rp->hostn);
79     call_ipbyhost(rp->hostn, &rp->sockname, 0);
80   } else
81     debug2("DNS resolve failed for unknown %s / %s",
82            iptostr(&rp->sockname.addr.sa), nonull(rp->hostn));
83   return;
84 }
85 
86 
87 /*
88  *    DNS Socket related code
89  */
90 
eof_dns_socket(int idx)91 static void eof_dns_socket(int idx)
92 {
93   putlog(LOG_MISC, "*", "DNS Error: socket closed.");
94   killsock(dcc[idx].sock);
95   /* Try to reopen socket */
96   if (init_dns_network()) {
97     putlog(LOG_MISC, "*", "DNS socket successfully reopened!");
98     dcc[idx].sock = resfd;
99     dcc[idx].timeval = now;
100   } else
101     lostdcc(idx);
102 }
103 
dns_socket(int idx,char * buf,int len)104 static void dns_socket(int idx, char *buf, int len)
105 {
106   dns_ack();
107 }
108 
display_dns_socket(int idx,char * buf)109 static void display_dns_socket(int idx, char *buf)
110 {
111   strcpy(buf, "dns   (ready)");
112 }
113 
114 static struct dcc_table DCC_DNS = {
115   "DNS",
116   DCT_LISTEN,
117   eof_dns_socket,
118   dns_socket,
119   NULL,
120   NULL,
121   display_dns_socket,
122   NULL,
123   NULL,
124   NULL
125 };
126 
127 static tcl_ints dnsints[] = {
128   {"dns-maxsends",   &dns_maxsends,   0},
129   {"dns-retrydelay", &dns_retrydelay, 0},
130   {"dns-cache",      &dns_cache,      0},
131   {"dns-negcache",   &dns_negcache,   0},
132   {NULL,             NULL,            0}
133 };
134 
135 static tcl_strings dnsstrings[] = {
136   {"dns-servers", dns_servers, 143,           0},
137   {NULL,          NULL,          0,           0}
138 };
139 
dns_change(ClientData cdata,Tcl_Interp * irp,EGG_CONST char * name1,EGG_CONST char * name2,int flags)140 static char *dns_change(ClientData cdata, Tcl_Interp *irp,
141                            EGG_CONST char *name1,
142                            EGG_CONST char *name2, int flags)
143 {
144   char buf[sizeof dns_servers], *p;
145   unsigned short port;
146   int i, lc, code;
147   EGG_CONST char **list, *slist;
148 
149   if (flags & (TCL_TRACE_READS | TCL_TRACE_UNSETS)) {
150     Tcl_DString ds;
151 
152     Tcl_DStringInit(&ds);
153     for (i = 0; i < myres.nscount; i++) {
154       egg_snprintf(buf, sizeof buf, "%s:%d", iptostr((struct sockaddr *)
155                &myres.nsaddr_list[i]), ntohs(myres.nsaddr_list[i].sin_port));
156       Tcl_DStringAppendElement(&ds, buf);
157     }
158     slist = Tcl_DStringValue(&ds);
159     Tcl_SetVar2(interp, name1, name2, slist, TCL_GLOBAL_ONLY);
160     Tcl_DStringFree(&ds);
161   } else { /* TCL_TRACE_WRITES */
162     slist = Tcl_GetVar2(interp, name1, name2, TCL_GLOBAL_ONLY);
163     code = Tcl_SplitList(interp, slist, &lc, &list);
164     if (code == TCL_ERROR)
165       return "variable must be a list";
166     /* reinitialize the list */
167     myres.nscount = 0;
168     for (i = 0; i < lc; i++) {
169       if (myres.nscount >= MAXNS) {
170         putlog(LOG_MISC, "*", "WARNING: %i dns-servers configured but kernel-defined "
171                "limit is %i, ignoring extra servers\n", lc, MAXNS);
172         break;
173       }
174       if ((p = strchr(list[i], ':'))) {
175         *p++ = 0;
176         /* allow non-standard ports */
177         port = atoi(p);
178       } else
179         port = NAMESERVER_PORT; /* port 53 */
180       /* Ignore invalid addresses */
181       if (egg_inet_aton(list[i], &myres.nsaddr_list[myres.nscount].sin_addr)) {
182         myres.nsaddr_list[myres.nscount].sin_port = htons(port);
183         myres.nsaddr_list[myres.nscount].sin_family = AF_INET;
184         myres.nscount++;
185       }
186       else
187         putlog(LOG_MISC, "*", "WARNING: Invalid dns-server %s", list[i]);
188     }
189     Tcl_Free((char *) list);
190   }
191   return NULL;
192 }
193 
194 
195 /*
196  *    DNS module related code
197  */
198 
dns_free_cache(void)199 static void dns_free_cache(void)
200 {
201   struct resolve *rp, *rpnext;
202 
203   for (rp = expireresolves; rp; rp = rpnext) {
204     rpnext = rp->next;
205     if (rp->hostn)
206       nfree(rp->hostn);
207     nfree(rp);
208   }
209   expireresolves = NULL;
210 }
211 
dns_cache_expmem(void)212 static int dns_cache_expmem(void)
213 {
214   struct resolve *rp;
215   int size = 0;
216 
217   for (rp = expireresolves; rp; rp = rp->next) {
218     size += sizeof(struct resolve);
219     if (rp->hostn)
220       size += strlen(rp->hostn) + 1;
221   }
222   return size;
223 }
224 
dns_expmem(void)225 static int dns_expmem(void)
226 {
227   return dns_cache_expmem();
228 }
229 
dns_report(int idx,int details)230 static int dns_report(int idx, int details)
231 {
232   if (details) {
233     int i, size = dns_expmem();
234 
235     dprintf(idx, "    Async DNS resolver is active.\n");
236     dprintf(idx, "    DNS server list:");
237     for (i = 0; i < myres.nscount; i++)
238       dprintf(idx, " %s:%d", iptostr((struct sockaddr *) &myres.nsaddr_list[i]),
239               ntohs(myres.nsaddr_list[i].sin_port));
240     if (!myres.nscount)
241       dprintf(idx, " NO DNS SERVERS FOUND!\n");
242     dprintf(idx, "\n");
243     dprintf(idx, "    Using %d byte%s of memory\n", size,
244             (size != 1) ? "s" : "");
245   }
246   return 0;
247 }
248 
dns_check_servercount(void)249 static int dns_check_servercount(void)
250 {
251   static int oldcount = -1;
252   if (oldcount != myres.nscount && !myres.nscount) {
253     putlog(LOG_MISC, "*", "WARNING: No nameservers found. Please set the dns-servers config variable.");
254   }
255   oldcount = myres.nscount;
256   return 0;
257 }
258 
dns_close()259 static char *dns_close()
260 {
261   int i;
262 
263   del_hook(HOOK_DNS_HOSTBYIP, (Function) dns_lookup);
264   del_hook(HOOK_DNS_IPBYHOST, (Function) dns_forward);
265   del_hook(HOOK_SECONDLY, (Function) dns_check_expires);
266   del_hook(HOOK_REHASH, (Function) dns_check_servercount);
267   rem_tcl_ints(dnsints);
268   rem_tcl_strings(dnsstrings);
269   Tcl_UntraceVar(interp, "dns-servers",
270                  TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
271                  dns_change, NULL);
272 
273   for (i = 0; i < dcc_total; i++) {
274     if (dcc[i].type == &DCC_DNS && dcc[i].sock == resfd) {
275       killsock(dcc[i].sock);
276       lostdcc(i);
277       break;
278     }
279   }
280 
281   dns_free_cache();
282   module_undepend(MODULE_NAME);
283   return NULL;
284 }
285 
286 EXPORT_SCOPE char *dns_start();
287 
288 static Function dns_table[] = {
289   /* 0 - 3 */
290   (Function) dns_start,
291   (Function) dns_close,
292   (Function) dns_expmem,
293   (Function) dns_report,
294   /* 4 - 7 */
295 };
296 #endif /* EGG_TDNS */
297 
dns_start(Function * global_funcs)298 char *dns_start(Function *global_funcs)
299 {
300 #ifdef EGG_TDNS
301   return "Eggdrop was compiled with threaded DNS core; this module will not run with it. Not loading...";
302 #else
303   int idx;
304 
305   global = global_funcs;
306 
307   module_register(MODULE_NAME, dns_table, 1, 2);
308   if (!module_depend(MODULE_NAME, "eggdrop", 108, 0)) {
309     module_undepend(MODULE_NAME);
310     return "This module requires Eggdrop 1.8.0 or later.";
311   }
312 
313   idx = new_dcc(&DCC_DNS, 0);
314   if (idx < 0)
315     return "NO MORE DCC CONNECTIONS -- Can't create DNS socket.";
316   if (!init_dns_core()) {
317     lostdcc(idx);
318     return "DNS initialisation failed.";
319   }
320   dcc[idx].sock = resfd;
321   dcc[idx].timeval = now;
322   strcpy(dcc[idx].nick, "(dns)");
323   memcpy(&dcc[idx].sockname.addr.sa, &myres.nsaddr_list[0],
324              sizeof(myres.nsaddr_list[0]));
325   dcc[idx].sockname.addrlen = sizeof(myres.nsaddr_list[0]);
326 
327   Tcl_TraceVar(interp, "dns-servers",
328                TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
329                dns_change, NULL);
330   add_hook(HOOK_SECONDLY, (Function) dns_check_expires);
331   add_hook(HOOK_DNS_HOSTBYIP, (Function) dns_lookup);
332   add_hook(HOOK_DNS_IPBYHOST, (Function) dns_forward);
333   add_hook(HOOK_REHASH, (Function) dns_check_servercount);
334   add_tcl_ints(dnsints);
335   add_tcl_strings(dnsstrings);
336   return NULL;
337 #endif /* EGG_TDNS */
338 }
339