1/*
2   Unix SMB/CIFS implementation.
3   a async DNS handler
4   Copyright (C) Andrew Tridgell 1997-1998
5
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 3 of the License, or
9   (at your option) any later version.
10
11   This program is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with this program.  If not, see <http://www.gnu.org/licenses/>.
18   */
19
20#include "includes.h"
21
22/***************************************************************************
23  Add a DNS result to the name cache.
24****************************************************************************/
25
26static struct name_record *add_dns_result(struct nmb_name *question, struct in_addr addr)
27{
28	int name_type = question->name_type;
29	unstring qname;
30
31	pull_ascii_nstring(qname, sizeof(qname), question->name);
32
33	if (!addr.s_addr) {
34		/* add the fail to WINS cache of names. give it 1 hour in the cache */
35		DEBUG(3,("add_dns_result: Negative DNS answer for %s\n", qname));
36		add_name_to_subnet( wins_server_subnet, qname, name_type,
37				NB_ACTIVE, 60*60, DNSFAIL_NAME, 1, &addr );
38		return NULL;
39	}
40
41	/* add it to our WINS cache of names. give it 2 hours in the cache */
42	DEBUG(3,("add_dns_result: DNS gave answer for %s of %s\n", qname, inet_ntoa(addr)));
43
44	add_name_to_subnet( wins_server_subnet, qname, name_type,
45                              NB_ACTIVE, 2*60*60, DNS_NAME, 1, &addr);
46
47	return find_name_on_subnet(wins_server_subnet, question, FIND_ANY_NAME);
48}
49
50#ifndef SYNC_DNS
51
52static int fd_in = -1, fd_out = -1;
53static pid_t child_pid = -1;
54static int in_dns;
55
56/* this is the structure that is passed between the parent and child */
57struct query_record {
58	struct nmb_name name;
59	struct in_addr result;
60};
61
62/* a queue of pending requests waiting to be sent to the DNS child */
63static struct packet_struct *dns_queue;
64
65/* the packet currently being processed by the dns child */
66static struct packet_struct *dns_current;
67
68
69/***************************************************************************
70  return the fd used to gather async dns replies. This is added to the select
71  loop
72  ****************************************************************************/
73
74int asyncdns_fd(void)
75{
76	return fd_in;
77}
78
79/***************************************************************************
80  handle DNS queries arriving from the parent
81  ****************************************************************************/
82static void asyncdns_process(void)
83{
84	struct query_record r;
85	unstring qname;
86
87	DEBUGLEVEL = -1;
88
89	while (1) {
90		NTSTATUS status;
91
92		status = read_data(fd_in, (char *)&r, sizeof(r));
93
94		if (!NT_STATUS_IS_OK(status)) {
95			break;
96		}
97
98		pull_ascii_nstring( qname, sizeof(qname), r.name.name);
99		r.result.s_addr = interpret_addr(qname);
100
101		if (write_data(fd_out, (char *)&r, sizeof(r)) != sizeof(r))
102			break;
103	}
104
105	_exit(0);
106}
107
108/**************************************************************************** **
109  catch a sigterm (in the child process - the parent has a different handler
110  see nmbd.c for details).
111  We need a separate term handler here so we don't release any
112  names that our parent is going to release, or overwrite a
113  WINS db that our parent is going to write.
114 **************************************************************************** */
115
116static void sig_term(int sig)
117{
118	_exit(0);
119}
120
121/***************************************************************************
122 Called by the parent process when it receives a SIGTERM - also kills the
123 child so we don't get child async dns processes lying around, causing trouble.
124  ****************************************************************************/
125
126void kill_async_dns_child(void)
127{
128	if (child_pid > 0) {
129		kill(child_pid, SIGTERM);
130		child_pid = -1;
131	}
132}
133
134/***************************************************************************
135  create a child process to handle DNS lookups
136  ****************************************************************************/
137void start_async_dns(void)
138{
139	int fd1[2], fd2[2];
140
141	CatchChild();
142
143	if (pipe(fd1) || pipe(fd2)) {
144		DEBUG(0,("can't create asyncdns pipes\n"));
145		return;
146	}
147
148	child_pid = sys_fork();
149
150	if (child_pid) {
151		fd_in = fd1[0];
152		fd_out = fd2[1];
153		close(fd1[1]);
154		close(fd2[0]);
155		DEBUG(0,("started asyncdns process %d\n", (int)child_pid));
156		return;
157	}
158
159	fd_in = fd2[0];
160	fd_out = fd1[1];
161
162	CatchSignal(SIGUSR2, SIG_IGN);
163	CatchSignal(SIGUSR1, SIG_IGN);
164	CatchSignal(SIGHUP, SIG_IGN);
165        CatchSignal(SIGTERM, SIGNAL_CAST sig_term );
166
167	if (!NT_STATUS_IS_OK(reinit_after_fork(nmbd_messaging_context(),
168					       nmbd_event_context(), true))) {
169		DEBUG(0,("reinit_after_fork() failed\n"));
170		smb_panic("reinit_after_fork() failed");
171	}
172
173	asyncdns_process();
174}
175
176
177/***************************************************************************
178check if a particular name is already being queried
179  ****************************************************************************/
180static bool query_current(struct query_record *r)
181{
182	return dns_current &&
183		nmb_name_equal(&r->name,
184			   &dns_current->packet.nmb.question.question_name);
185}
186
187
188/***************************************************************************
189  write a query to the child process
190  ****************************************************************************/
191static bool write_child(struct packet_struct *p)
192{
193	struct query_record r;
194
195	r.name = p->packet.nmb.question.question_name;
196
197	return write_data(fd_out, (char *)&r, sizeof(r)) == sizeof(r);
198}
199
200/***************************************************************************
201  check the DNS queue
202  ****************************************************************************/
203void run_dns_queue(void)
204{
205	struct query_record r;
206	struct packet_struct *p, *p2;
207	struct name_record *namerec;
208	NTSTATUS status;
209
210	if (fd_in == -1)
211		return;
212
213	if (!process_exists_by_pid(child_pid)) {
214		close(fd_in);
215		close(fd_out);
216		start_async_dns();
217	}
218
219	status = read_data(fd_in, (char *)&r, sizeof(r));
220
221	if (!NT_STATUS_IS_OK(status)) {
222		DEBUG(0, ("read from child failed: %s\n", nt_errstr(status)));
223		fd_in = -1;
224		return;
225	}
226
227	namerec = add_dns_result(&r.name, r.result);
228
229	if (dns_current) {
230		if (query_current(&r)) {
231			DEBUG(3,("DNS calling send_wins_name_query_response\n"));
232			in_dns = 1;
233			if(namerec == NULL)
234				send_wins_name_query_response(NAM_ERR, dns_current, NULL);
235			else
236				send_wins_name_query_response(0,dns_current,namerec);
237			in_dns = 0;
238		}
239
240		dns_current->locked = False;
241		free_packet(dns_current);
242		dns_current = NULL;
243	}
244
245	/* loop over the whole dns queue looking for entries that
246	   match the result we just got */
247	for (p = dns_queue; p;) {
248		struct nmb_packet *nmb = &p->packet.nmb;
249		struct nmb_name *question = &nmb->question.question_name;
250
251		if (nmb_name_equal(question, &r.name)) {
252			DEBUG(3,("DNS calling send_wins_name_query_response\n"));
253			in_dns = 1;
254			if(namerec == NULL)
255				send_wins_name_query_response(NAM_ERR, p, NULL);
256			else
257				send_wins_name_query_response(0,p,namerec);
258			in_dns = 0;
259			p->locked = False;
260
261			if (p->prev)
262				p->prev->next = p->next;
263			else
264				dns_queue = p->next;
265			if (p->next)
266				p->next->prev = p->prev;
267			p2 = p->next;
268			free_packet(p);
269			p = p2;
270		} else {
271			p = p->next;
272		}
273	}
274
275	if (dns_queue) {
276		dns_current = dns_queue;
277		dns_queue = dns_queue->next;
278		if (dns_queue)
279			dns_queue->prev = NULL;
280		dns_current->next = NULL;
281
282		if (!write_child(dns_current)) {
283			DEBUG(3,("failed to send DNS query to child!\n"));
284			return;
285		}
286	}
287}
288
289/***************************************************************************
290queue a DNS query
291  ****************************************************************************/
292
293bool queue_dns_query(struct packet_struct *p,struct nmb_name *question)
294{
295	if (in_dns || fd_in == -1)
296		return False;
297
298	if (!dns_current) {
299		if (!write_child(p)) {
300			DEBUG(3,("failed to send DNS query to child!\n"));
301			return False;
302		}
303		dns_current = p;
304		p->locked = True;
305	} else {
306		p->locked = True;
307		p->next = dns_queue;
308		p->prev = NULL;
309		if (p->next)
310			p->next->prev = p;
311		dns_queue = p;
312	}
313
314	DEBUG(3,("added DNS query for %s\n", nmb_namestr(question)));
315	return True;
316}
317
318#else
319
320
321/***************************************************************************
322  we use this when we can't do async DNS lookups
323  ****************************************************************************/
324
325bool queue_dns_query(struct packet_struct *p,struct nmb_name *question)
326{
327	struct name_record *namerec = NULL;
328	struct in_addr dns_ip;
329	unstring qname;
330
331	pull_ascii_nstring(qname, sizeof(qname), question->name);
332
333	DEBUG(3,("DNS search for %s - ", nmb_namestr(question)));
334
335	dns_ip.s_addr = interpret_addr(qname);
336
337	namerec = add_dns_result(question, dns_ip);
338	if(namerec == NULL) {
339		send_wins_name_query_response(NAM_ERR, p, NULL);
340	} else {
341		send_wins_name_query_response(0, p, namerec);
342	}
343	return False;
344}
345
346/***************************************************************************
347 With sync dns there is no child to kill on SIGTERM.
348  ****************************************************************************/
349
350void kill_async_dns_child(void)
351{
352	return;
353}
354#endif
355