Logo Search packages:      
Sourcecode: ldns version File versions  Download package

ldns-walk.c

/*
 * ldns-walk uses educated guesses and NSEC data to retrieve the
 * contents of a dnssec signed zone
 *
 * (c) NLnet Labs, 2005
 * See the file LICENSE for the license
 */

#include "config.h"

#include <ldns/ldns.h>

int verbosity = 0;

int
usage(FILE *fp, char *prog) {
      fprintf(fp, "%s [options] domain\n", prog);
      fprintf(fp, "  print out the owner names for domain and the record types for those names\n");
      fprintf(fp, "OPTIONS:\n");
      fprintf(fp, "-4\t\tonly use IPv4\n");
      fprintf(fp, "-6\t\tonly use IPv6\n");
      fprintf(fp, "-s <name>\t\tStart from this name\n");
      fprintf(fp, "-v <verbosity>\t\tVerbosity level [1-5]\n");
      fprintf(fp, "-version\tShow version and exit\n");
      fprintf(fp, "@<nameserver>\t\tUse this nameserver\n");
      return 0;
}

ldns_rdf *
create_dname_plus_1(ldns_rdf *dname)
{
      uint8_t *wire;
      ldns_rdf *newdname;
      uint8_t labellen;
      size_t pos;
      ldns_status status;
      size_t i;
      
      ldns_dname2canonical(dname);
      labellen = ldns_rdf_data(dname)[0];
      if (labellen < 63) {
            wire = malloc(ldns_rdf_size(dname) + 1);
            if (!wire) {
                  fprintf(stderr, "Malloc error: out of memory?\n");
                  exit(127);
            }
            wire[0] = labellen + 1;
            memcpy(&wire[1], ldns_rdf_data(dname) + 1, labellen);
            memcpy(&wire[labellen+1], ldns_rdf_data(dname) + labellen, ldns_rdf_size(dname) - labellen);
            wire[labellen+1] = (uint8_t) '\000';
            pos = 0;
            status = ldns_wire2dname(&newdname, wire, ldns_rdf_size(dname) + 1, &pos);
            free(wire);
      } else {
            wire = malloc(ldns_rdf_size(dname));
            if (!wire) {
                  fprintf(stderr, "Malloc error: out of memory?\n");
                  exit(127);
            }
            wire[0] = labellen;
            memcpy(&wire[1], ldns_rdf_data(dname) + 1, labellen);
            memcpy(&wire[labellen], ldns_rdf_data(dname) + labellen, ldns_rdf_size(dname) - labellen);
            i = labellen;
            while (wire[i] == 255) {
                  if (i == 0) {
                        printf("Error, don't know how to add 1 to a label with maximum length and all values on 255\n");
                        exit(9);
                  } else {
                        i--;
                  }
            }
            wire[i] = wire[i] + 1;
            pos = 0;
            status = ldns_wire2dname(&newdname, wire, ldns_rdf_size(dname) + 1, &pos);
            free(wire);
      }

      if (status != LDNS_STATUS_OK) {
        printf("Error: %s\n", ldns_get_errorstr_by_id(status));
        exit(10);
        }
      
      return newdname;
}

ldns_rdf *
create_plus_1_dname(ldns_rdf *dname)
{
      ldns_rdf *label;
      ldns_status status;
      
      ldns_dname2canonical(dname);
      status = ldns_str2rdf_dname(&label, "\\000");
      if (status != LDNS_STATUS_OK) {
            printf("error creating \\000 dname: %s\n\n", ldns_get_errorstr_by_id(status));
            exit(2);
      }
      status = ldns_dname_cat(label, dname);
      if (status != LDNS_STATUS_OK) {
            printf("error catting \\000 dname: %s\n\n", ldns_get_errorstr_by_id(status));
            exit(3);
      }
      return label;
}

int
main(int argc, char *argv[])
{
      ldns_status status;

      ldns_resolver *res;
      ldns_rdf *domain = NULL;
      ldns_pkt *p;
      ldns_rr *soa;
      ldns_rr_list *rrlist;
      ldns_rr_list *rrlist2;
      ldns_rdf *soa_p1;
      ldns_rdf *next_dname;
      ldns_rdf *last_dname;
      ldns_rdf *last_dname_p;
      ldns_rdf *startpoint = NULL;
      ldns_rdf *rrtypes = NULL;
      const char* arg_domain = NULL;

      char *serv = NULL;
      ldns_rdf *serv_rdf;
      ldns_resolver *cmdline_res;
      ldns_rr_list *cmdline_rr_list;
      ldns_rdf *cmdline_dname;

      uint8_t fam = LDNS_RESOLV_INETANY;
      int result = 0;
      int i;
      char *arg_end_ptr = NULL;
      size_t j;

      p = NULL;
      rrlist = NULL;
      rrlist2 = NULL;
      soa = NULL;
      domain = NULL;
      
      if (argc < 2) {
            usage(stdout, argv[0]);
            exit(EXIT_FAILURE);
      } else {
            for (i = 1; i < argc; i++) {
                  if (strncmp(argv[i], "-4", 3) == 0) {
                              if (fam != LDNS_RESOLV_INETANY) {
                                    fprintf(stderr, "You can only specify one of -4 or -6\n");
                                    exit(1);
                              }
                              fam = LDNS_RESOLV_INET;
                  } else if (strncmp(argv[i], "-6", 3) == 0) {
                              if (fam != LDNS_RESOLV_INETANY) {
                                    fprintf(stderr, "You can only specify one of -4 or -6\n");
                                    exit(1);
                              }
                              fam = LDNS_RESOLV_INET6;
                  } else if (strncmp(argv[i], "-s", 3) == 0) {
                        if (i + 1 < argc) {
                              if (!ldns_str2rdf_dname(&startpoint, argv[i + 1]) == LDNS_STATUS_OK) {
                                    printf("Bad start point name: %s\n", argv[i + 1]);
                                    exit(1);
                              }
                        } else {
                              printf("Missing argument for -s\n");
                              exit(1);
                        }
                        i++;
                  } else if (strncmp(argv[i], "-v", 3) == 0) {
                        if (i + 1 < argc) {
                              verbosity = strtol(argv[i+1], &arg_end_ptr, 10);
                              if (*arg_end_ptr != '\0') {
                                    printf("Bad argument for -v: %s\n", argv[i+1]);
                                    exit(1);
                              }
                        } else {
                              printf("Missing argument for -v\n");
                              exit(1);
                        }
                        i++;
                  } else if (strcmp("-version", argv[i]) == 0) {
                        printf("dns zone walker, version %s (ldns version %s)\n", LDNS_VERSION, ldns_version());
                        goto exit;
                  } else {
                              if (argv[i][0] == '@') {
                              if (strlen(argv[i]) == 1) {
                                    if (i + 1 < argc) {
                                          serv = argv[i + 1];
                                          i++;
                                    } else {
                                          printf("Missing argument for -s\n");
                                          exit(1);
                                    }
                              } else {
                                    serv = argv[i] + 1;
                              }
                              } else {
                              if (i < argc) {
                                    if (!domain) {
                                          /* create a rdf from the command line arg */
                                          arg_domain = argv[i];
                                          domain = ldns_dname_new_frm_str(arg_domain);
                                          if (!domain) {
                                                usage(stdout, argv[0]);
                                                exit(1);
                                          }
                                    } else {
                                          printf("One domain at a time please\n");
                                          exit(1);
                                    }
                              } else {
                                    printf("No domain given to walk\n");
                                    exit(1);
                              }
                        }
                  }
            }
      }
      if (!domain) {
            printf("Missing argument\n");
            exit(1);
      }


      /* create a new resolver from /etc/resolv.conf */
      if(!serv) {
            if (ldns_resolver_new_frm_file(&res, NULL) != LDNS_STATUS_OK) {
                  fprintf(stderr, "%s", "Could not create resolver obj");
                  result = EXIT_FAILURE;
                  goto exit;
            }
      } else {
            res = ldns_resolver_new();
            if (!res || strlen(serv) <= 0) {
                  result = EXIT_FAILURE;
                  goto exit;
            }
            /* add the nameserver */
            serv_rdf = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_A, serv);
            if (!serv_rdf) {
                  /* maybe ip6 */
                  serv_rdf = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_AAAA, serv);
            }
            if (!serv_rdf) {
                  /* try to resolv the name if possible */
                  status = ldns_resolver_new_frm_file(&cmdline_res, NULL);
                  
                  if (status != LDNS_STATUS_OK) {
                        fprintf(stderr, "%s", "@server ip could not be converted");
                        result = EXIT_FAILURE;
                        goto exit;
                  }

                  cmdline_dname = ldns_dname_new_frm_str(serv);
                  cmdline_rr_list = ldns_get_rr_list_addr_by_name(
                                    cmdline_res, 
                                    cmdline_dname,
                                    LDNS_RR_CLASS_IN,
                                    0);
                  ldns_rdf_deep_free(cmdline_dname);
                        ldns_resolver_deep_free(cmdline_res);
                  if (!cmdline_rr_list) {
                        fprintf(stderr, "%s %s", "could not find any address for the name: ", serv);
                        result = EXIT_FAILURE;
                        goto exit;
                  } else {
                        if (ldns_resolver_push_nameserver_rr_list(
                                    res, 
                                    cmdline_rr_list
                              ) != LDNS_STATUS_OK) {
                              fprintf(stderr, "%s", "pushing nameserver");
                              result = EXIT_FAILURE;
                              ldns_rr_list_deep_free(cmdline_rr_list);
                              goto exit;
                        }
                                ldns_rr_list_deep_free(cmdline_rr_list);
                  }
            } else {
                  if (ldns_resolver_push_nameserver(res, serv_rdf) != LDNS_STATUS_OK) {
                        fprintf(stderr, "%s", "pushing nameserver");
                        result = EXIT_FAILURE;
                        goto exit;
                  } else {
                        ldns_rdf_deep_free(serv_rdf);
                  }
            }

      }

      ldns_resolver_set_dnssec(res, true);
      ldns_resolver_set_dnssec_cd(res, true);
      ldns_resolver_set_ip6(res, fam);    

      if (!res) {
            exit(2);
      }

      /* use the resolver to send it a query for the soa
       * records of the domain given on the command line
       */
      if (verbosity >= 3) {
            printf("\nQuerying for: ");
            ldns_rdf_print(stdout, domain);
            printf("\n");
      }
      p = ldns_resolver_query(res, domain, LDNS_RR_TYPE_SOA, LDNS_RR_CLASS_IN, LDNS_RD);
      soa = NULL;
      if (verbosity >= 5) {
            if (p) {
                  ldns_pkt_print(stdout, p);
            } else {
                  fprintf(stdout, "No Packet Received from ldns_resolver_query()\n");
            }
      }

        if (!p)  {
            exit(3);
        } else {
            /* retrieve the MX records from the answer section of that
             * packet
             */
            rrlist = ldns_pkt_rr_list_by_type(p, LDNS_RR_TYPE_SOA, LDNS_SECTION_ANSWER);
            if (!rrlist || ldns_rr_list_rr_count(rrlist) != 1) {
                  if (rrlist) {
                        printf(" *** > 1 SOA: %u\n", (unsigned int) ldns_rr_list_rr_count(rrlist));
                  } else {
                        printf(" *** No rrlist...\b");
                  }
                  /* TODO: conversion memory */
                  fprintf(stderr, 
                              " *** invalid answer name after SOA query for %s\n",
                              arg_domain);
                  ldns_pkt_print(stdout, p);
                        ldns_pkt_free(p);
                        ldns_resolver_deep_free(res);
                  exit(4);
            } else {
                  soa = ldns_rr_clone(ldns_rr_list_rr(rrlist, 0));
                  ldns_rr_list_deep_free(rrlist);
                  rrlist = NULL;
                  /* check if zone contains DNSSEC data */
                  rrlist = ldns_pkt_rr_list_by_type(p, LDNS_RR_TYPE_RRSIG, LDNS_SECTION_ANSWER);
                  if (!rrlist) {
                        printf("No DNSSEC data received; either the zone is not secured or you should query it directly (with @nameserver)\n");
                        ldns_pkt_free(p);
                        ldns_resolver_deep_free(res);
                        exit(5);
                  }
                  ldns_rr_list_deep_free(rrlist);
            }
        }

      /* add \001 to soa */
      status = ldns_str2rdf_dname(&soa_p1, "\001");
      if (status != LDNS_STATUS_OK) {
            printf("error: %s\n", ldns_get_errorstr_by_id(status));
      }
      if (!soa) {
            printf("Error getting SOA\n");
            exit(1);
      }

      if (startpoint) {
            last_dname = startpoint;
            last_dname_p = create_dname_plus_1(last_dname);
      } else {
            last_dname = ldns_rdf_clone(domain);
            if (ldns_dname_cat(soa_p1, last_dname) != LDNS_STATUS_OK) {
                  printf("Error concatenating dnames\n");
                  exit(EXIT_FAILURE);
            }
            last_dname_p = ldns_rdf_clone(soa_p1);
      }

      ldns_rdf_print(stdout, ldns_rr_owner(soa));
      printf("\t");

      next_dname = NULL;
      while (!next_dname || ldns_rdf_compare(next_dname, domain) != 0) {
            if (p) {
                  ldns_pkt_free(p);
                  p = NULL;
            }
            if (verbosity >= 3) {
                  printf("Querying for: ");
                  ldns_rdf_print(stdout, last_dname_p);
                  printf("\n");
            }
            p = ldns_resolver_query(res, last_dname_p, LDNS_RR_TYPE_ANY, LDNS_RR_CLASS_IN, LDNS_RD);
            if (verbosity >= 5) {
                  if (p) {
                        ldns_pkt_print(stdout, p);
                  } else {
                        fprintf(stdout, "No Packet Received from ldns_resolver_query()\n");
                  }
            }


            if (next_dname) {
                  ldns_rdf_deep_free(next_dname);
                  ldns_rdf_deep_free(rrtypes);
                  next_dname = NULL;
            }

            if (!p)  {
              fprintf(stderr, "Error trying to resolve: ");
              ldns_rdf_print(stderr, last_dname_p);
              fprintf(stderr, "\n");
              while (!p) {
                if (verbosity >= 3) {
                  printf("Querying for: ");
                  ldns_rdf_print(stdout, last_dname_p);
                  printf("\n");
                }
                p = ldns_resolver_query(res, last_dname_p, LDNS_RR_TYPE_ANY, LDNS_RR_CLASS_IN, LDNS_RD);
                /* TODO: make a general option for this (something like ignore_rtt)? */
                for (j = 0; j < ldns_resolver_nameserver_count(res); j++) {
                  if (ldns_resolver_nameserver_rtt(res, j) != 0) {
                        ldns_resolver_set_nameserver_rtt(res, j, LDNS_RESOLV_RTT_MIN);
                  }
                }
                if (verbosity >= 5) {
                  if (p) {
                        ldns_pkt_print(stdout, p);
                  } else {
                        fprintf(stdout, "No Packet Received from ldns_resolver_query()\n");
                  }
                }

              }
            }

            /* if the current name is an empty non-terminal, bind returns
             * SERVFAIL on the plus1-query...
             * so requery with only the last dname
             */
            if (ldns_pkt_get_rcode(p) == LDNS_RCODE_SERVFAIL) {
                  ldns_pkt_free(p);
                  p = NULL;
                  if (verbosity >= 3) {
                        printf("Querying for: ");
                        ldns_rdf_print(stdout, last_dname);
                        printf("\n");
                  }
                  p = ldns_resolver_query(res, last_dname, LDNS_RR_TYPE_ANY, LDNS_RR_CLASS_IN, LDNS_RD);
                  if (verbosity >= 5) {
                        if (p) {
                              ldns_pkt_print(stdout, p);
                        } else {
                              fprintf(stdout, "No Packet Received from ldns_resolver_query()\n");
                        }
                  }

                  if (!p) {
                        exit(51);
                  }
                  rrlist = ldns_pkt_rr_list_by_name_and_type(p, last_dname, LDNS_RR_TYPE_NSEC, LDNS_SECTION_ANSWER);
                  rrlist2 = ldns_pkt_rr_list_by_name_and_type(p, last_dname_p, LDNS_RR_TYPE_NSEC, LDNS_SECTION_ANSWER);
            } else {
                  rrlist = ldns_pkt_rr_list_by_name_and_type(p, last_dname, LDNS_RR_TYPE_NSEC, LDNS_SECTION_AUTHORITY);
                  rrlist2 = ldns_pkt_rr_list_by_name_and_type(p, last_dname_p, LDNS_RR_TYPE_NSEC, LDNS_SECTION_ANSWER);
            }
      if (rrlist && rrlist2) {
            ldns_rr_list_cat(rrlist, rrlist2);
      } else if (rrlist2) {
            rrlist = rrlist2;
      }

      if (!rrlist || ldns_rr_list_rr_count(rrlist) != 1) {
/*
            if (!rrlist) {
                  fprintf(stderr, "Zone does not seem to be DNSSEC secured.\n");
                  goto exit;
            }
*/
      } else {
            next_dname = ldns_rdf_clone(ldns_rr_rdf(ldns_rr_list_rr(rrlist, 0), 0));
            rrtypes = ldns_rdf_clone(ldns_rr_rdf(ldns_rr_list_rr(rrlist, 0), 1));
            ldns_rr_list_deep_free(rrlist);
      }

      if (!next_dname) {
            /* apparently the zone also has prepended data (i.e. a.example and www.a.example, 
             * The www comes after the a but befpre a\\000, so we need to make another name (\\000.a)
             */
            if (last_dname_p) {
                  ldns_rdf_deep_free(last_dname_p);
            }
            last_dname_p = create_plus_1_dname(last_dname);
      } else {

            if (last_dname) {
                  if (ldns_rdf_compare(last_dname, next_dname) == 0) {
                        printf("\n\nNext dname is the same as current, this would loop forever. This is a problem that usually occurs when walking through a caching forwarder. Try using the authoritative nameserver to walk (with @nameserver).\n");
                        exit(2);
                  }
                  ldns_rdf_deep_free(last_dname);
            }
            last_dname = ldns_rdf_clone(next_dname);
            if (last_dname_p) {
                  ldns_rdf_deep_free(last_dname_p);
            }
            last_dname_p = create_dname_plus_1(last_dname);
            ldns_rdf_print(stdout, rrtypes);
            printf("\n");
            ldns_rdf_print(stdout, next_dname);
            printf("\t");
      }

}

      ldns_rdf_deep_free(domain);
      ldns_rdf_deep_free(soa_p1);
      ldns_rdf_deep_free(last_dname_p);
      ldns_rdf_deep_free(last_dname);
      ldns_rdf_deep_free(next_dname);
      ldns_rdf_deep_free(rrtypes);

        ldns_pkt_free(p);

      ldns_rr_free(soa);

      printf("\n\n");
        ldns_resolver_deep_free(res);

        exit:
        return result;
}

Generated by  Doxygen 1.6.0   Back to index