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

ldns-notify.c

/* 
 * ldns-notify.c - ldns-notify(8)
 * 
 * Copyright (c) 2001-2005, NLnet Labs, All right reserved
 *
 * See LICENSE for the license
 *
 * send a notify packet to a server
 */

#include <config.h>

#include <stdbool.h>
#include <netdb.h>
#include <errno.h>

/* ldns */
#include "ldns/ldns.h"

static int verbose = 1;
static int max_num_retry = 15; /* times to try */

static void
usage(void)
{
      fprintf(stderr, "usage: ldns-notify [other options] -z zone <servers>\n");
      fprintf(stderr, "Ldns notify utility\n\n");
      fprintf(stderr, " Supported options:\n");
      fprintf(stderr, "\t-z zone\t\tThe zone\n");
      fprintf(stderr, "\t-s version\tSOA version number to include\n");
      fprintf(stderr, "\t-y key:data\tTSIG sign the query\n");
      fprintf(stderr, "\t-p port\t\tport to use to send to\n");
      fprintf(stderr, "\t-v\t\tPrint version information\n");
      fprintf(stderr, "\t-d\t\tPrint verbose debug information\n");
      fprintf(stderr, "\t-r num\t\tmax number of retries (%d)\n", 
            max_num_retry);
      fprintf(stderr, "\t-h\t\tPrint this help information\n\n");
      fprintf(stderr, "Report bugs to <ldns-team@nlnetlabs.nl>\n");
      exit(1);
}

static void
version(void)
{
        fprintf(stderr, "%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
        fprintf(stderr, "Written by NLnet Labs.\n\n");
        fprintf(stderr,
                "Copyright (C) 2001-2005 NLnet Labs.  This is free software.\n"
                "There is NO warranty; not even for MERCHANTABILITY or FITNESS\n"
                "FOR A PARTICULAR PURPOSE.\n");
        exit(0);
}

static void
notify_host(int s, struct addrinfo* res, uint8_t* wire, size_t wiresize,
      const char* addrstr)
{
      int timeout_retry = 5; /* seconds */
      int num_retry = max_num_retry;
      fd_set rfds;
      struct timeval tv;
      int retval = 0;
      ssize_t received = 0;
      int got_ack = 0;
      socklen_t addrlen = 0;
      uint8_t replybuf[2048];
      ldns_status status;
      ldns_pkt* pkt = NULL;
      
      while(!got_ack) {
            /* send it */
            if(sendto(s, wire, wiresize, 0, res->ai_addr, res->ai_addrlen)
                  == -1) {
                  printf("warning: send to %s failed: %s\n",
                        addrstr, strerror(errno));
                  close(s);
                  return;
            }

            /* wait for ACK packet */
            FD_ZERO(&rfds);
            FD_SET(s, &rfds);
            tv.tv_sec = timeout_retry; /* seconds */
            tv.tv_usec = 0; /* microseconds */
            retval = select(s + 1, &rfds, NULL, NULL, &tv);
            if (retval == -1) {
                  printf("error waiting for reply from %s: %s\n",
                        addrstr, strerror(errno));
                  close(s);
                  return;
            }
            if(retval == 0) {
                  num_retry--;
                  if(num_retry == 0) {
                        printf("error: failed to send notify to %s.\n",
                              addrstr);
                        exit(1);
                  }
                  printf("timeout (%d s) expired, retry notify to %s.\n",
                        timeout_retry, addrstr);
            }
            if (retval == 1) {
                  got_ack = 1;
            }
      }

      /* got reply */
      addrlen = res->ai_addrlen;
      received = recvfrom(s, replybuf, sizeof(replybuf), 0,
            res->ai_addr, &addrlen);
      res->ai_addrlen = addrlen;

      close(s);
      if (received == -1) {
            printf("recv %s failed: %s\n", addrstr, strerror(errno));
            return;
      }

      /* check reply */
      status = ldns_wire2pkt(&pkt, replybuf, (size_t)received);
      if(status != LDNS_STATUS_OK) {
            ssize_t i;
            printf("Could not parse reply packet: %s\n",
                  ldns_get_errorstr_by_id(status));
            printf("hexdump of reply: ");
            for(i=0; i<received; i++)
                  printf("%02x", (unsigned)replybuf[i]);
            printf("\n");
            exit(1);
      }

      if(verbose) {
            ssize_t i;
            printf("# reply from %s:\n", addrstr);
            ldns_pkt_print(stdout, pkt);
            
            printf("hexdump of reply: ");
            for(i=0; i<received; i++)
                  printf("%02x", (unsigned)replybuf[i]);
            printf("\n");
      }
      ldns_pkt_free(pkt);
}

int
main(int argc, char **argv)
{
      int c;
      int i;

      /* LDNS types */
      ldns_pkt *notify;
      ldns_rr *question;
      ldns_resolver *res;
      ldns_rdf *ldns_zone_name = NULL;
      ldns_status status;
      const char *zone_name = NULL;
      int include_soa = 0;
      uint32_t soa_version = 0;
      ldns_tsig_credentials tsig_cred = {0,0,0};
      int do_hexdump = 1;
      uint8_t *wire = NULL;
      size_t wiresize = 0;
      char *port = "53";

      srandom(time(NULL) ^ getpid());
      
        while ((c = getopt(argc, argv, "vhdp:r:s:y:z:")) != -1) {
                switch (c) {
                case 'd':
                  verbose = 1;
                  break;
                case 'p':
                  port = optarg;
                  break;
                case 'r':
                  max_num_retry = atoi(optarg);
                  break;
                case 's':
                  include_soa = 1;
                  soa_version = (uint32_t)atoi(optarg);
                  break;
                case 'y':
                  tsig_cred.algorithm = "hmac-md5.sig-alg.reg.int.";
                  tsig_cred.keyname = optarg;
                  tsig_cred.keydata = strchr(optarg, ':');
                  *tsig_cred.keydata = '\0';
                  tsig_cred.keydata++;
                  printf("Sign with %s : %s\n", tsig_cred.keyname,
                        tsig_cred.keydata);
                  break;
                case 'z':
                  zone_name = optarg;
                  ldns_zone_name = ldns_dname_new_frm_str(zone_name);
                  if(!ldns_zone_name) {
                        printf("cannot parse zone name: %s\n", 
                              zone_name);
                        exit(1);
                  }
                        break;
            case 'v':
                  version();
                case 'h':
                case '?':
                default:
                        usage();
                }
        }
        argc -= optind;
        argv += optind;

        if (argc == 0 || zone_name == NULL) {
                usage();
        }

      notify = ldns_pkt_new();
      question = ldns_rr_new();
      res = ldns_resolver_new();

      if (!notify || !question || !res) {
            /* bail out */
            printf("error: cannot create ldns types\n");
            exit(1);
      }

      /* create the rr for inside the pkt */
      ldns_rr_set_class(question, LDNS_RR_CLASS_IN);
      ldns_rr_set_owner(question, ldns_zone_name);
      ldns_rr_set_type(question, LDNS_RR_TYPE_SOA);
      ldns_pkt_set_opcode(notify, LDNS_PACKET_NOTIFY);
      ldns_pkt_push_rr(notify, LDNS_SECTION_QUESTION, question);
      ldns_pkt_set_aa(notify, true);
      ldns_pkt_set_id(notify, random()&0xffff);
      if(include_soa) {
            char buf[10240];
            ldns_rr *soa_rr=NULL;
            ldns_rdf *prev=NULL;
            snprintf(buf, sizeof(buf), "%s 3600 IN SOA . . %u 0 0 0 0",
                  zone_name, (unsigned)soa_version);
            /*printf("Adding soa %s\n", buf);*/
            status = ldns_rr_new_frm_str(&soa_rr, buf, 3600, NULL, &prev);
            if(status != LDNS_STATUS_OK) {
                  printf("Error adding SOA version: %s\n",
                        ldns_get_errorstr_by_id(status));
            }
            ldns_pkt_push_rr(notify, LDNS_SECTION_ANSWER, soa_rr);
      }

      if(tsig_cred.keyname) {
            status = ldns_pkt_tsig_sign(notify, tsig_cred.keyname,
                  tsig_cred.keydata, 300, tsig_cred.algorithm,
                  NULL);
            if(status != LDNS_STATUS_OK) {
                  printf("Error TSIG sign query: %s\n",
                        ldns_get_errorstr_by_id(status));
            }
      }

      if(verbose) {
            printf("# Sending packet:\n");
            ldns_pkt_print(stdout, notify);

      }

      status = ldns_pkt2wire(&wire, notify, &wiresize);
      if(wiresize == 0) {
            printf("Error converting notify packet to hex.\n");
            exit(1);
      }

      if(do_hexdump && verbose) {
            printf("Hexdump of notify packet:\n");
            for(i=0; i<(int)wiresize; i++)
                  printf("%02x", (unsigned)wire[i]);
            printf("\n");
      }

      for(i=0; i<argc; i++)
      {
            struct addrinfo hints, *res0, *res;
            int error;
            int default_family = AF_INET;

            if(verbose)
                  printf("# sending to %s\n", argv[i]);
            memset(&hints, 0, sizeof(hints));
            hints.ai_family = default_family;
            hints.ai_socktype = SOCK_DGRAM;
            hints.ai_protocol = IPPROTO_UDP;
            error = getaddrinfo(argv[i], port, &hints, &res0);
            if (error) {
                  printf("skipping bad address: %s: %s\n", argv[i],
                        gai_strerror(error));
                  continue;
            }
            for (res = res0; res; res = res->ai_next) {
                  int s = socket(res->ai_family, res->ai_socktype, 
                        res->ai_protocol);
                  if(s == -1)
                        continue;
                  /* send the notify */
                  notify_host(s, res, wire, wiresize, argv[i]);
            }
            freeaddrinfo(res0);
      }

      ldns_pkt_free(notify);
      free(wire);
        return 0;
}

Generated by  Doxygen 1.6.0   Back to index