/***************************************************************************
posask.cpp - posask query tool
-------------------
begin : vr mei 23 2003
copyright : (C) 2003 by Meilof
email : meilof@users.sourceforge.net
***************************************************************************/
/***************************************************************************
* *
* 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 file has been taken, with some modifications, from the Poslib sources.
* The Poslib version, in turn, was just an ugly hack of the Posadis 0.50.x
* Posask. As such, you'll find some old Posadis artifacts. The code does
* "just work"(r) though.
*/
#include <stdio.h>
#include <posadis-config.h>
#ifdef HAVE_RESOLV_H
#ifdef HAVE_NAMESER_H
#include <nameser.h>
#endif
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/nameser.h>
#include <resolv.h>
#endif
#include <poslib/poslib.h>
#include <answertostring.cpp>
#include <getdnsservers.cpp>
/*_addr server;*/
stl_slist(_addr) servers;
domainname dname = ".";
int dname_given = 0;
int qtype = 0;
int serial = 0;
int rd = 1;
int use_tcp = -1; /* default */
int timeout = 5;
int tries = 3;
int cmd_server(char *line);
int cmd_dname(char *line);
int cmd_qtype(char *line);
int cmd_serial(char *line);
int cmd_rd(char *line);
int cmd_tcp(char *line);
int cmd_timeout(char *line);
int cmd_tries(char *line);
int cmd_query(char *line);
int cmd_set(char *line);
int cmd_help(char *line);
int cmd_exit(char *line);
typedef int(*commandfn)(char *);
typedef char*(*completefn)(const char *, int);
typedef struct {
char *command;
char *help;
commandfn fn;
} _command;
_command commands[] = {
{ "server", "Sets the server at run-time", cmd_server },
{ "dname", "Sets domain name to query", cmd_dname },
{ "qtype", "Sets the default QTYPE", cmd_qtype },
{ "serial", "Sets the previous serial for IXFR requests", cmd_serial },
{ "rd", "Sets whether we demand recursion", cmd_rd },
{ "use_tcp", "Sets whether we use TCP/IP (default only xfr)", cmd_tcp },
{ "timeout", "Sets the time in seconds to wait for an answer", cmd_timeout },
{ "tries", "Sets the number of tries for the query", cmd_tries },
{ "query", "Sends a query to the nameserver", cmd_query },
{ "q", "Synonym for 'query'", cmd_query },
{ "set", "Show current settings", cmd_set },
{ "help", "Print this message", cmd_help },
{ "exit", "Quits the program", cmd_exit },
};
int n_commands = sizeof(commands) / sizeof(_command);
char* settings[] = { "server", "dname", "qtype", "rd", "use_tcp", "timeout" };
int n_settings = sizeof(settings) / sizeof(char *);
int issue_command(char *name, char *arg) {
int t;
for (t = 0; t < n_commands; t++) {
if (strcasecmp(name, commands[t].command) == 0) {
return commands[t].fn(arg);
}
}
printf("*** Command unknown: %s\n", name);
return -1;
}
int cmd_server(char *line) {
_addr server;
try {
txt_to_addr(&server, line);
servers.push_front(server);
return 1;
} catch (PException p) {
if (!address_lookup(&server, line, DNS_PORT)) {
printf("*** Server %s: incorrect identifier: %s\n", line, p.message);
return -1;
}
servers.push_front(server);
}
return 1;
}
int cmd_dname(char *line) {
dname_given = 1;
try {
dname = line;
return 1;
} catch (PException p) {
printf("*** Incorrect dname %s: %s!\n", line, p.message);
return -1;
}
}
int cmd_qtype(char *line) {
int ret;
try {
if (strncasecmp(line, "ixfr", 4) == 0) {
if (line[4] == '/') {
ret = cmd_serial(&line[5]);
if (ret > 0) qtype = QTYPE_IXFR;
return ret;
} else {
qtype = QTYPE_IXFR;
return 1;
}
} else {
qtype = qtype_getcode(line);
}
return 1;
} catch (PException p) {
printf("*** Error in qtype %s: %s\n", line, p.message);
return -1;
}
}
int cmd_serial(char *line) {
try {
serial = txt_to_int(line);
return 1;
} catch (PException p) {
printf("*** Error in serial %s: %s\n", line, p.message);
return -1;
}
}
int cmd_rd(char *line) {
try {
rd = (txt_to_bool(line)) ? 1 : 0;
return 1;
} catch (PException p) {
printf("*** Error in RD value %s: %s\n", line, p.message);
return -1;
}
}
int cmd_tcp(char *line) {
try {
use_tcp = (txt_to_bool(line)) ? 1 : 0;
return 1;
} catch (PException p) {
printf("*** Error in use_tcp value %s: %s\n", line, p.message);
return -1;
}
}
int cmd_timeout(char *line) {
try {
timeout = txt_to_int(line);
if (timeout < 0) {
timeout = 5;
throw PException(true, "%s: cannot be negative!", line);
}
} catch (PException p) {
printf("**** Error in timeout: %s\n", p.message);
return -1;
}
return 1;
}
int cmd_tries(char *line) {
try {
tries = txt_to_int(line);
if (tries < 0) {
tries = 3;
throw PException(true, "#tries %s: cannot be negative!", line);
}
} catch (PException p) {
printf("**** Error in #tries: %s\n", p.message);
return -1;
}
return 1;
}
int cmd_query(char *line) {
DnsMessage *q = NULL, *a = NULL;
int prevtype = qtype;
char *tmp;
char *ptr;
postime_t endtime, begintime;
bool do_tcp;
pos_cliresolver resolver;
try {
if (line[0]) {
ptr = &line[strlen(line) - 1];
while (ptr > line && *ptr != ' ') ptr--;
if (*ptr == ' ') {
*ptr = '\0';
if (cmd_qtype(ptr + 1) < 0) return -1;
}
if (cmd_dname(line) < 0) { qtype = prevtype; return -1; }
}
begintime = getcurtime();
q = new DnsMessage();
q->questions.push_front(DnsQuestion(dname, qtype, CLASS_IN));
qtype = prevtype;
q->ID = 3000;
if (qtype == QTYPE_IXFR) {
/* add a SOA record */
tmp = (char *)malloc(22);
memset(tmp, 0, 22);
memcpy(tmp + 2, uint32_buff(serial), 4);
q->authority.push_front(DnsRR(dname, DNS_TYPE_SOA, CLASS_IN, 0));
q->authority.begin()->RDLENGTH = 22;
q->authority.begin()->RDATA = tmp;
}
if (rd) q->RD = true;
if (qtype == QTYPE_AXFR) q->RD = false;
if (use_tcp == 1 || (use_tcp == -1 && (qtype == QTYPE_AXFR || qtype == QTYPE_IXFR))) {
/* do a tcp query */
do_tcp = true;
int sockid = resolver.tcpconnect(&*servers.begin());
resolver.tcp_timeout = timeout * 1000;
resolver.tcpquery(q, a, sockid);
resolver.tcpdisconnect(sockid);
} else {
/* do an udp query */
free(resolver.udp_tries);
if (tries < 1) tries = 1;
if (tries > 10) tries = 10;
if (timeout < 1) timeout = 1;
resolver.udp_tries = (int *)malloc(tries * sizeof(int));
resolver.n_udp_tries = tries;
for (int tr = 0; tr < tries; tr++) {
resolver.udp_tries[tr] = timeout * 1000 * (tr + 1) * 2 / tries / (tries + 1);
}
do_tcp = false;
servers.reverse();
resolver.query(q, a, servers, Q_NOTCP);
}
endtime = getcurtime();
stl_list(stl_string) answer = answer_to_advanced_string(a,
addrs_to_string(servers).c_str(), do_tcp, endtime.after(begintime), true);
stl_list(stl_string)::iterator it = answer.begin();
while (it != answer.end()) {
printf("%s\n", it->c_str());
it++;
}
if (q) delete q;
if (a) delete a;
} catch (PException p) {
printf("*** Query failed: %s!\n", p.message);
if (q) delete q;
if (a) delete a;
}
return 1;
}
int cmd_set(char *line) {
if (!line[0] || strcasecmp(line, "server") == 0) {
printf("Server: %s\n", addrs_to_string(servers).c_str());
}
if (!line[0] || strcasecmp(line, "dname") == 0) {
printf("Domain name: %s\n", dname.tostring().c_str());
}
if (!line[0] || strcasecmp(line, "qtype") == 0)
printf("QTYPE: %s\n", str_qtype(qtype).c_str());
if (!line[0] || strcasecmp(line, "serial") == 0)
printf("IXFR serial: %d\n", serial);
if (!line[0] || strcasecmp(line, "rd") == 0)
printf("Recursion: %s\n", (rd == 1) ? "true" : "false");
if (!line[0] || strcasecmp(line, "use_tcp") == 0)
printf("Use TCP: %s\n", (use_tcp == 1) ? "true" : "false");
if (!line[0] || strcasecmp(line, "timeout") == 0)
printf("Timeout: %d\n", timeout);
if (!line[0] || strcasecmp(line, "tries") == 0)
printf("Tries: %d\n", tries);
return 1;
}
int cmd_help(char *line) {
int x, p = 0;
for (x = 0; x < n_commands; x++) {
if (!line[0] ||
strcasecmp(line, commands[x].command) == 0) {
printf("%-16s %s\n", commands[x].command, commands[x].help);
p = 1;
}
}
if (p == 0) {
printf("*** %s is not a known command!\n", line);
return -1;
}
return 1;
}
int cmd_exit(char *line) {
return 0;
}
int main(int argc, char **argv) {
int x, ret = 0;
char *ptr;
/* at first, read the command line */
for (x = 1; x < argc; x++) {
if (strcasecmp(argv[x], "--help") == 0 || strcasecmp(argv[x], "-h") == 0) {
printf(
"Posask - Command-line DNS querier\n"
"Usage:\n"
" posask [--help|-h]\n"
" posask [--version|-v]\n"
" posask [@[server][:port]] [option=value] [qname] [qtype]\n"
"If no server is given, the system resolver is assumed. If no qtype is given, A\n"
"is assumed. If no qname is given, a query for {. ns} is assumed.\n"
"\n"
"Possible options:\n"
" rd=[yes|no] Specifies whether to demand recursion. Default: yes\n"
" serial=n Default serial number for IXFR requests\n"
" use_tcp=[yes|no] Specifies whether or not to use TCP. Default: only xfr\n"
" timeout=n Number of seconds to wait for an answer. Default: 5\n"
" tries=n Number of times to retry query (in timeout). Default: 3\n"
"\n"
"Posask is part of the Posadis DNS Server package. It is distributed under the\n"
"terms and conditions of the GNU General Public License. More info:\n"
" http://posadis.sourceforge.net/\n");
return 1;
}
if (strcasecmp(argv[x], "--version") == 0 || strcasecmp(argv[x], "-v") == 0) {
printf(
"Posask - Command-line DNS querier\n"
"Version ID: $Revision: 1.14 $\n"
#ifdef __DATE
"Compiled on: " __DATE__
#endif
/*"Posadis version: " VERSION "\n"*/);
return 1;
}
// if (stricmp(argv[x], "--resolve") == 0 || stricmp(argv[x], "-r") == 0) {
//#ifdef HAVE_RESOLV_H
// res_init();
// memcpy(&server, &_res.nsaddr, sizeof(sockaddr_in));
// continue;
//#else
// printf("*** Posadis not built with system resolver support!\n");
// return 1;
//#endif
// }
if (argv[x][0] == '-') {
printf("*** Command-line option not supported: %s\n", argv[x]);
return 1;
}
if (argv[x][0] == '@') {
/* server */
if (cmd_server(&argv[x][1]) < 0) return 2;
continue;
}
ptr = strchr(argv[x], '=');
if (ptr) {
*ptr = '\0';
ret = issue_command(argv[x], ptr + 1);
*ptr = '=';
if (ret < 0) return 2;
} else {
if (dname_given != 0) {
/* qtype */
if (qtype) {
printf("*** Didn't expect another command-line argument: %s\n", argv[x]);
return 2;
} else {
if (cmd_qtype(argv[x]) < 0) return 2;
}
} else {
/* dname */
if (cmd_dname(argv[x]) < 0) return 2;
}
}
}
/* set default */
if (servers.size() == 0) {
try {
servers = get_os_dns_servers();
} catch(PException p) {
_addr tmp;
txt_to_addr(&tmp, "127.0.0.1", DNS_PORT);
servers.clear();
servers.push_front(tmp);
printf("*** Error: %s. Falling back to 127.0.0.1.\n", p.message);
}
} else servers.reverse();
if (!dname_given) {
cmd_dname(".");
qtype = DNS_TYPE_NS;
}
if (qtype == 0) {
if (dname >= "in-addr.arpa" || dname >= "ip6.int" || dname >= "ip6.arpa") {
qtype = DNS_TYPE_PTR;
} else {
qtype = DNS_TYPE_A;
}
}
cmd_query("");
if (ret < 0) return 3; else return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1