/*
    Posadis code snippet collection - http://www.posadis.org/
    Convert DNS query answer to text string
    Copyright (C) 2003  Meilof Veeningen <meilof@users.sourceforge.net>
    $Id: answertostring.cpp,v 1.11 2004/04/19 19:04:43 meilof Exp $
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/*
    answertostring.cpp - Convert a DNS answer to a text string.
    
    This source file is meant for applications that want to implement DNS
    query functionality, and want to display their information, either in an
    advanced, "raw DNS message" display mode, or in a simple, "human readable"
    form. It is used by the Posadis poshost, gtkquery and winquery programs.
*/

#include <poslib/poslib.h>

/* 
  a ->      answer
  server -> string representing server queried
*/
stl_list(stl_string) answer_to_simple_string(DnsMessage *a, const char *server=NULL);

/*
  a ->      answer
  server -> string representing server queried
  tcp ->    whether TCP was used
  time ->   time in msecs the query took
  format -> whether the answer should be formatted in a table (for monospaced output)
*/
stl_list(stl_string) answer_to_advanced_string(DnsMessage *a, const char *server = NULL,
  bool tcp = false, int time = 0, bool format = false);

/* ----- begin implementation ---------------------------------------------- */

/* ----- advanced answers ----- */

void print_section(stl_list(stl_string)& message, stl_list(DnsRR)& section, bool format) {
  stl_string str;
  stl_list(DnsRR)::iterator it = section.begin();
  char buff[2048];
  const char
    *formatted = "%-20s %-6s %-2s %-6s %s",
    *nonformatted = "%s %s %s %s %s",
    *formatstr = (format) ? formatted : nonformatted;
  
  if (it == section.end()) {
    message.push_back(";(none)");
  }

  while (it != section.end()) {
    snprintf(buff, sizeof(buff), formatstr,
      it->NAME.tocstr(),
      str_ttl(it->TTL).c_str(),
      str_class(it->CLASS).c_str(),
      str_type(it->TYPE).c_str(),
      rr_tostring(it->TYPE, it->RDATA, it->RDLENGTH).c_str());
    message.push_back(buff);
    it++;
  }
}

#define sbool(x) ( (x)?"true":"false" )

stl_list(stl_string) answer_to_advanced_string(DnsMessage *a, const char *server, bool tcp,
  int time, bool format) {
  /* advanced mode */
  stl_list(stl_string) message;
  char buff[2048];
    
  snprintf(buff, sizeof(buff), "; Answer ID: %d  QR: %s  OPCODE: %s  AA: %s  TC: %s  RD: %s",
    a->ID, sbool(a->QR), str_opcode(a->OPCODE).c_str(), sbool(a->AA), sbool(a->TC), sbool(a->RD));
  message.push_back(buff);
    
  snprintf(buff, sizeof(buff), "; %sRA: %s  RCODE: %s  qc %u  an %u  au %u  ad %u",
    format?"       ":"",
    sbool(a->RA), str_rcode(a->RCODE).c_str(), a->questions.size(),
    a->answers.size(), a->authority.size(), a->additional.size());
  message.push_back(buff);
        
  /* question items */
  message.push_back("");
  message.push_back("; Question section:");
  stl_list(DnsQuestion)::iterator it = a->questions.begin();
    
  while (it != a->questions.end()) {
    snprintf(buff, sizeof(buff), (format) ? ";%-19s %-2s %s" : ";%s %s %s",
      it->QNAME.tocstr(),
      str_qclass(it->QCLASS).c_str(),
      str_qtype(it->QTYPE).c_str());
    message.push_back(buff);
    it++;
  }
  /* other sections */
  message.push_back("");
  message.push_back("; Answer section:");
  print_section(message, a->answers, format);
  message.push_back("");
  message.push_back("; Authority section:");
  print_section(message, a->authority, format);
  message.push_back("");
  message.push_back("; Additional section:");
  print_section(message, a->additional, format);
  message.push_back("");

  if (time) {
    sprintf(buff, "%d", time);
    message.push_back(stl_string("; Query took: ") + (format?"    ":"") + buff + " msec");
  }
  
  if (server) {
    stl_string str = stl_string("; Server queried: ") + server;
    if (tcp) str += "[tcp]"; else str += "[udp]";
    message.push_back(str);
  }
  
	return message;
}

stl_list(stl_string) answer_to_simple_string(DnsMessage *a, const char *server) {
  _answer_type type;
  stl_list(stl_string) message;
  stl_list(domainname) followed_cnames;
  stl_list(rrdat) rrs;
  domainname initial;
  stl_list(domainname)::iterator it;
  stl_list(DnsRR)::iterator sit;
  stl_list(rrdat)::iterator it2;

  if (server)
    message.push_back(stl_string("Received answer from ") +server);
  else
    message.push_back("Received answer");

  if (a->AA)
    message.push_back("  Authoritative answer");
  else
    message.push_back("  Not authoritative");
  if (a->TC)
    message.push_back("  (the answer was truncated)");
  
  if (a->questions.begin() == a->questions.end()) {
    message.push_back("*** Error: query was not repeated in answer!");
    return message;
  }

  type = check_answer_type(a, a->questions.begin()->QNAME, a->questions.begin()->QTYPE);

  if (a->questions.begin()->QTYPE == QTYPE_AXFR) {
    message.push_back("");
    message.push_back((stl_string("Answers for ") +
                     a->questions.begin()->QNAME.tocstr() + ":"));
    message.push_back("");
    stl_list(DnsRR)::iterator it3 = a->answers.begin();
    while (it3 != a->answers.end()) {
      message.push_back((stl_string("  -> ") + it3->NAME.tocstr() + " [" +
        str_type(it3->TYPE).c_str() + "] " +
        rr_tostring(it3->TYPE, it3->RDATA, it3->RDLENGTH).c_str()));
      it3++;
    }      
  } else {
    switch (type) {
      case A_ERROR:
        message.push_back("");
        switch (a->RCODE) {
          case RCODE_NOTIMP: message.push_back("  The query failed: feature not implemented"); break;
          case RCODE_QUERYERR: message.push_back("  The query failed: query not understood"); break;
          case RCODE_SRVFAIL: message.push_back("  The query failed: server failure"); break;
          case RCODE_REFUSED: message.push_back("  The query failed: query refused"); break;
          default: message.push_back((stl_string("  The query failed: error ") + str_rcode(a->RCODE).c_str()));
        }
        break;
      case A_NXDOMAIN:
        message.push_back("  Domain name doesn't exist");
        break;
      case A_NODATA:
        message.push_back("");
        message.push_back("  No such data available; try 'ANY' query type");
        break;
      case A_CNAME:
      case A_ANSWER:
        /* find answers for the query */
        try {
          rrs = get_records(a, true,
            (a->questions.begin()->QTYPE == QTYPE_ANY) ? false : true, &followed_cnames);
        } catch (PException p) { }
        initial = a->questions.begin()->QNAME;
        it = followed_cnames.begin();
        while (it != followed_cnames.end()) {
          message.push_back((stl_string("  -> ") +
                                           initial.tocstr() +
                                           " points to " +
                                           it->tocstr()));
          initial = *it;
          it++;
        }

        message.push_back("");
        message.push_back((stl_string("Answers for ") + initial.tocstr() + ":"));

        if (!rrs.empty()) {
          it2 = rrs.begin();
          while (it2 != rrs.end()) {
            message.push_back((stl_string("  -> [") +
              str_type(it2->type).c_str() + "] " +
              rr_tostring(it2->type, it2->msg, it2->len).c_str()));
            it2++;
          }
        } else {
          message.push_back("  No such data available; try 'ANY' query type");
        }
        break;
      case A_REFERRAL:
        message.push_back("");
        message.push_back("  Referral:");
        sit = a->authority.begin();
        while (sit != a->authority.end()) {
          if (sit->TYPE == DNS_TYPE_NS && a->questions.begin()->QNAME >= sit->NAME)
            message.push_back(stl_string("  -> ") + sit->NAME.tocstr() + " [NS] " +
            domainname(true, sit->RDATA).tocstr());
          sit++;
        }
    }
  }

  return message;
}


syntax highlighted by Code2HTML, v. 0.9.1