/*
 * Getzone - Zone transfer client
 *
 * Copyright (C) Meilof Veeningen, 2003
 */

#include <poslib/poslib.h>

const int tcp_timeout = 5000;

void transfer_zone(_addr a, domainname znroot, FILE *out) {
  int sockid = tcpopen(&a), ilen, pos, n, t, l;
  DnsMessage *q = create_query(znroot, QTYPE_AXFR);
  message_buff buff = q->compile(TCP_MSG_SIZE);
  delete q;
  char len[2] = { buff.len/256, buff.len };
  tcpsendall(sockid, len, 2, tcp_timeout / 4);
  tcpsendall(sockid, buff.msg, buff.len, tcp_timeout / 4);
  char *msg = NULL;
  _domain retdom = NULL;
  char *rdata = NULL; uint16_t rdlen;
  int soa = 0;
  try {
    while (soa != 2) {
      /* read message */
      if (msg) { free(msg); msg = NULL; }
      tcpreadall(sockid, len, 2, tcp_timeout / 4);
      ilen = len[0] * 256 + len[1];
      if (ilen < 12) throw PException("Incomplete answer message");
      msg = (char *)malloc(ilen);
      tcpreadall(sockid, msg, ilen, tcp_timeout / 4);
      buff = message_buff(msg, ilen);
      /* interpret it */
      if ((msg[3] & 15) != RCODE_NOERROR) throw PException(true, "Erroneous answer: %s", str_rcode(msg[3]&15).c_str());
      n = msg[6] * 256 + msg[7];
      pos = 12;
      for (t = 0; t < n && pos < ilen; t++) {
        /* read this RR */
        if (retdom) { free(retdom); retdom = NULL; }
        if (rdata) { free(rdata); rdata = NULL; }
        retdom = dom_uncompress(buff, pos);
        pos += dom_comprlen(buff, pos);
        if (pos + 10 > ilen) throw PException(true, "Truncated answer message");
        l = buff.msg[pos+8] * 256 + buff.msg[pos+9];
        if (pos + 10 + l > ilen) throw PException(true, "Truncated answer message");
        if (buff.msg[pos] * 256 + buff.msg[pos+1] == DNS_TYPE_SOA) {
          soa++;
          if (soa == 2) {
            /* done! */
            break;
          }
        }
        rr_read(buff.msg[pos] * 256 + buff.msg[pos+1], rdata, rdlen, buff, pos + 10, l);
        fwrite(uint16_buff(domlen(retdom)) + l + 10, 1, 2, out);
        fwrite(retdom, 1, domlen(retdom), out);
        fwrite(buff.msg, 1, 10, out);
        fwrite(rdata, 1, rdlen, out);
        pos += 10 + l;
      }
    }
  } catch (PException p) {
    if (msg) free(msg);
    if (retdom) free(retdom);
    if (rdata) free(rdata);
    throw p;
  }
  if (msg) free(msg);
  if (retdom) free(retdom);
  if (rdata) free(rdata);
}

int main(int argc, char **argv) {
  _addr a;
  FILE *f;
  if (argc != 4) {
    printf("Usage: %s <server> <zone> <file>\n", argv[0]);
    return 1;
  }
  txt_to_addr(&a, argv[1]);
  f = try_fopen(argv[3], "w");
  if (!f) {
    printf("*** Failed to open %s for writing!\n", argv[3]);
    return 1;
  }
  try {
    transfer_zone(a, argv[2], f);
  } catch (PException p) {
    printf("*** Zone transfer failed: %s\n", p.message);
    fclose(f);
    return 1;
  }
  fclose(f);
  return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1