LCOV - code coverage report
Current view: top level - source3/nmbd - asyncdns.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 5 120 4.2 %
Date: 2024-04-21 15:09:00 Functions: 2 10 20.0 %

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

Generated by: LCOV version 1.14