Skip to content
Snippets Groups Projects
Commit 63855cf6 authored by Jan Včelák's avatar Jan Včelák :rocket:
Browse files

Merge 'DNS Cookies'

MR !540
parents bda7104e 51f2af1b
No related branches found
No related tags found
No related merge requests found
Showing
with 1503 additions and 55 deletions
......@@ -44,6 +44,9 @@ libcontrib_la_SOURCES = \
contrib/endian.h \
contrib/files.c \
contrib/files.h \
contrib/fnv/fnv.h \
contrib/fnv/hash_64a.c \
contrib/fnv/longlong.h \
contrib/getline.c \
contrib/getline.h \
contrib/hhash.c \
......@@ -102,6 +105,9 @@ nobase_libknot_la_HEADERS = \
libknot/codes.h \
libknot/consts.h \
libknot/control/control.h \
libknot/cookies/alg-fnv64.h \
libknot/cookies/client.h \
libknot/cookies/server.h \
libknot/descriptor.h \
libknot/dname.h \
libknot/errcode.h \
......@@ -128,6 +134,7 @@ nobase_libknot_la_HEADERS = \
libknot/rrtype/nsec3.h \
libknot/rrtype/nsec3param.h \
libknot/rrtype/opt.h \
libknot/rrtype/opt-cookie.h \
libknot/rrtype/rdname.h \
libknot/rrtype/rrsig.h \
libknot/rrtype/soa.h \
......@@ -142,6 +149,9 @@ libknot_la_SOURCES = \
libknot/binary.c \
libknot/codes.c \
libknot/control/control.c \
libknot/cookies/alg-fnv64.c \
libknot/cookies/client.c \
libknot/cookies/server.c \
libknot/descriptor.c \
libknot/dname.c \
libknot/error.c \
......@@ -162,6 +172,7 @@ libknot_la_SOURCES = \
libknot/rrtype/nsec3.c \
libknot/rrtype/nsec3param.c \
libknot/rrtype/opt.c \
libknot/rrtype/opt-cookie.c \
libknot/rrtype/rrsig.c \
libknot/rrtype/soa.c \
libknot/rrtype/tsig.c \
......
/*
* fnv - Fowler/Noll/Vo- hash code
*
* @(#) $Revision: 5.4 $
* @(#) $Id: fnv.h,v 5.4 2009/07/30 22:49:13 chongo Exp $
* @(#) $Source: /usr/local/src/cmd/fnv/RCS/fnv.h,v $
*
***
*
* Fowler/Noll/Vo- hash
*
* The basis of this hash algorithm was taken from an idea sent
* as reviewer comments to the IEEE POSIX P1003.2 committee by:
*
* Phong Vo (http://www.research.att.com/info/kpv/)
* Glenn Fowler (http://www.research.att.com/~gsf/)
*
* In a subsequent ballot round:
*
* Landon Curt Noll (http://www.isthe.com/chongo/)
*
* improved on their algorithm. Some people tried this hash
* and found that it worked rather well. In an EMail message
* to Landon, they named it the ``Fowler/Noll/Vo'' or FNV hash.
*
* FNV hashes are designed to be fast while maintaining a low
* collision rate. The FNV speed allows one to quickly hash lots
* of data while maintaining a reasonable collision rate. See:
*
* http://www.isthe.com/chongo/tech/comp/fnv/index.html
*
* for more details as well as other forms of the FNV hash.
*
***
*
* NOTE: The FNV-0 historic hash is not recommended. One should use
* the FNV-1 hash instead.
*
* To use the 32 bit FNV-0 historic hash, pass FNV0_32_INIT as the
* Fnv32_t hashval argument to fnv_32_buf() or fnv_32_str().
*
* To use the 64 bit FNV-0 historic hash, pass FNV0_64_INIT as the
* Fnv64_t hashval argument to fnv_64_buf() or fnv_64_str().
*
* To use the recommended 32 bit FNV-1 hash, pass FNV1_32_INIT as the
* Fnv32_t hashval argument to fnv_32_buf() or fnv_32_str().
*
* To use the recommended 64 bit FNV-1 hash, pass FNV1_64_INIT as the
* Fnv64_t hashval argument to fnv_64_buf() or fnv_64_str().
*
* To use the recommended 32 bit FNV-1a hash, pass FNV1_32A_INIT as the
* Fnv32_t hashval argument to fnv_32a_buf() or fnv_32a_str().
*
* To use the recommended 64 bit FNV-1a hash, pass FNV1A_64_INIT as the
* Fnv64_t hashval argument to fnv_64a_buf() or fnv_64a_str().
*
***
*
* Please do not copyright this code. This code is in the public domain.
*
* LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
* EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*
* By:
* chongo <Landon Curt Noll> /\oo/\
* http://www.isthe.com/chongo/
*
* Share and Enjoy! :-)
*/
#if !defined(__FNV_H__)
#define __FNV_H__
#include <sys/types.h>
#define FNV_VERSION "5.0.2" /* @(#) FNV Version */
/*
* 32 bit FNV-0 hash type
*/
typedef u_int32_t Fnv32_t;
/*
* 32 bit FNV-0 zero initial basis
*
* This historic hash is not recommended. One should use
* the FNV-1 hash and initial basis instead.
*/
#define FNV0_32_INIT ((Fnv32_t)0)
/*
* 32 bit FNV-1 and FNV-1a non-zero initial basis
*
* The FNV-1 initial basis is the FNV-0 hash of the following 32 octets:
*
* chongo <Landon Curt Noll> /\../\
*
* NOTE: The \'s above are not back-slashing escape characters.
* They are literal ASCII backslash 0x5c characters.
*
* NOTE: The FNV-1a initial basis is the same value as FNV-1 by definition.
*/
#define FNV1_32_INIT ((Fnv32_t)0x811c9dc5)
#define FNV1_32A_INIT FNV1_32_INIT
/*
* determine how 64 bit unsigned values are represented
*/
#include "longlong.h"
/*
* 64 bit FNV-0 hash
*/
#if defined(HAVE_64BIT_LONG_LONG)
typedef u_int64_t Fnv64_t;
#else /* HAVE_64BIT_LONG_LONG */
typedef struct {
u_int32_t w32[2]; /* w32[0] is low order, w32[1] is high order word */
} Fnv64_t;
#endif /* HAVE_64BIT_LONG_LONG */
/*
* 64 bit FNV-0 zero initial basis
*
* This historic hash is not recommended. One should use
* the FNV-1 hash and initial basis instead.
*/
#if defined(HAVE_64BIT_LONG_LONG)
#define FNV0_64_INIT ((Fnv64_t)0)
#else /* HAVE_64BIT_LONG_LONG */
extern const Fnv64_t fnv0_64_init;
#define FNV0_64_INIT (fnv0_64_init)
#endif /* HAVE_64BIT_LONG_LONG */
/*
* 64 bit FNV-1 non-zero initial basis
*
* The FNV-1 initial basis is the FNV-0 hash of the following 32 octets:
*
* chongo <Landon Curt Noll> /\../\
*
* NOTE: The \'s above are not back-slashing escape characters.
* They are literal ASCII backslash 0x5c characters.
*
* NOTE: The FNV-1a initial basis is the same value as FNV-1 by definition.
*/
#if defined(HAVE_64BIT_LONG_LONG)
#define FNV1_64_INIT ((Fnv64_t)0xcbf29ce484222325ULL)
#define FNV1A_64_INIT FNV1_64_INIT
#else /* HAVE_64BIT_LONG_LONG */
extern const fnv1_64_init;
extern const Fnv64_t fnv1a_64_init;
#define FNV1_64_INIT (fnv1_64_init)
#define FNV1A_64_INIT (fnv1a_64_init)
#endif /* HAVE_64BIT_LONG_LONG */
/*
* hash types
*/
enum fnv_type {
FNV_NONE = 0, /* invalid FNV hash type */
FNV0_32 = 1, /* FNV-0 32 bit hash */
FNV1_32 = 2, /* FNV-1 32 bit hash */
FNV1a_32 = 3, /* FNV-1a 32 bit hash */
FNV0_64 = 4, /* FNV-0 64 bit hash */
FNV1_64 = 5, /* FNV-1 64 bit hash */
FNV1a_64 = 6, /* FNV-1a 64 bit hash */
};
/*
* these test vectors are used as part o the FNV test suite
*/
struct test_vector {
void *buf; /* start of test vector buffer */
int len; /* length of test vector */
};
struct fnv0_32_test_vector {
struct test_vector *test; /* test vector buffer to hash */
Fnv32_t fnv0_32; /* expected FNV-0 32 bit hash value */
};
struct fnv1_32_test_vector {
struct test_vector *test; /* test vector buffer to hash */
Fnv32_t fnv1_32; /* expected FNV-1 32 bit hash value */
};
struct fnv1a_32_test_vector {
struct test_vector *test; /* test vector buffer to hash */
Fnv32_t fnv1a_32; /* expected FNV-1a 32 bit hash value */
};
struct fnv0_64_test_vector {
struct test_vector *test; /* test vector buffer to hash */
Fnv64_t fnv0_64; /* expected FNV-0 64 bit hash value */
};
struct fnv1_64_test_vector {
struct test_vector *test; /* test vector buffer to hash */
Fnv64_t fnv1_64; /* expected FNV-1 64 bit hash value */
};
struct fnv1a_64_test_vector {
struct test_vector *test; /* test vector buffer to hash */
Fnv64_t fnv1a_64; /* expected FNV-1a 64 bit hash value */
};
/*
* external functions
*/
/* hash_32.c */
extern Fnv32_t fnv_32_buf(void *buf, size_t len, Fnv32_t hashval);
extern Fnv32_t fnv_32_str(char *buf, Fnv32_t hashval);
/* hash_32a.c */
extern Fnv32_t fnv_32a_buf(void *buf, size_t len, Fnv32_t hashval);
extern Fnv32_t fnv_32a_str(char *buf, Fnv32_t hashval);
/* hash_64.c */
extern Fnv64_t fnv_64_buf(void *buf, size_t len, Fnv64_t hashval);
extern Fnv64_t fnv_64_str(char *buf, Fnv64_t hashval);
/* hash_64a.c */
extern Fnv64_t fnv_64a_buf(void *buf, size_t len, Fnv64_t hashval);
extern Fnv64_t fnv_64a_str(char *buf, Fnv64_t hashval);
/* test_fnv.c */
extern struct test_vector fnv_test_str[];
extern struct fnv0_32_test_vector fnv0_32_vector[];
extern struct fnv1_32_test_vector fnv1_32_vector[];
extern struct fnv1a_32_test_vector fnv1a_32_vector[];
extern struct fnv0_64_test_vector fnv0_64_vector[];
extern struct fnv1_64_test_vector fnv1_64_vector[];
extern struct fnv1a_64_test_vector fnv1a_64_vector[];
extern void unknown_hash_type(char *prog, enum fnv_type type, int code);
extern void print_fnv32(Fnv32_t hval, Fnv32_t mask, int verbose, char *arg);
extern void print_fnv64(Fnv64_t hval, Fnv64_t mask, int verbose, char *arg);
#endif /* __FNV_H__ */
/*
* hash_64 - 64 bit Fowler/Noll/Vo-0 FNV-1a hash code
*
* @(#) $Revision: 5.1 $
* @(#) $Id: hash_64a.c,v 5.1 2009/06/30 09:01:38 chongo Exp $
* @(#) $Source: /usr/local/src/cmd/fnv/RCS/hash_64a.c,v $
*
***
*
* Fowler/Noll/Vo hash
*
* The basis of this hash algorithm was taken from an idea sent
* as reviewer comments to the IEEE POSIX P1003.2 committee by:
*
* Phong Vo (http://www.research.att.com/info/kpv/)
* Glenn Fowler (http://www.research.att.com/~gsf/)
*
* In a subsequent ballot round:
*
* Landon Curt Noll (http://www.isthe.com/chongo/)
*
* improved on their algorithm. Some people tried this hash
* and found that it worked rather well. In an EMail message
* to Landon, they named it the ``Fowler/Noll/Vo'' or FNV hash.
*
* FNV hashes are designed to be fast while maintaining a low
* collision rate. The FNV speed allows one to quickly hash lots
* of data while maintaining a reasonable collision rate. See:
*
* http://www.isthe.com/chongo/tech/comp/fnv/index.html
*
* for more details as well as other forms of the FNV hash.
*
***
*
* To use the recommended 64 bit FNV-1a hash, pass FNV1A_64_INIT as the
* Fnv64_t hashval argument to fnv_64a_buf() or fnv_64a_str().
*
***
*
* Please do not copyright this code. This code is in the public domain.
*
* LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
* EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*
* By:
* chongo <Landon Curt Noll> /\oo/\
* http://www.isthe.com/chongo/
*
* Share and Enjoy! :-)
*/
#include <stdlib.h>
#include "fnv.h"
/*
* FNV-1a defines the initial basis to be non-zero
*/
#if !defined(HAVE_64BIT_LONG_LONG)
const Fnv64_t fnv1a_64_init = { 0x84222325, 0xcbf29ce4 };
#endif /* ! HAVE_64BIT_LONG_LONG */
/*
* 64 bit magic FNV-1a prime
*/
#if defined(HAVE_64BIT_LONG_LONG)
#define FNV_64_PRIME ((Fnv64_t)0x100000001b3ULL)
#else /* HAVE_64BIT_LONG_LONG */
#define FNV_64_PRIME_LOW ((unsigned long)0x1b3) /* lower bits of FNV prime */
#define FNV_64_PRIME_SHIFT (8) /* top FNV prime shift above 2^32 */
#endif /* HAVE_64BIT_LONG_LONG */
/*
* fnv_64a_buf - perform a 64 bit Fowler/Noll/Vo FNV-1a hash on a buffer
*
* input:
* buf - start of buffer to hash
* len - length of buffer in octets
* hval - previous hash value or 0 if first call
*
* returns:
* 64 bit hash as a static hash type
*
* NOTE: To use the recommended 64 bit FNV-1a hash, use FNV1A_64_INIT as the
* hval arg on the first call to either fnv_64a_buf() or fnv_64a_str().
*/
Fnv64_t
fnv_64a_buf(void *buf, size_t len, Fnv64_t hval)
{
unsigned char *bp = (unsigned char *)buf; /* start of buffer */
unsigned char *be = bp + len; /* beyond end of buffer */
#if defined(HAVE_64BIT_LONG_LONG)
/*
* FNV-1a hash each octet of the buffer
*/
while (bp < be) {
/* xor the bottom with the current octet */
hval ^= (Fnv64_t)*bp++;
/* multiply by the 64 bit FNV magic prime mod 2^64 */
#if defined(NO_FNV_GCC_OPTIMIZATION)
hval *= FNV_64_PRIME;
#else /* NO_FNV_GCC_OPTIMIZATION */
hval += (hval << 1) + (hval << 4) + (hval << 5) +
(hval << 7) + (hval << 8) + (hval << 40);
#endif /* NO_FNV_GCC_OPTIMIZATION */
}
#else /* HAVE_64BIT_LONG_LONG */
unsigned long val[4]; /* hash value in base 2^16 */
unsigned long tmp[4]; /* tmp 64 bit value */
/*
* Convert Fnv64_t hval into a base 2^16 array
*/
val[0] = hval.w32[0];
val[1] = (val[0] >> 16);
val[0] &= 0xffff;
val[2] = hval.w32[1];
val[3] = (val[2] >> 16);
val[2] &= 0xffff;
/*
* FNV-1a hash each octet of the buffer
*/
while (bp < be) {
/* xor the bottom with the current octet */
val[0] ^= (unsigned long)*bp++;
/*
* multiply by the 64 bit FNV magic prime mod 2^64
*
* Using 0x100000001b3 we have the following digits base 2^16:
*
* 0x0 0x100 0x0 0x1b3
*
* which is the same as:
*
* 0x0 1<<FNV_64_PRIME_SHIFT 0x0 FNV_64_PRIME_LOW
*/
/* multiply by the lowest order digit base 2^16 */
tmp[0] = val[0] * FNV_64_PRIME_LOW;
tmp[1] = val[1] * FNV_64_PRIME_LOW;
tmp[2] = val[2] * FNV_64_PRIME_LOW;
tmp[3] = val[3] * FNV_64_PRIME_LOW;
/* multiply by the other non-zero digit */
tmp[2] += val[0] << FNV_64_PRIME_SHIFT; /* tmp[2] += val[0] * 0x100 */
tmp[3] += val[1] << FNV_64_PRIME_SHIFT; /* tmp[3] += val[1] * 0x100 */
/* propagate carries */
tmp[1] += (tmp[0] >> 16);
val[0] = tmp[0] & 0xffff;
tmp[2] += (tmp[1] >> 16);
val[1] = tmp[1] & 0xffff;
val[3] = tmp[3] + (tmp[2] >> 16);
val[2] = tmp[2] & 0xffff;
/*
* Doing a val[3] &= 0xffff; is not really needed since it simply
* removes multiples of 2^64. We can discard these excess bits
* outside of the loop when we convert to Fnv64_t.
*/
}
/*
* Convert base 2^16 array back into an Fnv64_t
*/
hval.w32[1] = ((val[3]<<16) | val[2]);
hval.w32[0] = ((val[1]<<16) | val[0]);
#endif /* HAVE_64BIT_LONG_LONG */
/* return our new hash value */
return hval;
}
/*
* fnv_64a_str - perform a 64 bit Fowler/Noll/Vo FNV-1a hash on a buffer
*
* input:
* buf - start of buffer to hash
* hval - previous hash value or 0 if first call
*
* returns:
* 64 bit hash as a static hash type
*
* NOTE: To use the recommended 64 bit FNV-1a hash, use FNV1A_64_INIT as the
* hval arg on the first call to either fnv_64a_buf() or fnv_64a_str().
*/
Fnv64_t
fnv_64a_str(char *str, Fnv64_t hval)
{
unsigned char *s = (unsigned char *)str; /* unsigned string */
#if defined(HAVE_64BIT_LONG_LONG)
/*
* FNV-1a hash each octet of the string
*/
while (*s) {
/* xor the bottom with the current octet */
hval ^= (Fnv64_t)*s++;
/* multiply by the 64 bit FNV magic prime mod 2^64 */
#if defined(NO_FNV_GCC_OPTIMIZATION)
hval *= FNV_64_PRIME;
#else /* NO_FNV_GCC_OPTIMIZATION */
hval += (hval << 1) + (hval << 4) + (hval << 5) +
(hval << 7) + (hval << 8) + (hval << 40);
#endif /* NO_FNV_GCC_OPTIMIZATION */
}
#else /* !HAVE_64BIT_LONG_LONG */
unsigned long val[4]; /* hash value in base 2^16 */
unsigned long tmp[4]; /* tmp 64 bit value */
/*
* Convert Fnv64_t hval into a base 2^16 array
*/
val[0] = hval.w32[0];
val[1] = (val[0] >> 16);
val[0] &= 0xffff;
val[2] = hval.w32[1];
val[3] = (val[2] >> 16);
val[2] &= 0xffff;
/*
* FNV-1a hash each octet of the string
*/
while (*s) {
/* xor the bottom with the current octet */
/*
* multiply by the 64 bit FNV magic prime mod 2^64
*
* Using 1099511628211, we have the following digits base 2^16:
*
* 0x0 0x100 0x0 0x1b3
*
* which is the same as:
*
* 0x0 1<<FNV_64_PRIME_SHIFT 0x0 FNV_64_PRIME_LOW
*/
/* multiply by the lowest order digit base 2^16 */
tmp[0] = val[0] * FNV_64_PRIME_LOW;
tmp[1] = val[1] * FNV_64_PRIME_LOW;
tmp[2] = val[2] * FNV_64_PRIME_LOW;
tmp[3] = val[3] * FNV_64_PRIME_LOW;
/* multiply by the other non-zero digit */
tmp[2] += val[0] << FNV_64_PRIME_SHIFT; /* tmp[2] += val[0] * 0x100 */
tmp[3] += val[1] << FNV_64_PRIME_SHIFT; /* tmp[3] += val[1] * 0x100 */
/* propagate carries */
tmp[1] += (tmp[0] >> 16);
val[0] = tmp[0] & 0xffff;
tmp[2] += (tmp[1] >> 16);
val[1] = tmp[1] & 0xffff;
val[3] = tmp[3] + (tmp[2] >> 16);
val[2] = tmp[2] & 0xffff;
/*
* Doing a val[3] &= 0xffff; is not really needed since it simply
* removes multiples of 2^64. We can discard these excess bits
* outside of the loop when we convert to Fnv64_t.
*/
val[0] ^= (unsigned long)(*s++);
}
/*
* Convert base 2^16 array back into an Fnv64_t
*/
hval.w32[1] = ((val[3]<<16) | val[2]);
hval.w32[0] = ((val[1]<<16) | val[0]);
#endif /* !HAVE_64BIT_LONG_LONG */
/* return our new hash value */
return hval;
}
/*
* DO NOT EDIT -- generated by the Makefile
*/
#if !defined(__LONGLONG_H__)
#define __LONGLONG_H__
/* do we have/want to use a long long type? */
#define HAVE_64BIT_LONG_LONG /* yes */
/*
* NO64BIT_LONG_LONG undef HAVE_64BIT_LONG_LONG
*/
#if defined(NO64BIT_LONG_LONG)
#undef HAVE_64BIT_LONG_LONG
#endif /* NO64BIT_LONG_LONG */
#endif /* !__LONGLONG_H__ */
......@@ -181,20 +181,6 @@ static inline bool wire_ctx_can_write(wire_ctx_t *ctx, size_t size)
return true;
}
/*!
* \brief Set whole data to zero.
*
* \note Noop if previous error.
*/
static inline void wire_ctx_clear(wire_ctx_t *ctx)
{
assert(ctx);
if (wire_ctx_can_write(ctx, 0)) {
memset(ctx->wire, 0, ctx->size);
}
}
static inline uint8_t wire_ctx_read_u8(wire_ctx_t *ctx)
{
assert(ctx);
......@@ -411,3 +397,19 @@ static inline void wire_ctx_write(wire_ctx_t *ctx, const uint8_t *data, size_t s
memcpy(ctx->position, data, size);
ctx->position += size;
}
static inline void wire_ctx_clear(wire_ctx_t *ctx, size_t size)
{
assert(ctx);
if (ctx->error != KNOT_EOK) {
return;
}
if (!wire_ctx_can_write(ctx, size)) {
return;
}
memset(ctx->position, 0, size);
ctx->position += size;
}
......@@ -42,6 +42,7 @@ const knot_lookup_t knot_rcode_names[] = {
{ KNOT_RCODE_NOTAUTH, "NOTAUTH" },
{ KNOT_RCODE_NOTZONE, "NOTZONE" },
{ KNOT_RCODE_BADVERS, "BADVERS" },
{ KNOT_RCODE_BADCOOKIE, "BADCOOKIE" },
{ 0, NULL }
};
......
......@@ -86,7 +86,8 @@ typedef enum {
KNOT_RCODE_NXRRSET = 8, /*!< RR set does not exist. */
KNOT_RCODE_NOTAUTH = 9, /*!< Server not authoritative. / Query not authorized. */
KNOT_RCODE_NOTZONE = 10, /*!< Name is not inside zone. */
KNOT_RCODE_BADVERS = 16 /*!< Bad OPT Version. */
KNOT_RCODE_BADVERS = 16, /*!< Bad OPT Version. */
KNOT_RCODE_BADCOOKIE = 23 /*!< Bad/missing server cookie. */
} knot_rcode_t;
/*!
......
/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
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 3 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.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "contrib/fnv/fnv.h"
#include "contrib/sockaddr.h"
#include "libknot/attribute.h"
#include "libknot/cookies/alg-fnv64.h"
#include "libknot/rrtype/opt-cookie.h"
#include "libknot/errcode.h"
/*!
* \brief Update hash value.
*
* \param hash_val Hash value to be updated.
* \param sa Socket address.
*/
static inline void update_hash(Fnv64_t *hash_val, const struct sockaddr *sa)
{
assert(hash_val && sa);
size_t addr_len = 0;
void *addr = sockaddr_raw(sa, &addr_len);
if (addr) {
assert(addr_len);
*hash_val = fnv_64a_buf(addr, addr_len, *hash_val);
}
}
/*!
* \brief Compute client cookie using FNV-64.
*
* \note At least one input address must be provided.
*
* \param[in] input Input parameters.
* \param[in] cc_out Buffer for computed client cookie.
* \param[in,out] cc_len Size of buffer/written data.
*
* \return KNOT_EOK on success, error code else.
*/
static int cc_gen_fnv64(const struct knot_cc_input *input,
uint8_t *cc_out, uint16_t *cc_len)
{
if (!input || !cc_out || !cc_len || *cc_len < KNOT_OPT_COOKIE_CLNT) {
return KNOT_EINVAL;
}
if ((!input->clnt_sockaddr && !input->srvr_sockaddr) ||
!(input->secret_data && input->secret_len)) {
return KNOT_EINVAL;
}
Fnv64_t hash_val = FNV1A_64_INIT;
if (input->clnt_sockaddr) {
update_hash(&hash_val, input->clnt_sockaddr);
}
if (input->srvr_sockaddr) {
update_hash(&hash_val, input->srvr_sockaddr);
}
hash_val = fnv_64a_buf((void *)input->secret_data, input->secret_len,
hash_val);
assert(KNOT_OPT_COOKIE_CLNT == sizeof(hash_val));
*cc_len = KNOT_OPT_COOKIE_CLNT;
memcpy(cc_out, &hash_val, *cc_len);
return KNOT_EOK;
}
#define SRVR_FNV64_HASH_SIZE 8
/*!
* \brief Compute server cookie hash using FNV-64.
*
* Server cookie = nonce | FNV-64(client IP | nonce | client cookie | server secret)
*
* \note This function computes only the hash value.
*
* \param[in] input Data to compute cookie from.
* \param[in] hash_out Buffer to write the resulting hash data into.
* \param[in,out] hash_len On input set to hash buffer size. On successful
* return contains size of written hash.
*
* \return KNOT_EOK or error code.
*/
static int sc_gen_fnv64(const struct knot_sc_input *input,
uint8_t *hash_out, uint16_t *hash_len)
{
if (!input || !hash_out || !hash_len || (*hash_len < SRVR_FNV64_HASH_SIZE)) {
return KNOT_EINVAL;
}
if (!input->cc || !input->cc_len || !input->srvr_data ||
!input->srvr_data->secret_data || !input->srvr_data->secret_len) {
return KNOT_EINVAL;
}
Fnv64_t hash_val = FNV1A_64_INIT;
if (input->srvr_data->clnt_sockaddr) {
update_hash(&hash_val, input->srvr_data->clnt_sockaddr);
}
if (input->nonce && input->nonce_len) {
hash_val = fnv_64a_buf((void *)input->nonce, input->nonce_len, hash_val);
}
hash_val = fnv_64a_buf((void *)input->cc, input->cc_len, hash_val);
hash_val = fnv_64a_buf((void *)input->srvr_data->secret_data,
input->srvr_data->secret_len, hash_val);
*hash_len = sizeof(hash_val);
memcpy(hash_out, &hash_val, *hash_len);
assert(SRVR_FNV64_HASH_SIZE == *hash_len);
return KNOT_EOK;
}
_public_
const struct knot_cc_alg knot_cc_alg_fnv64 = {
KNOT_OPT_COOKIE_CLNT,
cc_gen_fnv64
};
_public_
const struct knot_sc_alg knot_sc_alg_fnv64 = {
SRVR_FNV64_HASH_SIZE,
sc_gen_fnv64
};
/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
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 3 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.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "libknot/cookies/client.h"
#include "libknot/cookies/server.h"
/*!
* \brief FNV-64 client cookie algorithm.
*/
extern const struct knot_cc_alg knot_cc_alg_fnv64;
/*!
* \brief FNV-64 server hash algorithm.
*
* \note The algorithm expects a nonce value, time stamp and hash value.
*/
extern const struct knot_sc_alg knot_sc_alg_fnv64;
/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
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 3 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.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include "libknot/attribute.h"
#include "libknot/cookies/client.h"
#include "libknot/errcode.h"
#include "libknot/rrtype/opt-cookie.h"
_public_
int knot_cc_check(const uint8_t *cc, uint16_t cc_len,
const struct knot_cc_input *input,
const struct knot_cc_alg *cc_alg)
{
if (!cc || !cc_len || !input ||
!cc_alg || !cc_alg->cc_size || !cc_alg->gen_func) {
return KNOT_EINVAL;
}
if (cc_alg->cc_size > KNOT_OPT_COOKIE_CLNT) {
return KNOT_EINVAL;
}
uint8_t generated_cc[KNOT_OPT_COOKIE_CLNT] = { 0 };
uint16_t generated_cc_len = KNOT_OPT_COOKIE_CLNT;
int ret = cc_alg->gen_func(input, generated_cc, &generated_cc_len);
if (ret != KNOT_EOK) {
return ret;
}
if (generated_cc_len != cc_len) {
return KNOT_EINVAL;
}
ret = memcmp(cc, generated_cc, generated_cc_len);
if (ret != 0) {
return KNOT_EINVAL;
}
return KNOT_EOK;
}
/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
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 3 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.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
#include <stdlib.h>
/*!
* \brief Input data needed to compute the client cookie value.
*/
struct knot_cc_input {
const struct sockaddr *clnt_sockaddr; /*!< Client (local) socket address. */
const struct sockaddr *srvr_sockaddr; /*!< Server (remote) socket address. */
const uint8_t *secret_data; /*!< Client secret data. */
size_t secret_len; /*!< Secret data length. */
};
/*!
* \brief Client cookie generator function type.
*
* \param[in] input Data which to generate the cookie from.
* \param[in] cc_out Buffer to write the resulting client cookie data into.
* \param[in,out] cc_len On input set to cookie buffer size. On successful
* return contains size of client cookie.
*
* \retval KNOT_EOK
* \retval KNOT_EINVAL
*/
typedef int (knot_cc_gen_t)(const struct knot_cc_input *input,
uint8_t *cc_out, uint16_t *cc_len);
/*!
* \brief Holds description of the client cookie algorithm.
*/
struct knot_cc_alg {
const uint16_t cc_size; /*!< Cookie size the algorithm operates with. */
knot_cc_gen_t *gen_func; /*!< Cookie generator function. */
};
/*!
* \brief Check whether client cookie \a cc was generated from given \a input.
*
* \param cc Client cookie that should be checked.
* \param cc_len Client cookie size.
* \param input Client cookie input algorithm parameters.
* \param cc_alg Client cookie algorithm.
*
* \retval KNOT_EOK
* \retval KNOT_EINVAL
*/
int knot_cc_check(const uint8_t *cc, uint16_t cc_len,
const struct knot_cc_input *input,
const struct knot_cc_alg *cc_alg);
/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
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 3 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.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include "libknot/attribute.h"
#include "libknot/cookies/server.h"
#include "libknot/errcode.h"
#include "libknot/rrtype/opt-cookie.h"
_public_
int knot_sc_parse(uint16_t nonce_len, const uint8_t *sc, uint16_t sc_len,
struct knot_sc_content *content)
{
if (!sc || !sc_len || !content) {
return KNOT_EINVAL;
}
if (nonce_len >= sc_len) {
return KNOT_EINVAL;
}
content->nonce = nonce_len ? sc : NULL;
content->nonce_len = nonce_len;
/* Rest of server cookie contains hash. */
content->hash = sc + nonce_len;
content->hash_len = sc_len - nonce_len;
return KNOT_EOK;
}
_public_
int knot_sc_check(uint16_t nonce_len, const struct knot_dns_cookies *cookies,
const struct knot_sc_private *srvr_data,
const struct knot_sc_alg *sc_alg)
{
if (!cookies || !srvr_data || !sc_alg) {
return KNOT_EINVAL;
}
if (!cookies->cc || !cookies->cc_len ||
!cookies->sc || !cookies->sc_len) {
return KNOT_EINVAL;
}
if (!srvr_data->clnt_sockaddr ||
!srvr_data->secret_data || !srvr_data->secret_len) {
return KNOT_EINVAL;
}
if (!sc_alg->hash_size || !sc_alg->hash_func) {
return KNOT_EINVAL;
}
if ((nonce_len + sc_alg->hash_size) > KNOT_OPT_COOKIE_SRVR_MAX) {
return KNOT_EINVAL;
}
if (cookies->sc_len != (nonce_len + sc_alg->hash_size)) {
return KNOT_EINVAL;
}
struct knot_sc_content content = { 0 };
/* Obtain data from received server cookie. */
int ret = knot_sc_parse(nonce_len, cookies->sc, cookies->sc_len, &content);
if (ret != KNOT_EOK) {
return ret;
}
uint8_t generated_hash[KNOT_OPT_COOKIE_SRVR_MAX] = { 0 };
uint16_t generated_hash_len = KNOT_OPT_COOKIE_SRVR_MAX;
struct knot_sc_input sc_input = {
.cc = cookies->cc,
.cc_len = cookies->cc_len,
.nonce = content.nonce,
.nonce_len = content.nonce_len,
.srvr_data = srvr_data
};
/* Generate a new hash. */
ret = sc_alg->hash_func(&sc_input, generated_hash, &generated_hash_len);
if (ret != KNOT_EOK) {
return ret;
}
/* Compare hashes. */
ret = memcmp(content.hash, generated_hash, generated_hash_len);
if (ret != 0) {
return KNOT_EINVAL;
}
return KNOT_EOK;
}
/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
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 3 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.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
#include <stdlib.h>
/*!
* \brief Convenience structure holding both, server and client, cookies.
*/
struct knot_dns_cookies {
const uint8_t *cc; /*!< Client cookie. */
uint16_t cc_len; /*!< Client cookie size. */
const uint8_t *sc; /*!< Server cookie. */
uint16_t sc_len; /*!< Server cookie size. */
};
/*!
* \brief Private data known to the server.
*
* \note Contains data needed to check the inbound server cookie and to
* generate a new one.
*/
struct knot_sc_private {
const struct sockaddr *clnt_sockaddr; /*!< Client (remote) socket address. */
const uint8_t *secret_data; /*!< Server secret data. */
size_t secret_len; /*!< Secret data length. */
};
/*!
* \brief Inbound server cookie content structure.
*
* \note These data are obtained from an incoming server cookie.
*/
struct knot_sc_content {
const uint8_t *nonce; /*!< Some value prefixed to the hash. */
uint16_t nonce_len; /*!< Nonce data length. */
const uint8_t *hash; /*!< Hash data. */
uint16_t hash_len; /*!< Hash data length. */
};
/*!
* \brief Input data needed to compute the server cookie value.
*
* \note All these data are needed to generate a new server cookie hash.
*/
struct knot_sc_input {
const uint8_t *cc; /*!< Client cookie. */
uint16_t cc_len; /*!< Client cookie size. */
const uint8_t *nonce; /*!< Some value prefixed before the hash. */
uint16_t nonce_len; /*!< Nonce data length. */
const struct knot_sc_private *srvr_data; /*!< Private data known to the server. */
};
/*!
* \brief Reads a server cookie that contains \a nonce_len bytes of data
* prefixed before the actual hash.
*
* \see DNS Cookies, RFC 7873, Appendix B.1 and B.2
*
* \param nonce_len Expected nonce data size.
* \param sc Server cookie.
* \param sc_len Server cookie length.
* \param content Server cookie content structure to be set.
*
* \retval KNOT_EOK
* \retval KNOT_EINVAL
*/
int knot_sc_parse(uint16_t nonce_len, const uint8_t *sc, uint16_t sc_len,
struct knot_sc_content *content);
/*!
* \brief Hash generator function type.
*
* \note The function writes only the hash value. It does not write any nonce
* data prefixed before the actual hash value. Nonce data must be written
* by an external function into the server cookie.
*
* \param[in] input Data which to generate the cookie from.
* \param[in] hash_out Buffer to write the resulting hash data into.
* \param[in,out] hash_len On input set to hash buffer size. On successful
* return contains size of written hash.
*
* \retval KNOT_EOK
* \retval KNOT_EINVAL
*/
typedef int (knot_sc_hash_t)(const struct knot_sc_input *input,
uint8_t *hash_out, uint16_t *hash_len);
/*!
* \brief Holds description of the server cookie algorithm.
*/
struct knot_sc_alg {
const uint16_t hash_size; /*!< Hash size the algorithm operates with. */
knot_sc_hash_t *hash_func; /*!< Cookie generator function. */
};
/*!
* \brief Check whether supplied client and server cookies match.
*
* \param nonce_len Expected nonce data size.
* \param cookies Cookie data.
* \param srvr_data Data known to the server needed for cookie validation.
* \param sc_alg Server cookie algorithm.
*
* \retval KNOT_EOK
* \retval KNOT_EINVAL
*/
int knot_sc_check(uint16_t nonce_len, const struct knot_dns_cookies *cookies,
const struct knot_sc_private *srvr_data,
const struct knot_sc_alg *sc_alg);
/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
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 3 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.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include "contrib/wire_ctx.h"
#include "libknot/errcode.h"
#include "libknot/rrtype/opt.h"
#include "libknot/rrtype/opt-cookie.h"
#define cookie_len_ok(clen) \
(((clen) == KNOT_OPT_COOKIE_CLNT) || \
((clen) >= (KNOT_OPT_COOKIE_CLNT + KNOT_OPT_COOKIE_SRVR_MIN) && \
(clen) <= (KNOT_OPT_COOKIE_CLNT + KNOT_OPT_COOKIE_SRVR_MAX)))
#define ccookie_len_ok(cclen) \
((cclen) == KNOT_OPT_COOKIE_CLNT)
#define scookie_len_ok(sclen) \
(((sclen) == 0) || \
((sclen) >= KNOT_OPT_COOKIE_SRVR_MIN && \
(sclen) <= KNOT_OPT_COOKIE_SRVR_MAX))
_public_
uint16_t knot_edns_opt_cookie_data_len(uint16_t clen, uint16_t slen)
{
return (ccookie_len_ok(clen) && scookie_len_ok(slen)) ? (clen + slen) : 0;
}
_public_
int knot_edns_opt_cookie_write(const uint8_t *cc, uint16_t cc_len,
const uint8_t *sc, uint16_t sc_len,
uint8_t *data, uint16_t *data_len)
{
if ((cc == NULL && cc_len > 0) || (sc == NULL && sc_len > 0)) {
return KNOT_EINVAL;
}
if (data == NULL || data_len == NULL) {
return KNOT_EINVAL;
}
uint16_t cookies_size = knot_edns_opt_cookie_data_len(cc_len, sc_len);
if (cookies_size == 0) {
return KNOT_EINVAL;
}
if (*data_len < cookies_size) {
return KNOT_ESPACE;
}
wire_ctx_t wire = wire_ctx_init(data, *data_len);
wire_ctx_write(&wire, cc, cc_len);
if (sc_len) {
wire_ctx_write(&wire, sc, sc_len);
}
if (wire.error != KNOT_EOK) {
return wire.error;
}
*data_len = wire_ctx_offset(&wire);
return KNOT_EOK;
}
_public_
int knot_edns_opt_cookie_parse(const uint8_t *data, uint16_t data_len,
const uint8_t **cc, uint16_t *cc_len,
const uint8_t **sc, uint16_t *sc_len)
{
if (data == NULL) {
return KNOT_EINVAL;
}
if (!cookie_len_ok(data_len)) {
return KNOT_EMALF;
}
if (cc != NULL && cc_len != NULL) {
*cc_len = KNOT_OPT_COOKIE_CLNT;
*cc = data;
}
if (sc != NULL && sc_len != NULL) {
*sc_len = data_len - KNOT_OPT_COOKIE_CLNT;
*sc = (*sc_len == 0) ? NULL : (data + KNOT_OPT_COOKIE_CLNT);
}
return KNOT_EOK;
}
/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
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 3 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.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
#define KNOT_OPT_COOKIE_MIN 8
#define KNOT_OPT_COOKIE_CLNT KNOT_OPT_COOKIE_MIN
#define KNOT_OPT_COOKIE_SRVR_MIN 8
#define KNOT_OPT_COOKIE_SRVR_MAX 32
/*!
* \brief Returns the size of the buffer required to store the cookie.
*
* \note The value of \a clen and \a slen must be within defined limits.
*
* \param clen Client cookie portion length.
* \param slen Server cookie portion length.
*
* \retval <> 0 if the supplied arguments are within limits
* \retval 0 if the supplied parameters violate the requirements
*/
uint16_t knot_edns_opt_cookie_data_len(uint16_t clen, uint16_t slen);
/*!
* \brief Write cookie wire data.
*
* \param cc Client cookie.
* \param cc_len Client cookie size.
* \param sc Server cookie.
* \param sc_len Server cookie size.
* \param data Output data buffer.
* \param data_len Size of output data buffer/written data.
*
* \retval KNOT_EOK
* \retval KNOT_EINVAL
* \retval KNOT_ESPACE
*/
int knot_edns_opt_cookie_write(const uint8_t *cc, uint16_t cc_len,
const uint8_t *sc, uint16_t sc_len,
uint8_t *data, uint16_t *data_len);
/*!
* \brief Parse cookie wire data.
*
* \note The function only sets the pointers into the buffer. It does not
* copy any data.
*
* \param data Input data buffer containing whole cookie option.
* \param data_len Length of input data buffer.
* \param cc Client cookie.
* \param cc_len Client cookie size.
* \param sc Server cookie.
* \param sc_len Server cookie size.
*
* \retval KNOT_EOK
* \retval KNOT_EINVAL
* \retval KNOT_EMALF
*/
int knot_edns_opt_cookie_parse(const uint8_t *data, uint16_t data_len,
const uint8_t **cc, uint16_t *cc_len,
const uint8_t **sc, uint16_t *sc_len);
......@@ -189,6 +189,9 @@ void knot_edns_set_do(knot_rrset_t *opt_rr)
/*!
* \brief Find OPTION with the given code in the OPT RDATA.
*
* \note It is ensured that the full option, as declared in option length,
* is encompassed in the RDATA when found.
*
* \param rdata RDATA to search in.
* \param opt_code Code of the OPTION to find.
* \param[out] pos Position of the OPTION or NULL if not found.
......@@ -197,18 +200,26 @@ static uint8_t *find_option(knot_rdata_t *rdata, uint16_t opt_code)
{
wire_ctx_t wire = wire_ctx_init_const(knot_rdata_data(rdata),
knot_rdata_rdlen(rdata));
uint8_t *found_position = NULL;
while (wire_ctx_available(&wire) > 0) {
found_position = wire.position;
uint16_t code = wire_ctx_read_u16(&wire);
if (wire.error != KNOT_EOK) {
break;
}
if (code == opt_code) {
return wire.position;
if (code != opt_code) {
found_position = NULL;
}
uint16_t opt_len = wire_ctx_read_u16(&wire);
/* Return position only when the entire option fits
* in the RDATA. */
if (found_position != NULL && wire.error == KNOT_EOK &&
wire_ctx_available(&wire) >= opt_len) {
return found_position;
}
wire_ctx_skip(&wire, opt_len);
}
......@@ -216,45 +227,94 @@ static uint8_t *find_option(knot_rdata_t *rdata, uint16_t opt_code)
}
/*----------------------------------------------------------------------------*/
_public_
int knot_edns_add_option(knot_rrset_t *opt_rr, uint16_t code,
uint16_t length, const uint8_t *data, knot_mm_t *mm)
{
if (opt_rr == NULL || (length != 0 && data == NULL)) {
return KNOT_EINVAL;
}
/* We need to replace the RDATA currently in the OPT RR */
/*!
* \brief Add new EDNS option by replacing RDATA of OPT RR.
*
* \param opt OPT RR structure to add the Option to.
* \param code Option code.
* \param size Option data length in bytes.
* \param mm Memory context.
*
* \return Pointer to uninitialized option data.
*/
static uint8_t *edns_add(knot_rrset_t *opt, uint16_t code, uint16_t size,
knot_mm_t *mm)
{
assert(opt->rrs.rr_count == 1);
/* 1) create new RDATA by appending the new option after the current
* RDATA.
*/
assert(opt_rr->rrs.rr_count == 1);
knot_rdata_t *old_rdata = knot_rdataset_at(&opt_rr->rrs, 0);
// extract old RDATA
knot_rdata_t *old_rdata = knot_rdataset_at(&opt->rrs, 0);
uint8_t *old_data = knot_rdata_data(old_rdata);
uint16_t old_data_len = knot_rdata_rdlen(old_rdata);
uint16_t new_data_len = old_data_len + KNOT_EDNS_OPTION_HDRLEN + length;
// construct new RDATA
uint16_t new_data_len = old_data_len + KNOT_EDNS_OPTION_HDRLEN + size;
uint8_t new_data[new_data_len];
wire_ctx_t wire = wire_ctx_init(new_data, new_data_len);
wire_ctx_write(&wire, old_data, old_data_len);
// write length and code in wireformat (convert endian)
wire_ctx_write_u16(&wire, code);
wire_ctx_write_u16(&wire, length);
// write the option data
wire_ctx_write(&wire, data, length);
wire_ctx_write_u16(&wire, size);
if (wire.error != KNOT_EOK) {
return wire.error;
// prepare EDNS option data
size_t offset = wire_ctx_offset(&wire);
wire_ctx_clear(&wire, size);
assert(wire_ctx_available(&wire) == 0);
assert(wire.error == KNOT_EOK);
// replace RDATA
uint32_t ttl = knot_rdata_ttl(old_rdata);
knot_rdataset_clear(&opt->rrs, mm);
if (knot_rrset_add_rdata(opt, new_data, new_data_len, ttl, mm) != KNOT_EOK) {
return NULL;
}
return knot_rdata_data(knot_rdataset_at(&opt->rrs, 0)) + offset;
}
_public_
int knot_edns_reserve_option(knot_rrset_t *opt_rr, uint16_t code,
uint16_t size, uint8_t **wire_ptr, knot_mm_t *mm)
{
if (!opt_rr) {
return KNOT_EINVAL;
}
uint8_t *wire = edns_add(opt_rr, code, size, mm);
if (!wire) {
return KNOT_ENOMEM;
}
memset(wire, 0, size);
if (wire_ptr) {
*wire_ptr = wire;
}
return KNOT_EOK;
}
_public_
int knot_edns_add_option(knot_rrset_t *opt_rr, uint16_t code,
uint16_t size, const uint8_t *data, knot_mm_t *mm)
{
if (!opt_rr || (size > 0 && !data)) {
return KNOT_EINVAL;
}
/* 2) Replace the RDATA in the RRSet. */
uint32_t old_ttl = knot_rdata_ttl(old_rdata);
knot_rdataset_clear(&opt_rr->rrs, mm);
return knot_rrset_add_rdata(opt_rr, new_data, new_data_len,
old_ttl, mm);
uint8_t *wire = edns_add(opt_rr, code, size, mm);
if (!wire) {
return KNOT_ENOMEM;
}
memcpy(wire, data, size);
return KNOT_EOK;
}
/*----------------------------------------------------------------------------*/
......@@ -271,6 +331,36 @@ bool knot_edns_has_option(const knot_rrset_t *opt_rr, uint16_t code)
return pos != NULL;
}
_public_
uint8_t *knot_edns_get_option(const knot_rrset_t *opt_rr, uint16_t code)
{
assert(opt_rr != NULL);
knot_rdata_t *rdata = knot_rdataset_at(&opt_rr->rrs, 0);
assert(rdata != NULL);
return find_option(rdata, code);
}
_public_
uint16_t knot_edns_opt_get_code(const uint8_t *opt)
{
assert(opt != NULL);
wire_ctx_t wire = wire_ctx_init_const(opt, sizeof(uint16_t));
return wire_ctx_read_u16(&wire);
}
_public_
uint16_t knot_edns_opt_get_length(const uint8_t *opt)
{
assert(opt != NULL);
wire_ctx_t wire = wire_ctx_init_const(opt + sizeof(uint16_t),
sizeof(uint16_t));
return wire_ctx_read_u16(&wire);
}
/*----------------------------------------------------------------------------*/
_public_
bool knot_edns_check_record(knot_rrset_t *opt_rr)
......
......@@ -24,6 +24,7 @@
#pragma once
#include "libknot/consts.h"
#include "libknot/rrset.h"
/*! \brief Constants related to EDNS. */
......@@ -50,7 +51,9 @@ enum knot_edns_const {
/*! \brief NSID option code. */
KNOT_EDNS_OPTION_NSID = 3,
/*! \brief EDNS client subnet option code. */
KNOT_EDNS_OPTION_CLIENT_SUBNET = 8
KNOT_EDNS_OPTION_CLIENT_SUBNET = 8,
/*! \brief EDNS DNS Cookie option code. */
KNOT_EDNS_OPTION_COOKIE = 10
};
/* Helpers for splitting extended RCODE. */
......@@ -214,6 +217,20 @@ bool knot_edns_do(const knot_rrset_t *opt_rr);
*/
void knot_edns_set_do(knot_rrset_t *opt_rr);
/*!
* \brief Add EDNS option into the package with empty (zeroed) content.
*
* \param[in] opt_rr OPT RR in the packet.
* \param[in] code Option code.
* \param[in] size Desired option size.
* \param[out] wire_ptr Pointer to reserved option data (can be NULL).
* \param[in] mm Memory context.
*
* \return Error code, KNOT_EOK if successful.
*/
int knot_edns_reserve_option(knot_rrset_t *opt_rr, uint16_t code,
uint16_t size, uint8_t **wire_ptr, knot_mm_t *mm);
/*!
* \brief Adds EDNS Option to the OPT RR.
*
......@@ -222,14 +239,15 @@ void knot_edns_set_do(knot_rrset_t *opt_rr);
*
* \param opt_rr OPT RR structure to add the Option to.
* \param code Option code.
* \param length Option data length in bytes.
* \param size Option data length in bytes.
* \param data Option data.
* \param mm Memory context.
*
* \retval KNOT_EOK
* \retval KNOT_ENOMEM
*/
int knot_edns_add_option(knot_rrset_t *opt_rr, uint16_t code,
uint16_t length, const uint8_t *data, knot_mm_t *mm);
uint16_t size, const uint8_t *data, knot_mm_t *mm);
/*!
* \brief Checks if the OPT RR contains Option with the specified code.
......@@ -242,6 +260,53 @@ int knot_edns_add_option(knot_rrset_t *opt_rr, uint16_t code,
*/
bool knot_edns_has_option(const knot_rrset_t *opt_rr, uint16_t code);
/*!
* \brief Searches the OPT RR for option with the specified code.
*
* \param opt_rr OPT RR structure to search for the Option in.
* \param code Option code to search for.
*
* \retval pointer to option if found
* \retval NULL otherwise.
*/
uint8_t *knot_edns_get_option(const knot_rrset_t *opt_rr, uint16_t code);
/*!
* \brief Returns the option code.
*
* \warning No safety checks are performed on the supplied data.
*
* \param opt EDNS option (including code, length and data portion).
*
* \retval EDNS option code
*/
uint16_t knot_edns_opt_get_code(const uint8_t *opt);
/*!
* \brief Returns the option data length.
*
* \warning No safety checks are performed on the supplied data.
*
* \param opt EDNS option (including code, length and data portion).
*
* \retval EDNS option length
*/
uint16_t knot_edns_opt_get_length(const uint8_t *opt);
/*!
* \brief Returns pointer to option data.
*
* \warning No safety checks are performed on the supplied data.
*
* \param opt EDNS option (including code, length and data portion).
*
* \retval pointer to place where ENDS option data would reside
*/
static inline uint8_t *knot_edns_opt_get_data(uint8_t *opt)
{
return opt + KNOT_EDNS_OPTION_HDRLEN;
}
/*! \brief Return true if RRSet has NSID option. */
bool knot_edns_has_nsid(const knot_rrset_t *opt_rr);
......
......@@ -17,6 +17,9 @@ contrib/test_wire
contrib/test_wire_ctx
libknot/test_control
libknot/test_cookie-client
libknot/test_cookie-opt
libknot/test_cookie-server
libknot/test_db
libknot/test_descriptor
libknot/test_dname
......@@ -59,4 +62,3 @@ zone_timers
zone_update
zonedb
ztree
......@@ -27,6 +27,9 @@ check_PROGRAMS = \
check_PROGRAMS += \
libknot/test_control \
libknot/test_cookies-client \
libknot/test_cookies-opt \
libknot/test_cookies-server \
libknot/test_db \
libknot/test_descriptor \
libknot/test_dname \
......
......@@ -122,19 +122,17 @@ void clear_test(void)
wire_ctx_t wire = wire_ctx_init(data, sizeof(data));
wire_ctx_clear(&wire);
wire_ctx_clear(&wire, 10);
NOK(&wire, KNOT_ESPACE);
is_int(1, data[0], "no attempt to clear");
wire = wire_ctx_init(data, sizeof(data));
wire_ctx_clear(&wire, 3);
OK(&wire);
is_int(0, wire_ctx_offset(&wire), "no position change after clear");
is_int(0, wire_ctx_available(&wire), "no space available");
for (int i = 0; i < sizeof(data); i++) {
is_int(0, data[i], "wire position %i is zero", i);
}
data[0] = 1;
wire_ctx_set_offset(&wire, 4);
NOK(&wire, KNOT_ERANGE);
wire_ctx_clear(&wire);
NOK(&wire, KNOT_ERANGE);
is_int(1, data[0], "no data change after clear after error");
}
#define check_rw(size, value, ...) { \
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment