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