123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645 |
- /**************************************
- * AUTHOR: Federico Tomassini *
- * Copyright (C) Federico Tomassini *
- * Contact effetom@gmail.com *
- ***********************************************
- ******* BEGIN 3/2006 ********
- *************************************************************************
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. *
- * *
- * This program is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
- * GNU General Public License for more details. *
- * *
- ************************************************************************/
-
- #include "andns_lib.h"
- #include "andns_net.h"
- #include "log.h"
- #include "err_errno.h"
- #include "xmalloc.h"
-
- #include <arpa/inet.h>
- #include <zlib.h>
-
- int
- andns_compress(char *src, int srclen)
- {
- int res;
- uLongf space;
-
- src += ANDNS_HDR_SZ;
- srclen -= ANDNS_HDR_SZ;
- space = compressBound(srclen);
-
- unsigned char dst[space + ANDNS_HDR_Z];
-
- /* The first four bytes will store
- * the uncompressed size */
- res = compress2(dst + ANDNS_HDR_Z, &space, (u_char *) src, srclen,
- ANDNS_COMPR_LEVEL);
- if (res != Z_OK)
- err_ret(ERR_ZLIBCP, -1);
- if (space >= srclen - ANDNS_HDR_Z) /* We have to consider the four
- bytes too */
- err_ret(ERR_ZLIBNU, -1); /* This is a
- silent return */
- res = htonl(srclen);
- memcpy(dst, &res, ANDNS_HDR_Z);
- memcpy(src, dst, space);
-
- return (int) space;
- }
-
- char *
- andns_uncompress(char *src, int srclen, int *dstlen)
- {
- unsigned char *dst;
- uLongf space;
- int res;
- int c_len;
- const int hdrsz = ANDNS_HDR_SZ + ANDNS_HDR_Z;
-
- memcpy(&c_len, src + ANDNS_HDR_SZ, ANDNS_HDR_Z);
- c_len = ntohl(c_len);
- dst = xmalloc(c_len + ANDNS_HDR_SZ);
-
- space = c_len;
-
- res =
- uncompress(dst + ANDNS_HDR_SZ, &space, (u_char *) src + hdrsz,
- srclen - hdrsz);
- if (res != Z_OK) {
- xfree(dst);
- err_ret(ERR_ZLIBUP, NULL);
- }
- if ((int) space != c_len) {
- xfree(dst);
- err_ret(ERR_ANDMAP, NULL);
- }
-
- memcpy(dst, src, ANDNS_HDR_SZ);
- *dstlen = c_len + ANDNS_HDR_SZ;
-
- return (char *) dst;
- }
-
- /*
- * Takes the buffer stream and translate headers to
- * andns_pkt struct.
- * Returns ALWAYS 4. The pkt_len has to be controlled
- * elsewhere.
- */
- int
- a_hdr_u(char *buf, andns_pkt * ap)
- {
- uint8_t c;
- uint16_t s;
-
- ap->r = *(buf + 1) & 0x01;
- memcpy(&s, buf, 2);
- ap->id = ntohs(s);
- ap->id >>= 1;
- buf += 2;
-
- memcpy(&c, buf, sizeof(uint8_t));
- ap->qr = (c >> 7) & 0x01;
- ap->p = c & 0x40 ? ANDNS_PROTO_UDP : ANDNS_PROTO_TCP;
- ap->z = c & 0x20;
- ap->qtype = (c >> 3) & 0x03;
- ap->ancount = (c << 1) & 0x0e;
-
- buf++;
- memcpy(&c, buf, sizeof(uint8_t));
- if (((*buf) & 0x80))
- ap->ancount++;
-
- ap->ipv = (c >> 6) & 0x01;
- ap->nk = (c >> 4) & 0x03;
- ap->rcode = c & 0x0f;
- return ANDNS_HDR_SZ;
- }
-
- /*
- * Translate the andns_pkt question stream to andns_pkt struct.
- * -1 on error. Bytes readed otherwise.
- * NOTE: The qst-data size is controlled: apkt won't need
- * this control.
- */
- int
- a_qst_u(char *buf, andns_pkt * ap, int limitlen)
- {
- int ret;
- uint16_t s;
-
- if (limitlen < 3)
- err_ret(ERR_ANDMAP, -1);
-
- switch (ap->qtype) {
- case AT_A:
- memcpy(&s, buf, 2);
- ap->service = ntohs(s);
- buf += 2;
- if (ap->nk == NTK_REALM) {
- ap->qstlength = ANDNS_HASH_H;
- if (ap->qstlength > limitlen - 2)
- err_ret(ERR_ANDPLB, -1);
- AP_ALIGN(ap);
- memcpy(ap->qstdata, buf, ANDNS_HASH_H);
- ret = ANDNS_HASH_H + 2;
- } else if (ap->nk == INET_REALM) {
- memcpy(&s, buf, 2);
- ap->qstlength = ntohs(s);
- buf += 2;
- if (ap->qstlength >= ANDNS_MAX_QST_LEN ||
- ap->qstlength > limitlen - 4)
- err_ret(ERR_ANDPLB, -1);
- AP_ALIGN(ap);
- memcpy(ap->qstdata, buf, ap->qstlength);
- ret = ap->qstlength + 4;
- } else
- return -1;
- break;
- case AT_PTR:
- ap->qstlength = ap->ipv ? 16 : 4;
- if (ap->qstlength > limitlen)
- err_ret(ERR_ANDMAP, -1)
- AP_ALIGN(ap);
- memcpy(ap->qstdata, buf, ap->qstlength);
- ret = ap->qstlength;
- break;
- case AT_G:
- if (ap->nk != NTK_REALM)
- err_ret(ERR_ANDMAP, -1);
- ap->qstlength = ANDNS_HASH_H;
- if (ap->qstlength > limitlen)
- err_ret(ERR_ANDPLB, -1);
- AP_ALIGN(ap);
- memcpy(ap->qstdata, buf, ANDNS_HASH_H);
- ret = ap->qstlength;
- break;
- default:
- debug(DBG_INSANE, "In a_qst_u: unknow query type.");
- err_ret(ERR_ANDMAP, -1)
- }
- return ret;
- }
-
- int
- a_answ_u(char *buf, andns_pkt * ap, int limitlen)
- {
- uint16_t alen;
- andns_pkt_data *apd;
- int limit;
-
- if (limitlen < 3)
- err_ret(ERR_ANDMAP, -1);
- switch (ap->qtype) {
- case AT_A:
- limit = 2;
- if (limitlen < limit)
- err_ret(ERR_ANDPLB, -1);
- apd = andns_add_answ(ap);
- if (*buf & 0x40) {
- apd->m |= APD_IP;
- if (*buf & 0x80)
- apd->m |= APD_MAIN_IP;
- limit = ap->ipv ? 16 : 4;
- } else
- limit = ANDNS_HASH_H;
- if (limitlen < limit + 2)
- err_ret(ERR_ANDPLB, -1);
- apd->wg = (*buf & 0x3f);
- apd->prio = (*(buf + 1));
- apd->rdlength = limit;
- APD_ALIGN(apd);
- memcpy(apd->rdata, buf + 2, limit);
- limit += 2;
- break;
- case AT_PTR:
- memcpy(&alen, buf, 2);
- alen = ntohs(alen);
- if (alen + 2 > limitlen)
- err_ret(ERR_ANDPLB, -1);
- if (alen > ANDNS_MAX_DATA_LEN)
- err_ret(ERR_ANDPLB, -1);
- apd = andns_add_answ(ap);
- apd->rdlength = alen;
- APD_ALIGN(apd);
- memcpy(apd->rdata, buf + 2, alen);
- limit = alen + 2;
- break;
- case AT_G:
- if (limitlen < 8)
- err_ret(ERR_ANDMAP, -1);
- apd = andns_add_answ(ap);
- if (*buf & 0x40) {
- apd->m |= APD_IP;
- if (*buf & 0x80)
- apd->m |= APD_MAIN_IP;
- }
- apd->m |= *buf & 0x20 ? APD_UDP : APD_TCP;
- apd->wg = (*buf & 0x1f);
- apd->prio = (*(buf + 1));
- buf += 2;
- memcpy(&alen, buf, 2);
- apd->service = ntohs(alen);
- buf += 2;
- if (apd->m & APD_IP)
- apd->rdlength = (ap->ipv ? 16 : 4);
- else
- apd->rdlength = ANDNS_HASH_H;
- limit = 4 + apd->rdlength;
- if (limitlen < limit)
- err_ret(ERR_ANDPLB, -1);
- APD_ALIGN(apd);
- memcpy(apd->rdata, buf, apd->rdlength);
- break;
- default:
- err_ret(ERR_ANDMAP, -1);
- }
- return limit;
- }
-
- int
- a_answs_u(char *buf, andns_pkt * ap, int limitlen)
- {
- int ancount, i;
- int offset = 0, res;
- uint16_t alen;
-
- if (ap->qtype == AT_G) {
- memcpy(&alen, buf, sizeof(uint16_t));
- ap->ancount = ntohs(alen);
- offset += 2;
- }
- ancount = ap->ancount;
- for (i = 0; i < ancount; i++) {
- res = a_answ_u(buf + offset, ap, limitlen - offset);
- if (res == -1) {
- error(err_str);
- err_ret(ERR_ANDMAD, -1);
- }
- offset += res;
- }
- return offset;
- }
-
- /*
- * This is a main function: takes the pkt-buf and translate
- * it in structured data.
- * It cares about andns_pkt allocation.
- * The apkt is allocate here.
- *
- * Returns:
- * -1 on E_INTRPRT
- * 0 if pkt must be discarded.
- * Number of bytes readed otherwise
- */
- int
- a_u(char *buf, int pktlen, andns_pkt ** app)
- {
- andns_pkt *ap;
- int offset, res;
- int limitlen, u_len;
- char *u_buf;
-
- if (pktlen < ANDNS_HDR_SZ)
- err_ret(ERR_ANDPLB, 0);
- *app = ap = create_andns_pkt();
- offset = a_hdr_u(buf, ap);
-
- if (ap->z) { /* Controls the space to read
- uncompressed size */
- if (pktlen < ANDNS_HDR_SZ + ANDNS_HDR_Z) {
- destroy_andns_pkt(ap);
- err_ret(ERR_ANDPLB, 0);
- }
- if (!(u_buf = andns_uncompress(buf, pktlen, &u_len)))
- goto andmap;
- destroy_andns_pkt(ap);
- ANDNS_UNSET_Z(u_buf);
- res = a_u(u_buf, u_len, app);
- xfree(u_buf);
- return res;
- }
- buf += offset;
- limitlen = pktlen - offset;
- if ((res = a_qst_u(buf, ap, limitlen)) == -1)
- goto andmap;
- offset += res;
- if (!ap->ancount) /*No answers */
- return offset;
- buf += res;
- limitlen -= res;
- if ((res = a_answs_u(buf, ap, limitlen)) == -1)
- goto andmap;
- offset += res;
- if (offset != pktlen)
- error
- ("In a_u(): pktlen differs from readed contents: ID query %d.",
- ap->id);
- return offset;
- andmap:
- destroy_andns_pkt(ap);
- error(err_str);
- err_ret(ERR_ANDMAP, -1);
- }
-
- int
- a_hdr_p(andns_pkt * ap, char *buf)
- {
- uint16_t s;
- uint8_t an;
-
- ap->id <<= 1;
- s = htons(ap->id);
- memcpy(buf, &s, sizeof(uint16_t));
- if (ap->r)
- *(buf + 1) |= 0x01;
- else
- *(buf + 1) &= 0xfe;
- buf += 2;
- if (ap->qr)
- (*buf) |= 0x80;
- if (ap->p)
- (*buf) |= 0x40;
- if (ap->z)
- (*buf) |= 0x20;
- (*buf) |= ((ap->qtype) << 3);
- an = ap->ancount;
- (*buf++) |= ((an) >> 1);
- (*buf) |= ((ap->ancount) << 7);
- if (ap->ipv)
- *buf |= 0x40;
- (*buf) |= ((ap->nk) << 4);
- (*buf) |= (ap->rcode);
- return ANDNS_HDR_SZ;
- }
-
- int
- a_qst_p(andns_pkt * ap, char *buf, int limitlen)
- {
- int ret = 0;
- uint16_t s;
- int limit;
-
- switch (ap->qtype) {
- case AT_A:
- limit = ap->nk == NTK_REALM ? ANDNS_HASH_H + 2 : ap->qstlength + 4;
- if (limitlen < limit)
- err_ret(ERR_ANDMAD, -1);
- s = htons(ap->service);
- memcpy(buf, &s, 2);
- buf += 2; /* here INET and NTK REALM change */
- if (ap->nk == NTK_REALM) {
- memcpy(buf, ap->qstdata, ANDNS_HASH_H);
- ret = ANDNS_HASH_H + 2;
- } else if (ap->nk == INET_REALM) {
- s = htons(ap->qstlength);
- memcpy(buf, &s, 2);
- buf += 2;
- memcpy(buf, ap->qstdata, ap->qstlength);
- ret = ap->qstlength + 4;
- } else
- err_ret(ERR_ANDMAD, -1);
- break;
- case AT_PTR:
- limit = ap->ipv ? 16 : 4;
- if (limitlen < limit)
- err_ret(ERR_ANDMAD, -1);
- memcpy(buf, ap->qstdata, limit);
- ret = limit;
- break;
- case AT_G:
- limit = ANDNS_HASH_H;
- if (limitlen < limit)
- err_ret(ERR_ANDMAD, -1);
- memcpy(buf, ap->qstdata, ANDNS_HASH_H);
- ret = ANDNS_HASH_H;
- break;
- default:
- debug(DBG_INSANE, "In a_qst_p: unknow query type.");
- err_ret(ERR_ANDMAD, -1);
- break;
- }
- return ret;
- }
-
- int
- a_answ_p(andns_pkt * ap, andns_pkt_data * apd, char *buf, int limitlen)
- {
- uint16_t s;
- int limit;
- int ret;
-
- switch (ap->qtype) {
- case AT_A:
- limit = ap->ipv ? 16 : 4;
- if (limitlen < limit + 2)
- err_ret(ERR_ANDPLB, -1);
- if (apd->m)
- *buf |= 0x80;
- *buf++ |= (apd->wg & 0x7f);
- *buf++ |= apd->prio;
- memcpy(buf, apd->rdata, limit);
- ret = limit + 2;
- break;
- case AT_PTR:
- if (limitlen < apd->rdlength + 2)
- err_ret(ERR_ANDPLB, -1);
- s = htons(apd->rdlength);
- memcpy(buf, &s, sizeof(uint16_t));
- buf += 2;
- memcpy(buf, apd->rdata, apd->rdlength);
- ret = apd->rdlength + 2;
- break;
- case AT_G: /* TODO */
- if (limitlen < 4)
- err_ret(ERR_ANDPLB, -1);
- if (apd->m == 1)
- (*buf) |= 0xc0;
- else if (apd->m)
- (*buf) |= 0x40;
- *buf++ |= (apd->wg & 0x3f);
- *buf++ |= apd->prio;
- s = htons(apd->service);
- memcpy(buf, &s, 2);
- if (apd->m) {
- limit = ap->ipv ? 16 : 4;
- if (limitlen < limit + 4)
- err_ret(ERR_ANDPLB, -1);
- memcpy(buf, apd->rdata, limit);
- ret = limit + 4;
- } else {
- limit = strlen(apd->rdata);
- if (limitlen < limit + 6)
- err_ret(ERR_ANDPLB, -1);
- s = htons(limit);
- memcpy(buf, &s, 2);
- buf += 2;
- memcpy(buf, apd->rdata, limit);
- ret = limit + 6;
- }
- break;
- default:
- debug(DBG_INSANE, "In a_answ_p(): unknow query type.");
- err_ret(ERR_ANDMAD, -1);
- break;
- }
- return ret;
- }
-
- int
- a_answs_p(andns_pkt * ap, char *buf, int limitlen)
- {
- andns_pkt_data *apd;
- int i;
- int offset = 0, res;
- uint16_t s;
-
- if (ap->qtype == AT_G) {
- if (limitlen < 2)
- err_ret(ERR_ANDPLB, -1);
- s = htons(ap->ancount);
- memcpy(buf, &s, 2);
- offset += 2;
- }
- apd = ap->pkt_answ;
- for (i = 0; i < ap->ancount && apd; i++) {
- if ((res =
- a_answ_p(ap, apd, buf + offset, limitlen - offset)) == -1) {
- error(err_str);
- err_ret(ERR_ANDMAD, -1);
- }
- offset += res;
- apd = apd->next;
- }
- return offset;
- }
-
- int
- a_p(andns_pkt * ap, char *buf)
- {
- int offset, res;
-
- memset(buf, 0, ANDNS_MAX_SZ);
-
- offset = a_hdr_p(ap, buf);
- buf += offset;
- if ((res = a_qst_p(ap, buf, ANDNS_MAX_SZ - offset)) == -1)
- goto server_fail;
- offset += res;
- buf += res;
- if (ap->ancount) {
- if ((res = a_answs_p(ap, buf, ANDNS_MAX_SZ - offset)) == -1)
- goto server_fail;
- offset += res;
- }
- destroy_andns_pkt(ap);
- /* Compression */
- if (offset > ANDNS_COMPR_THRESHOLD) {
- res = andns_compress(buf, offset);
- if (res == -1)
- error(err_str);
- else
- return res;
- }
- /* end compression */
- return offset;
- server_fail:
- destroy_andns_pkt(ap);
- error(err_str);
- err_ret(ERR_ANDMAD, -1);
- }
-
-
- /* MEM */
-
- /* Borning functions */
- andns_pkt *
- create_andns_pkt(void)
- {
- andns_pkt *ap;
- ap = xmalloc(ANDNS_PKT_SZ);
- memset(ap, 0, ANDNS_PKT_SZ);
- return ap;
- }
-
- andns_pkt_data *
- create_andns_pkt_data(void)
- {
- andns_pkt_data *apd;
- apd = xmalloc(ANDNS_PKT_DATA_SZ);
- memset(apd, 0, ANDNS_PKT_DATA_SZ);
- return apd;
- }
-
- andns_pkt_data *
- andns_add_answ(andns_pkt * ap)
- {
- andns_pkt_data *apd, *a;
-
- apd = create_andns_pkt_data();
- a = ap->pkt_answ;
- if (!a) {
- ap->pkt_answ = apd;
- return apd;
- }
- while (a->next)
- a = a->next;
- a->next = apd;
- return apd;
- }
-
- /* Death functions */
- void
- destroy_andns_pkt_data(andns_pkt_data * apd)
- {
- if (apd->rdata)
- xfree(apd->rdata);
- xfree(apd);
- }
-
- void
- andns_del_answ(andns_pkt * ap)
- {
- andns_pkt_data *apd, *apdt;
-
- apd = ap->pkt_answ;
- if (!apd)
- return;
- apdt = apd->next;
- while (apdt) {
- apd = apdt;
- apdt = apdt->next;
- }
- apd->next = NULL;
- destroy_andns_pkt_data(apdt);
- }
-
- void
- destroy_andns_pkt_datas(andns_pkt * ap)
- {
- andns_pkt_data *apd, *apd_t;
- apd = ap->pkt_answ;
- while (apd) {
- apd_t = apd->next;
- destroy_andns_pkt_data(apd);
- apd = apd_t;
- }
- }
-
- void
- destroy_andns_pkt(andns_pkt * ap)
- {
- if (ap->qstdata)
- xfree(ap->qstdata);
- destroy_andns_pkt_datas(ap);
- xfree(ap);
- }
|