Bindinfo v1.01: allows root to make DNS queries behind firewalls. Works on Solaris, OpenBSD.
e29d6b8da305c8f8800aec4d238d658d7d8ec51f60cf8b5a1659907388cfb189
/*
* This code was written by:
* Joshua James Drake (jdrake@pulsar.net)
*
* Published 6/9/98 @ 12:02 AM
*
* The following lines of code are, in a nutshell, written to pry
* some information from a nameserver. The information it gives
* you may or may not be useful. That is for you to decide.
*
* However, it will tell you if the server supports IQUERY and, if
* possible, what version of bind (by Paul Vixie) it is running.
*
*/
/* --- code was modified to allow root users to send queries using source
--- port 53 in order to query behind (some) packet filters
---
--- version v1.00 torh@bogus.net 29/9/1999
|
+-does not seem to work with Linux (won't run)
| fixes appreciated!
|
+-does not seem to work with OpenBSD (source port still random)
| fixes appreciated!
|
+- COMPILE WITH gcc -DSOLARIS -lsocket -lnsl (tested on Solaris 2.6)
--- version v1.01 torh@bogus.net 29/9/1999
|
+-now works with OpenBSD (tested on 2.4)
*/
/* local type includes */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <string.h>
#include <ctype.h>
#include <sys/errno.h>
/* network type includes */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <netdb.h>
/* another addition for 2.6 / torh */
#ifdef SOLARIS
#include <errno.h>
#endif
/* bulky shit for printing dnspkts need to link dnspkt.o from dnspkt.c too */
#ifdef DEBUG
#include "dnspkt.h"
#endif
/* prototypes */
int lookup_host(struct sockaddr_in *ra, char *hn, unsigned short rp);
void probe_bind(struct sockaddr_in ra);
int talk(int sd, char *pkt, int pktl, char opc);
int make_keypkt(char *pktbuf, char opc);
void print_ver(char *host, int vul, char *buf);
void handle_alarm(int signum);
/*
* here we simply check arguments, resolve the hostname given and
* if all is well, initialize the radom seed and probe away.
*/
void
main(argc, argv)
int argc;
char *argv[];
{
struct sockaddr_in ra;
if (argc != 2)
{
printf("usage: %s <host>\n", argv[0]);
return;
}
if (!lookup_host(&ra, argv[1], NAMESERVER_PORT))
return;
srand(time(NULL));
probe_bind(ra);
}
/*
* resolve a hostname to a sockaddr_in struct.
* we first try treating it like an ip address in a.b.c.d notation
* then, if that fails, we try to resolve using DNS ways
* if all fails, we return 0. (failed)
* if we get the sockaddr_in struct all filled out, we return 1.
*/
int
lookup_host(ra, hn, rp)
struct sockaddr_in *ra;
char *hn;
unsigned short rp;
{
struct hostent *he;
ra->sin_family = AF_INET;
ra->sin_port = htons(rp);
if ((ra->sin_addr.s_addr = inet_addr(hn)) != -1)
return 1;
if ((he = gethostbyname(hn)) != (struct hostent *)NULL)
{
memcpy(&ra->sin_addr.s_addr, he->h_addr, 4);
return 1;
}
#ifdef SOLARIS
perror("Unable to resolve hostname");
#else
herror("Unable to resolve hostname");
#endif
return 0;
}
/*
* here we allocate some space for our packets and make sure it's
* "fullanull". then we attempt to allocate and setup our socket.
* if failure occurs, we shall report error and return.
* the we attempt to reverse our address in the sockaddr_in structure
* passed as the only argument into a dns name, if that fails, we go
* with the ascii ip address representation. then we attempt to
* communicate the remote server, if failure, we return. after we
* have successfully got our packets back, we close the socket and
* print out a summary of what we got.
*/
void
probe_bind(ra)
struct sockaddr_in ra;
{
int sd;
char iquery[512], vquery[512], rname[256];
struct hostent *he;
/* --- in order to manipulate source details, use another sockaddr
--- struct (sa), which contains source port and source ip / torh */
struct sockaddr_in sa;
char localhost[128+1];
struct hostent *hp;
HEADER *dh = (HEADER *)iquery;
memset(vquery, 0, sizeof(vquery));
memset(iquery, 0, sizeof(iquery));
/* --- this portion has been modified so that root issues source_port=53
--- in attempt to bypass packet filters / torh */
if(getuid()) {
/* --- running as luser, no special privs, use original code / torh */
printf("Unable to set source port < 1024, using luser privileges (random port).\n");
if(((sd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) ||
(connect(sd, (struct sockaddr *)&ra, sizeof(ra)) == -1)) {
perror("Unable to connect");
if (sd != -1)
close(sd);
return;
}
} else {
/* --- running as root, so we are allowed to force the source port
--- value / torh */
printf("This code is executing as root, assuming source port 53.\n");
if((sd = socket(AF_INET,SOCK_DGRAM,0)) == -1) {
perror("Unable to create socket()");
return;
}
/* --- set localhost information / torh */
gethostname(localhost, 128);
printf("localhost: %s\n", localhost);
if ((hp = gethostbyname(localhost)) == NULL) {
perror("Couldn't resolve localhost");
return;
}
/* --- change source port, and bind the socket file descriptor
--- with the new host/port pair / torh */
sa.sin_family=AF_INET;
bcopy(hp->h_addr,(char *)&sa.sin_addr,hp->h_length);
sa.sin_port=htons(53);
if(bind(sd,(struct sockaddr *)&sa,sizeof(sa)) == -1) {
perror("Unable to bind()");
if(sd != -1) {close(sd);}
return;
}
if(connect(sd,(struct sockaddr *)&ra,sizeof(ra)) == -1) {
perror("Unable to connect()");
if(sd != -1) {close(sd);}
return;
}
}
if((he = gethostbyaddr((char *)&ra.sin_addr, sizeof(ra.sin_addr), AF_INET)) == (struct hostent *)NULL)
sprintf(rname, "%s", inet_ntoa(ra.sin_addr));
else
strncpy(rname, he->h_name, sizeof(rname));
if (!talk(sd, iquery, sizeof(iquery), IQUERY))
return;
if (!talk(sd, vquery, sizeof(vquery), QUERY))
return;
close(sd);
/* if dh->rcode == 0, then our iquery request was answered and the remote server
supports iquery */
print_ver(rname, dh->rcode == 0, vquery);
}
/*
* write our packet from pkt, wait for a response and put it in pkt.
* if the alarm goes off or the read fails, we print error
* and return 0. otherwise, our response packet is in pkt and we return 1.
*/
int
talk(sd, pkt, pktl, opc)
int sd, pktl;
char *pkt, opc;
{
int pktlen;
pktlen = make_keypkt(pkt, opc);
if (!write(sd, pkt, pktlen))
{
perror("write failed");
close(sd);
return 0;
}
#ifdef DEBUG
printf("write() success\n");
#endif
siginterrupt(SIGALRM, 1);
signal(SIGALRM, handle_alarm);
alarm(3);
pktlen = read(sd, pkt, pktl);
if (pktlen <= 0)
{
if (errno == EINTR)
errno = ETIMEDOUT;
perror("read failed");
close(sd);
return 0;
}
#ifdef DEBUG
printf("read success\n");
#endif
alarm(0);
return 1;
}
/*
* this forms a valid dns packet based on the op code given by opc.
* only two opcodes are supported because that's all we need to support.
* the packet ends up in pktbuf and the length of the packet is returned.
*/
int
make_keypkt(pktbuf, opc)
char *pktbuf;
char opc;
{
HEADER *dnsh;
char *ptr = pktbuf;
int pktlen = 0;
dnsh = (HEADER *) ptr;
/* fill out the parts of the DNS header that aren't 0 */
dnsh->id = htons(rand() % 65535);
dnsh->opcode = opc;
dnsh->rd = 1;
dnsh->ra = 1;
/* one answer for IQUERY, one question for QUERY */
if (opc == IQUERY)
dnsh->ancount = htons(1);
else if (opc == QUERY)
dnsh->qdcount = htons(1);
pktlen += sizeof(HEADER);
ptr += sizeof(HEADER);
/* we have to make a QUERY, fill out the question section */
if (opc == QUERY)
{
/* version.bind. == elite */
char qstr[] = "\007version\004bind\000";
int qlen = strlen(qstr) + 1;
memcpy(ptr, qstr, qlen);
ptr += qlen;
pktlen += qlen;
PUTSHORT(T_TXT, ptr);
PUTSHORT(C_CHAOS, ptr);
pktlen += sizeof(short) * 2;
}
/* add a resource record for the inverse query */
else if (opc == IQUERY)
{
unsigned long addr = inet_addr("1.2.3.4");
unsigned long ttl = 31337;
unsigned short addrlen = 4;
*(ptr++) = '\0';
pktlen++;
PUTSHORT(T_A, ptr);
PUTSHORT(C_IN, ptr);
PUTLONG(ttl, ptr);
PUTSHORT(addrlen, ptr);
PUTLONG(addr, ptr);
pktlen += (sizeof(short) * 3) + (sizeof(long) * 2);
}
/* if we're debugging, show what we just made */
#ifdef DEBUG
print_dnspkt(pktbuf, pktbuf + pktlen);
#endif
return pktlen;
}
/*
* This function takes a DNS packet in buf, and whether or not it reponds to IQUERY in vul.
* We cast the packet and extract the response as long as there is one.
* If there isn't one, then we assume that the remote server is an old version of bind.
* this is the end of the line.
*/
void
print_ver(host, vul, buf)
char *host, *buf;
int vul;
{
HEADER *dnsh = (HEADER *)buf;
char *ptr, *verstr;
int len;
if (dnsh->rcode != 0)
{
printf("%s's named that %s iquery does not respond to version.bind.\n", host, vul ? "supports" : "errors on");
return;
}
/* So we actually have a response. Lets skip the crap, starting with the header */
ptr = (buf + sizeof(HEADER));
/* then the question section domain name. */
while (*ptr != '\0')
ptr++;
/* then the trailing null and the type/class of the question */
ptr += 1 + (sizeof(short) * 2);
/* now we skip the answer section domain name. (should be the same as the question) */
while (*ptr != '\0')
ptr++;
/* don't forget the trailing null, type, class, and time to live. */
ptr += 1 + (sizeof(long) + (sizeof(short) * 2));
/* Here we are at the resource record data length, extract it */
GETSHORT(len, ptr);
/* avoid the need to decompress the string (treat it as one) */
ptr++;
/* allocate space for and copy the version response txt */
verstr = (char *)malloc(len);
memset(verstr, 0, len);
memcpy(verstr, ptr, len-1);
/* run through the vesion string and replace non-printable and non-whitespace characters
with a '.' */
for (ptr = verstr; ptr - verstr != len - 1; ptr++)
if (!isprint(*ptr) && !isspace(*ptr))
*ptr = '.';
/* print the version and iquery support status, woo hoo */
printf("%s's named that %s iquery is version: %s\n", host, vul ? "supports" : "errors on", verstr);
}
/*
* handle the alarm signal by resetting the alarm timer and
* the signal handler for SIGALRM. This stuff probably isn't needed,
* but I did it anyway. It's good for debugging, ran into some problems with
* alarm() not doing its job.
*/
void
handle_alarm(signum)
int signum;
{
alarm(0);
signal(SIGALRM, SIG_DFL);
#ifdef DEBUG
printf("recieved alarm\n");
#endif
}