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