403 lines
8.3 KiB
C
403 lines
8.3 KiB
C
/* zodiac - advanced dns spoofer
|
|
*
|
|
* by scut / teso
|
|
*
|
|
* dns id queue handling routines
|
|
*
|
|
*/
|
|
|
|
#define DNSID_MAIN
|
|
|
|
#include <sys/time.h>
|
|
#include <unistd.h>
|
|
#include <pthread.h>
|
|
#include <ncurses.h>
|
|
#include <stdlib.h>
|
|
#include "common.h"
|
|
#include "dnsid.h"
|
|
#include "dns.h"
|
|
#include "network.h"
|
|
#include "zodiac.h"
|
|
#include "output.h"
|
|
|
|
id_q *id_root = NULL;
|
|
|
|
/* id_qprint
|
|
*
|
|
* erase the window pointed to by `win' in `screen', then print all
|
|
* ID's stored in queue pointed to by `root', which is protected by `rm'
|
|
*/
|
|
|
|
void
|
|
id_qprint (mscr *screen, WINDOW *win)
|
|
{
|
|
id_q *this; /* list step-through pointer */
|
|
|
|
/* clear window
|
|
*/
|
|
pthread_mutex_lock (&screen->outm);
|
|
werase (win);
|
|
pthread_mutex_unlock (&screen->outm);
|
|
|
|
pthread_mutex_lock (&id_rmutex);
|
|
this = id_root;
|
|
|
|
while (this != NULL) {
|
|
char ip[64], *ipp;
|
|
unsigned long int age = 0;
|
|
id_q *next;
|
|
|
|
pthread_mutex_lock (&this->id_mutex);
|
|
|
|
ipp = ipv4_print (ip, this->ip, 2); /* print ip in quad-dot, padded with spaces */
|
|
age = id_tdiff (&this->mtime); /* compute the age of the known id */
|
|
|
|
m_printfnr (screen, win, "[%s] %04x = %8lu %s%s\n", ipp, this->id, age,
|
|
((this->flags & IDF_SEQ) == IDF_SEQ) ? "SEQUENTIAL " : "",
|
|
((this->flags & IDF_WINDUMB) == IDF_WINDUMB) ? "WINDOWS" : "");
|
|
|
|
next = this->next;
|
|
pthread_mutex_unlock (&this->id_mutex);
|
|
this = next;
|
|
}
|
|
|
|
pthread_mutex_unlock (&id_rmutex);
|
|
|
|
pthread_mutex_lock (&screen->outm);
|
|
wrefresh (win);
|
|
pthread_mutex_unlock (&screen->outm);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/* id_tdiff
|
|
*
|
|
* calculate the age in seconds from the given timeinterval and the current
|
|
* time.
|
|
*
|
|
* return the age in seconds
|
|
*/
|
|
|
|
unsigned long int
|
|
id_tdiff (struct timeval *mtime)
|
|
{
|
|
struct timeval current; /* current time */
|
|
|
|
/* get current time
|
|
*/
|
|
gettimeofday (¤t, NULL);
|
|
|
|
return (tdiff (mtime, ¤t));
|
|
}
|
|
|
|
|
|
/* id_free
|
|
*
|
|
* free's an id_q structure, pointed to by `tofree'. this routine assumes that
|
|
* the calling function hold the `id_mutex', and won't unlock it later, because
|
|
* it gets destroyed.
|
|
*/
|
|
|
|
void
|
|
id_free (id_q *tofree)
|
|
{
|
|
pthread_mutex_destroy (&tofree->id_mutex);
|
|
free (tofree);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/* id_seq
|
|
*
|
|
* make assumptions wether a dns id is predictable and used sequentially.
|
|
* use the time `o_time' of the old id `old_id' to compare with the new
|
|
* id `new_id'. use limit `idps' to get id rate per second.
|
|
*
|
|
* return 1 if it is sequentially (rate below or equal to `idps'
|
|
* return 0 if the id is not predictable or random (above the rate)
|
|
* return -1 if `old_id' is same as `new_id'
|
|
*/
|
|
|
|
int
|
|
id_seq (u_short new_id, u_short old_id, struct timeval *o_time, int idps)
|
|
{
|
|
unsigned long int age;
|
|
u_short id_diff = 0;
|
|
|
|
/* handle bigger/smaller cases signed, if equal it is most likely
|
|
* a second query approach, therefore id_diff stays zero
|
|
*/
|
|
if (new_id > old_id)
|
|
id_diff = (new_id - old_id);
|
|
else if (new_id < old_id)
|
|
id_diff = (old_id - new_id);
|
|
|
|
if (id_diff == 0)
|
|
return (-1);
|
|
|
|
/* make some calculations about predictability
|
|
* of the id's
|
|
*/
|
|
age = id_tdiff (o_time);
|
|
if (age == 0)
|
|
age = 1;
|
|
|
|
/* less then 10 id's per second
|
|
*/
|
|
if ((id_diff / age) <= idps)
|
|
return (1);
|
|
|
|
return (0);
|
|
}
|
|
|
|
|
|
/* id_windows
|
|
*
|
|
* check if both id's, `id_new' and `id_old' may be send out by a windows
|
|
* `operating system' dns resolver library.
|
|
*
|
|
* return 1 if it is most likely a windows box
|
|
* return 0 if it is most likely not a windows box
|
|
*/
|
|
|
|
int
|
|
id_windows (u_short id_new, u_short id_old)
|
|
{
|
|
if (id_new <= 20 && id_old <= 20)
|
|
return (1);
|
|
|
|
return (0);
|
|
}
|
|
|
|
|
|
/* id_add
|
|
*
|
|
* add/update a nameserver id entry for `ip' as the nameserver ip,
|
|
* `id' as the measured nameserver id, and `mtime' as the time measured.
|
|
*
|
|
* return nothing (since the packet is mutexed)
|
|
*/
|
|
|
|
void
|
|
id_add (struct in_addr ip, u_short id, struct timeval *mtime)
|
|
{
|
|
id_q *n_idq; /* new id queue element */
|
|
|
|
/* get memory for new linked list element
|
|
*/
|
|
n_idq = xcalloc (1, sizeof (id_q));
|
|
|
|
/* initialize structure
|
|
*/
|
|
pthread_mutex_init (&n_idq->id_mutex, NULL);
|
|
memcpy (&n_idq->ip, &ip, sizeof (struct in_addr));
|
|
n_idq->id = id;
|
|
n_idq->flags = 0;
|
|
memcpy (&n_idq->mtime, mtime, sizeof (struct timeval));
|
|
n_idq->next = NULL;
|
|
|
|
pthread_mutex_lock (&id_rmutex);
|
|
|
|
if (id_root == NULL) {
|
|
id_root = n_idq;
|
|
} else {
|
|
id_q *this, *last;
|
|
int bc = 0; /* for break condition */
|
|
|
|
/* step through the linked list until either an old entry
|
|
* was found, or we have reached the end of the list
|
|
* quite scuttish code ;-)
|
|
*
|
|
* fixed, optimized and rewritten 990614, please don't mod here
|
|
*/
|
|
|
|
last = this = id_root;
|
|
|
|
while (bc == 0) {
|
|
pthread_mutex_lock (&this->id_mutex);
|
|
|
|
/* if the id is already stored, unlink the old id_q,
|
|
* and put our one instead
|
|
*/
|
|
if (memcmp (&this->ip, &ip, sizeof (struct in_addr)) == 0) {
|
|
id_q *old;
|
|
int nr; /* temp. return value */
|
|
|
|
/* check wether the dns id is sequential
|
|
*/
|
|
nr = id_seq (id, this->id, &this->mtime, 40);
|
|
if (nr == -1) {
|
|
n_idq->flags = this->flags;
|
|
} else if (nr == 1) {
|
|
n_idq->flags |= IDF_SEQ;
|
|
} else if (nr == 0) {
|
|
/* n_idq->flags &= ~IDF_SEQ;
|
|
*/
|
|
n_idq->flags = this->flags;
|
|
}
|
|
|
|
nr = id_windows (id, this->id);
|
|
if (nr == 1)
|
|
n_idq->flags |= IDF_WINDUMB;
|
|
else
|
|
n_idq->flags &= ~IDF_WINDUMB;
|
|
|
|
/* if we have to replace the entry, we copy the link-
|
|
* data from it, then remove it from the linked list
|
|
*/
|
|
old = this;
|
|
n_idq->next = old->next;
|
|
|
|
/* if there were id_q's before, correct the last one
|
|
*/
|
|
if (old == id_root) {
|
|
id_root = n_idq;
|
|
} else {
|
|
pthread_mutex_lock (&last->id_mutex);
|
|
last->next = n_idq;
|
|
pthread_mutex_unlock (&last->id_mutex);
|
|
}
|
|
pthread_mutex_unlock (&old->id_mutex);
|
|
id_free (old);
|
|
bc = 1; /* break if entry already exists */
|
|
|
|
/* else, when the end of the id queue is reached, without
|
|
* any matching entry, then just add our one to the end
|
|
*/
|
|
} else if (this->next == NULL) {
|
|
this->next = n_idq;
|
|
if (id_windows (0, this->id) == 1)
|
|
this->flags |= IDF_WINDUMB;
|
|
|
|
bc = 2; /* break when end of list is reached */
|
|
}
|
|
|
|
if (bc != 1) {
|
|
last = this;
|
|
this = this->next;
|
|
pthread_mutex_unlock (&last->id_mutex);
|
|
}
|
|
}
|
|
/* bc == 2 is already carried out
|
|
*/
|
|
}
|
|
|
|
pthread_mutex_unlock (&id_rmutex);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/* id_speed
|
|
*
|
|
* fetch the id increasing speed.
|
|
*
|
|
* return the dns id increasing speed (in id's per 10 seconds) of the
|
|
* nameserver with ip `ip'.
|
|
* return 0 on failure.
|
|
*/
|
|
|
|
unsigned long int
|
|
id_speed (char *ip_a)
|
|
{
|
|
id_q *this; /* working pointer for queue */
|
|
struct in_addr ip_ad;
|
|
unsigned long int speed = 0;
|
|
|
|
pthread_mutex_lock (&id_rmutex);
|
|
ip_ad.s_addr = net_resolve (ip_a);
|
|
|
|
for (this = id_root; this != NULL; this = this->next) {
|
|
pthread_mutex_lock (&this->id_mutex);
|
|
if (memcmp (&this->ip, &ip_ad, sizeof (struct in_addr)) == 0) {
|
|
speed = this->id_speed;
|
|
}
|
|
pthread_mutex_unlock (&this->id_mutex);
|
|
}
|
|
pthread_mutex_unlock (&id_rmutex);
|
|
|
|
return (speed);
|
|
}
|
|
|
|
|
|
/* id_get
|
|
*
|
|
* return the last dns ID measured, with time pointed to by `tv'
|
|
* if `tv' is NULL, the timeval is not copied.
|
|
*
|
|
* return ID and copy timeval into *tv on success
|
|
* return 0 on failure
|
|
*/
|
|
|
|
u_short
|
|
id_get (char *ip, struct timeval *tv, unsigned long int *flags)
|
|
{
|
|
u_short id; /* id to return */
|
|
id_q *this; /* working pointer for queue */
|
|
int bc = 1; /* break condition */
|
|
struct in_addr ip_a;
|
|
|
|
/* lock queue mutex to sync all queue functions
|
|
*/
|
|
pthread_mutex_lock (&id_rmutex);
|
|
|
|
ip_a.s_addr = net_resolve (ip);
|
|
|
|
/* step through queue
|
|
*/
|
|
for (this = id_root; this != NULL && bc; this = this->next) {
|
|
pthread_mutex_lock (&this->id_mutex);
|
|
|
|
if (memcmp (&this->ip, &ip_a, sizeof (struct in_addr)) == 0) {
|
|
if (tv != NULL) {
|
|
memcpy (tv, &this->mtime, sizeof (struct timeval));
|
|
}
|
|
id = this->id;
|
|
*flags = this->flags;
|
|
bc = 0; /* break */
|
|
}
|
|
|
|
pthread_mutex_unlock (&this->id_mutex);
|
|
}
|
|
|
|
pthread_mutex_unlock (&id_rmutex);
|
|
|
|
return (bc == 0 ? (id) : 0);
|
|
}
|
|
|
|
|
|
/* id_qcleanup
|
|
*
|
|
* cleans up the whole id queue pointed to by `root', protected by `rm'.
|
|
*/
|
|
|
|
void
|
|
id_qcleanup (pthread_mutex_t *rm, id_q **root)
|
|
{
|
|
id_q *this;
|
|
|
|
pthread_mutex_lock (rm);
|
|
this = *root;
|
|
*root = NULL;
|
|
|
|
while (this != NULL) {
|
|
|
|
id_q *next;
|
|
|
|
/* lock, then destroy mutex
|
|
*/
|
|
pthread_mutex_lock (&this->id_mutex);
|
|
pthread_mutex_destroy (&this->id_mutex);
|
|
|
|
next = this->next;
|
|
id_free (this);
|
|
this = next;
|
|
}
|
|
|
|
pthread_mutex_unlock (rm);
|
|
|
|
return;
|
|
}
|
|
|