/* zodiac - advanced dns spoofer * * by scut / teso * * dns id queue handling routines * */ #define DNSID_MAIN #include #include #include #include #include #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; }