netsukuku/src/andns_lib.c
Alexander von Gluck IV e264723db2 all: Automatic style cleanup, KnR. No functional change.
* Style varied wildly between files and within files.
* find . -name "*.c" -exec indent {} -ut -ts4 -kr -psl \;
* find . -name "*.h" -exec indent {} -ut -ts4 -kr -psl \;
2014-09-17 07:41:24 -05:00

649 lines
14 KiB
C

/**************************************
* 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;
char *start_buf;
start_buf = buf;
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);
}