/* Posadis - A DNS Server Posask query program (using poslib) Copyright (C) 2002 Meilof Veeningen 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. */ #include #include _addr server; domainname dname = "."; int dname_given = 0; int qtype = 0; int serial = 0; int rd = 1; int use_tcp = -1; /* default */ int timeout = 5; 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_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); char *no_completion(const char *, int val); char *command_completion(const char *word, int val); char *bool_completion(const char *word, int val); char *setting_completion(const char *word, int val); void print_qitem(DnsQuestion& item) { stl_string dname = item.QNAME.tostring(), qtype = str_qtype(item.QTYPE); printf(";%-20s%s\n", dname.c_str(), qtype.c_str()); } void print_aitem(DnsRR& rr) { stl_string rrstr = rr_tostring(rr.TYPE, rr.RDATA, rr.RDLENGTH), domstr = rr.NAME.tostring(), classstr = str_class(rr.CLASS), typestr = str_type(rr.TYPE); printf("%-20s %-6d %-2s %-6s %s\n", domstr.c_str(), rr.TTL, classstr.c_str(), typestr.c_str(), rrstr.c_str()); } typedef struct { char *command; char *help; commandfn fn; completefn complete; } _command; _command commands[] = { { "server", "Sets the server at run-time", cmd_server, no_completion }, { "dname", "Sets domain name to query", cmd_dname, no_completion }, { "qtype", "Sets the default QTYPE", cmd_qtype, no_completion }, { "serial", "Sets the previous serial for IXFR requests", cmd_serial, no_completion }, { "rd", "Sets whether we demand recursion", cmd_rd, bool_completion }, { "use_tcp", "Sets whether we use TCP/IP (default only xfr)", cmd_tcp, bool_completion }, { "timeout", "Sets the time in seconds to wait for an answer", cmd_timeout, no_completion }, { "query", "Sends a query to the nameserver", cmd_query, no_completion }, { "q", "Synonym for 'query'", cmd_query, no_completion }, { "set", "Show current settings", cmd_set, setting_completion }, { "help", "Print this message", cmd_help, command_completion }, { "exit", "Quits the program", cmd_exit, no_completion }, }; 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) { int port = DNS_PORT; char *ptr = strchr(line, '#'); if (ptr) { /* both addr & port */ *ptr = '\0'; port = txt_to_int(ptr + 1); if (port < 0) { *ptr = ':'; printf("Server %s: no correct port!\n", line); return -1; } } if (strchr(line, ':') == NULL && strchr(line, '.') == NULL) { /* neither IPv4 nor IPv6 */ port = txt_to_int(line); if (port < 0) { printf("Server %s: no correct port!\n", line); return -1; } else if (port == 0) { port = DNS_PORT; } line = "127.0.0.1"; } if (!address_lookup(&server, line, port)) { printf("Server %s: no correct identifier!\n", line); return -1; } if (ptr) *ptr = ':'; 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) { rr_type *type; int ret; type = rrtype_getinfo(line); if (type == NULL) { 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; } } if (strcasecmp(line, "axfr") == 0) { qtype = QTYPE_AXFR; return 1; } if (strcasecmp(line, "any") == 0) { qtype = QTYPE_ALL; return 1; } printf("Unknown QTYPE %s!\n", line); return -1; } else { qtype = type->type; return 1; } } int cmd_serial(char *line) { int val = txt_to_int(line); if (val < 0) { printf("The value %s is not a valid serial!", line); return -1; } else { serial = val; return 1; } } int cmd_rd(char *line) { if (strcasecmp(line, "true") == 0) rd = 1; else if (strcasecmp(line, "false") == 0) rd = 0; else { printf("Unrecognized boolean %s in rd!\n", line); return -1; } return 1; } int cmd_tcp(char *line) { if (strcasecmp(line, "true") == 0) use_tcp = 1; else if (strcasecmp(line, "false") == 0) use_tcp = 0; else { printf("Unrecognized boolean %s in use_tcp!\n", line); return -1; } return 1; } int cmd_timeout(char *line) { int x; x = txt_to_int(line); if (x < 0) { printf("%s isn't a correct numeric timeout!\n", line); return -1; } timeout = x; return 1; } /* writes a long to a char buffer */ void write_long(char *res, int val) { res[0] = val / 16777216; res[1] = val / 65536; res[2] = val / 256; res[3] = val; } 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); write_long(&tmp[2], serial); 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(&server); resolver.tcp_timeout = timeout * 1000; resolver.tcpquery(q, a, sockid); resolver.tcpdisconnect(sockid); } else { /* do an udp query */ free(resolver.udp_tries); resolver.udp_tries = (int *)malloc(3 * sizeof(int)); resolver.n_udp_tries = 3; resolver.udp_tries[0] = timeout * 100; resolver.udp_tries[1] = timeout * 350; resolver.udp_tries[2] = timeout * 550; do_tcp = false; resolver.query(q, a, &server, Q_NOTCP); } printf( "; Answer ID: %d QR: %d OPCODE: %s AA: %d TC: %d RD: %d\n" "; RA: %d RCODE: %s qc %u an %u au %u ad %u\n", a->ID, a->QR, str_opcode(a->OPCODE).c_str(), a->AA, a->TC, a->RD, a->RA, str_rcode(a->RCODE).c_str(), a->questions.size(), a->answers.size(), a->authority.size(), a->additional.size()); printf("; Questions:\n"); stl_list(DnsQuestion)::iterator it = a->questions.begin(); if (it != a->questions.end()) { while (it != a->questions.end()) { print_qitem(*it); it++; } } else printf(";(none)\n"); stl_list(DnsRR)::iterator it2; printf("; Answers:\n"); it2 = a->answers.begin(); if (it2 != a->answers.end()) { while (it2 != a->answers.end()) { print_aitem(*it2); it2++; } } else printf(";(none)\n"); printf("; Authority:\n"); it2 = a->authority.begin(); if (it2 != a->authority.end()) { while (it2 != a->authority.end()) { print_aitem(*it2); it2++; } } else printf(";(none)\n"); printf("; Additional:\n"); it2 = a->additional.begin(); if (it2 != a->additional.end()) { while (it2 != a->additional.end()) { print_aitem(*it2); it2++; } } else printf(";(none)\n"); endtime = getcurtime(); printf("; Query took: %d msec\n", endtime.after(begintime)); printf("; Server queried: %s", addr_to_string(&server).c_str()); if (do_tcp) printf("[tcp]\n"); else printf("[udp]\n"); 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", addr_to_string(&server).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); 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; } char *no_completion(const char *word, int val) { return NULL; } char *command_completion(const char *word, int val) { static int cur; if (val == 0) cur = 0; while (cur < n_commands) { if (strncasecmp(word, commands[cur].command, strlen(word)) == 0) return strdup(commands[cur++].command); cur++; } return NULL; } char *setting_completion(const char *word, int val) { static int cur; if (val == 0) cur = 0; while (cur < n_settings) { if (strncasecmp(word, settings[cur], strlen(word)) == 0) return strdup(settings[cur++]); cur++; } return NULL; } char *bool_completion(const char *word, int val) { static int cur = 0; if (val == 0) cur = 0; if (cur == 0) { cur++; if (strncasecmp(word, "true", strlen(word)) == 0) { return strdup("true"); } } if (cur == 1) { cur++; if (strncasecmp(word, "false", strlen(word)) == 0) { return strdup("false"); } } return NULL; } #ifdef READLINE_INCLUDED char **posask_attempted_completion(const char *val, int start, int end) { int t, x; if (start == 0) return completion_matches((char *)val, (CPFunction *)command_completion); /* command, so look which one */ for (t = 0; t < n_commands; t++) { x = strlen(commands[t].command); if (strncasecmp(rl_line_buffer, commands[t].command, x) == 0 && rl_line_buffer[x] == ' ') { return completion_matches((char *)val, (CPFunction *)commands[t].complete); } } return completion_matches((char *)val, (CPFunction *)no_completion); } #endif int clean(int val) { return val; } int main(int argc, char **argv) { int x, ret = 0; char *ptr; /* set default */ getaddress(&server, "127.0.0.1", DNS_PORT); /* 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, 127.0.0.1 is assumed. If no qtype is given, A is\n" "assumed. If no qname is given, the program will start in interactive mode.\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" "\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 clean(1); } if (strcasecmp(argv[x], "--version") == 0 || strcasecmp(argv[x], "-v") == 0) { printf( "Posask - Command-line DNS querier\n" "Version ID: $Revision: 1.13 $\n" #ifdef __DATE "Compiled on: " __DATE__ #endif /*"Posadis version: " VERSION "\n"*/); return clean(1); } if (argv[x][0] == '@') { /* server */ if (cmd_server(&argv[x][1]) < 0) return clean(2); continue; } ptr = strchr(argv[x], '='); if (ptr) { *ptr = '\0'; ret = issue_command(argv[x], ptr + 1); *ptr = '='; if (ret < 0) return clean(2); } else { if (dname_given != 0) { /* qtype */ if (qtype) { printf("Didn't expect another command-line argument: %s\n", argv[x]); return clean(2); } else { if (cmd_qtype(argv[x]) < 0) return clean(2); } } else { /* dname */ if (cmd_dname(argv[x]) < 0) return clean(2); } } } if (qtype == 0) qtype = DNS_TYPE_A; if (dname_given) { /* we're in non-interactive mode here */ cmd_query(""); if (ret < 0) return clean(3); else return clean(0); } else { #ifdef READLINE_INCLUDED /* interactive mode */ char *ptr2, *ptr3; rl_readline_name = "posask"; #ifdef HAVE_RL_COMPLETION_ENTRY_FUNCTION rl_completion_entry_function = (compentry_fn) no_completion; #else completion_entry_function = no_completion; #endif #ifdef HAVE_RL_COMPLETION_FUNC_T rl_attempted_completion_function = (rl_completion_func_t *)posask_attempted_completion; #else rl_attempted_completion_function = (CPPFunction *)posask_attempted_completion; #endif while (1) { ptr = readline(">"); if (ptr) { if (ptr[0]) { add_history(ptr); ptr2 = strchr(ptr, ' '); if (ptr2 != NULL) { *ptr2++ = '\0'; /* trim last space */ ptr3 = &ptr2[strlen(ptr2) - 1]; if (*ptr3 == ' ') *ptr3 = '\0'; } else ptr2 = ""; ret = issue_command(ptr, ptr2); if (ret == 0) break; } free(ptr); } } #else printf("Sorry, interactive mode not available. GNU Readline was not detected.\n"); #endif } return clean(0); }