netsukuku/src/ntkresolv.c
2014-09-17 10:36:24 -05:00

668 lines
14 KiB
C

/* This file is part of Netsukuku
*
* This source code 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 source code 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.
* Please refer to the GNU Public License for more details.
*
* You should have received a copy of the GNU Public License along with
* this source code; if not, write to:
* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <netinet/in.h>
#include "includes.h"
#include "ntkresolv.h"
#include "andns_net.h"
#include "snsd_cache.h"
#include "crypto.h"
#include "common.h"
static ntkresolv_opts globopts;
static struct timeval time_start, time_stop;
uint8_t mode_compute_hash = 0;
uint8_t mode_parsable_output = 0;
void
version(void)
{
say("ntk-resolv version %s (Netsukuku tools)\n\n"
"Copyright (C) 2006.\n"
"This is free software. You may redistribute copies of it under the terms of\n"
"the GNU General Public License <http://www.gnu.org/licenses/gpl.html>.\n"
"There is NO WARRANTY, to the extent permitted by law.\n\n"
"Report bugs and ideas to <%s>.\n", VERSION, NTK_RESOLV_MAIL_BUGS);
ntkresolv_safe_exit(1);
}
void
usage(void)
{
say("Usage:\n"
"\tntk-resolv [OPTIONS] host\n"
"\tntk-resolv -H host\n\n"
" -v --version print version, then exit.\n"
" -n --nameserver=ns use nameserver `ns' instead of localhost.\n"
" -P --port=port nameserver port, default 53.\n"
" -t --query-type=qt query type (`-t help' shows more info).\n"
" -r --realm=realm realm to scan (`-r help' shows more info).\n"
" -s --service=service SNSD service (`-s help' shows more info).\n"
" -p --protocolo=proto SNSD protocol (`-p help' shows more info).\n"
" -S --silent ntk-resolv will be not loquacious.\n"
" -b --block-recursion set recursion OFF.\n"
" -m --md5-hash hostname specified is hash-ed.\n"
" -H --compute-hash print the hash'ed hostname.\n"
" -l --parsable-output print answers in a synthetic way.\n"
" -h --help display this help, then exit.\n\n"
"Report bugs and ideas to <%s>.\n", NTK_RESOLV_MAIL_BUGS);
ntkresolv_safe_exit(1);
}
void
qt_usage(char *arg)
{
if (arg)
say("Bad Query Type %s\n\n", arg);
else
say("ntk-resolv Query Type Help.\n\n");
say("Valid query types are:\n"
" * snsd\t\thost:port -> ip\n"
" ptr\t\tip -> host\n"
" global\thostname -> all services ip\n"
" mx\t\thostname MX -> ip\n\n"
"(you can also use univoque abbreviation)\n"
"Note: mx query is equivalent to --query-type="
"snsd AND --service=25\n\n");
ntkresolv_safe_exit(1);
}
void
realm_usage(char *arg)
{
if (arg)
say("Bad Realm %s\n\n", arg);
else
say("ntk-resolv Realm Help.\n\n");
say("Valid realms are:\n"
" * ntk\tnetsukuku realm\n"
" inet\tinternet realm\n\n"
"(you can also use univoque abbreviation)\n\n");
ntkresolv_safe_exit(1);
}
void
proto_usage(char *arg)
{
if (arg)
say("Bad Protocol %s\n\n", arg);
else
say("ntk-resolv Protocol Help.\n\n");
say("Valid protocols are:\n"
" * tcp\n"
" udp\n"
"(you can also use univoque abbreviation)\n"
"Note: you can also specify the protocol with option `-s'.\n"
"To know more, type:\n" "\tntk-resolv -s help\n\n");
ntkresolv_safe_exit(1);
}
void
service_and_proto_usage(char *arg)
{
if (arg)
say("Bad service/proto %s\n\n"
"Use `ntk-resolv -s help` for more info on"
" service and proto.\n", arg);
else
say("ntk-resolv Service and Proto Help.\n\n"
"The form to specify a service and a protocol are:\n"
" ntk-resolv -s service/proto\n"
" ntk-resolv -s service -p proto\n\n"
"Valid protocols are:\n"
" * tcp\n"
" udp\n\n"
"Valid services are expressed in /etc/services.\n"
"You can use numeric form too.\n\n"
"As example, the next commands are equivalent and\n"
"will return the IP of the hostname that offers\n"
"webpages for the hostname \"some_hostname\":\n\n"
" ntk-resolv -s http -p tcp some_hostname\n"
" ntk-resolv -s http/tcp some_hostname\n"
" ntk-resolv -s 80/tcp some_hostname\n"
" ntk-resolv -s 80 some_hostname\n\n");
ntkresolv_safe_exit(1);
}
double
diff_time(struct timeval a, struct timeval b)
{
double res;
res = (double) (b.tv_sec - a.tv_sec);
if (res < 0.9 || b.tv_usec >= a.tv_usec)
res += (b.tv_usec - a.tv_usec) / TIME_SCALE;
else {
res -= 1.0;
res += (TIME_SCALE + b.tv_usec - a.tv_usec) / TIME_SCALE;
}
return res;
}
void
opts_init(void)
{
memset(&GOP, 0, NTKRESOLV_OPTS_SZ);
strcpy(GOP.nsserver, LOCALHOST);
GOP.port = NTKRESOLV_PORT;
GQT = create_andns_pkt();
GQT->nk = REALM_NTK;
GQT->p = SNSD_PROTO_DEFAULT;
GQT->service = SNSD_SERVICE_DEFAULT;
GQT->r = 1;
xsrand();
}
void
opts_set_silent(void)
{
GOP.silent = 1;
}
void
opts_set_port(char *arg)
{
int res;
uint16_t port;
res = atoi(arg);
port = (uint16_t) res;
if (port != res) {
say("Bad port %s.", arg);
ntkresolv_safe_exit(1);
}
GOP.port = port;
}
void
opts_set_ns(char *arg)
{
int slen;
slen = strlen(arg);
if (slen >= MAX_HOSTNAME_LEN) {
say("Server hostname too long.");
ntkresolv_safe_exit(1);
}
strcpy(GOP.nsserver, arg);
GOP.nsserver[slen] = 0;
}
void
opts_set_qt(char *arg)
{
int res;
if (!strcmp(arg, HELP_STR))
qt_usage(NULL);
res = QTFROMPREF(arg);
if (res == -1)
qt_usage(arg);
if (res == QTYPE_MX) {
GQT->qtype = QTYPE_A;
GQT->service = 25;
GQT->p = SNSD_PROTO_TCP;
} else
GQT->qtype = res;
}
void
opts_set_realm(char *arg)
{
uint8_t res;
if (!strcmp(arg, HELP_STR))
realm_usage(NULL);
res = REALMFROMPREF(arg);
if (!res)
realm_usage(arg);
GQT->nk = res;
}
void
opts_set_service_and_proto(char *arg)
{
int ret;
if (!strcmp(arg, HELP_STR))
service_and_proto_usage(NULL);
ret = str_to_snsd_service(arg, (int *) &GQT->service, &GQT->p);
/* if(ret == -1)
say("Bad service %s.",arg);
else if(ret == -2)
proto_usage(arg);*/
if (ret < 0)
service_and_proto_usage(arg);
GQT->p -= 1;
}
void
opts_set_proto(char *arg)
{
int ret;
if (!strcmp(arg, HELP_STR))
proto_usage(NULL);
ret = PROTOFROMPREF(arg);
if (ret < 0)
proto_usage(arg);
GQT->p = ret;
}
/* This is a complex set of function. */
void
opts_set_recursion(void)
{
GQT->r = 0;
}
void
opts_set_hash(void)
{
GOP.hash = 1;
}
void
opts_set_compute_hash(void)
{
mode_compute_hash = 1;
}
void
opts_set_parsable_output(void)
{
mode_parsable_output = 1;
AMISILENT = 1;
}
void
opts_set_question(char *arg)
{
struct in_addr ia;
struct in6_addr i6a;
int res;
strcpy(GOP.obj, arg);
res = strlen(arg);
switch (GQT->qtype) {
case QTYPE_A:
if (GQT->nk == REALM_NTK) {
G_ALIGN(ANDNS_HASH_H);
if (GOP.hash) {
if (res != 2 * ANDNS_HASH_H) {
say("Malformed Hostname hash `%s'.\n", arg);
ntkresolv_safe_exit(1);
}
NTK_RESOLV_STR_HASH(arg, GQT->qstdata);
} else
hash_md5((unsigned char *) arg, res,
(unsigned char *) GQT->qstdata);
} else {
if (res > 255) {
say("Hostname %s is too long for DNS standard.", arg);
ntkresolv_safe_exit(1);
}
G_ALIGN(res);
strcpy(GQT->qstdata, arg);
}
return;
case QTYPE_PTR:
res = inet_pton(AF_INET, arg, &ia);
if (res) {
G_ALIGN(ANDNS_HASH_H);
memcpy(GQT->qstdata, &ia.s_addr, 4);
return;
}
res = inet_pton(AF_INET6, arg, &i6a);
if (!res) {
say("Bad address `%s'\n", arg);
ntkresolv_safe_exit(1);
}
G_ALIGN(16);
memcpy(GQT->qstdata, &i6a.__in6_u, 16);
GQT->ipv = ANDNS_IPV6;
return;
case QTYPE_G:
if (GQT->nk != REALM_NTK) {
say("Global query type is valid only for the Ntk realm.");
ntkresolv_safe_exit(1);
}
G_ALIGN(ANDNS_HASH_H);
if (GOP.hash)
NTK_RESOLV_STR_HASH(arg, GQT->qstdata);
else
hash_md5((unsigned char *) arg, res,
(unsigned char *) GQT->qstdata);
return;
default:
say("Unknow Query Type.\n");
return;
}
}
void
opts_finish(char *arg)
{
int r;
r = strlen(arg);
if (r > NTKRESOLV_MAX_OBJ_LEN) {
say("Object requested is too long: %s", arg);
ntkresolv_safe_exit(1);
}
if (mode_compute_hash) { /* Do command here and exit */
G_ALIGN(ANDNS_HASH_H);
hash_md5((unsigned char *) arg, r, (unsigned char *) GQT->qstdata);
NTK_RESOLV_HASH_STR(GQT->qstdata, GOP.obj);
say("%s\n", GOP.obj);
ntkresolv_safe_exit(0);
}
if (GOP.hash && GQT->qtype == AT_PTR) {
say("Option `-m' is not usable with inverse queries.\n");
ntkresolv_safe_exit(1);
}
r = rand();
GQT->id = r >> 16;
opts_set_question(arg);
}
void
print_headers()
{
andns_pkt *ap = GQT;
say("\n - Headers Section:\n"
"\tid ~ %6d\tqr ~ %4d\tqtype ~ %7s\n"
"\tan ~ %6d\tipv ~ %s\trealm ~ %7s\n"
"\tsv ~ %6s\tprt ~ %4s\trCode ~ %s\n"
"\trc ~ %6d\n",
ap->id, ap->qr, QTYPE_STR(ap),
ap->ancount, IPV_STR(ap), NK_STR(ap),
SERVICE_STR(ap), PROTO_STR(ap), RCODE_STR(ap), ap->r);
}
void
print_question()
{
say("\n - Question Section:\n" "\tObj ~ %s\n", GOP.obj);
}
void
ip_bin_to_str(void *data, char *dst)
{
int family;
struct in_addr ia;
struct in6_addr i6a;
const void *via;
const char *crow;
family = GQT->ipv == ANDNS_IPV4 ? AF_INET : AF_INET6;
switch (family) {
case AF_INET:
memcpy(&(ia.s_addr), data, 4);
via = (void *) (&ia);
break;
case AF_INET6:
memcpy(&(i6a.__in6_u), data, 16);
via = (void *) (&i6a);
break;
default:
strcpy(dst, "Unprintable Object");
return;
}
crow = inet_ntop(family, via, dst, NTKRESOLV_MAX_OBJ_LEN);
if (!crow)
strcpy(dst, "Unprintable Object");
}
void
answer_data_to_str(andns_pkt_data * apd, char *dst)
{
if (GQT->qtype == AT_PTR)
strcpy(dst, apd->rdata);
else if (GQT->qtype == AT_G || GQT->qtype == AT_A) {
if (apd->m & APD_IP)
ip_bin_to_str(apd->rdata, dst);
else
NTK_RESOLV_HASH_STR(apd->rdata, dst);
} else
strcpy(dst, "Unprintable Object");
}
void
print_answers()
{
int i = 0;
int ancount = GQT->ancount;
andns_pkt_data *apd;
if (!ancount)
return;
say("\n - Answers Section:\n");
apd = GQT->pkt_answ;
while (apd) {
i++;
if (i > ancount)
say("Answer not declared in Headers Packet.\n");
answer_data_to_str(apd, GOP.obj);
say("\t ~ %s", GOP.obj);
if (apd->m & APD_MAIN_IP)
say(" *");
else if (GQT->qtype != AT_PTR && !(apd->m & APD_IP) && GQT->r)
say("\t + Recursion Failed");
say("\n");
if (GQT->qtype == AT_A || GQT->qtype == AT_G)
say("\t\tPrio ~ %d Weigth ~ %d\n", apd->prio, apd->wg);
if (GQT->qtype == AT_G)
say("\t\tService ~ %d Proto ~ %s\n",
apd->service, apd->m & APD_UDP ? "udp" : "tcp");
say("\n");
apd = apd->next;
}
}
void
print_parsable_answers(void)
{
int i = 0;
int ancount = GQT->ancount;
andns_pkt_data *apd;
if (!ancount)
return;
apd = GQT->pkt_answ;
while (apd) {
i++;
if (i > ancount)
say("Answer not declared in Headers Packet.\n");
answer_data_to_str(apd, GOP.obj);
if (GQT->qtype == AT_PTR || (GQT->qtype == AT_A && !GQT->service))
say("%s %s\n", NTK_RESOLV_SYMBOL(apd), GOP.obj);
else if (GQT->qtype == AT_A)
say("%s %s %d %d\n", NTK_RESOLV_SYMBOL(apd),
GOP.obj, apd->prio, apd->wg);
else
say("%s %s %d %s %d %d\n", NTK_RESOLV_SYMBOL(apd),
GOP.obj, apd->service,
apd->m & APD_UDP ? "udp" : "tcp", apd->prio, apd->wg);
apd = apd->next;
}
}
void
print_results(void)
{
if (!AMISILENT) {
print_headers();
print_question();
}
if (mode_parsable_output)
print_parsable_answers();
else
print_answers();
}
void
do_command(void)
{
char buf[ANDNS_MAX_SZ];
char answer[ANDNS_MAX_PK_LEN];
int res;
memset(buf, 0, ANDNS_MAX_SZ);
GOP.id = GQT->id;
res = a_p(GQT, buf);
if (res == -1) {
say("Error building question.\n");
ntkresolv_exit(1);
}
res = hn_send_recv_close(GOP.nsserver, GOP.port,
SOCK_DGRAM, buf, res, answer,
ANDNS_MAX_PK_LEN, 0, NTK_RESOLV_TIMEOUT);
if (res == -1) {
say("Communication failed with %s.\n", GOP.nsserver);
ntkresolv_exit(1);
}
if (res == -2) {
say("Unable to send() to %s.\n", GOP.nsserver);
ntkresolv_exit(1);
}
if (res == -3) {
say("Unable to recv() from %s.\n", GOP.nsserver);
ntkresolv_exit(1);
}
res = a_u(answer, res, &GQT);
if (res <= 0) {
say("Error interpreting server answer.\n");
ntkresolv_exit(1);
}
if (GQT->id != GOP.id)
say("Warning: ID query (%d) is mismatching ID answer (%d)!\n",
GOP.id, GQT->id);
print_results();
destroy_andns_pkt(GQT);
}
void
ntkresolv_exit(int i)
{
exit(i);
}
void
ntkresolv_safe_exit(int i)
{
destroy_andns_pkt(GQT);
ntkresolv_exit(i);
}
int
main(int argc, char **argv)
{
int c;
extern int optind, opterr, optopt;
extern char *optarg;
log_init("", 0, 1);
gettimeofday(&time_start, NULL);
opts_init();
struct option longopts[] = {
{"version", 0, 0, 'v'},
{"nameserver", 1, 0, 'n'},
{"port", 1, 0, 'P'},
{"query-type", 1, 0, 't'},
{"realm", 1, 0, 'r'},
{"service", 1, 0, 's'},
{"proto", 1, 0, 'p'},
{"silent", 0, 0, 'S'},
{"block-recursion", 0, 0, 'b'},
{"md5-hash", 0, 0, 'm'},
{"compute-hash", 0, 0, 'H'},
{"parsable-output", 0, 0, 'l'},
{"help", 0, 0, 'h'},
{0, 0, 0, 0}
};
while (1) {
int oindex = 0;
c = getopt_long(argc, argv,
"vn:P:t:r:s:p:ShbmHl", longopts, &oindex);
if (c == -1)
break;
switch (c) {
case 'v':
version();
case 'n':
opts_set_ns(optarg);
break;
case 'P':
opts_set_port(optarg);
break;
case 't':
opts_set_qt(optarg);
break;
case 'r':
opts_set_realm(optarg);
break;
case 's':
opts_set_service_and_proto(optarg);
break;
case 'p':
opts_set_proto(optarg);
break;
case 'h':
usage();
case 'S':
opts_set_silent();
break;
case 'b':
opts_set_recursion();
break;
case 'm':
opts_set_hash();
break;
case 'H':
opts_set_compute_hash();
break;
case 'l':
opts_set_parsable_output();
break;
default:
usage();
}
}
if (optind == argc)
usage();
opts_finish(argv[optind]);
do_command();
time_report;
bye;
return 0;
}