/* * 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 #include #include #include #include #include #include #include /* network type includes */ #include #include #include #include #include #include /* another addition for 2.6 / torh */ #ifdef SOLARIS #include #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 \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 }