/* * DNS plugin. $Id$ */ #define IN_PLUGIN #include "../../echoping.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include struct addrinfo name_server; poptContext dns_poptcon; char *request; int type; char *type_name = NULL; boolean use_tcp = FALSE; boolean no_recurse = FALSE; /* nsError stolen from Liu & Albitz check_soa (in their book "DNS and BIND") */ /**************************************************************** * nsError -- Print an error message from h_errno for a failure * * looking up NS records. res_query() converts the DNS * * packet return code to a smaller list of errors and * * places the error value in h_errno. There is a routine * * called herror() for printing out strings from h_errno * * like perror() does for errno. Unfortunately, the * * herror() messages assume you are looking up address * * records for hosts. In this program, we are looking up * * NS records for domains, so we need our own list of error * * strings. * ****************************************************************/ int nsError (error, domain) int error; char *domain; { switch (error) { case HOST_NOT_FOUND: err_ret ("Unknown domain: %s\n", domain); return -1; case NO_DATA: err_ret ("No records of type %s for %s in the Answer section\n", to_upper (type_name), domain); return -1; case TRY_AGAIN: err_ret ("No response for query\n"); return -2; default: err_ret ("Unexpected error\n"); return -1; } } void dns_usage (const char *msg) { if (msg) { fprintf(stderr, "Error: %s\n", msg); } poptPrintUsage(dns_poptcon, stderr, 0); fprintf(stderr, " request\n"); exit(1); } char * init (const int argc, const char **argv) { int value; char *hostname; char *msg = malloc (256); char *upper_type_name = NULL; /* popt variables */ struct poptOption options[] = { {"type", 't', POPT_ARG_STRING, &type_name, 0, "Type of resources queried (A, MX, SOA, etc)", "type"}, {"tcp", NULL, POPT_ARG_NONE, &use_tcp, 0, "Use TCP for the request (virtual circuit)", "tcp"}, {"no-recurse", NULL, POPT_ARG_NONE, &no_recurse, 0, "Do not ask recursion", "no-recurse"}, POPT_AUTOHELP POPT_TABLEEND }; dns_poptcon = poptGetContext (NULL, argc, argv, options, POPT_CONTEXT_KEEP_FIRST); while ((value = poptGetNextOpt (dns_poptcon)) > 0) { } if (value < -1) { sprintf (msg, "%s: %s", poptBadOption (dns_poptcon, POPT_BADOPTION_NOALIAS), poptStrerror (value)); dns_usage (msg); } hostname = (char *) poptGetArg (dns_poptcon); /* Not used */ request = (char *) poptGetArg (dns_poptcon); if (request == NULL) dns_usage ("Mandatory request missing"); if ((type_name == NULL) || !strcmp (type_name, "")) { type = T_A; type_name = "A"; } else { upper_type_name = (char *) to_upper (type_name); /* * TODO: a better algorithm. Use dns_rdatatype_fromtext in * BIND ? */ if (!strcmp (upper_type_name, "A")) type = T_A; else if (!strcmp (upper_type_name, "AAAA")) type = T_AAAA; else if (!strcmp (upper_type_name, "NS")) type = T_NS; else if (!strcmp (upper_type_name, "SOA")) type = T_SOA; else if (!strcmp (upper_type_name, "MX")) type = T_MX; else if (!strcmp (upper_type_name, "SRV")) type = T_SRV; else if (!strcmp (upper_type_name, "CNAME")) type = T_CNAME; else if (!strcmp (upper_type_name, "PTR")) type = T_PTR; else if (!strcmp (upper_type_name, "TXT")) type = T_TXT; else if (!strcmp (upper_type_name, "ANY")) type = T_ANY; else dns_usage ("Unknown type"); } return "domain"; } void start (struct addrinfo *res) { struct sockaddr name_server_sockaddr; struct sockaddr_in name_server_sockaddr_in; struct sockaddr_in6 name_server_sockaddr_in6; name_server = *res; name_server_sockaddr = *name_server.ai_addr; if (name_server_sockaddr.sa_family == AF_INET) { /* Converts a generic sockaddr to an IPv4 sockaddr_in */ (void) memcpy ((void *) &name_server_sockaddr_in, &name_server_sockaddr, sizeof (struct sockaddr)); } else if (name_server_sockaddr.sa_family == AF_INET6) { /* TODO: the code for IPv6 servers is hopelessly broken. Start again */ fprintf (stderr, "WARNING: IPv6 nameservers not really supported yet (experts may apply). Falling back to IPv4 and the default server. You may use -4, too\n"); #ifdef HAVE_RES_EXT /* Converts a generic sockaddr to an IPv6 sockaddr_in6 */ (void) memcpy ((void *) &name_server_sockaddr_in6, &name_server_sockaddr, sizeof (struct sockaddr)); #else err_quit ("IPv6 name servers not supported on this platform, may be you should use the -4 option"); #endif } else { err_quit ("Unknown family for address of the server"); } if (res_init () < 0) err_sys ("res_init"); if (name_server_sockaddr.sa_family == AF_INET) { _res.nsaddr_list[0] = name_server_sockaddr_in; } else if (name_server_sockaddr.sa_family == AF_INET6) { #ifdef HAVE_RES_EXT /* TODO: completely broken, dioes not work. Check in Stevens */ (void)memcpy(_res_ext.nsaddr_list, &name_server_sockaddr_in6, sizeof(struct sockaddr_in6)); #endif } _res.nscount = 1; _res.options &= ~(RES_DNSRCH | RES_DEFNAMES | RES_NOALIASES); if (use_tcp) { _res.options |= RES_USEVC; } if (no_recurse) { _res.options &= ~RES_RECURSE; } } int execute () { union { HEADER hdr; /* defined in resolv.h */ u_char buf[PACKETSZ]; /* defined in arpa/nameser.h */ } response; /* response buffers */ int response_length; /* buffer length */ if ((response_length = res_query (request, /* the domain we care * about */ C_IN, /* Internet class records */ type, (u_char *) & response, /* response buffer */ sizeof (response))) /* buffer size */ < 0) { /* If negative */ nsError (h_errno, request); /* report the error */ if (h_errno == TRY_AGAIN) return -1; /* More luck next time? */ else return -2; /* Give in */ } /* * TODO: better analysis of the replies. For instance, replies can be * in the authority section (delegation info) */ return 0; } void terminate () { }