~zulcss/samba/server-dailies-3.4

« back to all changes in this revision

Viewing changes to source3/nmbd/asyncdns.c

  • Committer: Chuck Short
  • Date: 2010-09-28 20:38:39 UTC
  • Revision ID: zulcss@ubuntu.com-20100928203839-pgjulytsi9ue63x1
Initial version

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
26
static 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
 
 
52
static int fd_in = -1, fd_out = -1;
 
53
static pid_t child_pid = -1;
 
54
static int in_dns;
 
55
 
 
56
/* this is the structure that is passed between the parent and child */
 
57
struct 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 */
 
63
static struct packet_struct *dns_queue;
 
64
 
 
65
/* the packet currently being processed by the dns child */
 
66
static 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
 
 
74
int asyncdns_fd(void)
 
75
{
 
76
        return fd_in;
 
77
}
 
78
 
 
79
/***************************************************************************
 
80
  handle DNS queries arriving from the parent
 
81
  ****************************************************************************/
 
82
static 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
 
 
116
static 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
 
 
126
void 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
  ****************************************************************************/
 
137
void 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
/***************************************************************************
 
178
check if a particular name is already being queried
 
179
  ****************************************************************************/
 
180
static 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
  ****************************************************************************/
 
191
static 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
  ****************************************************************************/
 
203
void 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
/***************************************************************************
 
290
queue a DNS query
 
291
  ****************************************************************************/
 
292
 
 
293
bool 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
 
 
325
bool 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
 
 
350
void kill_async_dns_child(void)
 
351
{
 
352
        return;
 
353
}
 
354
#endif