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