mirror of
https://github.com/ChronosX88/netsukuku.git
synced 2024-11-22 18:22:18 +00:00
668 lines
14 KiB
C
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;
|
|
}
|