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