root/source4/libcli/resolve/dns_ex.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. dns_ex_destructor
  2. run_child_dns_lookup
  3. run_child_getaddrinfo
  4. pipe_handler
  5. resolve_name_dns_ex_send
  6. resolve_name_dns_ex_recv

   1 /*
   2    Unix SMB/CIFS implementation.
   3 
   4    async getaddrinfo()/dns_lookup() name resolution module
   5 
   6    Copyright (C) Andrew Tridgell 2005
   7    Copyright (C) Stefan Metzmacher 2008
   8 
   9    This program is free software; you can redistribute it and/or modify
  10    it under the terms of the GNU General Public License as published by
  11    the Free Software Foundation; either version 3 of the License, or
  12    (at your option) any later version.
  13 
  14    This program is distributed in the hope that it will be useful,
  15    but WITHOUT ANY WARRANTY; without even the implied warranty of
  16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17    GNU General Public License for more details.
  18 
  19    You should have received a copy of the GNU General Public License
  20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  21 */
  22 
  23 /*
  24   this module uses a fork() per getaddrinfo() or dns_looup() call.
  25   At first that might seem crazy, but it is actually very fast,
  26   and solves many of the tricky problems of keeping a child
  27   hanging around in a librar (like what happens when the parent forks).
  28   We use a talloc destructor to ensure that the child is cleaned up
  29   when we have finished with this name resolution.
  30 */
  31 
  32 #include "includes.h"
  33 #include "lib/events/events.h"
  34 #include "system/network.h"
  35 #include "system/filesys.h"
  36 #include "lib/socket/socket.h"
  37 #include "libcli/composite/composite.h"
  38 #include "librpc/gen_ndr/ndr_nbt.h"
  39 #include "libcli/resolve/resolve.h"
  40 
  41 #ifdef class
  42 #undef class
  43 #endif
  44 
  45 #include "heimdal/lib/roken/resolve.h"
  46 
  47 struct dns_ex_state {
  48         bool do_fallback;
  49         uint32_t flags;
  50         uint16_t port;
  51         struct nbt_name name;
  52         struct socket_address **addrs;
  53         char **names;
  54         pid_t child;
  55         int child_fd;
  56         struct tevent_fd *fde;
  57         struct tevent_context *event_ctx;
  58 };
  59 
  60 /*
  61   kill off a wayward child if needed. This allows us to stop an async
  62   name resolution without leaving a potentially blocking call running
  63   in a child
  64 */
  65 static int dns_ex_destructor(struct dns_ex_state *state)
     /* [<][>][^][v][top][bottom][index][help] */
  66 {
  67         int status;
  68 
  69         kill(state->child, SIGTERM);
  70         close(state->child_fd);
  71         if (waitpid(state->child, &status, WNOHANG) == 0) {
  72                 kill(state->child, SIGKILL);
  73                 waitpid(state->child, &status, 0);
  74         }
  75 
  76         return 0;
  77 }
  78 
  79 /*
  80   the blocking child
  81 */
  82 static void run_child_dns_lookup(struct dns_ex_state *state, int fd)
     /* [<][>][^][v][top][bottom][index][help] */
  83 {
  84         struct dns_reply *reply;
  85         struct resource_record *rr;
  86         uint32_t count = 0;
  87         uint32_t srv_valid = 0;
  88         struct resource_record **srv_rr;
  89         uint32_t addrs_valid = 0;
  90         struct resource_record **addrs_rr;
  91         char *addrs;
  92         bool first;
  93         uint32_t i;
  94         bool do_srv = (state->flags & RESOLVE_NAME_FLAG_DNS_SRV);
  95 
  96         /* this is the blocking call we are going to lots of trouble
  97            to avoid in the parent */
  98         reply = dns_lookup(state->name.name, do_srv?"SRV":"A");
  99         if (!reply) {
 100                 goto done;
 101         }
 102 
 103         if (do_srv) {
 104                 dns_srv_order(reply);
 105         }
 106 
 107         /* Loop over all returned records and pick the "srv" records */
 108         for (rr=reply->head; rr; rr=rr->next) {
 109                 /* we are only interested in the IN class */
 110                 if (rr->class != C_IN) {
 111                         continue;
 112                 }
 113 
 114                 if (do_srv) {
 115                         /* we are only interested in SRV records */
 116                         if (rr->type != T_SRV) {
 117                                 continue;
 118                         }
 119 
 120                         /* verify we actually have a SRV record here */
 121                         if (!rr->u.srv) {
 122                                 continue;
 123                         }
 124 
 125                         /* Verify we got a port */
 126                         if (rr->u.srv->port == 0) {
 127                                 continue;
 128                         }
 129                 } else {
 130                         /* we are only interested in A records */
 131                         /* TODO: add AAAA support */
 132                         if (rr->type != T_A) {
 133                                 continue;
 134                         }
 135 
 136                         /* verify we actually have a A record here */
 137                         if (!rr->u.a) {
 138                                 continue;
 139                         }
 140                 }
 141                 count++;
 142         }
 143 
 144         if (count == 0) {
 145                 goto done;
 146         }
 147 
 148         srv_rr = talloc_zero_array(state,
 149                                    struct resource_record *,
 150                                    count);
 151         if (!srv_rr) {
 152                 goto done;
 153         }
 154 
 155         addrs_rr = talloc_zero_array(state,
 156                                      struct resource_record *,
 157                                      count);
 158         if (!addrs_rr) {
 159                 goto done;
 160         }
 161 
 162         /* Loop over all returned records and pick the records */
 163         for (rr=reply->head;rr;rr=rr->next) {
 164                 /* we are only interested in the IN class */
 165                 if (rr->class != C_IN) {
 166                         continue;
 167                 }
 168 
 169                 if (do_srv) {
 170                         /* we are only interested in SRV records */
 171                         if (rr->type != T_SRV) {
 172                                 continue;
 173                         }
 174 
 175                         /* verify we actually have a srv record here */
 176                         if (!rr->u.srv) {
 177                                 continue;
 178                         }
 179 
 180                         /* Verify we got a port */
 181                         if (rr->u.srv->port == 0) {
 182                                 continue;
 183                         }
 184 
 185                         srv_rr[srv_valid] = rr;
 186                         srv_valid++;
 187                 } else {
 188                         /* we are only interested in A records */
 189                         /* TODO: add AAAA support */
 190                         if (rr->type != T_A) {
 191                                 continue;
 192                         }
 193 
 194                         /* verify we actually have a A record here */
 195                         if (!rr->u.a) {
 196                                 continue;
 197                         }
 198 
 199                         addrs_rr[addrs_valid] = rr;
 200                         addrs_valid++;
 201                 }
 202         }
 203 
 204         for (i=0; i < srv_valid; i++) {
 205                 for (rr=reply->head;rr;rr=rr->next) {
 206 
 207                         if (rr->class != C_IN) {
 208                                 continue;
 209                         }
 210 
 211                         /* we are only interested in SRV records */
 212                         if (rr->type != T_A) {
 213                                 continue;
 214                         }
 215 
 216                         /* verify we actually have a srv record here */
 217                         if (strcmp(&srv_rr[i]->u.srv->target[0], rr->domain) != 0) {
 218                                 continue;
 219                         }
 220 
 221                         addrs_rr[i] = rr;
 222                         addrs_valid++;
 223                         break;
 224                 }
 225         }
 226 
 227         if (addrs_valid == 0) {
 228                 goto done;
 229         }
 230 
 231         addrs = talloc_strdup(state, "");
 232         if (!addrs) {
 233                 goto done;
 234         }
 235         first = true;
 236         for (i=0; i < count; i++) {
 237                 uint16_t port;
 238                 if (!addrs_rr[i]) {
 239                         continue;
 240                 }
 241 
 242                 if (srv_rr[i] &&
 243                     (state->flags & RESOLVE_NAME_FLAG_OVERWRITE_PORT)) {
 244                         port = srv_rr[i]->u.srv->port;
 245                 } else {
 246                         port = state->port;
 247                 }
 248 
 249                 addrs = talloc_asprintf_append_buffer(addrs, "%s%s:%u/%s",
 250                                                       first?"":",",
 251                                                       inet_ntoa(*addrs_rr[i]->u.a),
 252                                                       port,
 253                                                       addrs_rr[i]->domain);
 254                 if (!addrs) {
 255                         goto done;
 256                 }
 257                 first = false;
 258         }
 259 
 260         if (addrs) {
 261                 write(fd, addrs, talloc_get_size(addrs));
 262         }
 263 
 264 done:
 265         close(fd);
 266 }
 267 
 268 /*
 269   the blocking child
 270 */
 271 static void run_child_getaddrinfo(struct dns_ex_state *state, int fd)
     /* [<][>][^][v][top][bottom][index][help] */
 272 {
 273         int ret;
 274         struct addrinfo hints;
 275         struct addrinfo *res;
 276         struct addrinfo *res_list = NULL;
 277         char *addrs;
 278         bool first;
 279 
 280         ZERO_STRUCT(hints);
 281         hints.ai_socktype = SOCK_STREAM;
 282         hints.ai_family = AF_INET;/* TODO: add AF_INET6 support */
 283         hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
 284 
 285         ret = getaddrinfo(state->name.name, "0", &hints, &res_list);
 286 #ifdef EAI_NODATA
 287         if (ret == EAI_NODATA && state->do_fallback) {
 288 #else
 289         if (ret == EAI_NONAME && state->do_fallback) {
 290 #endif
 291                 /* getaddrinfo() doesn't handle CNAME records */
 292                 run_child_dns_lookup(state, fd);
 293                 return;
 294         }
 295         if (ret != 0) {
 296                 goto done;
 297         }
 298 
 299         addrs = talloc_strdup(state, "");
 300         if (!addrs) {
 301                 goto done;
 302         }
 303         first = true;
 304         for (res = res_list; res; res = res->ai_next) {
 305                 struct sockaddr_in *in;
 306 
 307                 if (res->ai_family != AF_INET) {
 308                         continue;
 309                 }
 310                 in = (struct sockaddr_in *)res->ai_addr;
 311 
 312                 addrs = talloc_asprintf_append_buffer(addrs, "%s%s:%u/%s",
 313                                                       first?"":",",
 314                                                       inet_ntoa(in->sin_addr),
 315                                                       state->port,
 316                                                       state->name.name);
 317                 if (!addrs) {
 318                         goto done;
 319                 }
 320                 first = false;
 321         }
 322 
 323         if (addrs) {
 324                 write(fd, addrs, talloc_get_size(addrs));
 325         }
 326 done:
 327         if (res_list) {
 328                 freeaddrinfo(res_list);
 329         }
 330         close(fd);
 331 }
 332 
 333 /*
 334   handle a read event on the pipe
 335 */
 336 static void pipe_handler(struct tevent_context *ev, struct tevent_fd *fde, 
     /* [<][>][^][v][top][bottom][index][help] */
 337                          uint16_t flags, void *private_data)
 338 {
 339         struct composite_context *c = talloc_get_type(private_data, struct composite_context);
 340         struct dns_ex_state *state = talloc_get_type(c->private_data,
 341                                      struct dns_ex_state);
 342         char *address;
 343         uint32_t num_addrs, i;
 344         char **addrs;
 345         int ret;
 346         int status;
 347         int value = 0;
 348 
 349         /* if we get any event from the child then we know that we
 350            won't need to kill it off */
 351         talloc_set_destructor(state, NULL);
 352 
 353         if (ioctl(state->child_fd, FIONREAD, &value) != 0) {
 354                 value = 8192;
 355         }
 356 
 357         address = talloc_array(state, char, value+1);
 358         if (address) {
 359                 /* yes, we don't care about EAGAIN or other niceities
 360                    here. They just can't happen with this parent/child
 361                    relationship, and even if they did then giving an error is
 362                    the right thing to do */
 363                 ret = read(state->child_fd, address, value);
 364         } else {
 365                 ret = -1;
 366         }
 367         close(state->child_fd);
 368         if (waitpid(state->child, &status, WNOHANG) == 0) {
 369                 kill(state->child, SIGKILL);
 370                 waitpid(state->child, &status, 0);
 371         }
 372 
 373         if (ret <= 0) {
 374                 composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
 375                 return;
 376         }
 377 
 378         /* enusre the address looks good */
 379         address[ret] = 0;
 380 
 381         addrs = str_list_make(state, address, ",");
 382         if (composite_nomem(addrs, c)) return;
 383 
 384         num_addrs = str_list_length((const char * const *)addrs);
 385 
 386         state->addrs = talloc_array(state, struct socket_address *,
 387                                     num_addrs+1);
 388         if (composite_nomem(state->addrs, c)) return;
 389 
 390         state->names = talloc_array(state, char *, num_addrs+1);
 391         if (composite_nomem(state->names, c)) return;
 392 
 393         for (i=0; i < num_addrs; i++) {
 394                 uint32_t port = 0;
 395                 char *p = strrchr(addrs[i], ':');
 396                 char *n;
 397 
 398                 if (!p) {
 399                         composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
 400                         return;
 401                 }
 402 
 403                 *p = '\0';
 404                 p++;
 405 
 406                 n = strrchr(p, '/');
 407                 if (!n) {
 408                         composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
 409                         return;
 410                 }
 411 
 412                 *n = '\0';
 413                 n++;
 414 
 415                 if (strcmp(addrs[i], "0.0.0.0") == 0 ||
 416                     inet_addr(addrs[i]) == INADDR_NONE) {
 417                         composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
 418                         return;
 419                 }
 420                 port = strtoul(p, NULL, 10);
 421                 if (port > UINT16_MAX) {
 422                         composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
 423                         return;
 424                 }
 425                 state->addrs[i] = socket_address_from_strings(state->addrs,
 426                                                               "ipv4",
 427                                                               addrs[i],
 428                                                               port);
 429                 if (composite_nomem(state->addrs[i], c)) return;
 430 
 431                 state->names[i] = talloc_strdup(state->names, n);
 432                 if (composite_nomem(state->names[i], c)) return;
 433         }
 434         state->addrs[i] = NULL;
 435         state->names[i] = NULL;
 436 
 437         composite_done(c);
 438 }
 439 
 440 /*
 441   getaddrinfo() or dns_lookup() name resolution method - async send
 442  */
 443 struct composite_context *resolve_name_dns_ex_send(TALLOC_CTX *mem_ctx,
     /* [<][>][^][v][top][bottom][index][help] */
 444                                                    struct tevent_context *event_ctx,
 445                                                    void *privdata,
 446                                                    uint32_t flags,
 447                                                    uint16_t port,
 448                                                    struct nbt_name *name,
 449                                                    bool do_fallback)
 450 {
 451         struct composite_context *c;
 452         struct dns_ex_state *state;
 453         int fd[2] = { -1, -1 };
 454         int ret;
 455 
 456         c = composite_create(mem_ctx, event_ctx);
 457         if (c == NULL) return NULL;
 458 
 459         if (flags & RESOLVE_NAME_FLAG_FORCE_NBT) {
 460                 composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
 461                 return c;
 462         }
 463 
 464         state = talloc_zero(c, struct dns_ex_state);
 465         if (composite_nomem(state, c)) return c;
 466         c->private_data = state;
 467 
 468         c->status = nbt_name_dup(state, name, &state->name);
 469         if (!composite_is_ok(c)) return c;
 470 
 471         /* setup a pipe to chat to our child */
 472         ret = pipe(fd);
 473         if (ret == -1) {
 474                 composite_error(c, map_nt_error_from_unix(errno));
 475                 return c;
 476         }
 477 
 478         state->do_fallback = do_fallback;
 479         state->flags = flags;
 480         state->port = port;
 481 
 482         state->child_fd = fd[0];
 483         state->event_ctx = c->event_ctx;
 484 
 485         /* we need to put the child in our event context so
 486            we know when the dns_lookup() has finished */
 487         state->fde = event_add_fd(c->event_ctx, c, state->child_fd, EVENT_FD_READ, 
 488                                   pipe_handler, c);
 489         if (composite_nomem(state->fde, c)) {
 490                 close(fd[0]);
 491                 close(fd[1]);
 492                 return c;
 493         }
 494 
 495         state->child = fork();
 496         if (state->child == (pid_t)-1) {
 497                 composite_error(c, map_nt_error_from_unix(errno));
 498                 return c;
 499         }
 500 
 501         if (state->child == 0) {
 502                 close(fd[0]);
 503                 if (state->flags & RESOLVE_NAME_FLAG_FORCE_DNS) {
 504                         run_child_dns_lookup(state, fd[1]);
 505                 } else {
 506                         run_child_getaddrinfo(state, fd[1]);
 507                 }
 508                 _exit(0);
 509         }
 510         close(fd[1]);
 511 
 512         /* cleanup wayward children */
 513         talloc_set_destructor(state, dns_ex_destructor);
 514 
 515         return c;
 516 }
 517 
 518 /*
 519   getaddrinfo() or dns_lookup() name resolution method - recv side
 520 */
 521 NTSTATUS resolve_name_dns_ex_recv(struct composite_context *c, 
     /* [<][>][^][v][top][bottom][index][help] */
 522                                   TALLOC_CTX *mem_ctx,
 523                                   struct socket_address ***addrs,
 524                                   char ***names)
 525 {
 526         NTSTATUS status;
 527 
 528         status = composite_wait(c);
 529 
 530         if (NT_STATUS_IS_OK(status)) {
 531                 struct dns_ex_state *state = talloc_get_type(c->private_data,
 532                                              struct dns_ex_state);
 533                 *addrs = talloc_steal(mem_ctx, state->addrs);
 534                 if (names) {
 535                         *names = talloc_steal(mem_ctx, state->names);
 536                 }
 537         }
 538 
 539         talloc_free(c);
 540         return status;
 541 }

/* [<][>][^][v][top][bottom][index][help] */