diff options
author | Andrew Tridgell <tridge@samba.org> | 2003-08-13 01:53:07 +0000 |
---|---|---|
committer | Andrew Tridgell <tridge@samba.org> | 2003-08-13 01:53:07 +0000 |
commit | ef2e26c91b80556af033d3335e55f5dfa6fff31d (patch) | |
tree | faa21bfd7e7b5247250b47c7891dc1a5ebee6be9 /source4/nmbd/asyncdns.c | |
download | samba-ef2e26c91b80556af033d3335e55f5dfa6fff31d.tar.gz samba-ef2e26c91b80556af033d3335e55f5dfa6fff31d.tar.bz2 samba-ef2e26c91b80556af033d3335e55f5dfa6fff31d.zip |
first public release of samba4 code
(This used to be commit b0510b5428b3461aeb9bbe3cc95f62fc73e2b97f)
Diffstat (limited to 'source4/nmbd/asyncdns.c')
-rw-r--r-- | source4/nmbd/asyncdns.c | 349 |
1 files changed, 349 insertions, 0 deletions
diff --git a/source4/nmbd/asyncdns.c b/source4/nmbd/asyncdns.c new file mode 100644 index 0000000000..c86ee69a09 --- /dev/null +++ b/source4/nmbd/asyncdns.c @@ -0,0 +1,349 @@ +/* + Unix SMB/CIFS implementation. + a async DNS handler + Copyright (C) Andrew Tridgell 1997-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "includes.h" + +/*************************************************************************** + Add a DNS result to the name cache. +****************************************************************************/ + +static struct name_record *add_dns_result(struct nmb_name *question, struct in_addr addr) +{ + int name_type = question->name_type; + char *qname = question->name; + + + if (!addr.s_addr) { + /* add the fail to WINS cache of names. give it 1 hour in the cache */ + DEBUG(3,("add_dns_result: Negative DNS answer for %s\n", qname)); + (void)add_name_to_subnet( wins_server_subnet, qname, name_type, + NB_ACTIVE, 60*60, DNSFAIL_NAME, 1, &addr ); + return( NULL ); + } + + /* add it to our WINS cache of names. give it 2 hours in the cache */ + DEBUG(3,("add_dns_result: DNS gave answer for %s of %s\n", qname, inet_ntoa(addr))); + + return( add_name_to_subnet( wins_server_subnet, qname, name_type, + NB_ACTIVE, 2*60*60, DNS_NAME, 1, &addr ) ); +} + + + +#ifndef SYNC_DNS + +static int fd_in = -1, fd_out = -1; +static pid_t child_pid = -1; +static int in_dns; + +/* this is the structure that is passed between the parent and child */ +struct query_record { + struct nmb_name name; + struct in_addr result; +}; + +/* a queue of pending requests waiting to be sent to the DNS child */ +static struct packet_struct *dns_queue; + +/* the packet currently being processed by the dns child */ +static struct packet_struct *dns_current; + + +/*************************************************************************** + return the fd used to gather async dns replies. This is added to the select + loop + ****************************************************************************/ +int asyncdns_fd(void) +{ + return fd_in; +} + +/*************************************************************************** + handle DNS queries arriving from the parent + ****************************************************************************/ +static void asyncdns_process(void) +{ + struct query_record r; + fstring qname; + + DEBUGLEVEL = -1; + + while (1) { + if (read_data(fd_in, (char *)&r, sizeof(r)) != sizeof(r)) + break; + + fstrcpy(qname, r.name.name); + + r.result.s_addr = interpret_addr(qname); + + if (write_data(fd_out, (char *)&r, sizeof(r)) != sizeof(r)) + break; + } + + _exit(0); +} + +/**************************************************************************** ** + catch a sigterm (in the child process - the parent has a different handler + see nmbd.c for details). + We need a separate term handler here so we don't release any + names that our parent is going to release, or overwrite a + WINS db that our parent is going to write. + **************************************************************************** */ + +static void sig_term(int sig) +{ + _exit(0); +} + +/*************************************************************************** + Called by the parent process when it receives a SIGTERM - also kills the + child so we don't get child async dns processes lying around, causing trouble. + ****************************************************************************/ + +void kill_async_dns_child(void) +{ + if (child_pid > 0) { + kill(child_pid, SIGTERM); + child_pid = -1; + } +} + +/*************************************************************************** + create a child process to handle DNS lookups + ****************************************************************************/ +void start_async_dns(void) +{ + int fd1[2], fd2[2]; + + CatchChild(); + + if (pipe(fd1) || pipe(fd2)) { + DEBUG(0,("can't create asyncdns pipes\n")); + return; + } + + child_pid = sys_fork(); + + if (child_pid) { + fd_in = fd1[0]; + fd_out = fd2[1]; + close(fd1[1]); + close(fd2[0]); + DEBUG(0,("started asyncdns process %d\n", (int)child_pid)); + return; + } + + fd_in = fd2[0]; + fd_out = fd1[1]; + + CatchSignal(SIGUSR2, SIG_IGN); + CatchSignal(SIGUSR1, SIG_IGN); + CatchSignal(SIGHUP, SIG_IGN); + CatchSignal(SIGTERM, SIGNAL_CAST sig_term ); + + asyncdns_process(); +} + + +/*************************************************************************** +check if a particular name is already being queried + ****************************************************************************/ +static BOOL query_current(struct query_record *r) +{ + return dns_current && + nmb_name_equal(&r->name, + &dns_current->packet.nmb.question.question_name); +} + + +/*************************************************************************** + write a query to the child process + ****************************************************************************/ +static BOOL write_child(struct packet_struct *p) +{ + struct query_record r; + + r.name = p->packet.nmb.question.question_name; + + return write_data(fd_out, (char *)&r, sizeof(r)) == sizeof(r); +} + +/*************************************************************************** + check the DNS queue + ****************************************************************************/ +void run_dns_queue(void) +{ + struct query_record r; + struct packet_struct *p, *p2; + struct name_record *namerec; + int size; + + if (fd_in == -1) + return; + + /* Allow SIGTERM to kill us. */ + BlockSignals(False, SIGTERM); + + if (!process_exists(child_pid)) { + close(fd_in); + start_async_dns(); + } + + if ((size=read_data(fd_in, (char *)&r, sizeof(r))) != sizeof(r)) { + if (size) { + DEBUG(0,("Incomplete DNS answer from child!\n")); + fd_in = -1; + } + BlockSignals(True, SIGTERM); + return; + } + + BlockSignals(True, SIGTERM); + + namerec = add_dns_result(&r.name, r.result); + + if (dns_current) { + if (query_current(&r)) { + DEBUG(3,("DNS calling send_wins_name_query_response\n")); + in_dns = 1; + if(namerec == NULL) + send_wins_name_query_response(NAM_ERR, dns_current, NULL); + else + send_wins_name_query_response(0,dns_current,namerec); + in_dns = 0; + } + + dns_current->locked = False; + free_packet(dns_current); + dns_current = NULL; + } + + /* loop over the whole dns queue looking for entries that + match the result we just got */ + for (p = dns_queue; p;) { + struct nmb_packet *nmb = &p->packet.nmb; + struct nmb_name *question = &nmb->question.question_name; + + if (nmb_name_equal(question, &r.name)) { + DEBUG(3,("DNS calling send_wins_name_query_response\n")); + in_dns = 1; + if(namerec == NULL) + send_wins_name_query_response(NAM_ERR, p, NULL); + else + send_wins_name_query_response(0,p,namerec); + in_dns = 0; + p->locked = False; + + if (p->prev) + p->prev->next = p->next; + else + dns_queue = p->next; + if (p->next) + p->next->prev = p->prev; + p2 = p->next; + free_packet(p); + p = p2; + } else { + p = p->next; + } + } + + if (dns_queue) { + dns_current = dns_queue; + dns_queue = dns_queue->next; + if (dns_queue) dns_queue->prev = NULL; + dns_current->next = NULL; + + if (!write_child(dns_current)) { + DEBUG(3,("failed to send DNS query to child!\n")); + return; + } + } + +} + +/*************************************************************************** +queue a DNS query + ****************************************************************************/ +BOOL queue_dns_query(struct packet_struct *p,struct nmb_name *question, + struct name_record **n) +{ + if (in_dns || fd_in == -1) + return False; + + if (!dns_current) { + if (!write_child(p)) { + DEBUG(3,("failed to send DNS query to child!\n")); + return False; + } + dns_current = p; + p->locked = True; + } else { + p->locked = True; + p->next = dns_queue; + p->prev = NULL; + if (p->next) + p->next->prev = p; + dns_queue = p; + } + + DEBUG(3,("added DNS query for %s\n", nmb_namestr(question))); + return True; +} + +#else + + +/*************************************************************************** + we use this when we can't do async DNS lookups + ****************************************************************************/ +BOOL queue_dns_query(struct packet_struct *p,struct nmb_name *question, + struct name_record **n) +{ + char *qname = question->name; + struct in_addr dns_ip; + + DEBUG(3,("DNS search for %s - ", nmb_namestr(question))); + + /* Unblock TERM signal so we can be killed in DNS lookup. */ + BlockSignals(False, SIGTERM); + + dns_ip.s_addr = interpret_addr(qname); + + /* Re-block TERM signal. */ + BlockSignals(True, SIGTERM); + + *n = add_dns_result(question, dns_ip); + if(*n == NULL) + send_wins_name_query_response(NAM_ERR, p, NULL); + else + send_wins_name_query_response(0, p, *n); + return False; +} + +/*************************************************************************** + With sync dns there is no child to kill on SIGTERM. + ****************************************************************************/ +void kill_async_dns_child(void) +{ + return; +} +#endif |