/* Posadis code snippet collection - http://www.posadis.org/ Convert DNS query answer to text string Copyright (C) 2003 Meilof Veeningen $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 /* 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; }