@@ -1,12 +1,6 @@ | |||
[submodule "src/rdns"] | |||
path = src/rdns | |||
url = git://github.com/vstakhov/librdns | |||
[submodule "interface"] | |||
path = interface | |||
url = git://github.com/vstakhov/rspamd-interface | |||
[submodule "src/ucl"] | |||
path = src/ucl | |||
url = git://github.com/vstakhov/libucl | |||
[submodule "doc/doxydown"] | |||
path = doc/doxydown | |||
url = https://github.com/vstakhov/doxydown.git |
@@ -887,7 +887,7 @@ ENDIF(HG) | |||
INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/src" | |||
"${CMAKE_BINARY_DIR}/src" | |||
"${CMAKE_BINARY_DIR}/src/libcryptobox" | |||
"${CMAKE_SOURCE_DIR}/src/ucl/include" | |||
"${CMAKE_SOURCE_DIR}/contrib/libucl" | |||
"${CMAKE_SOURCE_DIR}/contrib/uthash" | |||
"${CMAKE_SOURCE_DIR}/contrib/http-parser" | |||
"${CMAKE_SOURCE_DIR}/contrib/libottery" | |||
@@ -895,7 +895,7 @@ INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/src" | |||
"${CMAKE_SOURCE_DIR}/contrib/snowball/include" | |||
"${CMAKE_SOURCE_DIR}/contrib/siphash" | |||
"${CMAKE_SOURCE_DIR}/contrib/blake2" | |||
"${CMAKE_SOURCE_DIR}/src/rdns/include") | |||
"${CMAKE_SOURCE_DIR}/contrib/librdns") | |||
################################ SUBDIRS SECTION ########################### | |||
@@ -916,7 +916,6 @@ LIST(APPEND RSPAMD_REQUIRED_LIBRARIES xxhash) | |||
LIST(APPEND RSPAMD_REQUIRED_LIBRARIES siphash) | |||
LIST(APPEND RSPAMD_REQUIRED_LIBRARIES blake2) | |||
LIST(APPEND RSPAMD_REQUIRED_LIBRARIES sqlite3) | |||
LIST(APPEND RSPAMD_REQUIRED_LIBRARIES lua-ucl) | |||
IF(OPENSSL_FOUND) | |||
LIST(APPEND RSPAMD_REQUIRED_LIBRARIES ${OPENSSL_LIBRARIES}) | |||
ENDIF(OPENSSL_FOUND) | |||
@@ -949,6 +948,8 @@ ADD_SUBDIRECTORY(contrib/libottery) | |||
ADD_SUBDIRECTORY(contrib/snowball) | |||
ADD_SUBDIRECTORY(contrib/siphash) | |||
ADD_SUBDIRECTORY(contrib/blake2) | |||
ADD_SUBDIRECTORY(contrib/libucl) | |||
ADD_SUBDIRECTORY(contrib/librdns) | |||
ADD_SUBDIRECTORY(src) | |||
ADD_SUBDIRECTORY(test) |
@@ -0,0 +1,10 @@ | |||
SET(LIBRDNSSRC util.c | |||
logger.c | |||
compression.c | |||
punycode.c | |||
curve.c | |||
parse.c | |||
packet.c | |||
resolver.c) | |||
ADD_LIBRARY(rdns STATIC ${LIBRDNSSRC}) |
@@ -0,0 +1,154 @@ | |||
/* Copyright (c) 2014, Vsevolod Stakhov | |||
* All rights reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions are met: | |||
* * Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* * Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in the | |||
* documentation and/or other materials provided with the distribution. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY | |||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY | |||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
#include "compression.h" | |||
#include "logger.h" | |||
static struct rdns_compression_entry * | |||
rdns_can_compress (const char *pos, struct rdns_compression_entry *comp) | |||
{ | |||
struct rdns_compression_entry *res; | |||
HASH_FIND_STR (comp, pos, res); | |||
return res; | |||
} | |||
static unsigned int | |||
rdns_calculate_label_len (const char *pos, const char *end) | |||
{ | |||
const char *p = pos; | |||
unsigned int res = 0; | |||
while (p != end) { | |||
if (*p == '.') { | |||
break; | |||
} | |||
res ++; | |||
p ++; | |||
} | |||
return res; | |||
} | |||
static void | |||
rdns_add_compressed (const char *pos, const char *end, | |||
struct rdns_compression_entry **comp, int offset) | |||
{ | |||
struct rdns_compression_entry *new; | |||
assert (offset >= 0); | |||
new = malloc (sizeof (*new)); | |||
if (new != NULL) { | |||
new->label = pos; | |||
new->offset = offset; | |||
HASH_ADD_KEYPTR (hh, *comp, pos, (end - pos), new); | |||
} | |||
} | |||
void | |||
rnds_compression_free (struct rdns_compression_entry *comp) | |||
{ | |||
struct rdns_compression_entry *cur, *tmp; | |||
if (comp) { | |||
free (comp->hh.tbl->buckets); | |||
free (comp->hh.tbl); | |||
HASH_ITER (hh, comp, cur, tmp) { | |||
free (cur); | |||
} | |||
} | |||
} | |||
bool | |||
rdns_write_name_compressed (struct rdns_request *req, | |||
const char *name, unsigned int namelen, | |||
struct rdns_compression_entry **comp) | |||
{ | |||
uint8_t *target = req->packet + req->pos; | |||
const char *pos = name, *end = name + namelen; | |||
unsigned int remain = req->packet_len - req->pos - 5, label_len; | |||
struct rdns_compression_entry *head = NULL, *test; | |||
struct rdns_resolver *resolver = req->resolver; | |||
uint16_t pointer; | |||
if (comp != NULL) { | |||
head = *comp; | |||
} | |||
while (pos < end && remain > 0) { | |||
if (head != NULL) { | |||
test = rdns_can_compress (pos, head); | |||
if (test != NULL) { | |||
if (remain < 2) { | |||
rdns_info ("no buffer remain for constructing query"); | |||
return false; | |||
} | |||
pointer = htons ((uint16_t)test->offset) | DNS_COMPRESSION_BITS; | |||
memcpy (target, &pointer, sizeof (pointer)); | |||
req->pos += 2; | |||
return true; | |||
} | |||
} | |||
label_len = rdns_calculate_label_len (pos, end); | |||
if (label_len == 0) { | |||
/* We have empty label it is allowed only if pos == end - 1 */ | |||
if (pos == end - 1) { | |||
break; | |||
} | |||
else { | |||
rdns_err ("double dots in the name requested"); | |||
return false; | |||
} | |||
} | |||
else if (label_len > DNS_D_MAXLABEL) { | |||
rdns_err ("too large label: %d", (int)label_len); | |||
return false; | |||
} | |||
if (label_len + 1 > remain) { | |||
rdns_info ("no buffer remain for constructing query, strip %d to %d", | |||
(int)label_len, (int)remain); | |||
label_len = remain - 1; | |||
} | |||
if (comp != NULL) { | |||
rdns_add_compressed (pos, end, comp, target - req->packet); | |||
} | |||
/* Write label as is */ | |||
*target++ = (uint8_t)label_len; | |||
memcpy (target, pos, label_len); | |||
target += label_len; | |||
pos += label_len + 1; | |||
} | |||
/* Termination label */ | |||
*target++ = '\0'; | |||
req->pos = target - req->packet; | |||
return true; | |||
} |
@@ -0,0 +1,45 @@ | |||
/* Copyright (c) 2014, Vsevolod Stakhov | |||
* All rights reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions are met: | |||
* * Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* * Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in the | |||
* documentation and/or other materials provided with the distribution. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY | |||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY | |||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
#ifndef COMPRESSION_H_ | |||
#define COMPRESSION_H_ | |||
#include "uthash.h" | |||
#include "dns_private.h" | |||
struct rdns_compression_entry { | |||
const char *label; /**< label packed */ | |||
unsigned int offset; /**< offset in the packet */ | |||
UT_hash_handle hh; /**< hash handle */ | |||
}; | |||
/** | |||
* Try to compress name passed or write it 'as is' | |||
* @return | |||
*/ | |||
bool rdns_write_name_compressed (struct rdns_request *req, | |||
const char *name, unsigned int namelen, | |||
struct rdns_compression_entry **comp); | |||
void rnds_compression_free (struct rdns_compression_entry *comp); | |||
#endif /* COMPRESSION_H_ */ |
@@ -0,0 +1,472 @@ | |||
/* | |||
* Copyright (c) 2014, Vsevolod Stakhov | |||
* | |||
* All rights reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions are met: | |||
* * Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* * Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in the | |||
* documentation and/or other materials provided with the distribution. | |||
* | |||
* THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY | |||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY | |||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
#include <sys/types.h> | |||
#include <sys/uio.h> | |||
#include <unistd.h> | |||
#include "rdns.h" | |||
#include "dns_private.h" | |||
#include "rdns_curve.h" | |||
#include "ottery.h" | |||
#include "ref.h" | |||
#include "logger.h" | |||
#ifdef TWEETNACL | |||
#include <tweetnacl.h> | |||
void | |||
randombytes(uint8_t *data, uint64_t len) | |||
{ | |||
ottery_rand_bytes (data, len); | |||
} | |||
void sodium_memzero (uint8_t *data, uint64_t len) | |||
{ | |||
volatile uint8_t *p = data; | |||
while (len--) { | |||
*p = '\0'; | |||
} | |||
} | |||
void sodium_init(void) | |||
{ | |||
} | |||
ssize_t rdns_curve_send (struct rdns_request *req, void *plugin_data); | |||
ssize_t rdns_curve_recv (struct rdns_io_channel *ioc, void *buf, size_t len, | |||
void *plugin_data, struct rdns_request **req_out); | |||
void rdns_curve_finish_request (struct rdns_request *req, void *plugin_data); | |||
void rdns_curve_dtor (struct rdns_resolver *resolver, void *plugin_data); | |||
struct rdns_curve_entry { | |||
char *name; | |||
unsigned char pk[crypto_box_PUBLICKEYBYTES]; | |||
UT_hash_handle hh; | |||
}; | |||
struct rdns_curve_nm_entry { | |||
unsigned char k[crypto_box_BEFORENMBYTES]; | |||
struct rdns_curve_entry *entry; | |||
struct rdns_curve_nm_entry *prev, *next; | |||
}; | |||
struct rdns_curve_client_key { | |||
unsigned char pk[crypto_box_PUBLICKEYBYTES]; | |||
unsigned char sk[crypto_box_SECRETKEYBYTES]; | |||
struct rdns_curve_nm_entry *nms; | |||
uint64_t counter; | |||
unsigned int uses; | |||
ref_entry_t ref; | |||
}; | |||
struct rdns_curve_request { | |||
struct rdns_request *req; | |||
struct rdns_curve_client_key *key; | |||
struct rdns_curve_entry *entry; | |||
struct rdns_curve_nm_entry *nm; | |||
unsigned char nonce[crypto_box_NONCEBYTES]; | |||
UT_hash_handle hh; | |||
}; | |||
struct rdns_curve_ctx { | |||
struct rdns_curve_entry *entries; | |||
struct rdns_curve_client_key *cur_key; | |||
struct rdns_curve_request *requests; | |||
double key_refresh_interval; | |||
void *key_refresh_event; | |||
struct rdns_resolver *resolver; | |||
}; | |||
static struct rdns_curve_client_key * | |||
rdns_curve_client_key_new (struct rdns_curve_ctx *ctx) | |||
{ | |||
struct rdns_curve_client_key *new; | |||
struct rdns_curve_nm_entry *nm; | |||
struct rdns_curve_entry *entry, *tmp; | |||
new = calloc (1, sizeof (struct rdns_curve_client_key)); | |||
crypto_box_keypair (new->pk, new->sk); | |||
HASH_ITER (hh, ctx->entries, entry, tmp) { | |||
nm = calloc (1, sizeof (struct rdns_curve_nm_entry)); | |||
nm->entry = entry; | |||
crypto_box_beforenm (nm->k, entry->pk, new->sk); | |||
DL_APPEND (new->nms, nm); | |||
} | |||
new->counter = ottery_rand_uint64 (); | |||
return new; | |||
} | |||
static struct rdns_curve_nm_entry * | |||
rdns_curve_find_nm (struct rdns_curve_client_key *key, struct rdns_curve_entry *entry) | |||
{ | |||
struct rdns_curve_nm_entry *nm; | |||
DL_FOREACH (key->nms, nm) { | |||
if (nm->entry == entry) { | |||
return nm; | |||
} | |||
} | |||
return NULL; | |||
} | |||
static void | |||
rdns_curve_client_key_free (struct rdns_curve_client_key *key) | |||
{ | |||
struct rdns_curve_nm_entry *nm, *tmp; | |||
DL_FOREACH_SAFE (key->nms, nm, tmp) { | |||
sodium_memzero (nm->k, sizeof (nm->k)); | |||
free (nm); | |||
} | |||
sodium_memzero (key->sk, sizeof (key->sk)); | |||
free (key); | |||
} | |||
struct rdns_curve_ctx* | |||
rdns_curve_ctx_new (double key_refresh_interval) | |||
{ | |||
struct rdns_curve_ctx *new; | |||
new = calloc (1, sizeof (struct rdns_curve_ctx)); | |||
new->key_refresh_interval = key_refresh_interval; | |||
return new; | |||
} | |||
void | |||
rdns_curve_ctx_add_key (struct rdns_curve_ctx *ctx, | |||
const char *name, const unsigned char *pubkey) | |||
{ | |||
struct rdns_curve_entry *entry; | |||
bool success = true; | |||
entry = malloc (sizeof (struct rdns_curve_entry)); | |||
if (entry != NULL) { | |||
entry->name = strdup (name); | |||
if (entry->name == NULL) { | |||
success = false; | |||
} | |||
memcpy (entry->pk, pubkey, sizeof (entry->pk)); | |||
if (success) { | |||
HASH_ADD_KEYPTR (hh, ctx->entries, entry->name, strlen (entry->name), entry); | |||
} | |||
} | |||
} | |||
#define rdns_curve_write_hex(in, out, offset, base) do { \ | |||
*(out) |= ((in)[(offset)] - (base)) << ((1 - offset) * 4); \ | |||
} while (0) | |||
static bool | |||
rdns_curve_hex_to_byte (const char *in, unsigned char *out) | |||
{ | |||
int i; | |||
for (i = 0; i <= 1; i ++) { | |||
if (in[i] >= '0' && in[i] <= '9') { | |||
rdns_curve_write_hex (in, out, i, '0'); | |||
} | |||
else if (in[i] >= 'a' && in[i] <= 'f') { | |||
rdns_curve_write_hex (in, out, i, 'a' - 10); | |||
} | |||
else if (in[i] >= 'A' && in[i] <= 'F') { | |||
rdns_curve_write_hex (in, out, i, 'A' - 10); | |||
} | |||
else { | |||
return false; | |||
} | |||
} | |||
return true; | |||
} | |||
#undef rdns_curve_write_hex | |||
unsigned char * | |||
rdns_curve_key_from_hex (const char *hex) | |||
{ | |||
unsigned int len = strlen (hex), i; | |||
unsigned char *res = NULL; | |||
if (len == crypto_box_PUBLICKEYBYTES * 2) { | |||
res = calloc (1, crypto_box_PUBLICKEYBYTES); | |||
for (i = 0; i < crypto_box_PUBLICKEYBYTES; i ++) { | |||
if (!rdns_curve_hex_to_byte (&hex[i * 2], &res[i])) { | |||
free (res); | |||
return NULL; | |||
} | |||
} | |||
} | |||
return res; | |||
} | |||
void | |||
rdns_curve_ctx_destroy (struct rdns_curve_ctx *ctx) | |||
{ | |||
struct rdns_curve_entry *entry, *tmp; | |||
HASH_ITER (hh, ctx->entries, entry, tmp) { | |||
free (entry->name); | |||
free (entry); | |||
} | |||
free (ctx); | |||
} | |||
static void | |||
rdns_curve_refresh_key_callback (void *user_data) | |||
{ | |||
struct rdns_curve_ctx *ctx = user_data; | |||
struct rdns_resolver *resolver; | |||
resolver = ctx->resolver; | |||
rdns_info ("refresh dnscurve keys"); | |||
REF_RELEASE (ctx->cur_key); | |||
ctx->cur_key = rdns_curve_client_key_new (ctx); | |||
REF_INIT_RETAIN (ctx->cur_key, rdns_curve_client_key_free); | |||
} | |||
void | |||
rdns_curve_register_plugin (struct rdns_resolver *resolver, | |||
struct rdns_curve_ctx *ctx) | |||
{ | |||
struct rdns_plugin *plugin; | |||
if (!resolver->async_binded) { | |||
return; | |||
} | |||
plugin = calloc (1, sizeof (struct rdns_plugin)); | |||
if (plugin != NULL) { | |||
plugin->data = ctx; | |||
plugin->type = RDNS_PLUGIN_CURVE; | |||
plugin->cb.curve_plugin.send_cb = rdns_curve_send; | |||
plugin->cb.curve_plugin.recv_cb = rdns_curve_recv; | |||
plugin->cb.curve_plugin.finish_cb = rdns_curve_finish_request; | |||
plugin->dtor = rdns_curve_dtor; | |||
sodium_init (); | |||
ctx->cur_key = rdns_curve_client_key_new (ctx); | |||
REF_INIT_RETAIN (ctx->cur_key, rdns_curve_client_key_free); | |||
if (ctx->key_refresh_interval > 0) { | |||
ctx->key_refresh_event = resolver->async->add_periodic ( | |||
resolver->async->data, ctx->key_refresh_interval, | |||
rdns_curve_refresh_key_callback, ctx); | |||
} | |||
ctx->resolver = resolver; | |||
rdns_resolver_register_plugin (resolver, plugin); | |||
} | |||
} | |||
ssize_t | |||
rdns_curve_send (struct rdns_request *req, void *plugin_data) | |||
{ | |||
struct rdns_curve_ctx *ctx = (struct rdns_curve_ctx *)plugin_data; | |||
struct rdns_curve_entry *entry; | |||
struct iovec iov[4]; | |||
unsigned char *m; | |||
static const char qmagic[] = "Q6fnvWj8"; | |||
struct rdns_curve_request *creq; | |||
struct rdns_curve_nm_entry *nm; | |||
ssize_t ret, boxed_len; | |||
/* Check for key */ | |||
HASH_FIND_STR (ctx->entries, req->io->srv->name, entry); | |||
if (entry != NULL) { | |||
nm = rdns_curve_find_nm (ctx->cur_key, entry); | |||
creq = malloc (sizeof (struct rdns_curve_request)); | |||
if (creq == NULL) { | |||
return -1; | |||
} | |||
boxed_len = req->pos + crypto_box_ZEROBYTES; | |||
m = malloc (boxed_len); | |||
if (m == NULL) { | |||
return -1; | |||
} | |||
/* Ottery is faster than sodium native PRG that uses /dev/random only */ | |||
memcpy (creq->nonce, &ctx->cur_key->counter, sizeof (uint64_t)); | |||
ottery_rand_bytes (creq->nonce + sizeof (uint64_t), 12 - sizeof (uint64_t)); | |||
sodium_memzero (creq->nonce + 12, crypto_box_NONCEBYTES - 12); | |||
sodium_memzero (m, crypto_box_ZEROBYTES); | |||
memcpy (m + crypto_box_ZEROBYTES, req->packet, req->pos); | |||
if (crypto_box_afternm (m, m, boxed_len, | |||
creq->nonce, nm->k) == -1) { | |||
sodium_memzero (m, boxed_len); | |||
free (m); | |||
return -1; | |||
} | |||
creq->key = ctx->cur_key; | |||
REF_RETAIN (ctx->cur_key); | |||
creq->entry = entry; | |||
creq->req = req; | |||
creq->nm = nm; | |||
HASH_ADD_KEYPTR (hh, ctx->requests, creq->nonce, 12, creq); | |||
req->curve_plugin_data = creq; | |||
ctx->cur_key->counter ++; | |||
ctx->cur_key->uses ++; | |||
/* Now form a dnscurve packet */ | |||
iov[0].iov_base = (void *)qmagic; | |||
iov[0].iov_len = sizeof (qmagic) - 1; | |||
iov[1].iov_base = ctx->cur_key->pk; | |||
iov[1].iov_len = sizeof (ctx->cur_key->pk); | |||
iov[2].iov_base = creq->nonce; | |||
iov[2].iov_len = 12; | |||
iov[3].iov_base = m + crypto_box_BOXZEROBYTES; | |||
iov[3].iov_len = boxed_len - crypto_box_BOXZEROBYTES; | |||
ret = writev (req->io->sock, iov, sizeof (iov) / sizeof (iov[0])); | |||
sodium_memzero (m, boxed_len); | |||
free (m); | |||
} | |||
else { | |||
ret = write (req->io->sock, req->packet, req->pos); | |||
req->curve_plugin_data = NULL; | |||
} | |||
return ret; | |||
} | |||
ssize_t | |||
rdns_curve_recv (struct rdns_io_channel *ioc, void *buf, size_t len, void *plugin_data, | |||
struct rdns_request **req_out) | |||
{ | |||
struct rdns_curve_ctx *ctx = (struct rdns_curve_ctx *)plugin_data; | |||
ssize_t ret, boxlen; | |||
static const char rmagic[] = "R6fnvWJ8"; | |||
unsigned char *p, *box; | |||
unsigned char enonce[crypto_box_NONCEBYTES]; | |||
struct rdns_curve_request *creq; | |||
struct rdns_resolver *resolver; | |||
resolver = ctx->resolver; | |||
ret = read (ioc->sock, buf, len); | |||
if (ret <= 0 || ret < 64) { | |||
/* Definitely not a DNSCurve packet */ | |||
return ret; | |||
} | |||
if (memcmp (buf, rmagic, sizeof (rmagic) - 1) == 0) { | |||
/* Likely DNSCurve packet */ | |||
p = ((unsigned char *)buf) + 8; | |||
HASH_FIND (hh, ctx->requests, p, 12, creq); | |||
if (creq == NULL) { | |||
rdns_info ("unable to find nonce in the internal hash"); | |||
return ret; | |||
} | |||
memcpy (enonce, p, crypto_box_NONCEBYTES); | |||
p += crypto_box_NONCEBYTES; | |||
boxlen = ret - crypto_box_NONCEBYTES + | |||
crypto_box_BOXZEROBYTES - | |||
sizeof (rmagic) + 1; | |||
if (boxlen < 0) { | |||
return ret; | |||
} | |||
box = malloc (boxlen); | |||
sodium_memzero (box, crypto_box_BOXZEROBYTES); | |||
memcpy (box + crypto_box_BOXZEROBYTES, p, | |||
boxlen - crypto_box_BOXZEROBYTES); | |||
if (crypto_box_open_afternm (box, box, boxlen, enonce, creq->nm->k) != -1) { | |||
memcpy (buf, box + crypto_box_ZEROBYTES, | |||
boxlen - crypto_box_ZEROBYTES); | |||
ret = boxlen - crypto_box_ZEROBYTES; | |||
*req_out = creq->req; | |||
} | |||
else { | |||
rdns_info ("unable open cryptobox of size %d", (int)boxlen); | |||
} | |||
free (box); | |||
} | |||
return ret; | |||
} | |||
void | |||
rdns_curve_finish_request (struct rdns_request *req, void *plugin_data) | |||
{ | |||
struct rdns_curve_ctx *ctx = (struct rdns_curve_ctx *)plugin_data; | |||
struct rdns_curve_request *creq = req->curve_plugin_data; | |||
if (creq != NULL) { | |||
REF_RELEASE (creq->key); | |||
HASH_DELETE (hh, ctx->requests, creq); | |||
} | |||
} | |||
void | |||
rdns_curve_dtor (struct rdns_resolver *resolver, void *plugin_data) | |||
{ | |||
struct rdns_curve_ctx *ctx = (struct rdns_curve_ctx *)plugin_data; | |||
if (ctx->key_refresh_event != NULL) { | |||
resolver->async->del_periodic (resolver->async->data, | |||
ctx->key_refresh_event); | |||
} | |||
REF_RELEASE (ctx->cur_key); | |||
} | |||
#else | |||
/* Fake functions */ | |||
struct rdns_curve_ctx* rdns_curve_ctx_new (double rekey_interval) | |||
{ | |||
return NULL; | |||
} | |||
void rdns_curve_ctx_add_key (struct rdns_curve_ctx *ctx, | |||
const char *name, const unsigned char *pubkey) | |||
{ | |||
} | |||
void rdns_curve_ctx_destroy (struct rdns_curve_ctx *ctx) | |||
{ | |||
} | |||
void rdns_curve_register_plugin (struct rdns_resolver *resolver, | |||
struct rdns_curve_ctx *ctx) | |||
{ | |||
} | |||
unsigned char * | |||
rdns_curve_key_from_hex (const char *hex) | |||
{ | |||
return NULL; | |||
} | |||
#endif |
@@ -0,0 +1,262 @@ | |||
/* Copyright (c) 2014, Vsevolod Stakhov | |||
* All rights reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions are met: | |||
* * Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* * Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in the | |||
* documentation and/or other materials provided with the distribution. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY | |||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY | |||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
#ifndef DNS_PRIVATE_H_ | |||
#define DNS_PRIVATE_H_ | |||
#include "uthash.h" | |||
#include "utlist.h" | |||
#include "rdns.h" | |||
#include "upstream.h" | |||
#include "ref.h" | |||
static const unsigned base = 36; | |||
static const unsigned t_min = 1; | |||
static const unsigned t_max = 26; | |||
static const unsigned skew = 38; | |||
static const unsigned damp = 700; | |||
static const unsigned initial_n = 128; | |||
static const unsigned initial_bias = 72; | |||
static const int dns_port = 53; | |||
static const int default_io_cnt = 8; | |||
#define UDP_PACKET_SIZE 4096 | |||
#define DNS_COMPRESSION_BITS 0xC0 | |||
#define DNS_D_MAXLABEL 63 /* + 1 '\0' */ | |||
#define DNS_D_MAXNAME 255 /* + 1 '\0' */ | |||
#define RESOLV_CONF "/etc/resolv.conf" | |||
/** | |||
* Represents DNS server | |||
*/ | |||
struct rdns_server { | |||
char *name; | |||
unsigned int port; | |||
unsigned int io_cnt; | |||
struct rdns_io_channel **io_channels; | |||
upstream_entry_t up; | |||
}; | |||
struct rdns_request { | |||
struct rdns_resolver *resolver; | |||
struct rdns_async_context *async; | |||
struct rdns_io_channel *io; | |||
struct rdns_reply *reply; | |||
enum rdns_request_type type; | |||
double timeout; | |||
unsigned int retransmits; | |||
int id; | |||
struct rdns_request_name *requested_names; | |||
unsigned int qcount; | |||
enum { | |||
RDNS_REQUEST_NEW = 0, | |||
RDNS_REQUEST_REGISTERED = 1, | |||
RDNS_REQUEST_SENT, | |||
RDNS_REQUEST_REPLIED | |||
} state; | |||
uint8_t *packet; | |||
off_t pos; | |||
unsigned int packet_len; | |||
dns_callback_type func; | |||
void *arg; | |||
void *async_event; | |||
#ifdef TWEETNACL | |||
void *curve_plugin_data; | |||
#endif | |||
UT_hash_handle hh; | |||
ref_entry_t ref; | |||
}; | |||
/** | |||
* IO channel for a specific DNS server | |||
*/ | |||
struct rdns_io_channel { | |||
struct rdns_server *srv; | |||
struct rdns_resolver *resolver; | |||
int sock; /**< persistent socket */ | |||
bool active; | |||
void *async_io; /** async opaque ptr */ | |||
struct rdns_request *requests; /**< requests in flight */ | |||
uint64_t uses; | |||
ref_entry_t ref; | |||
}; | |||
struct rdns_resolver { | |||
struct rdns_server *servers; | |||
struct rdns_io_channel *io_channels; /**< hash of io chains indexed by socket */ | |||
struct rdns_async_context *async; /** async callbacks */ | |||
void *periodic; /** periodic event for resolver */ | |||
struct rdns_plugin *curve_plugin; | |||
rdns_log_function logger; | |||
void *log_data; | |||
enum rdns_log_level log_level; | |||
uint64_t max_ioc_uses; | |||
void *refresh_ioc_periodic; | |||
bool async_binded; | |||
bool initialized; | |||
ref_entry_t ref; | |||
}; | |||
struct dns_header; | |||
struct dns_query; | |||
/* Internal DNS structs */ | |||
struct dns_header { | |||
unsigned int qid :16; | |||
#if BYTE_ORDER == BIG_ENDIAN | |||
unsigned int qr:1; | |||
unsigned int opcode:4; | |||
unsigned int aa:1; | |||
unsigned int tc:1; | |||
unsigned int rd:1; | |||
unsigned int ra:1; | |||
unsigned int unused:3; | |||
unsigned int rcode:4; | |||
#else | |||
unsigned int rd :1; | |||
unsigned int tc :1; | |||
unsigned int aa :1; | |||
unsigned int opcode :4; | |||
unsigned int qr :1; | |||
unsigned int rcode :4; | |||
unsigned int unused :3; | |||
unsigned int ra :1; | |||
#endif | |||
unsigned int qdcount :16; | |||
unsigned int ancount :16; | |||
unsigned int nscount :16; | |||
unsigned int arcount :16; | |||
}; | |||
enum dns_section { | |||
DNS_S_QD = 0x01, | |||
#define DNS_S_QUESTION DNS_S_QD | |||
DNS_S_AN = 0x02, | |||
#define DNS_S_ANSWER DNS_S_AN | |||
DNS_S_NS = 0x04, | |||
#define DNS_S_AUTHORITY DNS_S_NS | |||
DNS_S_AR = 0x08, | |||
#define DNS_S_ADDITIONAL DNS_S_AR | |||
DNS_S_ALL = 0x0f | |||
}; | |||
/* enum dns_section */ | |||
enum dns_opcode { | |||
DNS_OP_QUERY = 0, | |||
DNS_OP_IQUERY = 1, | |||
DNS_OP_STATUS = 2, | |||
DNS_OP_NOTIFY = 4, | |||
DNS_OP_UPDATE = 5, | |||
}; | |||
/* dns_opcode */ | |||
enum dns_class { | |||
DNS_C_IN = 1, | |||
DNS_C_ANY = 255 | |||
}; | |||
/* enum dns_class */ | |||
struct dns_query { | |||
char *qname; | |||
unsigned int qtype :16; | |||
unsigned int qclass :16; | |||
}; | |||
enum dns_type { | |||
DNS_T_A = RDNS_REQUEST_A, | |||
DNS_T_NS = RDNS_REQUEST_NS, | |||
DNS_T_CNAME = 5, | |||
DNS_T_SOA = RDNS_REQUEST_SOA, | |||
DNS_T_PTR = RDNS_REQUEST_PTR, | |||
DNS_T_MX = RDNS_REQUEST_MX, | |||
DNS_T_TXT = RDNS_REQUEST_TXT, | |||
DNS_T_AAAA = RDNS_REQUEST_AAAA, | |||
DNS_T_SRV = RDNS_REQUEST_SRV, | |||
DNS_T_OPT = 41, | |||
DNS_T_SSHFP = 44, | |||
DNS_T_TLSA = RDNS_REQUEST_TLSA, | |||
DNS_T_SPF = RDNS_REQUEST_SPF, | |||
DNS_T_ALL = RDNS_REQUEST_ANY | |||
}; | |||
/* enum dns_type */ | |||
static const char dns_rcodes[][32] = { | |||
[RDNS_RC_NOERROR] = "no error", | |||
[RDNS_RC_FORMERR] = "query format error", | |||
[RDNS_RC_SERVFAIL] = "server fail", | |||
[RDNS_RC_NXDOMAIN] = "no records with this name", | |||
[RDNS_RC_NOTIMP] = "not implemented", | |||
[RDNS_RC_REFUSED] = "query refused", | |||
[RDNS_RC_YXDOMAIN] = "YXDOMAIN", | |||
[RDNS_RC_YXRRSET] = "YXRRSET", | |||
[RDNS_RC_NXRRSET] = "NXRRSET", | |||
[RDNS_RC_NOTAUTH] = "not authorized", | |||
[RDNS_RC_NOTZONE] = "no such zone", | |||
[RDNS_RC_TIMEOUT] = "query timed out", | |||
[RDNS_RC_NETERR] = "network error", | |||
[RDNS_RC_NOREC] = "requested record is not found" | |||
}; | |||
static const char dns_types[][16] = { | |||
[RDNS_REQUEST_A] = "A request", | |||
[RDNS_REQUEST_NS] = "NS request", | |||
[RDNS_REQUEST_PTR] = "PTR request", | |||
[RDNS_REQUEST_MX] = "MX request", | |||
[RDNS_REQUEST_TXT] = "TXT request", | |||
[RDNS_REQUEST_SRV] = "SRV request", | |||
[RDNS_REQUEST_SPF] = "SPF request", | |||
[RDNS_REQUEST_AAAA] = "AAAA request", | |||
[RDNS_REQUEST_TLSA] = "TLSA request", | |||
[RDNS_REQUEST_ANY] = "ANY request" | |||
}; | |||
#endif /* DNS_PRIVATE_H_ */ |
@@ -0,0 +1,53 @@ | |||
/* Copyright (c) 2014, Vsevolod Stakhov | |||
* All rights reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions are met: | |||
* * Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* * Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in the | |||
* documentation and/or other materials provided with the distribution. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY | |||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY | |||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
#include <stdio.h> | |||
#include "dns_private.h" | |||
#include "logger.h" | |||
void | |||
rdns_logger_internal (void *log_data, enum rdns_log_level level, | |||
const char *function, const char *format, | |||
va_list args) | |||
{ | |||
struct rdns_resolver *resolver = log_data; | |||
if (level <= resolver->log_level) { | |||
fprintf (stderr, "rdns: %s: ", function); | |||
vfprintf (stderr, format, args); | |||
fprintf (stderr, "\n"); | |||
} | |||
} | |||
void rdns_logger_helper (struct rdns_resolver *resolver, | |||
enum rdns_log_level level, | |||
const char *function, const char *format, ...) | |||
{ | |||
va_list va; | |||
if (resolver->logger != NULL) { | |||
va_start (va, format); | |||
resolver->logger (resolver->log_data, level, function, format, va); | |||
va_end (va); | |||
} | |||
} |
@@ -0,0 +1,42 @@ | |||
/* Copyright (c) 2014, Vsevolod Stakhov | |||
* All rights reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions are met: | |||
* * Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* * Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in the | |||
* documentation and/or other materials provided with the distribution. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY | |||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY | |||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
#ifndef LOGGER_H_ | |||
#define LOGGER_H_ | |||
#include <stdarg.h> | |||
#include "dns_private.h" | |||
void rdns_logger_internal (void *log_data, enum rdns_log_level level, | |||
const char *function, const char *format, | |||
va_list args); | |||
void rdns_logger_helper (struct rdns_resolver *resolver, | |||
enum rdns_log_level level, | |||
const char *function, const char *format, ...); | |||
#define rdns_err(...) do { rdns_logger_helper (resolver, RDNS_LOG_ERROR, __FUNCTION__, __VA_ARGS__); } while (0) | |||
#define rdns_warn(...) do { rdns_logger_helper (resolver, RDNS_LOG_WARNING, __FUNCTION__, __VA_ARGS__); } while (0) | |||
#define rdns_info(...) do { rdns_logger_helper (resolver, RDNS_LOG_INFO, __FUNCTION__, __VA_ARGS__); } while (0) | |||
#define rdns_debug(...) do { rdns_logger_helper (resolver, RDNS_LOG_DEBUG, __FUNCTION__, __VA_ARGS__); } while (0) | |||
#endif /* LOGGER_H_ */ |
@@ -0,0 +1,271 @@ | |||
/* Copyright (c) 2014, Vsevolod Stakhov | |||
* All rights reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions are met: | |||
* * Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* * Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in the | |||
* documentation and/or other materials provided with the distribution. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY | |||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY | |||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
#include "rdns.h" | |||
#include "dns_private.h" | |||
#include "punycode.h" | |||
#include "packet.h" | |||
#include "util.h" | |||
#include "logger.h" | |||
#include "compression.h" | |||
void | |||
rdns_allocate_packet (struct rdns_request* req, unsigned int namelen) | |||
{ | |||
namelen += 96 + 2 + 4 + 11; /* EDNS0 RR */ | |||
req->packet = malloc (namelen); | |||
req->pos = 0; | |||
req->packet_len = namelen; | |||
} | |||
void | |||
rdns_make_dns_header (struct rdns_request *req, unsigned int qcount) | |||
{ | |||
struct dns_header *header; | |||
/* Set DNS header values */ | |||
header = (struct dns_header *)req->packet; | |||
memset (header, 0 , sizeof (struct dns_header)); | |||
header->qid = rdns_permutor_generate_id (); | |||
header->rd = 1; | |||
header->qdcount = htons (qcount); | |||
header->arcount = htons (1); | |||
req->pos += sizeof (struct dns_header); | |||
req->id = header->qid; | |||
} | |||
static bool | |||
rdns_maybe_punycode_label (const uint8_t *begin, | |||
uint8_t const **dot, size_t *label_len) | |||
{ | |||
bool ret = false; | |||
const uint8_t *p = begin; | |||
*dot = NULL; | |||
while (*p) { | |||
if (*p == '.') { | |||
*dot = p; | |||
break; | |||
} | |||
else if ((*p) & 0x80) { | |||
ret = true; | |||
} | |||
p ++; | |||
} | |||
if (*p) { | |||
*label_len = p - begin; | |||
} | |||
else { | |||
*label_len = p - begin; | |||
} | |||
return ret; | |||
} | |||
bool | |||
rdns_format_dns_name (struct rdns_resolver *resolver, const char *in, | |||
size_t inlen, | |||
char **out, size_t *outlen) | |||
{ | |||
const uint8_t *dot; | |||
const uint8_t *p = in, *end = in + inlen; | |||
char *o; | |||
int labels = 0; | |||
size_t label_len, olen, remain; | |||
uint32_t *uclabel; | |||
size_t punylabel_len, uclabel_len; | |||
char tmp_label[DNS_D_MAXLABEL]; | |||
bool need_encode = false; | |||
if (inlen == 0) { | |||
inlen = strlen (in); | |||
} | |||
/* Check for non-ascii characters */ | |||
while (p != end) { | |||
if (*p >= 0x80) { | |||
need_encode = true; | |||
} | |||
else if (*p == '.') { | |||
labels ++; | |||
} | |||
p ++; | |||
} | |||
if (!need_encode) { | |||
*out = malloc (inlen + 1); | |||
if (*out == NULL) { | |||
return false; | |||
} | |||
o = *out; | |||
memcpy (o, in, inlen); | |||
o[inlen] = '\0'; | |||
*outlen = inlen; | |||
return true; | |||
} | |||
/* We need to encode */ | |||
p = in; | |||
olen = inlen + 1 + sizeof ("xn--") * labels; | |||
*out = malloc (olen); | |||
if (*out == NULL) { | |||
return false; | |||
} | |||
o = *out; | |||
remain = olen; | |||
while (p != end) { | |||
/* Check label for unicode characters */ | |||
if (rdns_maybe_punycode_label (p, &dot, &label_len)) { | |||
/* Convert to ucs4 */ | |||
if (rdns_utf8_to_ucs4 (p, label_len, &uclabel, &uclabel_len) == 0) { | |||
punylabel_len = DNS_D_MAXLABEL; | |||
rdns_punycode_label_toascii (uclabel, uclabel_len, | |||
tmp_label, &punylabel_len); | |||
if (remain >= punylabel_len + 1) { | |||
memcpy (o, tmp_label, punylabel_len); | |||
o += punylabel_len; | |||
*o++ = '.'; | |||
remain -= punylabel_len + 1; | |||
} | |||
else { | |||
rdns_info ("no buffer remain for punycoding query"); | |||
free (*out); | |||
return false; | |||
} | |||
free (uclabel); | |||
if (dot) { | |||
p = dot + 1; | |||
} | |||
else { | |||
break; | |||
} | |||
} | |||
else { | |||
break; | |||
} | |||
} | |||
else { | |||
if (dot) { | |||
if (label_len > DNS_D_MAXLABEL) { | |||
rdns_info ("dns name component is longer than 63 bytes, should be stripped"); | |||
label_len = DNS_D_MAXLABEL; | |||
} | |||
if (remain < label_len + 1) { | |||
rdns_info ("no buffer remain for punycoding query"); | |||
return false; | |||
} | |||
if (label_len == 0) { | |||
/* Two dots in order, skip this */ | |||
rdns_info ("name contains two or more dots in a row, replace it with one dot"); | |||
p = dot + 1; | |||
continue; | |||
} | |||
memcpy (o, p, label_len); | |||
o += label_len; | |||
*o++ = '.'; | |||
remain -= label_len + 1; | |||
p = dot + 1; | |||
} | |||
else { | |||
if (label_len == 0) { | |||
/* If name is ended with dot */ | |||
break; | |||
} | |||
if (label_len > DNS_D_MAXLABEL) { | |||
rdns_info ("dns name component is longer than 63 bytes, should be stripped"); | |||
label_len = DNS_D_MAXLABEL; | |||
} | |||
if (remain < label_len + 1) { | |||
rdns_info ("no buffer remain for punycoding query"); | |||
return false; | |||
} | |||
memcpy (o, p, label_len); | |||
o += label_len; | |||
*o++ = '.'; | |||
remain -= label_len + 1; | |||
p = dot + 1; | |||
break; | |||
} | |||
} | |||
if (remain == 0) { | |||
rdns_info ("no buffer remain for punycoding query"); | |||
return false; | |||
} | |||
} | |||
*o = '\0'; | |||
*outlen = o - *out; | |||
return true; | |||
} | |||
bool | |||
rdns_add_rr (struct rdns_request *req, const char *name, unsigned int len, | |||
enum dns_type type, struct rdns_compression_entry **comp) | |||
{ | |||
uint16_t *p; | |||
if (!rdns_write_name_compressed (req, name, len, comp)) { | |||
return false; | |||
} | |||
p = (uint16_t *)(req->packet + req->pos); | |||
*p++ = htons (type); | |||
*p = htons (DNS_C_IN); | |||
req->pos += sizeof (uint16_t) * 2; | |||
return true; | |||
} | |||
bool | |||
rdns_add_edns0 (struct rdns_request *req) | |||
{ | |||
uint8_t *p8; | |||
uint16_t *p16; | |||
p8 = (uint8_t *)(req->packet + req->pos); | |||
*p8 = '\0'; /* Name is root */ | |||
p16 = (uint16_t *)(req->packet + req->pos + 1); | |||
*p16++ = htons (DNS_T_OPT); | |||
/* UDP packet length */ | |||
*p16++ = htons (UDP_PACKET_SIZE); | |||
/* Extended rcode 00 00 */ | |||
*p16++ = 0; | |||
/* Z 10000000 00000000 to allow dnssec, disabled currently */ | |||
*p16++ = 0; | |||
/* Length */ | |||
*p16 = 0; | |||
req->pos += sizeof (uint8_t) + sizeof (uint16_t) * 5; | |||
return true; | |||
} |
@@ -0,0 +1,71 @@ | |||
/* Copyright (c) 2014, Vsevolod Stakhov | |||
* All rights reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions are met: | |||
* * Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* * Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in the | |||
* documentation and/or other materials provided with the distribution. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY | |||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY | |||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
#ifndef PACKET_H_ | |||
#define PACKET_H_ | |||
#include <stdbool.h> | |||
#include <stdint.h> | |||
#include "dns_private.h" | |||
struct rdns_compression_entry; | |||
/** | |||
* Allocate dns packet suitable to handle up to `namelen` name | |||
* @param req request | |||
* @param namelen requested name | |||
*/ | |||
void rdns_allocate_packet (struct rdns_request* req, unsigned int namelen); | |||
/** | |||
* Add basic header to the dns packet | |||
* @param req | |||
*/ | |||
void rdns_make_dns_header (struct rdns_request *req, unsigned int qcount); | |||
/** | |||
* Format DNS name of the packet | |||
* @param req request | |||
* @param name name string | |||
* @param namelen length of name | |||
*/ | |||
bool rdns_format_dns_name (struct rdns_resolver *resolver, | |||
const char *name, size_t namelen, | |||
char **out, size_t *outlen); | |||
/** | |||
* Add a resource record to the DNS packet | |||
* @param req request | |||
* @param name requested name | |||
* @param type type of resource record | |||
*/ | |||
bool rdns_add_rr (struct rdns_request *req, const char *name, unsigned int len, | |||
enum dns_type type, struct rdns_compression_entry **comp); | |||
/** | |||
* Add EDNS0 section | |||
* @param req | |||
*/ | |||
bool rdns_add_edns0 (struct rdns_request *req); | |||
#endif /* PACKET_H_ */ |
@@ -0,0 +1,419 @@ | |||
/* Copyright (c) 2014, Vsevolod Stakhov | |||
* All rights reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions are met: | |||
* * Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* * Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in the | |||
* documentation and/or other materials provided with the distribution. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY | |||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY | |||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
#include "rdns.h" | |||
#include "dns_private.h" | |||
#include "parse.h" | |||
#include "logger.h" | |||
static uint8_t * | |||
rdns_decompress_label (uint8_t *begin, uint16_t *len, uint16_t max) | |||
{ | |||
uint16_t offset = (*len); | |||
if (offset > max) { | |||
return NULL; | |||
} | |||
*len = *(begin + offset); | |||
return begin + offset; | |||
} | |||
#define UNCOMPRESS_DNS_OFFSET(p) (((*(p)) ^ DNS_COMPRESSION_BITS) << 8) + *((p) + 1) | |||
uint8_t * | |||
rdns_request_reply_cmp (struct rdns_request *req, uint8_t *in, int len) | |||
{ | |||
uint8_t *p, *c, *l1, *l2; | |||
uint16_t len1, len2; | |||
int decompressed = 0; | |||
struct rdns_resolver *resolver = req->resolver; | |||
/* QR format: | |||
* labels - len:octets | |||
* null label - 0 | |||
* class - 2 octets | |||
* type - 2 octets | |||
*/ | |||
/* In p we would store current position in reply and in c - position in request */ | |||
p = in; | |||
c = req->packet + req->pos; | |||
for (;;) { | |||
/* Get current label */ | |||
len1 = *p; | |||
len2 = *c; | |||
if (p - in > len) { | |||
rdns_info ("invalid dns reply"); | |||
return NULL; | |||
} | |||
/* This may be compressed, so we need to decompress it */ | |||
if (len1 & DNS_COMPRESSION_BITS) { | |||
len1 = UNCOMPRESS_DNS_OFFSET(p); | |||
l1 = rdns_decompress_label (in, &len1, len); | |||
if (l1 == NULL) { | |||
return NULL; | |||
} | |||
decompressed ++; | |||
l1 ++; | |||
p += 2; | |||
} | |||
else { | |||
l1 = ++p; | |||
p += len1; | |||
} | |||
if (len2 & DNS_COMPRESSION_BITS) { | |||
len2 = UNCOMPRESS_DNS_OFFSET(c); | |||
l2 = rdns_decompress_label (c, &len2, len); | |||
if (l2 == NULL) { | |||
rdns_info ("invalid DNS pointer, cannot decompress"); | |||
return NULL; | |||
} | |||
decompressed ++; | |||
l2 ++; | |||
c += 2; | |||
} | |||
else { | |||
l2 = ++c; | |||
c += len2; | |||
} | |||
if (len1 != len2) { | |||
return NULL; | |||
} | |||
if (len1 == 0) { | |||
break; | |||
} | |||
if (memcmp (l1, l2, len1) != 0) { | |||
return NULL; | |||
} | |||
if (decompressed == 2) { | |||
break; | |||
} | |||
} | |||
/* p now points to the end of QR section */ | |||
/* Compare class and type */ | |||
if (memcmp (p, c, sizeof (uint16_t) * 2) == 0) { | |||
req->pos = c - req->packet + sizeof (uint16_t) * 2; | |||
return p + sizeof (uint16_t) * 2; | |||
} | |||
return NULL; | |||
} | |||
#define MAX_RECURSION_LEVEL 10 | |||
bool | |||
rdns_parse_labels (struct rdns_resolver *resolver, | |||
uint8_t *in, char **target, uint8_t **pos, struct rdns_reply *rep, | |||
int *remain, bool make_name) | |||
{ | |||
uint16_t namelen = 0; | |||
uint8_t *p = *pos, *begin = *pos, *l, *t, *end = *pos + *remain, *new_pos = *pos; | |||
uint16_t llen; | |||
int length = *remain, new_remain = *remain; | |||
int ptrs = 0, labels = 0; | |||
bool got_compression = false; | |||
/* First go through labels and calculate name length */ | |||
while (p - begin < length) { | |||
if (ptrs > MAX_RECURSION_LEVEL) { | |||
rdns_info ("dns pointers are nested too much"); | |||
return false; | |||
} | |||
llen = *p; | |||
if (llen == 0) { | |||
if (!got_compression) { | |||
/* In case of compression we have already decremented the processing position */ | |||
new_remain -= sizeof (uint8_t); | |||
new_pos += sizeof (uint8_t); | |||
} | |||
break; | |||
} | |||
else if ((llen & DNS_COMPRESSION_BITS)) { | |||
if (end - p > 1) { | |||
ptrs ++; | |||
llen = UNCOMPRESS_DNS_OFFSET(p); | |||
l = rdns_decompress_label (in, &llen, end - in); | |||
if (l == NULL) { | |||
rdns_info ("invalid DNS pointer"); | |||
return false; | |||
} | |||
if (!got_compression) { | |||
/* Our label processing is finished actually */ | |||
new_remain -= sizeof (uint16_t); | |||
new_pos += sizeof (uint16_t); | |||
got_compression = true; | |||
} | |||
if (l < in || l > begin + length) { | |||
rdns_info ("invalid pointer in DNS packet"); | |||
return false; | |||
} | |||
begin = l; | |||
length = end - begin; | |||
p = l + *l + 1; | |||
namelen += *l; | |||
labels ++; | |||
} | |||
else { | |||
rdns_info ("DNS packet has incomplete compressed label, input length: %d bytes, remain: %d", | |||
*remain, new_remain); | |||
return false; | |||
} | |||
} | |||
else { | |||
namelen += llen; | |||
p += llen + 1; | |||
labels ++; | |||
if (!got_compression) { | |||
new_remain -= llen + 1; | |||
new_pos += llen + 1; | |||
} | |||
} | |||
} | |||
if (!make_name) { | |||
goto end; | |||
} | |||
*target = malloc (namelen + labels + 3); | |||
t = (uint8_t *)*target; | |||
p = *pos; | |||
begin = *pos; | |||
length = *remain; | |||
/* Now copy labels to name */ | |||
while (p - begin < length) { | |||
llen = *p; | |||
if (llen == 0) { | |||
break; | |||
} | |||
else if (llen & DNS_COMPRESSION_BITS) { | |||
llen = UNCOMPRESS_DNS_OFFSET(p); | |||
l = rdns_decompress_label (in, &llen, end - in); | |||
begin = l; | |||
length = end - begin; | |||
p = l + *l + 1; | |||
memcpy (t, l + 1, *l); | |||
t += *l; | |||
*t ++ = '.'; | |||
} | |||
else { | |||
memcpy (t, p + 1, *p); | |||
t += *p; | |||
*t ++ = '.'; | |||
p += *p + 1; | |||
} | |||
} | |||
if (t > (uint8_t *)*target) { | |||
*(t - 1) = '\0'; | |||
} | |||
else { | |||
/* Handle empty labels */ | |||
**target = '\0'; | |||
} | |||
end: | |||
*remain = new_remain; | |||
*pos = new_pos; | |||
return true; | |||
} | |||
#define GET8(x) do {(x) = ((*p)); p += sizeof (uint8_t); *remain -= sizeof (uint8_t); } while(0) | |||
#define GET16(x) do {(x) = ((*p) << 8) + *(p + 1); p += sizeof (uint16_t); *remain -= sizeof (uint16_t); } while(0) | |||
#define GET32(x) do {(x) = ((*p) << 24) + ((*(p + 1)) << 16) + ((*(p + 2)) << 8) + *(p + 3); p += sizeof (uint32_t); *remain -= sizeof (uint32_t); } while(0) | |||
#define SKIP(type) do { p += sizeof(type); *remain -= sizeof(type); } while (0) | |||
int | |||
rdns_parse_rr (struct rdns_resolver *resolver, | |||
uint8_t *in, struct rdns_reply_entry *elt, uint8_t **pos, | |||
struct rdns_reply *rep, int *remain) | |||
{ | |||
uint8_t *p = *pos, parts; | |||
uint16_t type, datalen, txtlen, copied; | |||
int32_t ttl; | |||
bool parsed = false; | |||
/* Skip the whole name */ | |||
if (! rdns_parse_labels (resolver, in, NULL, &p, rep, remain, false)) { | |||
rdns_info ("bad RR name"); | |||
return -1; | |||
} | |||
if (*remain < (int)sizeof (uint16_t) * 6) { | |||
rdns_info ("stripped dns reply: %d bytes remain", *remain); | |||
return -1; | |||
} | |||
GET16 (type); | |||
/* Skip class */ | |||
SKIP (uint16_t); | |||
GET32 (ttl); | |||
GET16 (datalen); | |||
elt->type = type; | |||
/* Now p points to RR data */ | |||
switch (type) { | |||
case DNS_T_A: | |||
if (!(datalen & 0x3) && datalen <= *remain) { | |||
memcpy (&elt->content.a.addr, p, sizeof (struct in_addr)); | |||
p += datalen; | |||
*remain -= datalen; | |||
parsed = true; | |||
} | |||
else { | |||
rdns_info ("corrupted A record"); | |||
return -1; | |||
} | |||
break; | |||
case DNS_T_AAAA: | |||
if (datalen == sizeof (struct in6_addr) && datalen <= *remain) { | |||
memcpy (&elt->content.aaa.addr, p, sizeof (struct in6_addr)); | |||
p += datalen; | |||
*remain -= datalen; | |||
parsed = true; | |||
} | |||
else { | |||
rdns_info ("corrupted AAAA record"); | |||
return -1; | |||
} | |||
break; | |||
case DNS_T_PTR: | |||
if (! rdns_parse_labels (resolver, in, &elt->content.ptr.name, &p, | |||
rep, remain, true)) { | |||
rdns_info ("invalid labels in PTR record"); | |||
return -1; | |||
} | |||
parsed = true; | |||
break; | |||
case DNS_T_NS: | |||
if (! rdns_parse_labels (resolver, in, &elt->content.ns.name, &p, | |||
rep, remain, true)) { | |||
rdns_info ("invalid labels in NS record"); | |||
return -1; | |||
} | |||
parsed = true; | |||
break; | |||
case DNS_T_SOA: | |||
if (! rdns_parse_labels (resolver, in, &elt->content.soa.mname, &p, | |||
rep, remain, true)) { | |||
rdns_info ("invalid labels in NS record"); | |||
return -1; | |||
} | |||
if (! rdns_parse_labels (resolver, in, &elt->content.soa.admin, &p, | |||
rep, remain, true)) { | |||
rdns_info ("invalid labels in NS record"); | |||
return -1; | |||
} | |||
GET32 (elt->content.soa.serial); | |||
GET32 (elt->content.soa.refresh); | |||
GET32 (elt->content.soa.retry); | |||
GET32 (elt->content.soa.expire); | |||
GET32 (elt->content.soa.minimum); | |||
parsed = true; | |||
break; | |||
case DNS_T_MX: | |||
GET16 (elt->content.mx.priority); | |||
if (! rdns_parse_labels (resolver, in, &elt->content.mx.name, &p, | |||
rep, remain, true)) { | |||
rdns_info ("invalid labels in MX record"); | |||
return -1; | |||
} | |||
parsed = true; | |||
break; | |||
case DNS_T_TXT: | |||
case DNS_T_SPF: | |||
elt->content.txt.data = malloc (datalen + 1); | |||
if (elt->content.txt.data == NULL) { | |||
rdns_err ("failed to allocate %d bytes for TXT record", (int)datalen + 1); | |||
return -1; | |||
} | |||
/* Now we should compose data from parts */ | |||
copied = 0; | |||
parts = 0; | |||
while (copied + parts < datalen) { | |||
txtlen = *p; | |||
if (txtlen + copied + parts <= datalen) { | |||
parts ++; | |||
memcpy (elt->content.txt.data + copied, p + 1, txtlen); | |||
copied += txtlen; | |||
p += txtlen + 1; | |||
*remain -= txtlen + 1; | |||
} | |||
else { | |||
break; | |||
} | |||
} | |||
*(elt->content.txt.data + copied) = '\0'; | |||
parsed = true; | |||
elt->type = RDNS_REQUEST_TXT; | |||
break; | |||
case DNS_T_SRV: | |||
if (p - *pos > (int)(*remain - sizeof (uint16_t) * 3)) { | |||
rdns_info ("stripped dns reply while reading SRV record"); | |||
return -1; | |||
} | |||
GET16 (elt->content.srv.priority); | |||
GET16 (elt->content.srv.weight); | |||
GET16 (elt->content.srv.port); | |||
if (! rdns_parse_labels (resolver, in, &elt->content.srv.target, | |||
&p, rep, remain, true)) { | |||
rdns_info ("invalid labels in SRV record"); | |||
return -1; | |||
} | |||
parsed = true; | |||
break; | |||
case DNS_T_TLSA: | |||
if (p - *pos > (int)(*remain - sizeof (uint8_t) * 3) || datalen <= 3) { | |||
rdns_info ("stripped dns reply while reading TLSA record"); | |||
return -1; | |||
} | |||
GET8 (elt->content.tlsa.usage); | |||
GET8 (elt->content.tlsa.selector); | |||
GET8 (elt->content.tlsa.match_type); | |||
datalen -= 3; | |||
elt->content.tlsa.data = malloc (datalen); | |||
if (elt->content.tlsa.data == NULL) { | |||
rdns_err ("failed to allocate %d bytes for TLSA record", (int)datalen + 1); | |||
return -1; | |||
} | |||
elt->content.tlsa.datalen = datalen; | |||
memcpy (elt->content.tlsa.data, p, datalen); | |||
p += datalen; | |||
*remain -= datalen; | |||
parsed = true; | |||
break; | |||
case DNS_T_CNAME: | |||
/* Skip cname records */ | |||
p += datalen; | |||
*remain -= datalen; | |||
break; | |||
default: | |||
rdns_debug ("unexpected RR type: %d", type); | |||
p += datalen; | |||
*remain -= datalen; | |||
break; | |||
} | |||
*pos = p; | |||
if (parsed) { | |||
elt->ttl = ttl; | |||
return 1; | |||
} | |||
return 0; | |||
} |
@@ -0,0 +1,65 @@ | |||
/* Copyright (c) 2014, Vsevolod Stakhov | |||
* All rights reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions are met: | |||
* * Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* * Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in the | |||
* documentation and/or other materials provided with the distribution. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY | |||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY | |||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
#ifndef PARSE_H_ | |||
#define PARSE_H_ | |||
#include "dns_private.h" | |||
/** | |||
* Compare request and reply checking names | |||
* @param req request object | |||
* @param in incoming packet | |||
* @param len length of the incoming packet | |||
* @return new position in the incoming packet or NULL if request is not equal to reply | |||
*/ | |||
uint8_t * rdns_request_reply_cmp (struct rdns_request *req, uint8_t *in, int len); | |||
/** | |||
* Parse labels in the packet | |||
* @param in incoming packet | |||
* @param target target to write the parsed label (out) | |||
* @param pos output position in the packet (it/out) | |||
* @param rep dns reply | |||
* @param remain remaining bytes (in/out) | |||
* @param make_name create name or just skip to the next label | |||
* @return true if a label has been successfully parsed | |||
*/ | |||
bool rdns_parse_labels (struct rdns_resolver *resolver, | |||
uint8_t *in, char **target, | |||
uint8_t **pos, struct rdns_reply *rep, | |||
int *remain, bool make_name); | |||
/** | |||
* Parse resource record | |||
* @param in incoming packet | |||
* @param elt new reply entry | |||
* @param pos output position in the packet (it/out) | |||
* @param rep dns reply | |||
* @param remain remaining bytes (in/out) | |||
* @return 1 if rr has been parsed, 0 if rr has been skipped and -1 if there was a parsing error | |||
*/ | |||
int rdns_parse_rr (struct rdns_resolver *resolver, | |||
uint8_t *in, struct rdns_reply_entry *elt, uint8_t **pos, | |||
struct rdns_reply *rep, int *remain); | |||
#endif /* PARSE_H_ */ |
@@ -0,0 +1,297 @@ | |||
/* | |||
* Copyright (c) 2014, Vsevolod Stakhov | |||
* Copyright (c) 2004, 2006, 2007, 2008 Kungliga Tekniska Högskolan | |||
* (Royal Institute of Technology, Stockholm, Sweden). | |||
* All rights reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions | |||
* are met: | |||
* | |||
* 1. Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* | |||
* 2. Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in the | |||
* documentation and/or other materials provided with the distribution. | |||
* | |||
* 3. Neither the name of the Institute nor the names of its contributors | |||
* may be used to endorse or promote products derived from this software | |||
* without specific prior written permission. | |||
* | |||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND | |||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE | |||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
* SUCH DAMAGE. | |||
*/ | |||
#include "dns_private.h" | |||
/* Punycode utility */ | |||
static unsigned int | |||
digit (unsigned n) | |||
{ | |||
static const char ascii[] = "abcdefghijklmnopqrstuvwxyz0123456789"; | |||
return ascii[n]; | |||
} | |||
static unsigned int | |||
adapt (unsigned int delta, unsigned int numpoints, int first) | |||
{ | |||
unsigned int k; | |||
if (first) { | |||
delta = delta / damp; | |||
} | |||
else { | |||
delta /= 2; | |||
} | |||
delta += delta / numpoints; | |||
k = 0; | |||
while (delta > ((base - t_min) * t_max) / 2) { | |||
delta /= base - t_min; | |||
k += base; | |||
} | |||
return k + (((base - t_min + 1) * delta) / (delta + skew)); | |||
} | |||
/** | |||
* Convert an UCS4 string to a puny-coded DNS label string suitable | |||
* when combined with delimiters and other labels for DNS lookup. | |||
* | |||
* @param in an UCS4 string to convert | |||
* @param in_len the length of in. | |||
* @param out the resulting puny-coded string. The string is not NULL | |||
* terminated. | |||
* @param out_len before processing out_len should be the length of | |||
* the out variable, after processing it will be the length of the out | |||
* string. | |||
* | |||
* @return returns 0 on success, an wind error code otherwise | |||
*/ | |||
bool | |||
rdns_punycode_label_toascii (const uint32_t *in, size_t in_len, char *out, | |||
size_t *out_len) | |||
{ | |||
unsigned int n = initial_n; | |||
unsigned int delta = 0; | |||
unsigned int bias = initial_bias; | |||
unsigned int h = 0; | |||
unsigned int b; | |||
unsigned int i; | |||
unsigned int o = 0; | |||
unsigned int m; | |||
for (i = 0; i < in_len; ++i) { | |||
if (in[i] < 0x80) { | |||
++h; | |||
if (o >= *out_len) { | |||
return false; | |||
} | |||
out[o++] = in[i]; | |||
} | |||
} | |||
b = h; | |||
if (b > 0) { | |||
if (o >= *out_len) { | |||
return false; | |||
} | |||
out[o++] = 0x2D; | |||
} | |||
/* is this string punycoded */ | |||
if (h < in_len) { | |||
if (o + 4 >= *out_len) { | |||
return false; | |||
} | |||
memmove (out + 4, out, o); | |||
memcpy (out, "xn--", 4); | |||
o += 4; | |||
} | |||
while (h < in_len) { | |||
m = (unsigned int) -1; | |||
for (i = 0; i < in_len; ++i) { | |||
if (in[i] < m && in[i] >= n) { | |||
m = in[i]; | |||
} | |||
} | |||
delta += (m - n) * (h + 1); | |||
n = m; | |||
for (i = 0; i < in_len; ++i) { | |||
if (in[i] < n) { | |||
++delta; | |||
} | |||
else if (in[i] == n) { | |||
unsigned int q = delta; | |||
unsigned int k; | |||
for (k = base;; k += base) { | |||
unsigned int t; | |||
if (k <= bias) { | |||
t = t_min; | |||
} | |||
else if (k >= bias + t_max) { | |||
t = t_max; | |||
} | |||
else { | |||
t = k - bias; | |||
} | |||
if (q < t) { | |||
break; | |||
} | |||
if (o >= *out_len) { | |||
return -1; | |||
} | |||
out[o++] = digit (t + ((q - t) % (base - t))); | |||
q = (q - t) / (base - t); | |||
} | |||
if (o >= *out_len) { | |||
return -1; | |||
} | |||
out[o++] = digit (q); | |||
/* output */ | |||
bias = adapt (delta, h + 1, h == b); | |||
delta = 0; | |||
++h; | |||
} | |||
} | |||
++delta; | |||
++n; | |||
} | |||
*out_len = o; | |||
return true; | |||
} | |||
static int | |||
utf8toutf32 (const unsigned char **pp, uint32_t *out, size_t *remain) | |||
{ | |||
const unsigned char *p = *pp; | |||
unsigned c = *p; | |||
size_t reduce; | |||
if (c & 0x80) { | |||
if ((c & 0xE0) == 0xC0 && *remain >= 2) { | |||
const unsigned c2 = *++p; | |||
reduce = 2; | |||
if ((c2 & 0xC0) == 0x80) { | |||
*out = ((c & 0x1F) << 6) | (c2 & 0x3F); | |||
} | |||
else { | |||
return -1; | |||
} | |||
} | |||
else if ((c & 0xF0) == 0xE0 && *remain >= 3) { | |||
const unsigned c2 = *++p; | |||
if ((c2 & 0xC0) == 0x80) { | |||
const unsigned c3 = *++p; | |||
reduce = 3; | |||
if ((c3 & 0xC0) == 0x80) { | |||
*out = ((c & 0x0F) << 12) | ((c2 & 0x3F) << 6) | |||
| (c3 & 0x3F); | |||
} | |||
else { | |||
return -1; | |||
} | |||
} | |||
else { | |||
return -1; | |||
} | |||
} | |||
else if ((c & 0xF8) == 0xF0 && *remain >= 4) { | |||
const unsigned c2 = *++p; | |||
if ((c2 & 0xC0) == 0x80) { | |||
const unsigned c3 = *++p; | |||
if ((c3 & 0xC0) == 0x80) { | |||
const unsigned c4 = *++p; | |||
reduce = 4; | |||
if ((c4 & 0xC0) == 0x80) { | |||
*out = ((c & 0x07) << 18) | ((c2 & 0x3F) << 12) | |||
| ((c3 & 0x3F) << 6) | (c4 & 0x3F); | |||
} | |||
else { | |||
return -1; | |||
} | |||
} | |||
else { | |||
return -1; | |||
} | |||
} | |||
else { | |||
return -1; | |||
} | |||
} | |||
else { | |||
return -1; | |||
} | |||
} | |||
else { | |||
*out = c; | |||
reduce = 1; | |||
} | |||
*pp = ++p; | |||
*remain -= reduce; | |||
return 0; | |||
} | |||
/** | |||
* Convert an UTF-8 string to an UCS4 string. | |||
* | |||
* @param in an UTF-8 string to convert. | |||
* @param out the resulting UCS4 string | |||
* @param out_len before processing out_len should be the length of | |||
* the out variable, after processing it will be the length of the out | |||
* string. | |||
* | |||
* @return returns 0 on success, an -1 otherwise | |||
* @ingroup wind | |||
*/ | |||
int | |||
rdns_utf8_to_ucs4 (const char *in, size_t in_len, uint32_t **out, size_t *out_len) | |||
{ | |||
const unsigned char *p; | |||
size_t remain = in_len, olen = 0; | |||
int ret; | |||
uint32_t *res; | |||
p = (const unsigned char *)in; | |||
while (remain > 0) { | |||
uint32_t u; | |||
ret = utf8toutf32 (&p, &u, &remain); | |||
if (ret != 0) { | |||
return ret; | |||
} | |||
olen ++; | |||
} | |||
res = malloc (olen * sizeof (uint32_t)); | |||
if (res == NULL) { | |||
return -1; | |||
} | |||
p = (const unsigned char *)in; | |||
remain = in_len; | |||
olen = 0; | |||
while (remain > 0) { | |||
uint32_t u; | |||
(void)utf8toutf32 (&p, &u, &remain); | |||
res[olen++] = u; | |||
} | |||
*out_len = olen; | |||
*out = res; | |||
return 0; | |||
} |
@@ -0,0 +1,59 @@ | |||
/* Copyright (c) 2014, Vsevolod Stakhov | |||
* All rights reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions are met: | |||
* * Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* * Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in the | |||
* documentation and/or other materials provided with the distribution. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY | |||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY | |||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
#ifndef PUNYCODE_H_ | |||
#define PUNYCODE_H_ | |||
#include <stdbool.h> | |||
#include <stdint.h> | |||
/** | |||
* Convert an UCS4 string to a puny-coded DNS label string suitable | |||
* when combined with delimiters and other labels for DNS lookup. | |||
* | |||
* @param in an UCS4 string to convert | |||
* @param in_len the length of in. | |||
* @param out the resulting puny-coded string. The string is not NULL | |||
* terminated. | |||
* @param out_len before processing out_len should be the length of | |||
* the out variable, after processing it will be the length of the out | |||
* string. | |||
* | |||
* @return returns 0 on success, an wind error code otherwise | |||
*/ | |||
bool rdns_punycode_label_toascii (const uint32_t *in, size_t in_len, char *out, size_t *out_len); | |||
/** | |||
* Convert an UTF-8 string to an UCS4 string. | |||
* | |||
* @param in an UTF-8 string to convert. | |||
* @param out the resulting UCS4 string | |||
* @param out_len before processing out_len should be the length of | |||
* the out variable, after processing it will be the length of the out | |||
* string. | |||
* | |||
* @return returns 0 on success, an -1 otherwise | |||
* @ingroup wind | |||
*/ | |||
int rdns_utf8_to_ucs4 (const char *in, size_t in_len, uint32_t **out, size_t *out_len); | |||
#endif /* PUNYCODE_H_ */ |
@@ -0,0 +1,372 @@ | |||
/* | |||
* Copyright (c) 2013-2014, Vsevolod Stakhov | |||
* | |||
* All rights reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions are met: | |||
* * Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* * Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in the | |||
* documentation and/or other materials provided with the distribution. | |||
* | |||
* THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY | |||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY | |||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
#ifndef RDNS_H | |||
#define RDNS_H | |||
#include <sys/types.h> | |||
#include <stddef.h> | |||
#include <stdint.h> | |||
#include <stdbool.h> | |||
#include <stdarg.h> | |||
#include <netinet/in.h> | |||
#include <arpa/inet.h> | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
struct rdns_reply; | |||
struct rdns_request; | |||
struct rdns_io_channel; | |||
typedef void (*dns_callback_type) (struct rdns_reply *reply, void *arg); | |||
enum rdns_request_type { | |||
RDNS_REQUEST_A = 1, | |||
RDNS_REQUEST_NS = 2, | |||
RDNS_REQUEST_SOA = 6, | |||
RDNS_REQUEST_PTR = 12, | |||
RDNS_REQUEST_MX = 15, | |||
RDNS_REQUEST_TXT = 16, | |||
RDNS_REQUEST_SRV = 33, | |||
RDNS_REQUEST_SPF = 99, | |||
RDNS_REQUEST_AAAA = 28, | |||
RDNS_REQUEST_TLSA = 52, | |||
RDNS_REQUEST_ANY = 255 | |||
}; | |||
union rdns_reply_element_un { | |||
struct { | |||
struct in_addr addr; | |||
} a; | |||
struct { | |||
struct in6_addr addr; | |||
} aaa; | |||
struct { | |||
char *name; | |||
} ptr; | |||
struct { | |||
char *name; | |||
} ns; | |||
struct { | |||
char *name; | |||
uint16_t priority; | |||
} mx; | |||
struct { | |||
char *data; | |||
} txt; | |||
struct { | |||
uint16_t priority; | |||
uint16_t weight; | |||
uint16_t port; | |||
char *target; | |||
} srv; | |||
struct { | |||
char *mname; | |||
char *admin; | |||
uint32_t serial; | |||
int32_t refresh; | |||
int32_t retry; | |||
int32_t expire; | |||
uint32_t minimum; | |||
} soa; | |||
struct { | |||
uint8_t usage; | |||
uint8_t selector; | |||
uint8_t match_type; | |||
uint16_t datalen; | |||
uint8_t *data; | |||
} tlsa; | |||
}; | |||
struct rdns_reply_entry { | |||
union rdns_reply_element_un content; | |||
uint16_t type; | |||
int32_t ttl; | |||
struct rdns_reply_entry *prev, *next; | |||
}; | |||
enum dns_rcode { | |||
RDNS_RC_NOERROR = 0, | |||
RDNS_RC_FORMERR = 1, | |||
RDNS_RC_SERVFAIL = 2, | |||
RDNS_RC_NXDOMAIN = 3, | |||
RDNS_RC_NOTIMP = 4, | |||
RDNS_RC_REFUSED = 5, | |||
RDNS_RC_YXDOMAIN = 6, | |||
RDNS_RC_YXRRSET = 7, | |||
RDNS_RC_NXRRSET = 8, | |||
RDNS_RC_NOTAUTH = 9, | |||
RDNS_RC_NOTZONE = 10, | |||
RDNS_RC_TIMEOUT = 11, | |||
RDNS_RC_NETERR = 12, | |||
RDNS_RC_NOREC = 13 | |||
}; | |||
struct rdns_reply { | |||
struct rdns_request *request; | |||
struct rdns_resolver *resolver; | |||
struct rdns_reply_entry *entries; | |||
const char *requested_name; | |||
enum dns_rcode code; | |||
}; | |||
typedef void (*rdns_periodic_callback)(void *user_data); | |||
struct rdns_async_context { | |||
void *data; | |||
void* (*add_read)(void *priv_data, int fd, void *user_data); | |||
void (*del_read)(void *priv_data, void *ev_data); | |||
void* (*add_write)(void *priv_data, int fd, void *user_data); | |||
void (*del_write)(void *priv_data, void *ev_data); | |||
void* (*add_timer)(void *priv_data, double after, void *user_data); | |||
void (*repeat_timer)(void *priv_data, void *ev_data); | |||
void (*del_timer)(void *priv_data, void *ev_data); | |||
void* (*add_periodic)(void *priv_data, double after, | |||
rdns_periodic_callback cb, void *user_data); | |||
void (*del_periodic)(void *priv_data, void *ev_data); | |||
void (*cleanup)(void *priv_data); | |||
}; | |||
/** | |||
* Type of rdns plugin | |||
*/ | |||
enum rdns_plugin_type { | |||
RDNS_PLUGIN_CURVE = 0//!< use the specified plugin instead of send/recv functions | |||
}; | |||
typedef ssize_t (*rdns_network_send_callback) (struct rdns_request *req, void *plugin_data); | |||
typedef ssize_t (*rdns_network_recv_callback) (struct rdns_io_channel *ioc, void *buf, | |||
size_t len, void *plugin_data, struct rdns_request **req_out); | |||
typedef void (*rdns_network_finish_callback) (struct rdns_request *req, void *plugin_data); | |||
typedef void (*rdns_plugin_dtor_callback) (struct rdns_resolver *resolver, void *plugin_data); | |||
struct rdns_plugin { | |||
enum rdns_plugin_type type; | |||
union { | |||
struct { | |||
rdns_network_send_callback send_cb; | |||
rdns_network_recv_callback recv_cb; | |||
rdns_network_finish_callback finish_cb; | |||
} curve_plugin; | |||
} cb; | |||
rdns_plugin_dtor_callback dtor; | |||
void *data; | |||
}; | |||
/* | |||
* RDNS logger types | |||
*/ | |||
/* | |||
* These types are somehow compatible with glib | |||
*/ | |||
enum rdns_log_level { | |||
RDNS_LOG_ERROR = 1 << 3, | |||
RDNS_LOG_WARNING = 1 << 4, | |||
RDNS_LOG_INFO = 1 << 6, | |||
RDNS_LOG_DEBUG = 1 << 7 | |||
}; | |||
typedef void (*rdns_log_function) ( | |||
void *log_data, //!< opaque data pointer | |||
enum rdns_log_level level, //!< level of message | |||
const char *function, //!< calling function | |||
const char *format, //!< format | |||
va_list args //!< set of arguments | |||
); | |||
struct rdns_request_name { | |||
char *name; | |||
enum rdns_request_type type; | |||
unsigned int len; | |||
}; | |||
/* | |||
* RDNS API | |||
*/ | |||
/** | |||
* Create DNS resolver structure | |||
*/ | |||
struct rdns_resolver *rdns_resolver_new (void); | |||
/** | |||
* Bind resolver to specified async context | |||
* @param ctx | |||
*/ | |||
void rdns_resolver_async_bind (struct rdns_resolver *resolver, | |||
struct rdns_async_context *ctx); | |||
/** | |||
* Add new DNS server definition to the resolver | |||
* @param resolver resolver object | |||
* @param name name of DNS server (should be ipv4 or ipv6 address) | |||
* @param priority priority (can be 0 for fair round-robin) | |||
* @param io_cnt a number of sockets that are simultaneously opened to this server | |||
* @return true if a server has been added to resolver | |||
*/ | |||
bool rdns_resolver_add_server (struct rdns_resolver *resolver, | |||
const char *name, unsigned int port, | |||
int priority, unsigned int io_cnt); | |||
/** | |||
* Load nameservers definition from resolv.conf file | |||
* @param resolver resolver object | |||
* @param path path to resolv.conf file (/etc/resolv.conf typically) | |||
* @return true if resolv.conf has been parsed | |||
*/ | |||
bool rdns_resolver_parse_resolv_conf (struct rdns_resolver *resolver, | |||
const char *path); | |||
/** | |||
* Set an external logger function to log messages from the resolver | |||
* @param resolver resolver object | |||
* @param logger logger callback | |||
* @param log_data opaque data | |||
*/ | |||
void rdns_resolver_set_logger (struct rdns_resolver *resolver, | |||
rdns_log_function logger, void *log_data); | |||
/** | |||
* Set log level for an internal logger (stderr one) | |||
* @param resolver resolver object | |||
* @param level desired log level | |||
*/ | |||
void rdns_resolver_set_log_level (struct rdns_resolver *resolver, | |||
enum rdns_log_level level); | |||
/** | |||
* Set maximum number of dns requests to be sent to a socket to be refreshed | |||
* @param resolver resolver object | |||
* @param max_ioc_uses unsigned count of socket usage limit | |||
* @param check_time specifies how often to check for sockets and refresh them | |||
*/ | |||
void rdns_resolver_set_max_io_uses (struct rdns_resolver *resolver, | |||
uint64_t max_ioc_uses, double check_time); | |||
/** | |||
* Register new plugin for rdns resolver | |||
* @param resolver | |||
* @param plugin | |||
*/ | |||
void rdns_resolver_register_plugin (struct rdns_resolver *resolver, | |||
struct rdns_plugin *plugin); | |||
/** | |||
* Init DNS resolver | |||
* @param resolver | |||
* @return | |||
*/ | |||
bool rdns_resolver_init (struct rdns_resolver *resolver); | |||
/** | |||
* Decrease refcount for a resolver and free it if refcount is 0 | |||
* @param resolver | |||
*/ | |||
void rdns_resolver_release (struct rdns_resolver *resolver); | |||
/** | |||
* Make a DNS request | |||
* @param resolver resolver object | |||
* @param cb callback to call on resolve completing | |||
* @param ud user data for callback | |||
* @param timeout timeout in seconds | |||
* @param repeats how much time to retransmit query | |||
* @param queries how much RR queries to send | |||
* @param ... -> queries in format: <query_type>[,type_argument[,type_argument...]] | |||
* @return opaque request object or NULL | |||
*/ | |||
struct rdns_request* rdns_make_request_full ( | |||
struct rdns_resolver *resolver, | |||
dns_callback_type cb, | |||
void *cbdata, | |||
double timeout, | |||
unsigned int repeats, | |||
unsigned int queries, | |||
... | |||
); | |||
/** | |||
* Get textual presentation of DNS error code | |||
*/ | |||
const char *rdns_strerror (enum dns_rcode rcode); | |||
/** | |||
* Get textual presentation of DNS request type | |||
*/ | |||
const char *rdns_strtype (enum rdns_request_type type); | |||
/** | |||
* Increase refcount for a request | |||
* @param req | |||
* @return | |||
*/ | |||
struct rdns_request* rdns_request_retain (struct rdns_request *req); | |||
/** | |||
* Decrease refcount for a request and free it if refcount is 0 | |||
* @param req | |||
*/ | |||
void rdns_request_release (struct rdns_request *req); | |||
/** | |||
* Check whether a request contains `type` request | |||
* @param req request object | |||
* @param type check for a specified type | |||
* @return true if `type` has been requested | |||
*/ | |||
bool rdns_request_has_type (struct rdns_request *req, enum rdns_request_type type); | |||
/** | |||
* Return requested name for a request | |||
* @param req request object | |||
* @return requested name as it was passed to `rdns_make_request` | |||
*/ | |||
const struct rdns_request_name* rdns_request_get_name (struct rdns_request *req, | |||
unsigned int *count); | |||
/** | |||
* Return PTR string for a request (ipv4 or ipv6) addresses | |||
* @param str string representation of IP address | |||
* @return name to resolve or NULL if `str` is not an IP address; caller must free result when it is unused | |||
*/ | |||
char * rdns_generate_ptr_from_str (const char *str); | |||
/* | |||
* Private functions used by async libraries as callbacks | |||
*/ | |||
void rdns_process_read (int fd, void *arg); | |||
void rdns_process_timer (void *arg); | |||
void rdns_process_retransmit (int fd, void *arg); | |||
#ifdef __cplusplus | |||
} | |||
#endif | |||
#endif |
@@ -0,0 +1,75 @@ | |||
/* | |||
* Copyright (c) 2014, Vsevolod Stakhov | |||
* | |||
* All rights reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions are met: | |||
* * Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* * Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in the | |||
* documentation and/or other materials provided with the distribution. | |||
* | |||
* THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY | |||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY | |||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
#ifndef RDNS_CURVE_H_ | |||
#define RDNS_CURVE_H_ | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
struct rdns_curve_ctx; | |||
/** | |||
* Create new dnscurve ctx | |||
* @return | |||
*/ | |||
struct rdns_curve_ctx* rdns_curve_ctx_new (double rekey_interval); | |||
/** | |||
* Add key for server `name` | |||
* @param ctx curve context | |||
* @param name name of server (ip address) | |||
* @param pubkey pubkey bytes (must be `RDSN_CURVE_PUBKEY_LEN`) | |||
*/ | |||
void rdns_curve_ctx_add_key (struct rdns_curve_ctx *ctx, | |||
const char *name, const unsigned char *pubkey); | |||
/** | |||
* Destroy curve context | |||
* @param ctx | |||
*/ | |||
void rdns_curve_ctx_destroy (struct rdns_curve_ctx *ctx); | |||
/** | |||
* Register DNSCurve plugin (libsodium should be enabled for this) | |||
* @param resolver | |||
* @param ctx | |||
*/ | |||
void rdns_curve_register_plugin (struct rdns_resolver *resolver, | |||
struct rdns_curve_ctx *ctx); | |||
/** | |||
* Create DNSCurve key from the base16 encoded string | |||
* @param hex input hex (must be NULL terminated) | |||
* @return a key or NULL (not NULL terminated) | |||
*/ | |||
unsigned char * rdns_curve_key_from_hex (const char *hex); | |||
#ifdef __cplusplus | |||
} | |||
#endif | |||
#endif /* RDNS_CURVE_H_ */ |
@@ -0,0 +1,232 @@ | |||
/* | |||
* Copyright (c) 2014, Vsevolod Stakhov | |||
* | |||
* All rights reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions are met: | |||
* * Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* * Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in the | |||
* documentation and/or other materials provided with the distribution. | |||
* | |||
* THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY | |||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY | |||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
#ifndef RDNS_EV_H_ | |||
#define RDNS_EV_H_ | |||
#include <ev.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include "rdns.h" | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
static void* rdns_libev_add_read (void *priv_data, int fd, void *user_data); | |||
static void rdns_libev_del_read(void *priv_data, void *ev_data); | |||
static void* rdns_libev_add_write (void *priv_data, int fd, void *user_data); | |||
static void rdns_libev_del_write (void *priv_data, void *ev_data); | |||
static void* rdns_libev_add_timer (void *priv_data, double after, void *user_data); | |||
static void* rdns_libev_add_periodic (void *priv_data, double after, | |||
rdns_periodic_callback cb, void *user_data); | |||
static void rdns_libev_del_periodic (void *priv_data, void *ev_data); | |||
static void rdns_libev_repeat_timer (void *priv_data, void *ev_data); | |||
static void rdns_libev_del_timer (void *priv_data, void *ev_data); | |||
struct rdns_ev_periodic_cbdata { | |||
ev_timer *ev; | |||
rdns_periodic_callback cb; | |||
void *cbdata; | |||
}; | |||
static void | |||
rdns_bind_libev (struct rdns_resolver *resolver, struct ev_loop *loop) | |||
{ | |||
static struct rdns_async_context ev_ctx = { | |||
.data = NULL, | |||
.add_read = rdns_libev_add_read, | |||
.del_read = rdns_libev_del_read, | |||
.add_write = rdns_libev_add_write, | |||
.del_write = rdns_libev_del_write, | |||
.add_timer = rdns_libev_add_timer, | |||
.repeat_timer = rdns_libev_repeat_timer, | |||
.del_timer = rdns_libev_del_timer, | |||
.add_periodic = rdns_libev_add_periodic, | |||
.del_periodic = rdns_libev_del_periodic, | |||
.cleanup = NULL | |||
}, *nctx; | |||
void *ptr; | |||
/* XXX: never got freed */ | |||
ptr = malloc (sizeof (struct rdns_async_context)); | |||
if (ptr != NULL) { | |||
nctx = (struct rdns_async_context *)ptr; | |||
memcpy (ptr, (void *)&ev_ctx, sizeof (struct rdns_async_context)); | |||
nctx->data = (void*)loop; | |||
rdns_resolver_async_bind (resolver, nctx); | |||
} | |||
} | |||
static void | |||
rdns_libev_read_event (struct ev_loop *loop, ev_io *ev, int revents) | |||
{ | |||
rdns_process_read (ev->fd, ev->data); | |||
} | |||
static void | |||
rdns_libev_write_event (struct ev_loop *loop, ev_io *ev, int revents) | |||
{ | |||
rdns_process_retransmit (ev->fd, ev->data); | |||
} | |||
static void | |||
rdns_libev_timer_event (struct ev_loop *loop, ev_timer *ev, int revents) | |||
{ | |||
rdns_process_timer (ev->data); | |||
} | |||
static void | |||
rdns_libev_periodic_event (struct ev_loop *loop, ev_timer *ev, int revents) | |||
{ | |||
struct rdns_ev_periodic_cbdata *cbdata = (struct rdns_ev_periodic_cbdata *) | |||
ev->data; | |||
cbdata->cb (cbdata->cbdata); | |||
} | |||
static void* | |||
rdns_libev_add_read (void *priv_data, int fd, void *user_data) | |||
{ | |||
ev_io *ev; | |||
void *ptr; | |||
ptr = malloc (sizeof (ev_io)); | |||
if (ptr != NULL) { | |||
ev = (ev_io *)ptr; | |||
ev_io_init (ev, rdns_libev_read_event, fd, EV_READ); | |||
ev->data = user_data; | |||
ev_io_start ((struct ev_loop *)priv_data, ev); | |||
} | |||
return ptr; | |||
} | |||
static void | |||
rdns_libev_del_read (void *priv_data, void *ev_data) | |||
{ | |||
ev_io *ev = (ev_io*)ev_data; | |||
if (ev != NULL) { | |||
ev_io_stop ((struct ev_loop *)priv_data, ev); | |||
free ((void *)ev); | |||
} | |||
} | |||
static void* | |||
rdns_libev_add_write (void *priv_data, int fd, void *user_data) | |||
{ | |||
ev_io *ev; | |||
ev = (ev_io *)malloc (sizeof (ev_io)); | |||
if (ev != NULL) { | |||
ev_io_init (ev, rdns_libev_write_event, fd, EV_WRITE); | |||
ev->data = user_data; | |||
ev_io_start ((struct ev_loop *)priv_data, ev); | |||
} | |||
return (void *)ev; | |||
} | |||
static void | |||
rdns_libev_del_write (void *priv_data, void *ev_data) | |||
{ | |||
ev_io *ev = (ev_io *)ev_data; | |||
if (ev != NULL) { | |||
ev_io_stop ((struct ev_loop *)priv_data, ev); | |||
free ((void *)ev); | |||
} | |||
} | |||
static void* | |||
rdns_libev_add_timer (void *priv_data, double after, void *user_data) | |||
{ | |||
ev_timer *ev; | |||
ev = (ev_timer *)malloc (sizeof (ev_timer)); | |||
if (ev != NULL) { | |||
ev_timer_init (ev, rdns_libev_timer_event, after, after); | |||
ev->data = user_data; | |||
ev_timer_start ((struct ev_loop *)priv_data, ev); | |||
} | |||
return (void *)ev; | |||
} | |||
static void* | |||
rdns_libev_add_periodic (void *priv_data, double after, | |||
rdns_periodic_callback cb, void *user_data) | |||
{ | |||
ev_timer *ev; | |||
struct rdns_ev_periodic_cbdata *cbdata = NULL; | |||
ev = (ev_timer *)malloc (sizeof (ev_timer)); | |||
if (ev != NULL) { | |||
cbdata = (struct rdns_ev_periodic_cbdata *) | |||
malloc (sizeof (struct rdns_ev_periodic_cbdata)); | |||
if (cbdata != NULL) { | |||
cbdata->cb = cb; | |||
cbdata->cbdata = user_data; | |||
cbdata->ev = ev; | |||
ev_timer_init (ev, rdns_libev_periodic_event, after, after); | |||
ev->data = cbdata; | |||
ev_timer_start ((struct ev_loop *)priv_data, ev); | |||
} | |||
else { | |||
free ((void *)ev); | |||
return NULL; | |||
} | |||
} | |||
return (void *)cbdata; | |||
} | |||
static void | |||
rdns_libev_del_periodic (void *priv_data, void *ev_data) | |||
{ | |||
struct rdns_ev_periodic_cbdata *cbdata = (struct rdns_ev_periodic_cbdata *) | |||
ev_data; | |||
if (cbdata != NULL) { | |||
ev_timer_stop ((struct ev_loop *)priv_data, (ev_timer *)cbdata->ev); | |||
free ((void *)cbdata->ev); | |||
free ((void *)cbdata); | |||
} | |||
} | |||
static void | |||
rdns_libev_repeat_timer (void *priv_data, void *ev_data) | |||
{ | |||
ev_timer *ev = (ev_timer *)ev_data; | |||
if (ev != NULL) { | |||
ev_timer_again ((struct ev_loop *)priv_data, ev); | |||
} | |||
} | |||
static void | |||
rdns_libev_del_timer (void *priv_data, void *ev_data) | |||
{ | |||
ev_timer *ev = (ev_timer *)ev_data; | |||
if (ev != NULL) { | |||
ev_timer_stop ((struct ev_loop *)priv_data, ev); | |||
free ((void *)ev); | |||
} | |||
} | |||
#ifdef __cplusplus | |||
} | |||
#endif | |||
#endif /* RDNS_EV_H_ */ |
@@ -0,0 +1,231 @@ | |||
/* | |||
* Copyright (c) 2014, Vsevolod Stakhov | |||
* | |||
* All rights reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions are met: | |||
* * Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* * Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in the | |||
* documentation and/or other materials provided with the distribution. | |||
* | |||
* THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY | |||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY | |||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
#ifndef RDNS_EVENT_H_ | |||
#define RDNS_EVENT_H_ | |||
#include <event.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include "rdns.h" | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
static void* rdns_libevent_add_read (void *priv_data, int fd, void *user_data); | |||
static void rdns_libevent_del_read(void *priv_data, void *ev_data); | |||
static void* rdns_libevent_add_write (void *priv_data, int fd, void *user_data); | |||
static void rdns_libevent_del_write (void *priv_data, void *ev_data); | |||
static void* rdns_libevent_add_timer (void *priv_data, double after, void *user_data); | |||
static void* rdns_libevent_add_periodic (void *priv_data, double after, | |||
rdns_periodic_callback cb, void *user_data); | |||
static void rdns_libevent_del_periodic (void *priv_data, void *ev_data); | |||
static void rdns_libevent_repeat_timer (void *priv_data, void *ev_data); | |||
static void rdns_libevent_del_timer (void *priv_data, void *ev_data); | |||
struct rdns_event_periodic_cbdata { | |||
struct event *ev; | |||
rdns_periodic_callback cb; | |||
void *cbdata; | |||
}; | |||
static void | |||
rdns_bind_libevent (struct rdns_resolver *resolver, struct event_base *ev_base) | |||
{ | |||
struct rdns_async_context ev_ctx = { | |||
.add_read = rdns_libevent_add_read, | |||
.del_read = rdns_libevent_del_read, | |||
.add_write = rdns_libevent_add_write, | |||
.del_write = rdns_libevent_del_write, | |||
.add_timer = rdns_libevent_add_timer, | |||
.add_periodic = rdns_libevent_add_periodic, | |||
.del_periodic = rdns_libevent_del_periodic, | |||
.repeat_timer = rdns_libevent_repeat_timer, | |||
.del_timer = rdns_libevent_del_timer, | |||
.cleanup = NULL | |||
}, *nctx; | |||
/* XXX: never got freed */ | |||
nctx = malloc (sizeof (struct rdns_async_context)); | |||
if (nctx != NULL) { | |||
memcpy (nctx, &ev_ctx, sizeof (struct rdns_async_context)); | |||
nctx->data = ev_base; | |||
} | |||
rdns_resolver_async_bind (resolver, nctx); | |||
} | |||
static void | |||
rdns_libevent_read_event (int fd, short what, void *ud) | |||
{ | |||
rdns_process_read (fd, ud); | |||
} | |||
static void | |||
rdns_libevent_write_event (int fd, short what, void *ud) | |||
{ | |||
rdns_process_retransmit (fd, ud); | |||
} | |||
static void | |||
rdns_libevent_timer_event (int fd, short what, void *ud) | |||
{ | |||
rdns_process_timer (ud); | |||
} | |||
static void | |||
rdns_libevent_periodic_event (int fd, short what, void *ud) | |||
{ | |||
struct rdns_event_periodic_cbdata *cbdata = ud; | |||
cbdata->cb (cbdata->cbdata); | |||
} | |||
static void* | |||
rdns_libevent_add_read (void *priv_data, int fd, void *user_data) | |||
{ | |||
struct event *ev; | |||
ev = malloc (sizeof (struct event)); | |||
if (ev != NULL) { | |||
event_set (ev, fd, EV_READ | EV_PERSIST, rdns_libevent_read_event, user_data); | |||
event_base_set (priv_data, ev); | |||
event_add (ev, NULL); | |||
} | |||
return ev; | |||
} | |||
static void | |||
rdns_libevent_del_read(void *priv_data, void *ev_data) | |||
{ | |||
struct event *ev = ev_data; | |||
if (ev != NULL) { | |||
event_del (ev); | |||
free (ev); | |||
} | |||
} | |||
static void* | |||
rdns_libevent_add_write (void *priv_data, int fd, void *user_data) | |||
{ | |||
struct event *ev; | |||
ev = malloc (sizeof (struct event)); | |||
if (ev != NULL) { | |||
event_set (ev, fd, EV_WRITE | EV_PERSIST, | |||
rdns_libevent_write_event, user_data); | |||
event_base_set (priv_data, ev); | |||
event_add (ev, NULL); | |||
} | |||
return ev; | |||
} | |||
static void | |||
rdns_libevent_del_write (void *priv_data, void *ev_data) | |||
{ | |||
struct event *ev = ev_data; | |||
if (ev != NULL) { | |||
event_del (ev); | |||
free (ev); | |||
} | |||
} | |||
#define rdns_event_double_to_tv(dbl, tv) do { \ | |||
(tv)->tv_sec = (int)(dbl); \ | |||
(tv)->tv_usec = ((dbl) - (int)(dbl))*1000*1000; \ | |||
} while(0) | |||
static void* | |||
rdns_libevent_add_timer (void *priv_data, double after, void *user_data) | |||
{ | |||
struct event *ev; | |||
struct timeval tv; | |||
ev = malloc (sizeof (struct event)); | |||
if (ev != NULL) { | |||
rdns_event_double_to_tv (after, &tv); | |||
event_set (ev, -1, EV_TIMEOUT|EV_PERSIST, rdns_libevent_timer_event, user_data); | |||
event_base_set (priv_data, ev); | |||
event_add (ev, &tv); | |||
} | |||
return ev; | |||
} | |||
static void* | |||
rdns_libevent_add_periodic (void *priv_data, double after, | |||
rdns_periodic_callback cb, void *user_data) | |||
{ | |||
struct event *ev; | |||
struct timeval tv; | |||
struct rdns_event_periodic_cbdata *cbdata = NULL; | |||
ev = malloc (sizeof (struct event)); | |||
if (ev != NULL) { | |||
cbdata = malloc (sizeof (struct rdns_event_periodic_cbdata)); | |||
if (cbdata != NULL) { | |||
rdns_event_double_to_tv (after, &tv); | |||
cbdata->cb = cb; | |||
cbdata->cbdata = user_data; | |||
cbdata->ev = ev; | |||
event_set (ev, -1, EV_TIMEOUT|EV_PERSIST, rdns_libevent_periodic_event, cbdata); | |||
event_base_set (priv_data, ev); | |||
event_add (ev, &tv); | |||
} | |||
else { | |||
free (ev); | |||
return NULL; | |||
} | |||
} | |||
return cbdata; | |||
} | |||
static void | |||
rdns_libevent_del_periodic (void *priv_data, void *ev_data) | |||
{ | |||
struct rdns_event_periodic_cbdata *cbdata = ev_data; | |||
if (cbdata != NULL) { | |||
event_del (cbdata->ev); | |||
free (cbdata->ev); | |||
free (cbdata); | |||
} | |||
} | |||
static void | |||
rdns_libevent_repeat_timer (void *priv_data, void *ev_data) | |||
{ | |||
/* XXX: libevent hides timeval, so timeouts are persistent here */ | |||
} | |||
#undef rdns_event_double_to_tv | |||
static void | |||
rdns_libevent_del_timer (void *priv_data, void *ev_data) | |||
{ | |||
struct event *ev = ev_data; | |||
if (ev != NULL) { | |||
event_del (ev); | |||
free (ev); | |||
} | |||
} | |||
#ifdef __cplusplus | |||
} | |||
#endif | |||
#endif /* RDNS_EV_H_ */ |
@@ -0,0 +1,71 @@ | |||
/* Copyright (c) 2014, Vsevolod Stakhov | |||
* All rights reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions are met: | |||
* * Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* * Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in the | |||
* documentation and/or other materials provided with the distribution. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY | |||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY | |||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
#ifndef REF_H_ | |||
#define REF_H_ | |||
/** | |||
* @file ref.h | |||
* A set of macros to handle refcounts | |||
*/ | |||
typedef void (*ref_dtor_cb_t)(void *data); | |||
typedef struct ref_entry_s { | |||
unsigned int refcount; | |||
ref_dtor_cb_t dtor; | |||
} ref_entry_t; | |||
#define REF_INIT(obj, dtor_cb) do { \ | |||
(obj)->ref.refcount = 0; \ | |||
(obj)->ref.dtor = (ref_dtor_cb_t)(dtor_cb); \ | |||
} while (0) | |||
#define REF_INIT_RETAIN(obj, dtor_cb) do { \ | |||
(obj)->ref.refcount = 1; \ | |||
(obj)->ref.dtor = (ref_dtor_cb_t)(dtor_cb); \ | |||
} while (0) | |||
#ifdef HAVE_ATOMIC_BUILTINS | |||
#define REF_RETAIN(obj) do { \ | |||
__sync_add_and_fetch (&(obj)->ref.refcount, 1); \ | |||
} while (0) | |||
#define REF_RELEASE(obj) do { \ | |||
unsigned int rc = __sync_sub_and_fetch (&(obj)->ref.refcount, 1); \ | |||
if (rc == 0 && (obj)->ref.dtor) { \ | |||
(obj)->ref.dtor (obj); \ | |||
} \ | |||
} while (0) | |||
#else | |||
#define REF_RETAIN(obj) do { \ | |||
(obj)->ref.refcount ++; \ | |||
} while (0) | |||
#define REF_RELEASE(obj) do { \ | |||
if (--(obj)->ref.refcount == 0 && (obj)->ref.dtor) { \ | |||
(obj)->ref.dtor (obj); \ | |||
} \ | |||
} while (0) | |||
#endif | |||
#endif /* REF_H_ */ |
@@ -0,0 +1,782 @@ | |||
/* | |||
* Copyright (c) 2014, Vsevolod Stakhov | |||
* | |||
* All rights reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions are met: | |||
* * Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* * Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in the | |||
* documentation and/or other materials provided with the distribution. | |||
* | |||
* THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY | |||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY | |||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
#include <sys/socket.h> | |||
#include <netinet/in.h> | |||
#include <arpa/inet.h> | |||
#include <unistd.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <errno.h> | |||
#include <stdarg.h> | |||
#include "rdns.h" | |||
#include "dns_private.h" | |||
#include "ottery.h" | |||
#include "util.h" | |||
#include "packet.h" | |||
#include "parse.h" | |||
#include "logger.h" | |||
#include "compression.h" | |||
static int | |||
rdns_send_request (struct rdns_request *req, int fd, bool new_req) | |||
{ | |||
int r; | |||
struct rdns_server *serv = req->io->srv; | |||
struct rdns_resolver *resolver = req->resolver; | |||
struct rdns_request *tmp; | |||
struct dns_header *header; | |||
const int max_id_cycles = 32; | |||
/* Find ID collision */ | |||
if (new_req) { | |||
r = 0; | |||
HASH_FIND_INT (req->io->requests, &req->id, tmp); | |||
while (tmp != NULL) { | |||
/* Check for unique id */ | |||
header = (struct dns_header *)req->packet; | |||
header->qid = rdns_permutor_generate_id (); | |||
req->id = header->qid; | |||
if (++r > max_id_cycles) { | |||
return -1; | |||
} | |||
HASH_FIND_INT (req->io->requests, &req->id, tmp); | |||
} | |||
} | |||
if (resolver->curve_plugin == NULL) { | |||
r = send (fd, req->packet, req->pos, 0); | |||
} | |||
else { | |||
r = resolver->curve_plugin->cb.curve_plugin.send_cb (req, | |||
resolver->curve_plugin->data); | |||
} | |||
if (r == -1) { | |||
if (errno == EAGAIN || errno == EINTR) { | |||
if (new_req) { | |||
/* Write when socket is ready */ | |||
HASH_ADD_INT (req->io->requests, id, req); | |||
req->async_event = resolver->async->add_write (resolver->async->data, | |||
fd, req); | |||
} | |||
/* | |||
* If request is already processed then the calling function | |||
* should take care about events processing | |||
*/ | |||
return 0; | |||
} | |||
else { | |||
rdns_debug ("send failed: %s for server %s", strerror (errno), serv->name); | |||
return -1; | |||
} | |||
} | |||
if (new_req) { | |||
/* Add request to hash table */ | |||
HASH_ADD_INT (req->io->requests, id, req); | |||
/* Fill timeout */ | |||
req->async_event = resolver->async->add_timer (resolver->async->data, | |||
req->timeout, req); | |||
req->state = RDNS_REQUEST_SENT; | |||
} | |||
return 1; | |||
} | |||
static struct rdns_reply * | |||
rdns_make_reply (struct rdns_request *req, enum dns_rcode rcode) | |||
{ | |||
struct rdns_reply *rep; | |||
rep = malloc (sizeof (struct rdns_reply)); | |||
if (rep != NULL) { | |||
rep->request = req; | |||
rep->resolver = req->resolver; | |||
rep->entries = NULL; | |||
rep->code = rcode; | |||
req->reply = rep; | |||
} | |||
return rep; | |||
} | |||
static struct rdns_request * | |||
rdns_find_dns_request (uint8_t *in, struct rdns_io_channel *ioc) | |||
{ | |||
struct dns_header *header = (struct dns_header *)in; | |||
struct rdns_request *req; | |||
int id; | |||
struct rdns_resolver *resolver = ioc->resolver; | |||
id = header->qid; | |||
HASH_FIND_INT (ioc->requests, &id, req); | |||
if (req == NULL) { | |||
/* No such requests found */ | |||
rdns_debug ("DNS request with id %d has not been found for IO channel", (int)id); | |||
} | |||
return req; | |||
} | |||
static bool | |||
rdns_parse_reply (uint8_t *in, int r, struct rdns_request *req, | |||
struct rdns_reply **_rep) | |||
{ | |||
struct dns_header *header = (struct dns_header *)in; | |||
struct rdns_reply *rep; | |||
struct rdns_reply_entry *elt; | |||
uint8_t *pos, *npos; | |||
struct rdns_resolver *resolver = req->resolver; | |||
uint16_t qdcount; | |||
int type; | |||
bool found = false; | |||
int i, t; | |||
/* First check header fields */ | |||
if (header->qr == 0) { | |||
rdns_info ("got request while waiting for reply"); | |||
return false; | |||
} | |||
qdcount = ntohs (header->qdcount); | |||
if (qdcount != req->qcount) { | |||
rdns_info ("request has %d queries, reply has %d queries", (int)req->qcount, (int)header->qdcount); | |||
return false; | |||
} | |||
/* | |||
* Now we have request and query data is now at the end of header, so compare | |||
* request QR section and reply QR section | |||
*/ | |||
req->pos = sizeof (struct dns_header); | |||
pos = in + sizeof (struct dns_header); | |||
t = r - sizeof (struct dns_header); | |||
for (i = 0; i < (int)qdcount; i ++) { | |||
if ((npos = rdns_request_reply_cmp (req, pos,t)) == NULL) { | |||
rdns_info ("DNS request with id %d is for different query, ignoring", (int)req->id); | |||
return false; | |||
} | |||
t -= npos - pos; | |||
pos = npos; | |||
} | |||
/* | |||
* Now pos is in answer section, so we should extract data and form reply | |||
*/ | |||
rep = rdns_make_reply (req, header->rcode); | |||
if (rep == NULL) { | |||
rdns_warn ("Cannot allocate memory for reply"); | |||
return false; | |||
} | |||
type = req->requested_names[0].type; | |||
if (rep->code == RDNS_RC_NOERROR) { | |||
r -= pos - in; | |||
/* Extract RR records */ | |||
for (i = 0; i < ntohs (header->ancount); i ++) { | |||
elt = malloc (sizeof (struct rdns_reply_entry)); | |||
t = rdns_parse_rr (resolver, in, elt, &pos, rep, &r); | |||
if (t == -1) { | |||
free (elt); | |||
rdns_debug ("incomplete reply"); | |||
break; | |||
} | |||
else if (t == 1) { | |||
DL_APPEND (rep->entries, elt); | |||
if (elt->type == type) { | |||
found = true; | |||
} | |||
} | |||
else { | |||
rdns_debug ("no matching reply for %s", | |||
req->requested_names[0].name); | |||
free (elt); | |||
} | |||
} | |||
} | |||
if (!found && type != RDNS_REQUEST_ANY) { | |||
/* We have not found the requested RR type */ | |||
rep->code = RDNS_RC_NOREC; | |||
} | |||
*_rep = rep; | |||
return true; | |||
} | |||
static void | |||
rdns_request_unschedule (struct rdns_request *req) | |||
{ | |||
req->async->del_timer (req->async->data, | |||
req->async_event); | |||
/* Remove from id hashes */ | |||
HASH_DEL (req->io->requests, req); | |||
} | |||
void | |||
rdns_process_read (int fd, void *arg) | |||
{ | |||
struct rdns_io_channel *ioc = arg; | |||
struct rdns_resolver *resolver; | |||
struct rdns_request *req = NULL; | |||
ssize_t r; | |||
struct rdns_reply *rep; | |||
uint8_t in[UDP_PACKET_SIZE]; | |||
resolver = ioc->resolver; | |||
/* First read packet from socket */ | |||
if (resolver->curve_plugin == NULL) { | |||
r = read (fd, in, sizeof (in)); | |||
if (r > (int)(sizeof (struct dns_header) + sizeof (struct dns_query))) { | |||
req = rdns_find_dns_request (in, ioc); | |||
} | |||
} | |||
else { | |||
r = resolver->curve_plugin->cb.curve_plugin.recv_cb (ioc, in, | |||
sizeof (in), resolver->curve_plugin->data, &req); | |||
if (req == NULL && | |||
r > (int)(sizeof (struct dns_header) + sizeof (struct dns_query))) { | |||
req = rdns_find_dns_request (in, ioc); | |||
} | |||
} | |||
if (req != NULL) { | |||
if (rdns_parse_reply (in, r, req, &rep)) { | |||
UPSTREAM_OK (req->io->srv); | |||
req->state = RDNS_REQUEST_REPLIED; | |||
rdns_request_unschedule (req); | |||
req->func (rep, req->arg); | |||
REF_RELEASE (req); | |||
} | |||
} | |||
else { | |||
/* Still want to increase uses */ | |||
ioc->uses ++; | |||
} | |||
} | |||
void | |||
rdns_process_timer (void *arg) | |||
{ | |||
struct rdns_request *req = (struct rdns_request *)arg; | |||
struct rdns_reply *rep; | |||
int r; | |||
bool renew = false; | |||
struct rdns_resolver *resolver; | |||
struct rdns_server *serv = NULL; | |||
req->retransmits --; | |||
resolver = req->resolver; | |||
if (req->retransmits == 0) { | |||
UPSTREAM_FAIL (req->io->srv, time (NULL)); | |||
rep = rdns_make_reply (req, RDNS_RC_TIMEOUT); | |||
req->state = RDNS_REQUEST_REPLIED; | |||
rdns_request_unschedule (req); | |||
req->func (rep, req->arg); | |||
REF_RELEASE (req); | |||
return; | |||
} | |||
if (!req->io->active) { | |||
/* Do not reschedule IO requests on inactive sockets */ | |||
rdns_debug ("reschedule request with id: %d", (int)req->id); | |||
rdns_request_unschedule (req); | |||
REF_RELEASE (req->io); | |||
UPSTREAM_SELECT_ROUND_ROBIN (resolver->servers, serv); | |||
if (serv == NULL) { | |||
rdns_warn ("cannot find suitable server for request"); | |||
rep = rdns_make_reply (req, RDNS_RC_SERVFAIL); | |||
req->state = RDNS_REQUEST_REPLIED; | |||
req->func (rep, req->arg); | |||
REF_RELEASE (req); | |||
} | |||
/* Select random IO channel */ | |||
req->io = serv->io_channels[ottery_rand_uint32 () % serv->io_cnt]; | |||
req->io->uses ++; | |||
REF_RETAIN (req->io); | |||
renew = true; | |||
} | |||
r = rdns_send_request (req, req->io->sock, renew); | |||
if (r == 0) { | |||
/* Retransmit one more time */ | |||
req->async->del_timer (req->async->data, | |||
req->async_event); | |||
req->async_event = req->async->add_write (req->async->data, | |||
req->io->sock, req); | |||
req->state = RDNS_REQUEST_REGISTERED; | |||
} | |||
else if (r == -1) { | |||
UPSTREAM_FAIL (req->io->srv, time (NULL)); | |||
rep = rdns_make_reply (req, RDNS_RC_NETERR); | |||
req->state = RDNS_REQUEST_REPLIED; | |||
rdns_request_unschedule (req); | |||
req->func (rep, req->arg); | |||
REF_RELEASE (req); | |||
} | |||
else { | |||
req->async->repeat_timer (req->async->data, req->async_event); | |||
} | |||
} | |||
static void | |||
rdns_process_periodic (void *arg) | |||
{ | |||
struct rdns_resolver *resolver = (struct rdns_resolver*)arg; | |||
UPSTREAM_RESCAN (resolver->servers, time (NULL)); | |||
} | |||
static void | |||
rdns_process_ioc_refresh (void *arg) | |||
{ | |||
struct rdns_resolver *resolver = (struct rdns_resolver*)arg; | |||
struct rdns_server *serv; | |||
struct rdns_io_channel *ioc, *nioc; | |||
unsigned int i; | |||
if (resolver->max_ioc_uses > 0) { | |||
UPSTREAM_FOREACH (resolver->servers, serv) { | |||
for (i = 0; i < serv->io_cnt; i ++) { | |||
ioc = serv->io_channels[i]; | |||
if (ioc->uses > resolver->max_ioc_uses) { | |||
/* Schedule IOC removing */ | |||
nioc = calloc (1, sizeof (struct rdns_io_channel)); | |||
if (nioc == NULL) { | |||
rdns_err ("calloc fails to allocate rdns_io_channel"); | |||
continue; | |||
} | |||
nioc->sock = rdns_make_client_socket (serv->name, serv->port, | |||
SOCK_DGRAM); | |||
if (nioc->sock == -1) { | |||
rdns_err ("cannot open socket to %s: %s", serv->name, | |||
strerror (errno)); | |||
free (nioc); | |||
continue; | |||
} | |||
nioc->srv = serv; | |||
nioc->active = true; | |||
nioc->resolver = resolver; | |||
nioc->async_io = resolver->async->add_read (resolver->async->data, | |||
nioc->sock, nioc); | |||
REF_INIT_RETAIN (nioc, rdns_ioc_free); | |||
serv->io_channels[i] = nioc; | |||
rdns_debug ("scheduled io channel for server %s to be refreshed after " | |||
"%lu usages", serv->name, (unsigned long)ioc->uses); | |||
ioc->active = false; | |||
REF_RELEASE (ioc); | |||
} | |||
} | |||
} | |||
} | |||
} | |||
void | |||
rdns_process_retransmit (int fd, void *arg) | |||
{ | |||
struct rdns_request *req = (struct rdns_request *)arg; | |||
struct rdns_resolver *resolver; | |||
struct rdns_reply *rep; | |||
int r; | |||
resolver = req->resolver; | |||
resolver->async->del_write (resolver->async->data, | |||
req->async_event); | |||
r = rdns_send_request (req, fd, false); | |||
if (r == 0) { | |||
/* Retransmit one more time */ | |||
req->async_event = req->async->add_write (req->async->data, | |||
fd, req); | |||
req->state = RDNS_REQUEST_REGISTERED; | |||
} | |||
else if (r == -1) { | |||
UPSTREAM_FAIL (req->io->srv, time (NULL)); | |||
rep = rdns_make_reply (req, RDNS_RC_NETERR); | |||
req->state = RDNS_REQUEST_REPLIED; | |||
req->func (rep, req->arg); | |||
REF_RELEASE (req); | |||
} | |||
else { | |||
req->async_event = req->async->add_timer (req->async->data, | |||
req->timeout, req); | |||
req->state = RDNS_REQUEST_SENT; | |||
} | |||
} | |||
struct rdns_request* | |||
rdns_make_request_full ( | |||
struct rdns_resolver *resolver, | |||
dns_callback_type cb, | |||
void *cbdata, | |||
double timeout, | |||
unsigned int repeats, | |||
unsigned int queries, | |||
... | |||
) | |||
{ | |||
va_list args; | |||
struct rdns_request *req; | |||
struct rdns_server *serv; | |||
int r, type; | |||
unsigned int i, tlen = 0, clen = 0, cur; | |||
size_t olen; | |||
const char *cur_name, *last_name = NULL; | |||
struct rdns_compression_entry *comp = NULL; | |||
if (!resolver->initialized) { | |||
return NULL; | |||
} | |||
req = malloc (sizeof (struct rdns_request)); | |||
if (req == NULL) { | |||
return NULL; | |||
} | |||
req->resolver = resolver; | |||
req->func = cb; | |||
req->arg = cbdata; | |||
req->reply = NULL; | |||
req->qcount = queries; | |||
req->io = NULL; | |||
req->state = RDNS_REQUEST_NEW; | |||
req->packet = NULL; | |||
req->requested_names = calloc (queries, sizeof (struct rdns_request_name)); | |||
if (req->requested_names == NULL) { | |||
free (req); | |||
return NULL; | |||
} | |||
req->type = 0; | |||
#ifdef TWEETNACL | |||
req->curve_plugin_data = NULL; | |||
#endif | |||
REF_INIT_RETAIN (req, rdns_request_free); | |||
/* Calculate packet's total length based on records count */ | |||
va_start (args, queries); | |||
for (i = 0; i < queries * 2; i += 2) { | |||
cur = i / 2; | |||
cur_name = va_arg (args, const char *); | |||
if (cur_name != NULL) { | |||
last_name = cur_name; | |||
clen = strlen (cur_name); | |||
if (clen == 0) { | |||
rdns_info ("got empty name to resolve"); | |||
rdns_request_free (req); | |||
return NULL; | |||
} | |||
tlen += clen; | |||
} | |||
else if (last_name == NULL) { | |||
rdns_info ("got NULL as the first name to resolve"); | |||
rdns_request_free (req); | |||
return NULL; | |||
} | |||
if (!rdns_format_dns_name (resolver, last_name, clen, | |||
&req->requested_names[cur].name, &olen)) { | |||
rdns_request_free (req); | |||
return NULL; | |||
} | |||
type = va_arg (args, int); | |||
req->requested_names[cur].type = type; | |||
req->requested_names[cur].len = olen; | |||
} | |||
va_end (args); | |||
rdns_allocate_packet (req, tlen); | |||
rdns_make_dns_header (req, queries); | |||
for (i = 0; i < queries; i ++) { | |||
cur_name = req->requested_names[i].name; | |||
clen = req->requested_names[i].len; | |||
type = req->requested_names[i].type; | |||
if (queries > 1) { | |||
if (!rdns_add_rr (req, cur_name, clen, type, &comp)) { | |||
REF_RELEASE (req); | |||
rnds_compression_free (comp); | |||
return NULL; | |||
} | |||
} | |||
else { | |||
if (!rdns_add_rr (req, cur_name, clen, type, NULL)) { | |||
REF_RELEASE (req); | |||
rnds_compression_free (comp); | |||
return NULL; | |||
} | |||
} | |||
} | |||
rnds_compression_free (comp); | |||
/* Add EDNS RR */ | |||
rdns_add_edns0 (req); | |||
req->retransmits = repeats; | |||
req->timeout = timeout; | |||
req->state = RDNS_REQUEST_NEW; | |||
req->async = resolver->async; | |||
UPSTREAM_SELECT_ROUND_ROBIN (resolver->servers, serv); | |||
if (serv == NULL) { | |||
rdns_warn ("cannot find suitable server for request"); | |||
REF_RELEASE (req); | |||
return NULL; | |||
} | |||
/* Select random IO channel */ | |||
req->io = serv->io_channels[ottery_rand_uint32 () % serv->io_cnt]; | |||
req->io->uses ++; | |||
/* Now send request to server */ | |||
r = rdns_send_request (req, req->io->sock, true); | |||
if (r == -1) { | |||
REF_RELEASE (req); | |||
return NULL; | |||
} | |||
REF_RETAIN (req->io); | |||
REF_RETAIN (req->resolver); | |||
return req; | |||
} | |||
bool | |||
rdns_resolver_init (struct rdns_resolver *resolver) | |||
{ | |||
unsigned int i; | |||
struct rdns_server *serv; | |||
struct rdns_io_channel *ioc; | |||
if (!resolver->async_binded) { | |||
return false; | |||
} | |||
if (resolver->servers == NULL) { | |||
return false; | |||
} | |||
/* Now init io channels to all servers */ | |||
UPSTREAM_FOREACH (resolver->servers, serv) { | |||
serv->io_channels = calloc (serv->io_cnt, sizeof (struct rdns_io_channel *)); | |||
for (i = 0; i < serv->io_cnt; i ++) { | |||
ioc = calloc (1, sizeof (struct rdns_io_channel)); | |||
if (ioc == NULL) { | |||
rdns_err ("cannot allocate memory for the resolver"); | |||
return false; | |||
} | |||
ioc->sock = rdns_make_client_socket (serv->name, serv->port, SOCK_DGRAM); | |||
ioc->active = true; | |||
if (ioc->sock == -1) { | |||
rdns_err ("cannot open socket to %s:%d %s", serv->name, serv->port, strerror (errno)); | |||
free (ioc); | |||
return false; | |||
} | |||
else { | |||
ioc->srv = serv; | |||
ioc->resolver = resolver; | |||
ioc->async_io = resolver->async->add_read (resolver->async->data, | |||
ioc->sock, ioc); | |||
REF_INIT_RETAIN (ioc, rdns_ioc_free); | |||
serv->io_channels[i] = ioc; | |||
} | |||
} | |||
} | |||
if (resolver->async->add_periodic) { | |||
resolver->periodic = resolver->async->add_periodic (resolver->async->data, | |||
UPSTREAM_REVIVE_TIME, rdns_process_periodic, resolver); | |||
} | |||
resolver->initialized = true; | |||
return true; | |||
} | |||
void | |||
rdns_resolver_register_plugin (struct rdns_resolver *resolver, | |||
struct rdns_plugin *plugin) | |||
{ | |||
if (resolver != NULL && plugin != NULL) { | |||
/* XXX: support only network plugin now, and only a single one */ | |||
if (plugin->type == RDNS_PLUGIN_CURVE) { | |||
resolver->curve_plugin = plugin; | |||
} | |||
} | |||
} | |||
bool | |||
rdns_resolver_add_server (struct rdns_resolver *resolver, | |||
const char *name, unsigned int port, | |||
int priority, unsigned int io_cnt) | |||
{ | |||
struct rdns_server *serv; | |||
union { | |||
struct in_addr v4; | |||
struct in6_addr v6; | |||
} addr; | |||
if (inet_pton (AF_INET, name, &addr) == 0 && | |||
inet_pton (AF_INET6, name, &addr) == 0) { | |||
/* Invalid IP */ | |||
return false; | |||
} | |||
if (io_cnt == 0) { | |||
return false; | |||
} | |||
if (port == 0 || port > UINT16_MAX) { | |||
return false; | |||
} | |||
serv = calloc (1, sizeof (struct rdns_server)); | |||
if (serv == NULL) { | |||
return false; | |||
} | |||
serv->name = strdup (name); | |||
if (serv->name == NULL) { | |||
free (serv); | |||
return false; | |||
} | |||
serv->io_cnt = io_cnt; | |||
serv->port = port; | |||
UPSTREAM_ADD (resolver->servers, serv, priority); | |||
return true; | |||
} | |||
void | |||
rdns_resolver_set_logger (struct rdns_resolver *resolver, | |||
rdns_log_function logger, void *log_data) | |||
{ | |||
resolver->logger = logger; | |||
resolver->log_data = log_data; | |||
} | |||
void | |||
rdns_resolver_set_log_level (struct rdns_resolver *resolver, | |||
enum rdns_log_level level) | |||
{ | |||
resolver->log_level = level; | |||
} | |||
void | |||
rdns_resolver_set_max_io_uses (struct rdns_resolver *resolver, | |||
uint64_t max_ioc_uses, double check_time) | |||
{ | |||
if (resolver->refresh_ioc_periodic != NULL) { | |||
resolver->async->del_periodic (resolver->async->data, | |||
resolver->refresh_ioc_periodic); | |||
resolver->refresh_ioc_periodic = NULL; | |||
} | |||
resolver->max_ioc_uses = max_ioc_uses; | |||
if (check_time > 0.0 && resolver->async->add_periodic) { | |||
resolver->refresh_ioc_periodic = | |||
resolver->async->add_periodic (resolver->async->data, | |||
check_time, rdns_process_ioc_refresh, resolver); | |||
} | |||
} | |||
static void | |||
rdns_resolver_free (struct rdns_resolver *resolver) | |||
{ | |||
struct rdns_server *serv, *stmp; | |||
struct rdns_io_channel *ioc; | |||
unsigned int i; | |||
if (resolver->initialized) { | |||
if (resolver->periodic != NULL) { | |||
resolver->async->del_periodic (resolver->async->data, resolver->periodic); | |||
} | |||
if (resolver->refresh_ioc_periodic != NULL) { | |||
resolver->async->del_periodic (resolver->async->data, | |||
resolver->refresh_ioc_periodic); | |||
} | |||
if (resolver->curve_plugin != NULL && resolver->curve_plugin->dtor != NULL) { | |||
resolver->curve_plugin->dtor (resolver, resolver->curve_plugin->data); | |||
} | |||
/* Stop IO watch on all IO channels */ | |||
UPSTREAM_FOREACH_SAFE (resolver->servers, serv, stmp) { | |||
for (i = 0; i < serv->io_cnt; i ++) { | |||
ioc = serv->io_channels[i]; | |||
REF_RELEASE (ioc); | |||
} | |||
serv->io_cnt = 0; | |||
UPSTREAM_DEL (resolver->servers, serv); | |||
free (serv->io_channels); | |||
free (serv->name); | |||
free (serv); | |||
} | |||
} | |||
free (resolver->async); | |||
free (resolver); | |||
} | |||
struct rdns_resolver * | |||
rdns_resolver_new (void) | |||
{ | |||
struct rdns_resolver *new; | |||
new = calloc (1, sizeof (struct rdns_resolver)); | |||
REF_INIT_RETAIN (new, rdns_resolver_free); | |||
new->logger = rdns_logger_internal; | |||
new->log_data = new; | |||
return new; | |||
} | |||
void | |||
rdns_resolver_async_bind (struct rdns_resolver *resolver, | |||
struct rdns_async_context *ctx) | |||
{ | |||
if (resolver != NULL && ctx != NULL) { | |||
resolver->async = ctx; | |||
resolver->async_binded = true; | |||
} | |||
} |
@@ -0,0 +1,257 @@ | |||
/* | |||
* Copyright (c) 2014, Vsevolod Stakhov | |||
* | |||
* All rights reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions are met: | |||
* * Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* * Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in the | |||
* documentation and/or other materials provided with the distribution. | |||
* | |||
* THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY | |||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY | |||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
#ifndef UPSTREAM_H_ | |||
#define UPSTREAM_H_ | |||
#include <time.h> | |||
#include <stdio.h> | |||
/** | |||
* @file upstream.h | |||
* The basic macros to define upstream objects | |||
*/ | |||
#ifndef upstream_fatal | |||
#define upstream_fatal(msg) do { perror (msg); exit (-1); } while (0) | |||
#endif | |||
#ifndef upstream_malloc | |||
#define upstream_malloc(size) malloc (size) | |||
#endif | |||
#ifndef upstream_free | |||
#define upstream_free(size, ptr) free (ptr) | |||
#endif | |||
struct upstream_entry_s; | |||
struct upstream_common_data { | |||
void **upstreams; | |||
unsigned int allocated_nelts; | |||
unsigned int nelts; | |||
unsigned int alive; | |||
}; | |||
typedef struct upstream_entry_s { | |||
unsigned short errors; /**< errors for this upstream */ | |||
unsigned short dead; | |||
unsigned short priority; | |||
unsigned short weight; | |||
time_t time; /**< time of marking */ | |||
void *parent; /**< parent object */ | |||
struct upstream_common_data *common; /**< common data */ | |||
void *next; /**< link to the next */ | |||
} upstream_entry_t; | |||
/* | |||
* Here we define some reasonable defaults: | |||
* if an upstream has more than `UPSTREAM_MAX_ERRORS` in the period of time | |||
* of `UPSTREAM_ERROR_TIME` then we shut it down for `UPSTREAM_REVIVE_TIME`. | |||
* In this particular case times are 10 seconds for 10 errors and revive in | |||
* 30 seconds. | |||
*/ | |||
#ifndef UPSTREAM_REVIVE_TIME | |||
#define UPSTREAM_REVIVE_TIME 30 | |||
#endif | |||
#ifndef UPSTREAM_ERROR_TIME | |||
#define UPSTREAM_ERROR_TIME 10 | |||
#endif | |||
#ifndef UPSTREAM_MAX_ERRORS | |||
#define UPSTREAM_MAX_ERRORS 10 | |||
#endif | |||
#define UPSTREAM_FAIL(u, now) do { \ | |||
if ((u)->up.time != 0) { \ | |||
if ((now) - (u)->up.time >= UPSTREAM_ERROR_TIME) { \ | |||
if ((u)->up.errors >= UPSTREAM_MAX_ERRORS) { \ | |||
(u)->up.dead = 1; \ | |||
(u)->up.time = now; \ | |||
(u)->up.common->alive --; \ | |||
} \ | |||
else { \ | |||
(u)->up.errors = 1; \ | |||
(u)->up.time = (now); \ | |||
} \ | |||
} \ | |||
else { \ | |||
(u)->up.errors ++; \ | |||
} \ | |||
} \ | |||
else { \ | |||
(u)->up.errors ++; \ | |||
(u)->up.time = (now); \ | |||
} \ | |||
} while (0) | |||
#define UPSTREAM_OK(u) do { \ | |||
(u)->up.errors = 0; \ | |||
(u)->up.time = 0; \ | |||
} while (0) | |||
#define UPSTREAM_ADD(head, u, priority) do { \ | |||
if (head == NULL) { \ | |||
struct upstream_common_data *cd; \ | |||
cd = upstream_malloc (sizeof (struct upstream_common_data)); \ | |||
if (cd == NULL) { \ | |||
upstream_fatal ("malloc failed"); \ | |||
} \ | |||
cd->upstreams = upstream_malloc (sizeof (void *) * 8); \ | |||
if (cd == NULL) { \ | |||
upstream_fatal ("malloc failed"); \ | |||
} \ | |||
cd->allocated_nelts = 8; \ | |||
cd->nelts = 1; \ | |||
cd->alive = 1; \ | |||
cd->upstreams[0] = (u); \ | |||
(u)->up.common = cd; \ | |||
} \ | |||
else { \ | |||
struct upstream_common_data *cd = (head)->up.common; \ | |||
(u)->up.common = cd; \ | |||
if (cd->nelts == cd->allocated_nelts) { \ | |||
void **nup; \ | |||
nup = upstream_malloc (sizeof (void *) * cd->nelts * 2); \ | |||
if (nup == NULL) { \ | |||
upstream_fatal ("malloc failed"); \ | |||
} \ | |||
memcpy (nup, cd->upstreams, cd->nelts * sizeof (void *)); \ | |||
upstream_free (cd->nelts * sizeof (void *), cd->upstreams); \ | |||
cd->upstreams = nup; \ | |||
cd->allocated_nelts *= 2; \ | |||
} \ | |||
cd->upstreams[cd->nelts++] = (u); \ | |||
cd->alive ++; \ | |||
} \ | |||
(u)->up.next = (head); \ | |||
(head) = (u); \ | |||
if (priority > 0) { \ | |||
(u)->up.priority = (u)->up.weight = (priority); \ | |||
} \ | |||
else { \ | |||
(u)->up.priority = (u)->up.weight = 65535; \ | |||
} \ | |||
(u)->up.time = 0; \ | |||
(u)->up.errors = 0; \ | |||
(u)->up.dead = 0; \ | |||
(u)->up.parent = (u); \ | |||
} while (0) | |||
#define UPSTREAM_DEL(head, u) do { \ | |||
if (head != NULL) { \ | |||
struct upstream_common_data *cd = (head)->up.common; \ | |||
if ((u)->up.next != NULL) { \ | |||
(head) = (u)->up.next; \ | |||
cd->nelts --; \ | |||
cd->alive --; \ | |||
} \ | |||
else { \ | |||
upstream_free (cd->allocated_nelts * sizeof (void *), \ | |||
cd->upstreams); \ | |||
upstream_free (sizeof (struct upstream_common_data), cd); \ | |||
(head) = NULL; \ | |||
} \ | |||
} \ | |||
} while (0) | |||
#define UPSTREAM_FOREACH(head, u) for ((u) = (head); (u) != NULL; (u) = (u)->up.next) | |||
#define UPSTREAM_FOREACH_SAFE(head, u, tmp) \ | |||
for ((u) = (head); \ | |||
(u) != NULL && ((tmp = (u)->up.next) || true); \ | |||
(u) = (tmp)) | |||
#define UPSTREAM_REVIVE_ALL(head) do { \ | |||
__typeof(head) elt = (head); \ | |||
while (elt != NULL) { \ | |||
elt->up.dead = 0; \ | |||
elt->up.errors = 0; \ | |||
elt->up.time = 0; \ | |||
elt = elt->up.next; \ | |||
} \ | |||
(head)->up.common->alive = (head)->up.common->nelts; \ | |||
} while (0) | |||
#define UPSTREAM_RESCAN(head, now) do { \ | |||
__typeof(head) elt = (head); \ | |||
if ((head)->up.common->alive == 0) { \ | |||
UPSTREAM_REVIVE_ALL((head)); \ | |||
} \ | |||
else { \ | |||
while (elt != NULL) { \ | |||
if (elt->up.dead) { \ | |||
if ((now) - elt->up.time >= UPSTREAM_REVIVE_TIME) { \ | |||
elt->up.dead = 0; \ | |||
elt->up.errors = 0; \ | |||
elt->up.weight = elt->up.priority; \ | |||
(head)->up.common->alive ++; \ | |||
} \ | |||
} \ | |||
else { \ | |||
if ((now) - elt->up.time >= UPSTREAM_ERROR_TIME && \ | |||
elt->up.errors >= UPSTREAM_MAX_ERRORS) { \ | |||
elt->up.dead = 1; \ | |||
elt->up.time = now; \ | |||
(head)->up.common->alive --; \ | |||
} \ | |||
} \ | |||
elt = elt->up.next; \ | |||
} \ | |||
} \ | |||
} while (0) | |||
#define UPSTREAM_SELECT_ROUND_ROBIN(head, selected) do { \ | |||
__typeof(head) elt = (head); \ | |||
(selected) = NULL; \ | |||
int alive = 0; \ | |||
unsigned max_weight = 0; \ | |||
if ((head)->up.common->alive == 0){ \ | |||
UPSTREAM_REVIVE_ALL(head); \ | |||
} \ | |||
while (elt != NULL) { \ | |||
if (!elt->up.dead) { \ | |||
if (elt->up.weight > max_weight) { \ | |||
max_weight = elt->up.weight; \ | |||
(selected) = elt; \ | |||
} \ | |||
alive ++; \ | |||
} \ | |||
elt = elt->up.next; \ | |||
} \ | |||
if (max_weight == 0) { \ | |||
elt = (head); \ | |||
while (elt != NULL) { \ | |||
elt->up.weight = elt->up.priority; \ | |||
if (!elt->up.dead) { \ | |||
if (elt->up.priority > max_weight) { \ | |||
max_weight = elt->up.priority; \ | |||
(selected) = elt; \ | |||
} \ | |||
} \ | |||
elt = elt->up.next; \ | |||
} \ | |||
} \ | |||
(selected)->up.weight --; \ | |||
} while (0) | |||
#endif /* UPSTREAM_H_ */ |
@@ -0,0 +1,523 @@ | |||
/* Copyright (c) 2014, Vsevolod Stakhov | |||
* All rights reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions are met: | |||
* * Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* * Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in the | |||
* documentation and/or other materials provided with the distribution. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY | |||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY | |||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
#include <sys/socket.h> | |||
#include <netinet/in.h> | |||
#include <arpa/inet.h> | |||
#include <sys/un.h> | |||
#include <sys/stat.h> | |||
#include <unistd.h> | |||
#include <stdlib.h> | |||
#include <stdio.h> | |||
#include <string.h> | |||
#include <errno.h> | |||
#include <netdb.h> | |||
#include <fcntl.h> | |||
#include <ctype.h> | |||
#include "ottery.h" | |||
#include "util.h" | |||
#include "logger.h" | |||
static int | |||
rdns_make_socket_nonblocking (int fd) | |||
{ | |||
int ofl; | |||
ofl = fcntl (fd, F_GETFL, 0); | |||
if (fcntl (fd, F_SETFL, ofl | O_NONBLOCK) == -1) { | |||
return -1; | |||
} | |||
return 0; | |||
} | |||
static int | |||
rdns_make_inet_socket (int type, struct addrinfo *addr) | |||
{ | |||
int fd, r, s_error; | |||
socklen_t optlen; | |||
struct addrinfo *cur; | |||
cur = addr; | |||
while (cur) { | |||
/* Create socket */ | |||
fd = socket (cur->ai_family, type, 0); | |||
if (fd == -1) { | |||
goto out; | |||
} | |||
if (rdns_make_socket_nonblocking (fd) < 0) { | |||
goto out; | |||
} | |||
/* Set close on exec */ | |||
if (fcntl (fd, F_SETFD, FD_CLOEXEC) == -1) { | |||
goto out; | |||
} | |||
r = connect (fd, cur->ai_addr, cur->ai_addrlen); | |||
if (r == -1) { | |||
if (errno != EINPROGRESS) { | |||
goto out; | |||
} | |||
} | |||
else { | |||
/* Still need to check SO_ERROR on socket */ | |||
optlen = sizeof (s_error); | |||
getsockopt (fd, SOL_SOCKET, SO_ERROR, (void *)&s_error, &optlen); | |||
if (s_error) { | |||
errno = s_error; | |||
goto out; | |||
} | |||
} | |||
break; | |||
out: | |||
if (fd != -1) { | |||
close (fd); | |||
} | |||
fd = -1; | |||
cur = cur->ai_next; | |||
} | |||
return (fd); | |||
} | |||
static int | |||
rdns_make_unix_socket (const char *path, struct sockaddr_un *addr, int type) | |||
{ | |||
int fd = -1, s_error, r, serrno; | |||
socklen_t optlen; | |||
if (path == NULL) { | |||
return -1; | |||
} | |||
addr->sun_family = AF_UNIX; | |||
memset (addr->sun_path, 0, sizeof (addr->sun_path)); | |||
memccpy (addr->sun_path, path, 0, sizeof (addr->sun_path) - 1); | |||
#ifdef FREEBSD | |||
addr->sun_len = SUN_LEN (addr); | |||
#endif | |||
fd = socket (PF_LOCAL, type, 0); | |||
if (fd == -1) { | |||
return -1; | |||
} | |||
if (rdns_make_socket_nonblocking (fd) < 0) { | |||
goto out; | |||
} | |||
/* Set close on exec */ | |||
if (fcntl (fd, F_SETFD, FD_CLOEXEC) == -1) { | |||
goto out; | |||
} | |||
r = connect (fd, (struct sockaddr *)addr, SUN_LEN (addr)); | |||
if (r == -1) { | |||
if (errno != EINPROGRESS) { | |||
goto out; | |||
} | |||
} | |||
else { | |||
/* Still need to check SO_ERROR on socket */ | |||
optlen = sizeof (s_error); | |||
getsockopt (fd, SOL_SOCKET, SO_ERROR, (void *)&s_error, &optlen); | |||
if (s_error) { | |||
errno = s_error; | |||
goto out; | |||
} | |||
} | |||
return (fd); | |||
out: | |||
serrno = errno; | |||
if (fd != -1) { | |||
close (fd); | |||
} | |||
errno = serrno; | |||
return (-1); | |||
} | |||
/** | |||
* Make a universal socket | |||
* @param credits host, ip or path to unix socket | |||
* @param port port (used for network sockets) | |||
* @param async make this socket asynced | |||
* @param is_server make this socket as server socket | |||
* @param try_resolve try name resolution for a socket (BLOCKING) | |||
*/ | |||
int | |||
rdns_make_client_socket (const char *credits, uint16_t port, | |||
int type) | |||
{ | |||
struct sockaddr_un un; | |||
struct stat st; | |||
struct addrinfo hints, *res; | |||
int r; | |||
char portbuf[8]; | |||
if (*credits == '/') { | |||
r = stat (credits, &st); | |||
if (r == -1) { | |||
/* Unix socket doesn't exists it must be created first */ | |||
errno = ENOENT; | |||
return -1; | |||
} | |||
else { | |||
if ((st.st_mode & S_IFSOCK) == 0) { | |||
/* Path is not valid socket */ | |||
errno = EINVAL; | |||
return -1; | |||
} | |||
else { | |||
return rdns_make_unix_socket (credits, &un, type); | |||
} | |||
} | |||
} | |||
else { | |||
/* TCP related part */ | |||
memset (&hints, 0, sizeof (hints)); | |||
hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ | |||
hints.ai_socktype = type; /* Type of the socket */ | |||
hints.ai_flags = 0; | |||
hints.ai_protocol = 0; /* Any protocol */ | |||
hints.ai_canonname = NULL; | |||
hints.ai_addr = NULL; | |||
hints.ai_next = NULL; | |||
hints.ai_flags |= AI_NUMERICHOST | AI_NUMERICSERV; | |||
snprintf (portbuf, sizeof (portbuf), "%d", (int)port); | |||
if ((r = getaddrinfo (credits, portbuf, &hints, &res)) == 0) { | |||
r = rdns_make_inet_socket (type, res); | |||
freeaddrinfo (res); | |||
return r; | |||
} | |||
else { | |||
return -1; | |||
} | |||
} | |||
/* Not reached */ | |||
return -1; | |||
} | |||
const char * | |||
rdns_strerror (enum dns_rcode rcode) | |||
{ | |||
rcode &= 0xf; | |||
static char numbuf[16]; | |||
if ('\0' == dns_rcodes[rcode][0]) { | |||
snprintf (numbuf, sizeof (numbuf), "UNKNOWN: %d", (int)rcode); | |||
return numbuf; | |||
} | |||
return dns_rcodes[rcode]; | |||
} | |||
const char * | |||
rdns_strtype (enum rdns_request_type type) | |||
{ | |||
return dns_types[type]; | |||
} | |||
uint16_t | |||
rdns_permutor_generate_id (void) | |||
{ | |||
uint16_t id; | |||
id = ottery_rand_unsigned (); | |||
return id; | |||
} | |||
void | |||
rdns_reply_free (struct rdns_reply *rep) | |||
{ | |||
struct rdns_reply_entry *entry, *tmp; | |||
LL_FOREACH_SAFE (rep->entries, entry, tmp) { | |||
switch (entry->type) { | |||
case RDNS_REQUEST_PTR: | |||
free (entry->content.ptr.name); | |||
break; | |||
case RDNS_REQUEST_NS: | |||
free (entry->content.ns.name); | |||
break; | |||
case RDNS_REQUEST_MX: | |||
free (entry->content.mx.name); | |||
break; | |||
case RDNS_REQUEST_TXT: | |||
case RDNS_REQUEST_SPF: | |||
free (entry->content.txt.data); | |||
break; | |||
case RDNS_REQUEST_SRV: | |||
free (entry->content.srv.target); | |||
break; | |||
case RDNS_REQUEST_TLSA: | |||
free (entry->content.tlsa.data); | |||
break; | |||
case RDNS_REQUEST_SOA: | |||
free (entry->content.soa.mname); | |||
free (entry->content.soa.admin); | |||
break; | |||
} | |||
free (entry); | |||
} | |||
free (rep); | |||
} | |||
void | |||
rdns_request_free (struct rdns_request *req) | |||
{ | |||
unsigned int i; | |||
if (req != NULL) { | |||
if (req->packet != NULL) { | |||
free (req->packet); | |||
} | |||
for (i = 0; i < req->qcount; i ++) { | |||
free (req->requested_names[i].name); | |||
} | |||
if (req->requested_names != NULL) { | |||
free (req->requested_names); | |||
} | |||
if (req->reply != NULL) { | |||
rdns_reply_free (req->reply); | |||
} | |||
if (req->state >= RDNS_REQUEST_SENT && | |||
req->state < RDNS_REQUEST_REPLIED) { | |||
/* Remove timer */ | |||
req->async->del_timer (req->async->data, | |||
req->async_event); | |||
/* Remove from id hashes */ | |||
HASH_DEL (req->io->requests, req); | |||
} | |||
else if (req->state == RDNS_REQUEST_REGISTERED) { | |||
/* Remove retransmit event */ | |||
req->async->del_write (req->async->data, | |||
req->async_event); | |||
} | |||
#ifdef TWEETNACL | |||
if (req->curve_plugin_data != NULL) { | |||
req->resolver->curve_plugin->cb.curve_plugin.finish_cb ( | |||
req, req->resolver->curve_plugin->data); | |||
} | |||
#endif | |||
if (req->io != NULL && req->state > RDNS_REQUEST_NEW) { | |||
REF_RELEASE (req->io); | |||
REF_RELEASE (req->resolver); | |||
} | |||
free (req); | |||
} | |||
} | |||
void | |||
rdns_ioc_free (struct rdns_io_channel *ioc) | |||
{ | |||
struct rdns_request *req, *rtmp; | |||
HASH_ITER (hh, ioc->requests, req, rtmp) { | |||
REF_RELEASE (req); | |||
} | |||
ioc->resolver->async->del_read (ioc->resolver->async->data, | |||
ioc->async_io); | |||
close (ioc->sock); | |||
free (ioc); | |||
} | |||
void | |||
rdns_resolver_release (struct rdns_resolver *resolver) | |||
{ | |||
REF_RELEASE (resolver); | |||
} | |||
struct rdns_request* | |||
rdns_request_retain (struct rdns_request *req) | |||
{ | |||
REF_RETAIN (req); | |||
return req; | |||
} | |||
void | |||
rdns_request_release (struct rdns_request *req) | |||
{ | |||
REF_RELEASE (req); | |||
} | |||
static bool | |||
rdns_resolver_conf_process_line (struct rdns_resolver *resolver, char *line) | |||
{ | |||
char *p, *c; | |||
bool has_obrace = false; | |||
unsigned int port = dns_port; | |||
if (strncmp (line, "nameserver", sizeof ("nameserver") - 1) == 0) { | |||
p = line + sizeof ("nameserver") - 1; | |||
/* Skip spaces */ | |||
while (*p == ' ' || *p == '\t') { | |||
p ++; | |||
} | |||
if (*p == '[') { | |||
has_obrace = true; | |||
p ++; | |||
} | |||
if (isxdigit (*p) || *p == ':') { | |||
c = p; | |||
while (isxdigit (*p) || *p == ':' || *p == '.') { | |||
p ++; | |||
} | |||
if (has_obrace && *p != ']') { | |||
return false; | |||
} | |||
else if (*p != '\0' && *p != '\n') { | |||
return false; | |||
} | |||
*p = '\0'; | |||
if (has_obrace) { | |||
p ++; | |||
if (*p == ':') { | |||
/* Maybe we have a port definition */ | |||
port = strtoul (p + 1, NULL, 10); | |||
if (port == 0 || port > UINT16_MAX) { | |||
return false; | |||
} | |||
} | |||
} | |||
return rdns_resolver_add_server (resolver, c, port, 0, default_io_cnt); | |||
} | |||
else { | |||
return false; | |||
} | |||
} | |||
/* XXX: skip unknown resolv.conf lines */ | |||
return true; | |||
} | |||
bool | |||
rdns_resolver_parse_resolv_conf (struct rdns_resolver *resolver, const char *path) | |||
{ | |||
FILE *in; | |||
char buf[BUFSIZ]; | |||
in = fopen (path, "r"); | |||
if (in == NULL) { | |||
return false; | |||
} | |||
while (!feof (in)) { | |||
if (fgets (buf, sizeof (buf), in) == NULL) { | |||
break; | |||
} | |||
if (!rdns_resolver_conf_process_line (resolver, buf)) { | |||
rdns_warn ("rdns_resolver_parse_resolv_conf: cannot parse line: %s", buf); | |||
fclose (in); | |||
return false; | |||
} | |||
} | |||
fclose (in); | |||
return true; | |||
} | |||
bool | |||
rdns_request_has_type (struct rdns_request *req, enum rdns_request_type type) | |||
{ | |||
unsigned int i; | |||
for (i = 0; i < req->qcount; i ++) { | |||
if (req->requested_names[i].type == type) { | |||
return true; | |||
} | |||
} | |||
return false; | |||
} | |||
const struct rdns_request_name * | |||
rdns_request_get_name (struct rdns_request *req, unsigned int *count) | |||
{ | |||
if (count != NULL) { | |||
*count = req->qcount; | |||
} | |||
return req->requested_names; | |||
} | |||
char * | |||
rdns_generate_ptr_from_str (const char *str) | |||
{ | |||
union { | |||
struct in_addr v4; | |||
struct in6_addr v6; | |||
} addr; | |||
char *res = NULL; | |||
unsigned char *bytes; | |||
size_t len; | |||
if (inet_pton (AF_INET, str, &addr.v4) == 1) { | |||
bytes = (unsigned char *)&addr.v4; | |||
len = 4 * 4 + sizeof ("in-addr.arpa"); | |||
res = malloc (len); | |||
if (res) { | |||
snprintf (res, len, "%u.%u.%u.%u.in-addr.arpa", | |||
(unsigned)bytes[3]&0xFF, | |||
(unsigned)bytes[2]&0xFF, | |||
(unsigned)bytes[1]&0xFF, | |||
(unsigned)bytes[0]&0xFF); | |||
} | |||
} | |||
else if (inet_pton (AF_INET6, str, &addr.v6) == 1) { | |||
bytes = (unsigned char *)&addr.v6; | |||
len = 2*32 + sizeof ("ip6.arpa"); | |||
res = malloc (len); | |||
if (res) { | |||
snprintf(res, len, | |||
"%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x." | |||
"%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.ip6.arpa", | |||
bytes[15]&0xF, bytes[15] >> 4, bytes[14]&0xF, bytes[14] >> 4, | |||
bytes[13]&0xF, bytes[13] >> 4, bytes[12]&0xF, bytes[12] >> 4, | |||
bytes[11]&0xF, bytes[11] >> 4, bytes[10]&0xF, bytes[10] >> 4, | |||
bytes[9]&0xF, bytes[9] >> 4, bytes[8]&0xF, bytes[8] >> 4, | |||
bytes[7]&0xF, bytes[7] >> 4, bytes[6]&0xF, bytes[6] >> 4, | |||
bytes[5]&0xF, bytes[5] >> 4, bytes[4]&0xF, bytes[4] >> 4, | |||
bytes[3]&0xF, bytes[3] >> 4, bytes[2]&0xF, bytes[2] >> 4, | |||
bytes[1]&0xF, bytes[1] >> 4, bytes[0]&0xF, bytes[0] >> 4); | |||
} | |||
} | |||
return res; | |||
} |
@@ -0,0 +1,62 @@ | |||
/* Copyright (c) 2014, Vsevolod Stakhov | |||
* All rights reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions are met: | |||
* * Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* * Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in the | |||
* documentation and/or other materials provided with the distribution. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY | |||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY | |||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
#ifndef UTIL_H_ | |||
#define UTIL_H_ | |||
#include "dns_private.h" | |||
/** | |||
* Make a universal socket | |||
* @param credits host, ip or path to unix socket | |||
* @param port port (used for network sockets) | |||
* @param type of socket (SOCK_STREAM or SOCK_DGRAM) | |||
*/ | |||
int | |||
rdns_make_client_socket (const char *credits, uint16_t port, | |||
int type); | |||
/** | |||
* Generate new random DNS id | |||
* @return dns id | |||
*/ | |||
uint16_t rdns_permutor_generate_id (void); | |||
/** | |||
* Free IO channel | |||
*/ | |||
void rdns_ioc_free (struct rdns_io_channel *ioc); | |||
/** | |||
* Free request | |||
* @param req | |||
*/ | |||
void rdns_request_free (struct rdns_request *req); | |||
/** | |||
* Free reply | |||
* @param rep | |||
*/ | |||
void rdns_reply_free (struct rdns_reply *rep); | |||
#endif /* UTIL_H_ */ |
@@ -0,0 +1,18 @@ | |||
SET(UCLSRC ucl_util.c | |||
ucl_parser.c | |||
ucl_emitter.c | |||
ucl_emitter_streamline.c | |||
ucl_emitter_utils.c | |||
ucl_hash.c | |||
ucl_schema.c | |||
lua_ucl.c) | |||
SET (LIB_TYPE STATIC) | |||
ADD_LIBRARY(ucl ${LIB_TYPE} ${UCLSRC}) | |||
IF(ENABLE_URL_SIGN MATCHES "ON") | |||
IF(OPENSSL_FOUND) | |||
TARGET_LINK_LIBRARIES(ucl ${OPENSSL_LIBRARIES}) | |||
ENDIF(OPENSSL_FOUND) | |||
ENDIF(ENABLE_URL_SIGN MATCHES "ON") |
@@ -0,0 +1,627 @@ | |||
/* The MIT License | |||
Copyright (c) 2008, 2009, 2011 by Attractive Chaos <attractor@live.co.uk> | |||
Permission is hereby granted, free of charge, to any person obtaining | |||
a copy of this software and associated documentation files (the | |||
"Software"), to deal in the Software without restriction, including | |||
without limitation the rights to use, copy, modify, merge, publish, | |||
distribute, sublicense, and/or sell copies of the Software, and to | |||
permit persons to whom the Software is furnished to do so, subject to | |||
the following conditions: | |||
The above copyright notice and this permission notice shall be | |||
included in all copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
SOFTWARE. | |||
*/ | |||
/* | |||
An example: | |||
#include "khash.h" | |||
KHASH_MAP_INIT_INT(32, char) | |||
int main() { | |||
int ret, is_missing; | |||
khiter_t k; | |||
khash_t(32) *h = kh_init(32); | |||
k = kh_put(32, h, 5, &ret); | |||
kh_value(h, k) = 10; | |||
k = kh_get(32, h, 10); | |||
is_missing = (k == kh_end(h)); | |||
k = kh_get(32, h, 5); | |||
kh_del(32, h, k); | |||
for (k = kh_begin(h); k != kh_end(h); ++k) | |||
if (kh_exist(h, k)) kh_value(h, k) = 1; | |||
kh_destroy(32, h); | |||
return 0; | |||
} | |||
*/ | |||
/* | |||
2013-05-02 (0.2.8): | |||
* Use quadratic probing. When the capacity is power of 2, stepping function | |||
i*(i+1)/2 guarantees to traverse each bucket. It is better than double | |||
hashing on cache performance and is more robust than linear probing. | |||
In theory, double hashing should be more robust than quadratic probing. | |||
However, my implementation is probably not for large hash tables, because | |||
the second hash function is closely tied to the first hash function, | |||
which reduce the effectiveness of double hashing. | |||
Reference: http://research.cs.vt.edu/AVresearch/hashing/quadratic.php | |||
2011-12-29 (0.2.7): | |||
* Minor code clean up; no actual effect. | |||
2011-09-16 (0.2.6): | |||
* The capacity is a power of 2. This seems to dramatically improve the | |||
speed for simple keys. Thank Zilong Tan for the suggestion. Reference: | |||
- http://code.google.com/p/ulib/ | |||
- http://nothings.org/computer/judy/ | |||
* Allow to optionally use linear probing which usually has better | |||
performance for random input. Double hashing is still the default as it | |||
is more robust to certain non-random input. | |||
* Added Wang's integer hash function (not used by default). This hash | |||
function is more robust to certain non-random input. | |||
2011-02-14 (0.2.5): | |||
* Allow to declare global functions. | |||
2009-09-26 (0.2.4): | |||
* Improve portability | |||
2008-09-19 (0.2.3): | |||
* Corrected the example | |||
* Improved interfaces | |||
2008-09-11 (0.2.2): | |||
* Improved speed a little in kh_put() | |||
2008-09-10 (0.2.1): | |||
* Added kh_clear() | |||
* Fixed a compiling error | |||
2008-09-02 (0.2.0): | |||
* Changed to token concatenation which increases flexibility. | |||
2008-08-31 (0.1.2): | |||
* Fixed a bug in kh_get(), which has not been tested previously. | |||
2008-08-31 (0.1.1): | |||
* Added destructor | |||
*/ | |||
#ifndef __AC_KHASH_H | |||
#define __AC_KHASH_H | |||
/*! | |||
@header | |||
Generic hash table library. | |||
*/ | |||
#define AC_VERSION_KHASH_H "0.2.8" | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <limits.h> | |||
/* compiler specific configuration */ | |||
#if UINT_MAX == 0xffffffffu | |||
typedef unsigned int khint32_t; | |||
#elif ULONG_MAX == 0xffffffffu | |||
typedef unsigned long khint32_t; | |||
#endif | |||
#if ULONG_MAX == ULLONG_MAX | |||
typedef unsigned long khint64_t; | |||
#else | |||
typedef unsigned long long khint64_t; | |||
#endif | |||
#ifndef kh_inline | |||
#ifdef _MSC_VER | |||
#define kh_inline __inline | |||
#else | |||
#define kh_inline inline | |||
#endif | |||
#endif /* kh_inline */ | |||
#ifndef kh_unused | |||
# ifdef __GNUC__ | |||
# define kh_unused(x) __attribute__((__unused__)) x | |||
# else | |||
# define kh_unused(x) x | |||
# endif | |||
#endif | |||
typedef khint32_t khint_t; | |||
typedef khint_t khiter_t; | |||
#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2) | |||
#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1) | |||
#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3) | |||
#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1))) | |||
#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1))) | |||
#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1))) | |||
#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1)) | |||
#define __ac_fsize(m) ((m) < 16? 1 : (m)>>4) | |||
#ifndef kroundup32 | |||
#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x)) | |||
#endif | |||
#ifndef kcalloc | |||
#define kcalloc(N,Z) calloc(N,Z) | |||
#endif | |||
#ifndef kmalloc | |||
#define kmalloc(Z) malloc(Z) | |||
#endif | |||
#ifndef krealloc | |||
#define krealloc(P,Z) realloc(P,Z) | |||
#endif | |||
#ifndef kfree | |||
#define kfree(P) free(P) | |||
#endif | |||
static const double __ac_HASH_UPPER = 0.77; | |||
#define __KHASH_TYPE(name, khkey_t, khval_t) \ | |||
typedef struct kh_##name##_s { \ | |||
khint_t n_buckets, size, n_occupied, upper_bound; \ | |||
khint32_t *flags; \ | |||
khkey_t *keys; \ | |||
khval_t *vals; \ | |||
} kh_##name##_t; | |||
#define __KHASH_PROTOTYPES(name, khkey_t, khval_t) \ | |||
extern kh_##name##_t * kh_init_##name(void); \ | |||
extern void kh_destroy_##name(kh_##name##_t *h); \ | |||
extern void kh_clear_##name(kh_##name##_t *h); \ | |||
extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \ | |||
extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \ | |||
extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \ | |||
extern void kh_del_##name(kh_##name##_t *h, khint_t x); | |||
#define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ | |||
SCOPE kh_##name##_t *kh_init_##name(void) { \ | |||
return (kh_##name##_t*)kcalloc(1, sizeof(kh_##name##_t)); \ | |||
} \ | |||
SCOPE void kh_destroy_##name(kh_##name##_t *h) \ | |||
{ \ | |||
if (h) { \ | |||
kfree((void *)h->keys); kfree(h->flags); \ | |||
kfree((void *)h->vals); \ | |||
kfree(h); \ | |||
} \ | |||
} \ | |||
SCOPE void kh_unused(kh_clear_##name)(kh_##name##_t *h) \ | |||
{ \ | |||
if (h && h->flags) { \ | |||
memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \ | |||
h->size = h->n_occupied = 0; \ | |||
} \ | |||
} \ | |||
SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \ | |||
{ \ | |||
if (h->n_buckets) { \ | |||
khint_t k, i, last, mask, step = 0; \ | |||
mask = h->n_buckets - 1; \ | |||
k = __hash_func(key); i = k & mask; \ | |||
last = i; \ | |||
while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ | |||
i = (i + (++step)) & mask; \ | |||
if (i == last) return h->n_buckets; \ | |||
} \ | |||
return __ac_iseither(h->flags, i)? h->n_buckets : i; \ | |||
} else return 0; \ | |||
} \ | |||
SCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \ | |||
{ /* This function uses 0.25*n_buckets bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \ | |||
khint32_t *new_flags = 0; \ | |||
khint_t j = 1; \ | |||
{ \ | |||
kroundup32(new_n_buckets); \ | |||
if (new_n_buckets < 4) new_n_buckets = 4; \ | |||
if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; /* requested size is too small */ \ | |||
else { /* hash table size to be changed (shrink or expand); rehash */ \ | |||
new_flags = (khint32_t*)kmalloc(__ac_fsize(new_n_buckets) * sizeof(khint32_t)); \ | |||
if (!new_flags) return -1; \ | |||
memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \ | |||
if (h->n_buckets < new_n_buckets) { /* expand */ \ | |||
khkey_t *new_keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \ | |||
if (!new_keys) { kfree(new_flags); return -1; } \ | |||
h->keys = new_keys; \ | |||
if (kh_is_map) { \ | |||
khval_t *new_vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \ | |||
if (!new_vals) { kfree(new_flags); return -1; } \ | |||
h->vals = new_vals; \ | |||
} \ | |||
} /* otherwise shrink */ \ | |||
} \ | |||
} \ | |||
if (j) { /* rehashing is needed */ \ | |||
for (j = 0; j != h->n_buckets; ++j) { \ | |||
if (__ac_iseither(h->flags, j) == 0) { \ | |||
khkey_t key = h->keys[j]; \ | |||
khval_t val; \ | |||
khint_t new_mask; \ | |||
new_mask = new_n_buckets - 1; \ | |||
if (kh_is_map) val = h->vals[j]; \ | |||
__ac_set_isdel_true(h->flags, j); \ | |||
while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \ | |||
khint_t k, i, step = 0; \ | |||
k = __hash_func(key); \ | |||
i = k & new_mask; \ | |||
while (!__ac_isempty(new_flags, i)) i = (i + (++step)) & new_mask; \ | |||
__ac_set_isempty_false(new_flags, i); \ | |||
if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \ | |||
{ khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \ | |||
if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \ | |||
__ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \ | |||
} else { /* write the element and jump out of the loop */ \ | |||
h->keys[i] = key; \ | |||
if (kh_is_map) h->vals[i] = val; \ | |||
break; \ | |||
} \ | |||
} \ | |||
} \ | |||
} \ | |||
if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \ | |||
h->keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \ | |||
if (kh_is_map) h->vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \ | |||
} \ | |||
kfree(h->flags); /* free the working space */ \ | |||
h->flags = new_flags; \ | |||
h->n_buckets = new_n_buckets; \ | |||
h->n_occupied = h->size; \ | |||
h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \ | |||
} \ | |||
return 0; \ | |||
} \ | |||
SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \ | |||
{ \ | |||
khint_t x; \ | |||
if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \ | |||
if (h->n_buckets > (h->size<<1)) { \ | |||
if (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear "deleted" elements */ \ | |||
*ret = -1; return h->n_buckets; \ | |||
} \ | |||
} else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \ | |||
*ret = -1; return h->n_buckets; \ | |||
} \ | |||
} /* TODO: to implement automatically shrinking; resize() already support shrinking */ \ | |||
{ \ | |||
khint_t k, i, site, last, mask = h->n_buckets - 1, step = 0; \ | |||
x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \ | |||
if (__ac_isempty(h->flags, i)) x = i; /* for speed up */ \ | |||
else { \ | |||
last = i; \ | |||
while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ | |||
if (__ac_isdel(h->flags, i)) site = i; \ | |||
i = (i + (++step)) & mask; \ | |||
if (i == last) { x = site; break; } \ | |||
} \ | |||
if (x == h->n_buckets) { \ | |||
if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \ | |||
else x = i; \ | |||
} \ | |||
} \ | |||
} \ | |||
if (__ac_isempty(h->flags, x)) { /* not present at all */ \ | |||
h->keys[x] = key; \ | |||
__ac_set_isboth_false(h->flags, x); \ | |||
++h->size; ++h->n_occupied; \ | |||
*ret = 1; \ | |||
} else if (__ac_isdel(h->flags, x)) { /* deleted */ \ | |||
h->keys[x] = key; \ | |||
__ac_set_isboth_false(h->flags, x); \ | |||
++h->size; \ | |||
*ret = 2; \ | |||
} else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \ | |||
return x; \ | |||
} \ | |||
SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \ | |||
{ \ | |||
if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \ | |||
__ac_set_isdel_true(h->flags, x); \ | |||
--h->size; \ | |||
} \ | |||
} | |||
#define KHASH_DECLARE(name, khkey_t, khval_t) \ | |||
__KHASH_TYPE(name, khkey_t, khval_t) \ | |||
__KHASH_PROTOTYPES(name, khkey_t, khval_t) | |||
#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ | |||
__KHASH_TYPE(name, khkey_t, khval_t) \ | |||
__KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) | |||
#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ | |||
KHASH_INIT2(name, static kh_inline, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) | |||
/* --- BEGIN OF HASH FUNCTIONS --- */ | |||
/*! @function | |||
@abstract Integer hash function | |||
@param key The integer [khint32_t] | |||
@return The hash value [khint_t] | |||
*/ | |||
#define kh_int_hash_func(key) (khint32_t)(key) | |||
/*! @function | |||
@abstract Integer comparison function | |||
*/ | |||
#define kh_int_hash_equal(a, b) ((a) == (b)) | |||
/*! @function | |||
@abstract 64-bit integer hash function | |||
@param key The integer [khint64_t] | |||
@return The hash value [khint_t] | |||
*/ | |||
#define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11) | |||
/*! @function | |||
@abstract 64-bit integer comparison function | |||
*/ | |||
#define kh_int64_hash_equal(a, b) ((a) == (b)) | |||
/*! @function | |||
@abstract const char* hash function | |||
@param s Pointer to a null terminated string | |||
@return The hash value | |||
*/ | |||
static kh_inline khint_t __ac_X31_hash_string(const char *s) | |||
{ | |||
khint_t h = (khint_t)*s; | |||
if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s; | |||
return h; | |||
} | |||
/*! @function | |||
@abstract Another interface to const char* hash function | |||
@param key Pointer to a null terminated string [const char*] | |||
@return The hash value [khint_t] | |||
*/ | |||
#define kh_str_hash_func(key) __ac_X31_hash_string(key) | |||
/*! @function | |||
@abstract Const char* comparison function | |||
*/ | |||
#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0) | |||
static kh_inline khint_t __ac_Wang_hash(khint_t key) | |||
{ | |||
key += ~(key << 15); | |||
key ^= (key >> 10); | |||
key += (key << 3); | |||
key ^= (key >> 6); | |||
key += ~(key << 11); | |||
key ^= (key >> 16); | |||
return key; | |||
} | |||
#define kh_int_hash_func2(k) __ac_Wang_hash((khint_t)key) | |||
/* --- END OF HASH FUNCTIONS --- */ | |||
/* Other convenient macros... */ | |||
/*! | |||
@abstract Type of the hash table. | |||
@param name Name of the hash table [symbol] | |||
*/ | |||
#define khash_t(name) kh_##name##_t | |||
/*! @function | |||
@abstract Initiate a hash table. | |||
@param name Name of the hash table [symbol] | |||
@return Pointer to the hash table [khash_t(name)*] | |||
*/ | |||
#define kh_init(name) kh_init_##name() | |||
/*! @function | |||
@abstract Destroy a hash table. | |||
@param name Name of the hash table [symbol] | |||
@param h Pointer to the hash table [khash_t(name)*] | |||
*/ | |||
#define kh_destroy(name, h) kh_destroy_##name(h) | |||
/*! @function | |||
@abstract Reset a hash table without deallocating memory. | |||
@param name Name of the hash table [symbol] | |||
@param h Pointer to the hash table [khash_t(name)*] | |||
*/ | |||
#define kh_clear(name, h) kh_clear_##name(h) | |||
/*! @function | |||
@abstract Resize a hash table. | |||
@param name Name of the hash table [symbol] | |||
@param h Pointer to the hash table [khash_t(name)*] | |||
@param s New size [khint_t] | |||
*/ | |||
#define kh_resize(name, h, s) kh_resize_##name(h, s) | |||
/*! @function | |||
@abstract Insert a key to the hash table. | |||
@param name Name of the hash table [symbol] | |||
@param h Pointer to the hash table [khash_t(name)*] | |||
@param k Key [type of keys] | |||
@param r Extra return code: -1 if the operation failed; | |||
0 if the key is present in the hash table; | |||
1 if the bucket is empty (never used); 2 if the element in | |||
the bucket has been deleted [int*] | |||
@return Iterator to the inserted element [khint_t] | |||
*/ | |||
#define kh_put(name, h, k, r) kh_put_##name(h, k, r) | |||
/*! @function | |||
@abstract Retrieve a key from the hash table. | |||
@param name Name of the hash table [symbol] | |||
@param h Pointer to the hash table [khash_t(name)*] | |||
@param k Key [type of keys] | |||
@return Iterator to the found element, or kh_end(h) if the element is absent [khint_t] | |||
*/ | |||
#define kh_get(name, h, k) kh_get_##name(h, k) | |||
/*! @function | |||
@abstract Remove a key from the hash table. | |||
@param name Name of the hash table [symbol] | |||
@param h Pointer to the hash table [khash_t(name)*] | |||
@param k Iterator to the element to be deleted [khint_t] | |||
*/ | |||
#define kh_del(name, h, k) kh_del_##name(h, k) | |||
/*! @function | |||
@abstract Test whether a bucket contains data. | |||
@param h Pointer to the hash table [khash_t(name)*] | |||
@param x Iterator to the bucket [khint_t] | |||
@return 1 if containing data; 0 otherwise [int] | |||
*/ | |||
#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x))) | |||
/*! @function | |||
@abstract Get key given an iterator | |||
@param h Pointer to the hash table [khash_t(name)*] | |||
@param x Iterator to the bucket [khint_t] | |||
@return Key [type of keys] | |||
*/ | |||
#define kh_key(h, x) ((h)->keys[x]) | |||
/*! @function | |||
@abstract Get value given an iterator | |||
@param h Pointer to the hash table [khash_t(name)*] | |||
@param x Iterator to the bucket [khint_t] | |||
@return Value [type of values] | |||
@discussion For hash sets, calling this results in segfault. | |||
*/ | |||
#define kh_val(h, x) ((h)->vals[x]) | |||
/*! @function | |||
@abstract Alias of kh_val() | |||
*/ | |||
#define kh_value(h, x) ((h)->vals[x]) | |||
/*! @function | |||
@abstract Get the start iterator | |||
@param h Pointer to the hash table [khash_t(name)*] | |||
@return The start iterator [khint_t] | |||
*/ | |||
#define kh_begin(h) (khint_t)(0) | |||
/*! @function | |||
@abstract Get the end iterator | |||
@param h Pointer to the hash table [khash_t(name)*] | |||
@return The end iterator [khint_t] | |||
*/ | |||
#define kh_end(h) ((h)->n_buckets) | |||
/*! @function | |||
@abstract Get the number of elements in the hash table | |||
@param h Pointer to the hash table [khash_t(name)*] | |||
@return Number of elements in the hash table [khint_t] | |||
*/ | |||
#define kh_size(h) ((h)->size) | |||
/*! @function | |||
@abstract Get the number of buckets in the hash table | |||
@param h Pointer to the hash table [khash_t(name)*] | |||
@return Number of buckets in the hash table [khint_t] | |||
*/ | |||
#define kh_n_buckets(h) ((h)->n_buckets) | |||
/*! @function | |||
@abstract Iterate over the entries in the hash table | |||
@param h Pointer to the hash table [khash_t(name)*] | |||
@param kvar Variable to which key will be assigned | |||
@param vvar Variable to which value will be assigned | |||
@param code Block of code to execute | |||
*/ | |||
#define kh_foreach(h, kvar, vvar, code) { khint_t __i; \ | |||
for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \ | |||
if (!kh_exist(h,__i)) continue; \ | |||
(kvar) = kh_key(h,__i); \ | |||
(vvar) = kh_val(h,__i); \ | |||
code; \ | |||
} } | |||
/*! @function | |||
@abstract Iterate over the values in the hash table | |||
@param h Pointer to the hash table [khash_t(name)*] | |||
@param vvar Variable to which value will be assigned | |||
@param code Block of code to execute | |||
*/ | |||
#define kh_foreach_value(h, vvar, code) { khint_t __i; \ | |||
for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \ | |||
if (!kh_exist(h,__i)) continue; \ | |||
(vvar) = kh_val(h,__i); \ | |||
code; \ | |||
} } | |||
/* More conenient interfaces */ | |||
/*! @function | |||
@abstract Instantiate a hash set containing integer keys | |||
@param name Name of the hash table [symbol] | |||
*/ | |||
#define KHASH_SET_INIT_INT(name) \ | |||
KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal) | |||
/*! @function | |||
@abstract Instantiate a hash map containing integer keys | |||
@param name Name of the hash table [symbol] | |||
@param khval_t Type of values [type] | |||
*/ | |||
#define KHASH_MAP_INIT_INT(name, khval_t) \ | |||
KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal) | |||
/*! @function | |||
@abstract Instantiate a hash map containing 64-bit integer keys | |||
@param name Name of the hash table [symbol] | |||
*/ | |||
#define KHASH_SET_INIT_INT64(name) \ | |||
KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal) | |||
/*! @function | |||
@abstract Instantiate a hash map containing 64-bit integer keys | |||
@param name Name of the hash table [symbol] | |||
@param khval_t Type of values [type] | |||
*/ | |||
#define KHASH_MAP_INIT_INT64(name, khval_t) \ | |||
KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal) | |||
typedef const char *kh_cstr_t; | |||
/*! @function | |||
@abstract Instantiate a hash map containing const char* keys | |||
@param name Name of the hash table [symbol] | |||
*/ | |||
#define KHASH_SET_INIT_STR(name) \ | |||
KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal) | |||
/*! @function | |||
@abstract Instantiate a hash map containing const char* keys | |||
@param name Name of the hash table [symbol] | |||
@param khval_t Type of values [type] | |||
*/ | |||
#define KHASH_MAP_INIT_STR(name, khval_t) \ | |||
KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal) | |||
#endif /* __AC_KHASH_H */ |
@@ -0,0 +1,103 @@ | |||
/* The MIT License | |||
Copyright (c) 2008, by Attractive Chaos <attractor@live.co.uk> | |||
Permission is hereby granted, free of charge, to any person obtaining | |||
a copy of this software and associated documentation files (the | |||
"Software"), to deal in the Software without restriction, including | |||
without limitation the rights to use, copy, modify, merge, publish, | |||
distribute, sublicense, and/or sell copies of the Software, and to | |||
permit persons to whom the Software is furnished to do so, subject to | |||
the following conditions: | |||
The above copyright notice and this permission notice shall be | |||
included in all copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
SOFTWARE. | |||
*/ | |||
/* | |||
An example: | |||
#include "kvec.h" | |||
int main() { | |||
kvec_t(int) array; | |||
kv_init(array); | |||
kv_push(int, array, 10); // append | |||
kv_a(int, array, 20) = 5; // dynamic | |||
kv_A(array, 20) = 4; // static | |||
kv_destroy(array); | |||
return 0; | |||
} | |||
*/ | |||
/* | |||
2008-09-22 (0.1.0): | |||
* The initial version. | |||
*/ | |||
#ifndef AC_KVEC_H | |||
#define AC_KVEC_H | |||
#include <stdlib.h> | |||
#define kv_roundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x)) | |||
#define kvec_t(type) struct { size_t n, m; type *a; } | |||
#define kv_init(v) ((v).n = (v).m = 0, (v).a = 0) | |||
#define kv_destroy(v) free((v).a) | |||
#define kv_A(v, i) ((v).a[(i)]) | |||
#define kv_pop(v) ((v).a[--(v).n]) | |||
#define kv_size(v) ((v).n) | |||
#define kv_max(v) ((v).m) | |||
#define kv_resize(type, v, s) ((v).m = (s), (v).a = (type*)realloc((v).a, sizeof(type) * (v).m)) | |||
#define kv_grow_factor 1.5 | |||
#define kv_grow(type, v) ((v).m = ((v).m > 1 ? (v).m * kv_grow_factor : 2), \ | |||
(v).a = (type*)realloc((v).a, sizeof(type) * (v).m)) | |||
#define kv_copy(type, v1, v0) do { \ | |||
if ((v1).m < (v0).n) kv_resize(type, v1, (v0).n); \ | |||
(v1).n = (v0).n; \ | |||
memcpy((v1).a, (v0).a, sizeof(type) * (v0).n); \ | |||
} while (0) \ | |||
#define kv_push(type, v, x) do { \ | |||
if ((v).n == (v).m) { \ | |||
kv_grow(type, v); \ | |||
} \ | |||
(v).a[(v).n++] = (x); \ | |||
} while (0) | |||
#define kv_prepend(type, v, x) do { \ | |||
if ((v).n == (v).m) { \ | |||
kv_grow(type, v); \ | |||
} \ | |||
memmove((v).a + 1, (v).a, sizeof(type) * (v).n); \ | |||
(v).a[0] = (x); \ | |||
(v).n ++; \ | |||
} while (0) | |||
#define kv_concat(type, v1, v0) do { \ | |||
if ((v1).m < (v0).n + (v1).n) kv_resize(type, v1, (v0).n + (v1).n); \ | |||
memcpy((v1).a + (v1).n, (v0).a, sizeof(type) * ((v0).n + (v1).n)); \ | |||
(v1).n = (v0).n + (v1).n; \ | |||
} while (0) | |||
#define kv_del(type, v, i) do { \ | |||
if ((i) < (v).n) { \ | |||
memmove((v).a + (i), (v).a + ((i) + 1), sizeof(type) * ((v).n - (i) - 1)); \ | |||
(v).n --; \ | |||
} \ | |||
} while (0) | |||
#endif |
@@ -0,0 +1,820 @@ | |||
/* Copyright (c) 2014, Vsevolod Stakhov | |||
* All rights reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions are met: | |||
* * Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* * Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in the | |||
* documentation and/or other materials provided with the distribution. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY | |||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY | |||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
/** | |||
* @file lua ucl bindings | |||
*/ | |||
#include "ucl.h" | |||
#include "ucl_internal.h" | |||
#include "lua_ucl.h" | |||
#include <strings.h> | |||
/*** | |||
* @module ucl | |||
* This lua module allows to parse objects from strings and to store data into | |||
* ucl objects. It uses `libucl` C library to parse and manipulate with ucl objects. | |||
* @example | |||
local ucl = require("ucl") | |||
local parser = ucl.parser() | |||
local res,err = parser:parse_string('{key=value}') | |||
if not res then | |||
print('parser error: ' .. err) | |||
else | |||
local obj = parser:get_object() | |||
local got = ucl.to_format(obj, 'json') | |||
endif | |||
local table = { | |||
str = 'value', | |||
num = 100500, | |||
null = ucl.null, | |||
func = function () | |||
return 'huh' | |||
end | |||
} | |||
print(ucl.to_format(table, 'ucl')) | |||
-- Output: | |||
--[[ | |||
num = 100500; | |||
str = "value"; | |||
null = null; | |||
func = "huh"; | |||
--]] | |||
*/ | |||
#define PARSER_META "ucl.parser.meta" | |||
#define EMITTER_META "ucl.emitter.meta" | |||
#define NULL_META "null.emitter.meta" | |||
static int ucl_object_lua_push_array (lua_State *L, const ucl_object_t *obj); | |||
static int ucl_object_lua_push_scalar (lua_State *L, const ucl_object_t *obj, bool allow_array); | |||
static ucl_object_t* ucl_object_lua_fromtable (lua_State *L, int idx); | |||
static ucl_object_t* ucl_object_lua_fromelt (lua_State *L, int idx); | |||
static void *ucl_null; | |||
/** | |||
* Push a single element of an object to lua | |||
* @param L | |||
* @param key | |||
* @param obj | |||
*/ | |||
static void | |||
ucl_object_lua_push_element (lua_State *L, const char *key, | |||
const ucl_object_t *obj) | |||
{ | |||
lua_pushstring (L, key); | |||
ucl_object_push_lua (L, obj, true); | |||
lua_settable (L, -3); | |||
} | |||
static void | |||
lua_ucl_userdata_dtor (void *ud) | |||
{ | |||
struct ucl_lua_funcdata *fd = (struct ucl_lua_funcdata *)ud; | |||
luaL_unref (fd->L, LUA_REGISTRYINDEX, fd->idx); | |||
if (fd->ret != NULL) { | |||
free (fd->ret); | |||
} | |||
free (fd); | |||
} | |||
static const char * | |||
lua_ucl_userdata_emitter (void *ud) | |||
{ | |||
struct ucl_lua_funcdata *fd = (struct ucl_lua_funcdata *)ud; | |||
const char *out = ""; | |||
lua_rawgeti (fd->L, LUA_REGISTRYINDEX, fd->idx); | |||
lua_pcall (fd->L, 0, 1, 0); | |||
out = lua_tostring (fd->L, -1); | |||
if (out != NULL) { | |||
/* We need to store temporary string in a more appropriate place */ | |||
if (fd->ret) { | |||
free (fd->ret); | |||
} | |||
fd->ret = strdup (out); | |||
} | |||
lua_settop (fd->L, 0); | |||
return fd->ret; | |||
} | |||
/** | |||
* Push a single object to lua | |||
* @param L | |||
* @param obj | |||
* @return | |||
*/ | |||
static int | |||
ucl_object_lua_push_object (lua_State *L, const ucl_object_t *obj, | |||
bool allow_array) | |||
{ | |||
const ucl_object_t *cur; | |||
ucl_object_iter_t it = NULL; | |||
int nelt = 0; | |||
if (allow_array && obj->next != NULL) { | |||
/* Actually we need to push this as an array */ | |||
return ucl_object_lua_push_array (L, obj); | |||
} | |||
/* Optimize allocation by preallocation of table */ | |||
while (ucl_iterate_object (obj, &it, true) != NULL) { | |||
nelt ++; | |||
} | |||
lua_createtable (L, 0, nelt); | |||
it = NULL; | |||
while ((cur = ucl_iterate_object (obj, &it, true)) != NULL) { | |||
ucl_object_lua_push_element (L, ucl_object_key (cur), cur); | |||
} | |||
return 1; | |||
} | |||
/** | |||
* Push an array to lua as table indexed by integers | |||
* @param L | |||
* @param obj | |||
* @return | |||
*/ | |||
static int | |||
ucl_object_lua_push_array (lua_State *L, const ucl_object_t *obj) | |||
{ | |||
const ucl_object_t *cur; | |||
int i = 1, nelt = 0; | |||
/* Optimize allocation by preallocation of table */ | |||
LL_FOREACH (obj, cur) { | |||
nelt ++; | |||
} | |||
lua_createtable (L, nelt, 0); | |||
LL_FOREACH (obj, cur) { | |||
ucl_object_push_lua (L, cur, false); | |||
lua_rawseti (L, -2, i); | |||
i ++; | |||
} | |||
return 1; | |||
} | |||
/** | |||
* Push a simple object to lua depending on its actual type | |||
*/ | |||
static int | |||
ucl_object_lua_push_scalar (lua_State *L, const ucl_object_t *obj, | |||
bool allow_array) | |||
{ | |||
struct ucl_lua_funcdata *fd; | |||
if (allow_array && obj->next != NULL) { | |||
/* Actually we need to push this as an array */ | |||
return ucl_object_lua_push_array (L, obj); | |||
} | |||
switch (obj->type) { | |||
case UCL_BOOLEAN: | |||
lua_pushboolean (L, ucl_obj_toboolean (obj)); | |||
break; | |||
case UCL_STRING: | |||
lua_pushstring (L, ucl_obj_tostring (obj)); | |||
break; | |||
case UCL_INT: | |||
#if LUA_VERSION_NUM >= 501 | |||
lua_pushinteger (L, ucl_obj_toint (obj)); | |||
#else | |||
lua_pushnumber (L, ucl_obj_toint (obj)); | |||
#endif | |||
break; | |||
case UCL_FLOAT: | |||
case UCL_TIME: | |||
lua_pushnumber (L, ucl_obj_todouble (obj)); | |||
break; | |||
case UCL_NULL: | |||
lua_getfield (L, LUA_REGISTRYINDEX, "ucl.null"); | |||
break; | |||
case UCL_USERDATA: | |||
fd = (struct ucl_lua_funcdata *)obj->value.ud; | |||
lua_rawgeti (L, LUA_REGISTRYINDEX, fd->idx); | |||
break; | |||
default: | |||
lua_pushnil (L); | |||
break; | |||
} | |||
return 1; | |||
} | |||
/*** | |||
* @function ucl_object_push_lua(L, obj, allow_array) | |||
* This is a `C` function to push `UCL` object as lua variable. This function | |||
* converts `obj` to lua representation using the following conversions: | |||
* | |||
* - *scalar* values are directly presented by lua objects | |||
* - *userdata* values are converted to lua function objects using `LUA_REGISTRYINDEX`, | |||
* this can be used to pass functions from lua to c and vice-versa | |||
* - *arrays* are converted to lua tables with numeric indicies suitable for `ipairs` iterations | |||
* - *objects* are converted to lua tables with string indicies | |||
* @param {lua_State} L lua state pointer | |||
* @param {ucl_object_t} obj object to push | |||
* @param {bool} allow_array expand implicit arrays (should be true for all but partial arrays) | |||
* @return {int} `1` if an object is pushed to lua | |||
*/ | |||
int | |||
ucl_object_push_lua (lua_State *L, const ucl_object_t *obj, bool allow_array) | |||
{ | |||
switch (obj->type) { | |||
case UCL_OBJECT: | |||
return ucl_object_lua_push_object (L, obj, allow_array); | |||
case UCL_ARRAY: | |||
return ucl_object_lua_push_array (L, obj->value.av); | |||
default: | |||
return ucl_object_lua_push_scalar (L, obj, allow_array); | |||
} | |||
} | |||
/** | |||
* Parse lua table into object top | |||
* @param L | |||
* @param top | |||
* @param idx | |||
*/ | |||
static ucl_object_t * | |||
ucl_object_lua_fromtable (lua_State *L, int idx) | |||
{ | |||
ucl_object_t *obj, *top = NULL; | |||
size_t keylen; | |||
const char *k; | |||
bool is_array = true; | |||
int max = INT_MIN; | |||
if (idx < 0) { | |||
/* For negative indicies we want to invert them */ | |||
idx = lua_gettop (L) + idx + 1; | |||
} | |||
/* Check for array */ | |||
lua_pushnil (L); | |||
while (lua_next (L, idx) != 0) { | |||
if (lua_type (L, -2) == LUA_TNUMBER) { | |||
double num = lua_tonumber (L, -2); | |||
if (num == (int)num) { | |||
if (num > max) { | |||
max = num; | |||
} | |||
} | |||
else { | |||
/* Keys are not integer */ | |||
lua_pop (L, 2); | |||
is_array = false; | |||
break; | |||
} | |||
} | |||
else { | |||
/* Keys are not numeric */ | |||
lua_pop (L, 2); | |||
is_array = false; | |||
break; | |||
} | |||
lua_pop (L, 1); | |||
} | |||
/* Table iterate */ | |||
if (is_array) { | |||
int i; | |||
top = ucl_object_typed_new (UCL_ARRAY); | |||
for (i = 1; i <= max; i ++) { | |||
lua_pushinteger (L, i); | |||
lua_gettable (L, idx); | |||
obj = ucl_object_lua_fromelt (L, lua_gettop (L)); | |||
if (obj != NULL) { | |||
ucl_array_append (top, obj); | |||
} | |||
} | |||
} | |||
else { | |||
lua_pushnil (L); | |||
top = ucl_object_typed_new (UCL_OBJECT); | |||
while (lua_next (L, idx) != 0) { | |||
/* copy key to avoid modifications */ | |||
k = lua_tolstring (L, -2, &keylen); | |||
obj = ucl_object_lua_fromelt (L, lua_gettop (L)); | |||
if (obj != NULL) { | |||
ucl_object_insert_key (top, obj, k, keylen, true); | |||
} | |||
lua_pop (L, 1); | |||
} | |||
} | |||
return top; | |||
} | |||
/** | |||
* Get a single element from lua to object obj | |||
* @param L | |||
* @param obj | |||
* @param idx | |||
*/ | |||
static ucl_object_t * | |||
ucl_object_lua_fromelt (lua_State *L, int idx) | |||
{ | |||
int type; | |||
double num; | |||
ucl_object_t *obj = NULL; | |||
struct ucl_lua_funcdata *fd; | |||
type = lua_type (L, idx); | |||
switch (type) { | |||
case LUA_TSTRING: | |||
obj = ucl_object_fromstring_common (lua_tostring (L, idx), 0, 0); | |||
break; | |||
case LUA_TNUMBER: | |||
num = lua_tonumber (L, idx); | |||
if (num == (int64_t)num) { | |||
obj = ucl_object_fromint (num); | |||
} | |||
else { | |||
obj = ucl_object_fromdouble (num); | |||
} | |||
break; | |||
case LUA_TBOOLEAN: | |||
obj = ucl_object_frombool (lua_toboolean (L, idx)); | |||
break; | |||
case LUA_TUSERDATA: | |||
if (lua_topointer (L, idx) == ucl_null) { | |||
obj = ucl_object_typed_new (UCL_NULL); | |||
} | |||
break; | |||
case LUA_TTABLE: | |||
case LUA_TFUNCTION: | |||
case LUA_TTHREAD: | |||
if (luaL_getmetafield (L, idx, "__gen_ucl")) { | |||
if (lua_isfunction (L, -1)) { | |||
lua_settop (L, 3); /* gen, obj, func */ | |||
lua_insert (L, 1); /* func, gen, obj */ | |||
lua_insert (L, 2); /* func, obj, gen */ | |||
lua_call(L, 2, 1); | |||
obj = ucl_object_lua_fromelt (L, 1); | |||
} | |||
lua_pop (L, 2); | |||
} | |||
else { | |||
if (type == LUA_TTABLE) { | |||
obj = ucl_object_lua_fromtable (L, idx); | |||
} | |||
else if (type == LUA_TFUNCTION) { | |||
fd = malloc (sizeof (*fd)); | |||
if (fd != NULL) { | |||
lua_pushvalue (L, idx); | |||
fd->L = L; | |||
fd->ret = NULL; | |||
fd->idx = luaL_ref (L, LUA_REGISTRYINDEX); | |||
obj = ucl_object_new_userdata (lua_ucl_userdata_dtor, | |||
lua_ucl_userdata_emitter); | |||
obj->type = UCL_USERDATA; | |||
obj->value.ud = (void *)fd; | |||
} | |||
} | |||
} | |||
break; | |||
} | |||
return obj; | |||
} | |||
/** | |||
* @function ucl_object_lua_import(L, idx) | |||
* Extracts ucl object from lua variable at `idx` position, | |||
* @see ucl_object_push_lua for conversion definitions | |||
* @param {lua_state} L lua state machine pointer | |||
* @param {int} idx index where the source variable is placed | |||
* @return {ucl_object_t} new ucl object extracted from lua variable. Reference count of this object is 1, | |||
* this object thus needs to be unref'ed after usage. | |||
*/ | |||
ucl_object_t * | |||
ucl_object_lua_import (lua_State *L, int idx) | |||
{ | |||
ucl_object_t *obj; | |||
int t; | |||
t = lua_type (L, idx); | |||
switch (t) { | |||
case LUA_TTABLE: | |||
obj = ucl_object_lua_fromtable (L, idx); | |||
break; | |||
default: | |||
obj = ucl_object_lua_fromelt (L, idx); | |||
break; | |||
} | |||
return obj; | |||
} | |||
static int | |||
lua_ucl_parser_init (lua_State *L) | |||
{ | |||
struct ucl_parser *parser, **pparser; | |||
int flags = 0; | |||
if (lua_gettop (L) >= 1) { | |||
flags = lua_tonumber (L, 1); | |||
} | |||
parser = ucl_parser_new (flags); | |||
if (parser == NULL) { | |||
lua_pushnil (L); | |||
} | |||
pparser = lua_newuserdata (L, sizeof (parser)); | |||
*pparser = parser; | |||
luaL_getmetatable (L, PARSER_META); | |||
lua_setmetatable (L, -2); | |||
return 1; | |||
} | |||
static struct ucl_parser * | |||
lua_ucl_parser_get (lua_State *L, int index) | |||
{ | |||
return *((struct ucl_parser **) luaL_checkudata(L, index, PARSER_META)); | |||
} | |||
/*** | |||
* @method parser:parse_file(name) | |||
* Parse UCL object from file. | |||
* @param {string} name filename to parse | |||
* @return {bool[, string]} if res is `true` then file has been parsed successfully, otherwise an error string is also returned | |||
@example | |||
local parser = ucl.parser() | |||
local res,err = parser:parse_file('/some/file.conf') | |||
if not res then | |||
print('parser error: ' .. err) | |||
else | |||
-- Do something with object | |||
end | |||
*/ | |||
static int | |||
lua_ucl_parser_parse_file (lua_State *L) | |||
{ | |||
struct ucl_parser *parser; | |||
const char *file; | |||
int ret = 2; | |||
parser = lua_ucl_parser_get (L, 1); | |||
file = luaL_checkstring (L, 2); | |||
if (parser != NULL && file != NULL) { | |||
if (ucl_parser_add_file (parser, file)) { | |||
lua_pushboolean (L, true); | |||
ret = 1; | |||
} | |||
else { | |||
lua_pushboolean (L, false); | |||
lua_pushstring (L, ucl_parser_get_error (parser)); | |||
} | |||
} | |||
else { | |||
lua_pushboolean (L, false); | |||
lua_pushstring (L, "invalid arguments"); | |||
} | |||
return ret; | |||
} | |||
/*** | |||
* @method parser:parse_string(input) | |||
* Parse UCL object from file. | |||
* @param {string} input string to parse | |||
* @return {bool[, string]} if res is `true` then file has been parsed successfully, otherwise an error string is also returned | |||
*/ | |||
static int | |||
lua_ucl_parser_parse_string (lua_State *L) | |||
{ | |||
struct ucl_parser *parser; | |||
const char *string; | |||
size_t llen; | |||
int ret = 2; | |||
parser = lua_ucl_parser_get (L, 1); | |||
string = luaL_checklstring (L, 2, &llen); | |||
if (parser != NULL && string != NULL) { | |||
if (ucl_parser_add_chunk (parser, (const unsigned char *)string, llen)) { | |||
lua_pushboolean (L, true); | |||
ret = 1; | |||
} | |||
else { | |||
lua_pushboolean (L, false); | |||
lua_pushstring (L, ucl_parser_get_error (parser)); | |||
} | |||
} | |||
else { | |||
lua_pushboolean (L, false); | |||
lua_pushstring (L, "invalid arguments"); | |||
} | |||
return ret; | |||
} | |||
/*** | |||
* @method parser:get_object() | |||
* Get top object from parser and export it to lua representation. | |||
* @return {variant or nil} ucl object as lua native variable | |||
*/ | |||
static int | |||
lua_ucl_parser_get_object (lua_State *L) | |||
{ | |||
struct ucl_parser *parser; | |||
ucl_object_t *obj; | |||
int ret = 1; | |||
parser = lua_ucl_parser_get (L, 1); | |||
obj = ucl_parser_get_object (parser); | |||
if (obj != NULL) { | |||
ret = ucl_object_push_lua (L, obj, false); | |||
/* no need to keep reference */ | |||
ucl_object_unref (obj); | |||
} | |||
else { | |||
lua_pushnil (L); | |||
} | |||
return ret; | |||
} | |||
static int | |||
lua_ucl_parser_gc (lua_State *L) | |||
{ | |||
struct ucl_parser *parser; | |||
parser = lua_ucl_parser_get (L, 1); | |||
ucl_parser_free (parser); | |||
return 0; | |||
} | |||
static void | |||
lua_ucl_parser_mt (lua_State *L) | |||
{ | |||
luaL_newmetatable (L, PARSER_META); | |||
lua_pushvalue(L, -1); | |||
lua_setfield(L, -2, "__index"); | |||
lua_pushcfunction (L, lua_ucl_parser_gc); | |||
lua_setfield (L, -2, "__gc"); | |||
lua_pushcfunction (L, lua_ucl_parser_parse_file); | |||
lua_setfield (L, -2, "parse_file"); | |||
lua_pushcfunction (L, lua_ucl_parser_parse_string); | |||
lua_setfield (L, -2, "parse_string"); | |||
lua_pushcfunction (L, lua_ucl_parser_get_object); | |||
lua_setfield (L, -2, "get_object"); | |||
lua_pop (L, 1); | |||
} | |||
static int | |||
lua_ucl_to_string (lua_State *L, const ucl_object_t *obj, enum ucl_emitter type) | |||
{ | |||
unsigned char *result; | |||
result = ucl_object_emit (obj, type); | |||
if (result != NULL) { | |||
lua_pushstring (L, (const char *)result); | |||
free (result); | |||
} | |||
else { | |||
lua_pushnil (L); | |||
} | |||
return 1; | |||
} | |||
static int | |||
lua_ucl_to_json (lua_State *L) | |||
{ | |||
ucl_object_t *obj; | |||
int format = UCL_EMIT_JSON; | |||
if (lua_gettop (L) > 1) { | |||
if (lua_toboolean (L, 2)) { | |||
format = UCL_EMIT_JSON_COMPACT; | |||
} | |||
} | |||
obj = ucl_object_lua_import (L, 1); | |||
if (obj != NULL) { | |||
lua_ucl_to_string (L, obj, format); | |||
ucl_object_unref (obj); | |||
} | |||
else { | |||
lua_pushnil (L); | |||
} | |||
return 1; | |||
} | |||
static int | |||
lua_ucl_to_config (lua_State *L) | |||
{ | |||
ucl_object_t *obj; | |||
obj = ucl_object_lua_import (L, 1); | |||
if (obj != NULL) { | |||
lua_ucl_to_string (L, obj, UCL_EMIT_CONFIG); | |||
ucl_object_unref (obj); | |||
} | |||
else { | |||
lua_pushnil (L); | |||
} | |||
return 1; | |||
} | |||
/*** | |||
* @function ucl.to_format(var, format) | |||
* Converts lua variable `var` to the specified `format`. Formats supported are: | |||
* | |||
* - `json` - fine printed json | |||
* - `json-compact` - compacted json | |||
* - `config` - fine printed configuration | |||
* - `ucl` - same as `config` | |||
* - `yaml` - embedded yaml | |||
* | |||
* If `var` contains function, they are called during output formatting and if | |||
* they return string value, then this value is used for ouptut. | |||
* @param {variant} var any sort of lua variable (if userdata then metafield `__to_ucl` is searched for output) | |||
* @param {string} format any available format | |||
* @return {string} string representation of `var` in the specific `format`. | |||
* @example | |||
local table = { | |||
str = 'value', | |||
num = 100500, | |||
null = ucl.null, | |||
func = function () | |||
return 'huh' | |||
end | |||
} | |||
print(ucl.to_format(table, 'ucl')) | |||
-- Output: | |||
--[[ | |||
num = 100500; | |||
str = "value"; | |||
null = null; | |||
func = "huh"; | |||
--]] | |||
*/ | |||
static int | |||
lua_ucl_to_format (lua_State *L) | |||
{ | |||
ucl_object_t *obj; | |||
int format = UCL_EMIT_JSON; | |||
if (lua_gettop (L) > 1) { | |||
if (lua_type (L, 2) == LUA_TNUMBER) { | |||
format = lua_tonumber (L, 2); | |||
if (format < 0 || format >= UCL_EMIT_YAML) { | |||
lua_pushnil (L); | |||
return 1; | |||
} | |||
} | |||
else if (lua_type (L, 2) == LUA_TSTRING) { | |||
const char *strtype = lua_tostring (L, 2); | |||
if (strcasecmp (strtype, "json") == 0) { | |||
format = UCL_EMIT_JSON; | |||
} | |||
else if (strcasecmp (strtype, "json-compact") == 0) { | |||
format = UCL_EMIT_JSON_COMPACT; | |||
} | |||
else if (strcasecmp (strtype, "yaml") == 0) { | |||
format = UCL_EMIT_YAML; | |||
} | |||
else if (strcasecmp (strtype, "config") == 0 || | |||
strcasecmp (strtype, "ucl") == 0) { | |||
format = UCL_EMIT_CONFIG; | |||
} | |||
} | |||
} | |||
obj = ucl_object_lua_import (L, 1); | |||
if (obj != NULL) { | |||
lua_ucl_to_string (L, obj, format); | |||
ucl_object_unref (obj); | |||
} | |||
else { | |||
lua_pushnil (L); | |||
} | |||
return 1; | |||
} | |||
static int | |||
lua_ucl_null_tostring (lua_State* L) | |||
{ | |||
lua_pushstring (L, "null"); | |||
return 1; | |||
} | |||
static void | |||
lua_ucl_null_mt (lua_State *L) | |||
{ | |||
luaL_newmetatable (L, NULL_META); | |||
lua_pushcfunction (L, lua_ucl_null_tostring); | |||
lua_setfield (L, -2, "__tostring"); | |||
lua_pop (L, 1); | |||
} | |||
int | |||
luaopen_ucl (lua_State *L) | |||
{ | |||
lua_ucl_parser_mt (L); | |||
lua_ucl_null_mt (L); | |||
/* Create the refs weak table: */ | |||
lua_createtable (L, 0, 2); | |||
lua_pushliteral (L, "v"); /* tbl, "v" */ | |||
lua_setfield (L, -2, "__mode"); | |||
lua_pushvalue (L, -1); /* tbl, tbl */ | |||
lua_setmetatable (L, -2); /* tbl */ | |||
lua_setfield (L, LUA_REGISTRYINDEX, "ucl.refs"); | |||
lua_newtable (L); | |||
lua_pushcfunction (L, lua_ucl_parser_init); | |||
lua_setfield (L, -2, "parser"); | |||
lua_pushcfunction (L, lua_ucl_to_json); | |||
lua_setfield (L, -2, "to_json"); | |||
lua_pushcfunction (L, lua_ucl_to_config); | |||
lua_setfield (L, -2, "to_config"); | |||
lua_pushcfunction (L, lua_ucl_to_format); | |||
lua_setfield (L, -2, "to_format"); | |||
ucl_null = lua_newuserdata (L, 0); | |||
luaL_getmetatable (L, NULL_META); | |||
lua_setmetatable (L, -2); | |||
lua_pushvalue (L, -1); | |||
lua_setfield (L, LUA_REGISTRYINDEX, "ucl.null"); | |||
lua_setfield (L, -2, "null"); | |||
return 1; | |||
} | |||
struct ucl_lua_funcdata* | |||
ucl_object_toclosure (const ucl_object_t *obj) | |||
{ | |||
if (obj == NULL || obj->type != UCL_USERDATA) { | |||
return NULL; | |||
} | |||
return (struct ucl_lua_funcdata*)obj->value.ud; | |||
} |
@@ -0,0 +1,69 @@ | |||
/* Copyright (c) 2014, Vsevolod Stakhov | |||
* All rights reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions are met: | |||
* * Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* * Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in the | |||
* documentation and/or other materials provided with the distribution. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY | |||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY | |||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
#ifndef LUA_UCL_H_ | |||
#define LUA_UCL_H_ | |||
#ifdef HAVE_CONFIG_H | |||
#include "config.h" | |||
#endif | |||
#include <lua.h> | |||
#include <lauxlib.h> | |||
#include <lualib.h> | |||
#include "ucl.h" | |||
/** | |||
* Closure structure for lua function storing inside UCL | |||
*/ | |||
struct ucl_lua_funcdata { | |||
lua_State *L; | |||
int idx; | |||
char *ret; | |||
}; | |||
/** | |||
* Initialize lua UCL API | |||
*/ | |||
UCL_EXTERN int luaopen_ucl (lua_State *L); | |||
/** | |||
* Import UCL object from lua state | |||
* @param L lua state | |||
* @param idx index of object at the lua stack to convert to UCL | |||
* @return new UCL object or NULL, the caller should unref object after using | |||
*/ | |||
UCL_EXTERN ucl_object_t* ucl_object_lua_import (lua_State *L, int idx); | |||
/** | |||
* Push an object to lua | |||
* @param L lua state | |||
* @param obj object to push | |||
* @param allow_array traverse over implicit arrays | |||
*/ | |||
UCL_EXTERN int ucl_object_push_lua (lua_State *L, | |||
const ucl_object_t *obj, bool allow_array); | |||
UCL_EXTERN struct ucl_lua_funcdata* ucl_object_toclosure ( | |||
const ucl_object_t *obj); | |||
#endif /* LUA_UCL_H_ */ |
@@ -0,0 +1,212 @@ | |||
/* tree.h -- AVL trees (in the spirit of BSD's 'queue.h') -*- C -*- */ | |||
/* Copyright (c) 2005 Ian Piumarta | |||
* | |||
* All rights reserved. | |||
* | |||
* Permission is hereby granted, free of charge, to any person obtaining a copy | |||
* of this software and associated documentation files (the 'Software'), to deal | |||
* in the Software without restriction, including without limitation the rights | |||
* to use, copy, modify, merge, publish, distribute, and/or sell copies of the | |||
* Software, and to permit persons to whom the Software is furnished to do so, | |||
* provided that the above copyright notice(s) and this permission notice appear | |||
* in all copies of the Software and that both the above copyright notice(s) and | |||
* this permission notice appear in supporting documentation. | |||
* | |||
* THE SOFTWARE IS PROVIDED 'AS IS'. USE ENTIRELY AT YOUR OWN RISK. | |||
*/ | |||
/* This file defines an AVL balanced binary tree [Georgii M. Adelson-Velskii and | |||
* Evgenii M. Landis, 'An algorithm for the organization of information', | |||
* Doklady Akademii Nauk SSSR, 146:263-266, 1962 (Russian). Also in Myron | |||
* J. Ricci (trans.), Soviet Math, 3:1259-1263, 1962 (English)]. | |||
* | |||
* An AVL tree is headed by pointers to the root node and to a function defining | |||
* the ordering relation between nodes. Each node contains an arbitrary payload | |||
* plus three fields per tree entry: the depth of the subtree for which it forms | |||
* the root and two pointers to child nodes (singly-linked for minimum space, at | |||
* the expense of direct access to the parent node given a pointer to one of the | |||
* children). The tree is rebalanced after every insertion or removal. The | |||
* tree may be traversed in two directions: forward (in-order left-to-right) and | |||
* reverse (in-order, right-to-left). | |||
* | |||
* Because of the recursive nature of many of the operations on trees it is | |||
* necessary to define a number of helper functions for each type of tree node. | |||
* The macro TREE_DEFINE(node_tag, entry_name) defines these functions with | |||
* unique names according to the node_tag. This macro should be invoked, | |||
* thereby defining the necessary functions, once per node tag in the program. | |||
* | |||
* For details on the use of these macros, see the tree(3) manual page. | |||
*/ | |||
#ifndef __tree_h | |||
#define __tree_h | |||
#define TREE_DELTA_MAX 1 | |||
#define TREE_ENTRY(type) \ | |||
struct { \ | |||
struct type *avl_left; \ | |||
struct type *avl_right; \ | |||
int avl_height; \ | |||
} | |||
#define TREE_HEAD(name, type) \ | |||
struct name { \ | |||
struct type *th_root; \ | |||
int (*th_cmp)(struct type *lhs, struct type *rhs); \ | |||
} | |||
#define TREE_INITIALIZER(cmp) { 0, cmp } | |||
#define TREE_DELTA(self, field) \ | |||
(( (((self)->field.avl_left) ? (self)->field.avl_left->field.avl_height : 0)) \ | |||
- (((self)->field.avl_right) ? (self)->field.avl_right->field.avl_height : 0)) | |||
/* Recursion prevents the following from being defined as macros. */ | |||
#define TREE_DEFINE(node, field) \ | |||
\ | |||
struct node *TREE_BALANCE_##node##_##field(struct node *); \ | |||
\ | |||
struct node *TREE_ROTL_##node##_##field(struct node *self) \ | |||
{ \ | |||
struct node *r= self->field.avl_right; \ | |||
self->field.avl_right= r->field.avl_left; \ | |||
r->field.avl_left= TREE_BALANCE_##node##_##field(self); \ | |||
return TREE_BALANCE_##node##_##field(r); \ | |||
} \ | |||
\ | |||
struct node *TREE_ROTR_##node##_##field(struct node *self) \ | |||
{ \ | |||
struct node *l= self->field.avl_left; \ | |||
self->field.avl_left= l->field.avl_right; \ | |||
l->field.avl_right= TREE_BALANCE_##node##_##field(self); \ | |||
return TREE_BALANCE_##node##_##field(l); \ | |||
} \ | |||
\ | |||
struct node *TREE_BALANCE_##node##_##field(struct node *self) \ | |||
{ \ | |||
int delta= TREE_DELTA(self, field); \ | |||
\ | |||
if (delta < -TREE_DELTA_MAX) \ | |||
{ \ | |||
if (TREE_DELTA(self->field.avl_right, field) > 0) \ | |||
self->field.avl_right= TREE_ROTR_##node##_##field(self->field.avl_right); \ | |||
return TREE_ROTL_##node##_##field(self); \ | |||
} \ | |||
else if (delta > TREE_DELTA_MAX) \ | |||
{ \ | |||
if (TREE_DELTA(self->field.avl_left, field) < 0) \ | |||
self->field.avl_left= TREE_ROTL_##node##_##field(self->field.avl_left); \ | |||
return TREE_ROTR_##node##_##field(self); \ | |||
} \ | |||
self->field.avl_height= 0; \ | |||
if (self->field.avl_left && (self->field.avl_left->field.avl_height > self->field.avl_height)) \ | |||
self->field.avl_height= self->field.avl_left->field.avl_height; \ | |||
if (self->field.avl_right && (self->field.avl_right->field.avl_height > self->field.avl_height)) \ | |||
self->field.avl_height= self->field.avl_right->field.avl_height; \ | |||
self->field.avl_height += 1; \ | |||
return self; \ | |||
} \ | |||
\ | |||
struct node *TREE_INSERT_##node##_##field \ | |||
(struct node *self, struct node *elm, int (*compare)(struct node *lhs, struct node *rhs)) \ | |||
{ \ | |||
if (!self) \ | |||
return elm; \ | |||
if (compare(elm, self) < 0) \ | |||
self->field.avl_left= TREE_INSERT_##node##_##field(self->field.avl_left, elm, compare); \ | |||
else \ | |||
self->field.avl_right= TREE_INSERT_##node##_##field(self->field.avl_right, elm, compare); \ | |||
return TREE_BALANCE_##node##_##field(self); \ | |||
} \ | |||
\ | |||
struct node *TREE_FIND_##node##_##field \ | |||
(struct node *self, struct node *elm, int (*compare)(struct node *lhs, struct node *rhs)) \ | |||
{ \ | |||
if (!self) \ | |||
return 0; \ | |||
if (compare(elm, self) == 0) \ | |||
return self; \ | |||
if (compare(elm, self) < 0) \ | |||
return TREE_FIND_##node##_##field(self->field.avl_left, elm, compare); \ | |||
else \ | |||
return TREE_FIND_##node##_##field(self->field.avl_right, elm, compare); \ | |||
} \ | |||
\ | |||
struct node *TREE_MOVE_RIGHT(struct node *self, struct node *rhs) \ | |||
{ \ | |||
if (!self) \ | |||
return rhs; \ | |||
self->field.avl_right= TREE_MOVE_RIGHT(self->field.avl_right, rhs); \ | |||
return TREE_BALANCE_##node##_##field(self); \ | |||
} \ | |||
\ | |||
struct node *TREE_REMOVE_##node##_##field \ | |||
(struct node *self, struct node *elm, int (*compare)(struct node *lhs, struct node *rhs)) \ | |||
{ \ | |||
if (!self) return 0; \ | |||
\ | |||
if (compare(elm, self) == 0) \ | |||
{ \ | |||
struct node *tmp= TREE_MOVE_RIGHT(self->field.avl_left, self->field.avl_right); \ | |||
self->field.avl_left= 0; \ | |||
self->field.avl_right= 0; \ | |||
return tmp; \ | |||
} \ | |||
if (compare(elm, self) < 0) \ | |||
self->field.avl_left= TREE_REMOVE_##node##_##field(self->field.avl_left, elm, compare); \ | |||
else \ | |||
self->field.avl_right= TREE_REMOVE_##node##_##field(self->field.avl_right, elm, compare); \ | |||
return TREE_BALANCE_##node##_##field(self); \ | |||
} \ | |||
\ | |||
void TREE_FORWARD_APPLY_ALL_##node##_##field \ | |||
(struct node *self, void (*function)(struct node *node, void *data), void *data) \ | |||
{ \ | |||
if (self) \ | |||
{ \ | |||
TREE_FORWARD_APPLY_ALL_##node##_##field(self->field.avl_left, function, data); \ | |||
function(self, data); \ | |||
TREE_FORWARD_APPLY_ALL_##node##_##field(self->field.avl_right, function, data); \ | |||
} \ | |||
} \ | |||
\ | |||
void TREE_REVERSE_APPLY_ALL_##node##_##field \ | |||
(struct node *self, void (*function)(struct node *node, void *data), void *data) \ | |||
{ \ | |||
if (self) \ | |||
{ \ | |||
TREE_REVERSE_APPLY_ALL_##node##_##field(self->field.avl_right, function, data); \ | |||
function(self, data); \ | |||
TREE_REVERSE_APPLY_ALL_##node##_##field(self->field.avl_left, function, data); \ | |||
} \ | |||
} | |||
#define TREE_INSERT(head, node, field, elm) \ | |||
((head)->th_root= TREE_INSERT_##node##_##field((head)->th_root, (elm), (head)->th_cmp)) | |||
#define TREE_FIND(head, node, field, elm) \ | |||
(TREE_FIND_##node##_##field((head)->th_root, (elm), (head)->th_cmp)) | |||
#define TREE_REMOVE(head, node, field, elm) \ | |||
((head)->th_root= TREE_REMOVE_##node##_##field((head)->th_root, (elm), (head)->th_cmp)) | |||
#define TREE_DEPTH(head, field) \ | |||
((head)->th_root->field.avl_height) | |||
#define TREE_FORWARD_APPLY(head, node, field, function, data) \ | |||
TREE_FORWARD_APPLY_ALL_##node##_##field((head)->th_root, function, data) | |||
#define TREE_REVERSE_APPLY(head, node, field, function, data) \ | |||
TREE_REVERSE_APPLY_ALL_##node##_##field((head)->th_root, function, data) | |||
#define TREE_INIT(head, cmp) do { \ | |||
(head)->th_root= 0; \ | |||
(head)->th_cmp= (cmp); \ | |||
} while (0) | |||
#endif /* __tree_h */ |
@@ -0,0 +1,267 @@ | |||
/* Copyright (c) 2013, Vsevolod Stakhov | |||
* All rights reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions are met: | |||
* * Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* * Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in the | |||
* documentation and/or other materials provided with the distribution. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY | |||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY | |||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
#ifndef UCL_CHARTABLE_H_ | |||
#define UCL_CHARTABLE_H_ | |||
#include "ucl_internal.h" | |||
static const unsigned int ucl_chartable[255] = { | |||
UCL_CHARACTER_VALUE_END, UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, | |||
UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, | |||
UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, | |||
UCL_CHARACTER_JSON_UNSAFE|UCL_CHARACTER_UCL_UNSAFE, | |||
UCL_CHARACTER_WHITESPACE|UCL_CHARACTER_WHITESPACE_UNSAFE|UCL_CHARACTER_KEY_SEP|UCL_CHARACTER_JSON_UNSAFE|UCL_CHARACTER_UCL_UNSAFE, | |||
UCL_CHARACTER_WHITESPACE_UNSAFE|UCL_CHARACTER_VALUE_END|UCL_CHARACTER_JSON_UNSAFE|UCL_CHARACTER_UCL_UNSAFE, | |||
UCL_CHARACTER_WHITESPACE_UNSAFE, | |||
UCL_CHARACTER_WHITESPACE_UNSAFE|UCL_CHARACTER_JSON_UNSAFE|UCL_CHARACTER_UCL_UNSAFE, | |||
UCL_CHARACTER_WHITESPACE_UNSAFE|UCL_CHARACTER_VALUE_END|UCL_CHARACTER_JSON_UNSAFE|UCL_CHARACTER_UCL_UNSAFE, | |||
UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, | |||
UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, | |||
UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, | |||
UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, | |||
UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, | |||
UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, | |||
UCL_CHARACTER_WHITESPACE|UCL_CHARACTER_WHITESPACE_UNSAFE|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_KEY_SEP|UCL_CHARACTER_UCL_UNSAFE /* */, | |||
UCL_CHARACTER_VALUE_STR /* ! */, | |||
UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_ESCAPE|UCL_CHARACTER_JSON_UNSAFE|UCL_CHARACTER_UCL_UNSAFE /* " */, | |||
UCL_CHARACTER_VALUE_END /* # */, UCL_CHARACTER_VALUE_STR /* $ */, | |||
UCL_CHARACTER_VALUE_STR /* % */, UCL_CHARACTER_VALUE_STR /* & */, | |||
UCL_CHARACTER_VALUE_STR /* ' */, UCL_CHARACTER_VALUE_STR /* ( */, | |||
UCL_CHARACTER_VALUE_STR /* ) */, UCL_CHARACTER_VALUE_STR /* * */, | |||
UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* + */, | |||
UCL_CHARACTER_VALUE_END /* , */, | |||
UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* - */, | |||
UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* . */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_ESCAPE /* / */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 0 */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 1 */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 2 */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 3 */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 4 */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 5 */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 6 */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 7 */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 8 */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 9 */, | |||
UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_KEY_SEP|UCL_CHARACTER_UCL_UNSAFE /* : */, | |||
UCL_CHARACTER_VALUE_END /* ; */, UCL_CHARACTER_VALUE_STR /* < */, | |||
UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_KEY_SEP|UCL_CHARACTER_UCL_UNSAFE /* = */, | |||
UCL_CHARACTER_VALUE_STR /* > */, UCL_CHARACTER_VALUE_STR /* ? */, | |||
UCL_CHARACTER_VALUE_STR /* @ */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* A */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* B */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* C */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* D */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* E */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* F */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* G */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* H */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* I */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* J */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* K */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* L */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* M */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* N */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* O */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* P */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* Q */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* R */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* S */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* T */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* U */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* V */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* W */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* X */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* Y */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* Z */, | |||
UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_UCL_UNSAFE /* [ */, | |||
UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_ESCAPE|UCL_CHARACTER_JSON_UNSAFE|UCL_CHARACTER_UCL_UNSAFE /* \ */, | |||
UCL_CHARACTER_VALUE_END /* ] */, UCL_CHARACTER_VALUE_STR /* ^ */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR /* _ */, | |||
UCL_CHARACTER_VALUE_STR /* ` */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* a */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT|UCL_CHARACTER_ESCAPE /* b */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* c */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* d */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* e */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT|UCL_CHARACTER_ESCAPE /* f */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* g */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* h */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* i */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* j */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* k */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* l */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* m */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT|UCL_CHARACTER_ESCAPE /* n */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* o */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* p */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* q */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT|UCL_CHARACTER_ESCAPE /* r */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* s */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT|UCL_CHARACTER_ESCAPE /* t */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT|UCL_CHARACTER_ESCAPE /* u */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* v */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* w */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* x */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* y */, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* z */, | |||
UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_UCL_UNSAFE /* { */, | |||
UCL_CHARACTER_VALUE_STR /* | */, UCL_CHARACTER_VALUE_END /* } */, | |||
UCL_CHARACTER_VALUE_STR /* ~ */, UCL_CHARACTER_DENIED, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, | |||
UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR | |||
}; | |||
static inline bool | |||
ucl_test_character (unsigned char c, int type_flags) | |||
{ | |||
return (ucl_chartable[c] & type_flags) != 0; | |||
} | |||
#endif /* UCL_CHARTABLE_H_ */ |
@@ -0,0 +1,511 @@ | |||
/* Copyright (c) 2013, Vsevolod Stakhov | |||
* All rights reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions are met: | |||
* * Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* * Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in the | |||
* documentation and/or other materials provided with the distribution. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY | |||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY | |||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
#ifdef HAVE_CONFIG_H | |||
#include "config.h" | |||
#endif | |||
#include "ucl.h" | |||
#include "ucl_internal.h" | |||
#include "ucl_chartable.h" | |||
#ifdef HAVE_FLOAT_H | |||
#include <float.h> | |||
#endif | |||
#ifdef HAVE_MATH_H | |||
#include <math.h> | |||
#endif | |||
/** | |||
* @file ucl_emitter.c | |||
* Serialise UCL object to various of output formats | |||
*/ | |||
static void ucl_emitter_common_elt (struct ucl_emitter_context *ctx, | |||
const ucl_object_t *obj, bool first, bool print_key, bool compact); | |||
#define UCL_EMIT_TYPE_OPS(type) \ | |||
static void ucl_emit_ ## type ## _elt (struct ucl_emitter_context *ctx, \ | |||
const ucl_object_t *obj, bool first, bool print_key); \ | |||
static void ucl_emit_ ## type ## _start_obj (struct ucl_emitter_context *ctx, \ | |||
const ucl_object_t *obj, bool print_key); \ | |||
static void ucl_emit_ ## type## _start_array (struct ucl_emitter_context *ctx, \ | |||
const ucl_object_t *obj, bool print_key); \ | |||
static void ucl_emit_ ##type## _end_object (struct ucl_emitter_context *ctx, \ | |||
const ucl_object_t *obj); \ | |||
static void ucl_emit_ ##type## _end_array (struct ucl_emitter_context *ctx, \ | |||
const ucl_object_t *obj) | |||
/* | |||
* JSON format operations | |||
*/ | |||
UCL_EMIT_TYPE_OPS(json); | |||
UCL_EMIT_TYPE_OPS(json_compact); | |||
UCL_EMIT_TYPE_OPS(config); | |||
UCL_EMIT_TYPE_OPS(yaml); | |||
#define UCL_EMIT_TYPE_CONTENT(type) { \ | |||
.ucl_emitter_write_elt = ucl_emit_ ## type ## _elt, \ | |||
.ucl_emitter_start_object = ucl_emit_ ## type ##_start_obj, \ | |||
.ucl_emitter_start_array = ucl_emit_ ## type ##_start_array, \ | |||
.ucl_emitter_end_object = ucl_emit_ ## type ##_end_object, \ | |||
.ucl_emitter_end_array = ucl_emit_ ## type ##_end_array \ | |||
} | |||
const struct ucl_emitter_operations ucl_standartd_emitter_ops[] = { | |||
[UCL_EMIT_JSON] = UCL_EMIT_TYPE_CONTENT(json), | |||
[UCL_EMIT_JSON_COMPACT] = UCL_EMIT_TYPE_CONTENT(json_compact), | |||
[UCL_EMIT_CONFIG] = UCL_EMIT_TYPE_CONTENT(config), | |||
[UCL_EMIT_YAML] = UCL_EMIT_TYPE_CONTENT(yaml) | |||
}; | |||
/* | |||
* Utility to check whether we need a top object | |||
*/ | |||
#define UCL_EMIT_IDENT_TOP_OBJ(ctx, obj) ((ctx)->top != (obj) || \ | |||
((ctx)->id == UCL_EMIT_JSON_COMPACT || (ctx)->id == UCL_EMIT_JSON)) | |||
/** | |||
* Add tabulation to the output buffer | |||
* @param buf target buffer | |||
* @param tabs number of tabs to add | |||
*/ | |||
static inline void | |||
ucl_add_tabs (const struct ucl_emitter_functions *func, unsigned int tabs, | |||
bool compact) | |||
{ | |||
if (!compact && tabs > 0) { | |||
func->ucl_emitter_append_character (' ', tabs * 4, func->ud); | |||
} | |||
} | |||
/** | |||
* Print key for the element | |||
* @param ctx | |||
* @param obj | |||
*/ | |||
static void | |||
ucl_emitter_print_key (bool print_key, struct ucl_emitter_context *ctx, | |||
const ucl_object_t *obj, bool compact) | |||
{ | |||
const struct ucl_emitter_functions *func = ctx->func; | |||
if (!print_key) { | |||
return; | |||
} | |||
if (ctx->id == UCL_EMIT_CONFIG) { | |||
if (obj->flags & UCL_OBJECT_NEED_KEY_ESCAPE) { | |||
ucl_elt_string_write_json (obj->key, obj->keylen, ctx); | |||
} | |||
else { | |||
func->ucl_emitter_append_len (obj->key, obj->keylen, func->ud); | |||
} | |||
if (obj->type != UCL_OBJECT && obj->type != UCL_ARRAY) { | |||
func->ucl_emitter_append_len (" = ", 3, func->ud); | |||
} | |||
else { | |||
func->ucl_emitter_append_character (' ', 1, func->ud); | |||
} | |||
} | |||
else if (ctx->id == UCL_EMIT_YAML) { | |||
if (obj->keylen > 0 && (obj->flags & UCL_OBJECT_NEED_KEY_ESCAPE)) { | |||
ucl_elt_string_write_json (obj->key, obj->keylen, ctx); | |||
} | |||
else if (obj->keylen > 0) { | |||
func->ucl_emitter_append_len (obj->key, obj->keylen, func->ud); | |||
} | |||
else { | |||
func->ucl_emitter_append_len ("null", 4, func->ud); | |||
} | |||
func->ucl_emitter_append_len (": ", 2, func->ud); | |||
} | |||
else { | |||
if (obj->keylen > 0) { | |||
ucl_elt_string_write_json (obj->key, obj->keylen, ctx); | |||
} | |||
else { | |||
func->ucl_emitter_append_len ("null", 4, func->ud); | |||
} | |||
if (compact) { | |||
func->ucl_emitter_append_character (':', 1, func->ud); | |||
} | |||
else { | |||
func->ucl_emitter_append_len (": ", 2, func->ud); | |||
} | |||
} | |||
} | |||
static void | |||
ucl_emitter_finish_object (struct ucl_emitter_context *ctx, | |||
const ucl_object_t *obj, bool compact, bool is_array) | |||
{ | |||
const struct ucl_emitter_functions *func = ctx->func; | |||
if (ctx->id == UCL_EMIT_CONFIG && obj != ctx->top) { | |||
if (obj->type != UCL_OBJECT && obj->type != UCL_ARRAY) { | |||
if (!is_array) { | |||
/* Objects are split by ';' */ | |||
func->ucl_emitter_append_len (";\n", 2, func->ud); | |||
} | |||
else { | |||
/* Use commas for arrays */ | |||
func->ucl_emitter_append_len (",\n", 2, func->ud); | |||
} | |||
} | |||
else { | |||
func->ucl_emitter_append_character ('\n', 1, func->ud); | |||
} | |||
} | |||
} | |||
/** | |||
* End standard ucl object | |||
* @param ctx emitter context | |||
* @param compact compact flag | |||
*/ | |||
static void | |||
ucl_emitter_common_end_object (struct ucl_emitter_context *ctx, | |||
const ucl_object_t *obj, bool compact) | |||
{ | |||
const struct ucl_emitter_functions *func = ctx->func; | |||
if (UCL_EMIT_IDENT_TOP_OBJ(ctx, obj)) { | |||
ctx->indent --; | |||
if (compact) { | |||
func->ucl_emitter_append_character ('}', 1, func->ud); | |||
} | |||
else { | |||
if (ctx->id != UCL_EMIT_CONFIG) { | |||
/* newline is already added for this format */ | |||
func->ucl_emitter_append_character ('\n', 1, func->ud); | |||
} | |||
ucl_add_tabs (func, ctx->indent, compact); | |||
func->ucl_emitter_append_character ('}', 1, func->ud); | |||
} | |||
} | |||
ucl_emitter_finish_object (ctx, obj, compact, false); | |||
} | |||
/** | |||
* End standard ucl array | |||
* @param ctx emitter context | |||
* @param compact compact flag | |||
*/ | |||
static void | |||
ucl_emitter_common_end_array (struct ucl_emitter_context *ctx, | |||
const ucl_object_t *obj, bool compact) | |||
{ | |||
const struct ucl_emitter_functions *func = ctx->func; | |||
ctx->indent --; | |||
if (compact) { | |||
func->ucl_emitter_append_character (']', 1, func->ud); | |||
} | |||
else { | |||
if (ctx->id != UCL_EMIT_CONFIG) { | |||
/* newline is already added for this format */ | |||
func->ucl_emitter_append_character ('\n', 1, func->ud); | |||
} | |||
ucl_add_tabs (func, ctx->indent, compact); | |||
func->ucl_emitter_append_character (']', 1, func->ud); | |||
} | |||
ucl_emitter_finish_object (ctx, obj, compact, true); | |||
} | |||
/** | |||
* Start emit standard UCL array | |||
* @param ctx emitter context | |||
* @param obj object to write | |||
* @param compact compact flag | |||
*/ | |||
static void | |||
ucl_emitter_common_start_array (struct ucl_emitter_context *ctx, | |||
const ucl_object_t *obj, bool print_key, bool compact) | |||
{ | |||
const ucl_object_t *cur; | |||
ucl_object_iter_t iter = NULL; | |||
const struct ucl_emitter_functions *func = ctx->func; | |||
bool first = true; | |||
ucl_emitter_print_key (print_key, ctx, obj, compact); | |||
if (compact) { | |||
func->ucl_emitter_append_character ('[', 1, func->ud); | |||
} | |||
else { | |||
func->ucl_emitter_append_len ("[\n", 2, func->ud); | |||
} | |||
ctx->indent ++; | |||
if (obj->type == UCL_ARRAY) { | |||
/* explicit array */ | |||
while ((cur = ucl_iterate_object (obj, &iter, true)) != NULL) { | |||
ucl_emitter_common_elt (ctx, cur, first, false, compact); | |||
first = false; | |||
} | |||
} | |||
else { | |||
/* implicit array */ | |||
cur = obj; | |||
while (cur) { | |||
ucl_emitter_common_elt (ctx, cur, first, false, compact); | |||
first = false; | |||
cur = cur->next; | |||
} | |||
} | |||
} | |||
/** | |||
* Start emit standard UCL object | |||
* @param ctx emitter context | |||
* @param obj object to write | |||
* @param compact compact flag | |||
*/ | |||
static void | |||
ucl_emitter_common_start_object (struct ucl_emitter_context *ctx, | |||
const ucl_object_t *obj, bool print_key, bool compact) | |||
{ | |||
ucl_hash_iter_t it = NULL; | |||
const ucl_object_t *cur, *elt; | |||
const struct ucl_emitter_functions *func = ctx->func; | |||
bool first = true; | |||
ucl_emitter_print_key (print_key, ctx, obj, compact); | |||
/* | |||
* Print <ident_level>{ | |||
* <ident_level + 1><object content> | |||
*/ | |||
if (UCL_EMIT_IDENT_TOP_OBJ(ctx, obj)) { | |||
if (compact) { | |||
func->ucl_emitter_append_character ('{', 1, func->ud); | |||
} | |||
else { | |||
func->ucl_emitter_append_len ("{\n", 2, func->ud); | |||
} | |||
ctx->indent ++; | |||
} | |||
while ((cur = ucl_hash_iterate (obj->value.ov, &it))) { | |||
if (ctx->id == UCL_EMIT_CONFIG) { | |||
LL_FOREACH (cur, elt) { | |||
ucl_emitter_common_elt (ctx, elt, first, true, compact); | |||
} | |||
} | |||
else { | |||
/* Expand implicit arrays */ | |||
if (cur->next != NULL) { | |||
if (!first) { | |||
if (compact) { | |||
func->ucl_emitter_append_character (',', 1, func->ud); | |||
} | |||
else { | |||
func->ucl_emitter_append_len (",\n", 2, func->ud); | |||
} | |||
} | |||
ucl_add_tabs (func, ctx->indent, compact); | |||
ucl_emitter_common_start_array (ctx, cur, true, compact); | |||
ucl_emitter_common_end_array (ctx, cur, compact); | |||
} | |||
else { | |||
ucl_emitter_common_elt (ctx, cur, first, true, compact); | |||
} | |||
} | |||
first = false; | |||
} | |||
} | |||
/** | |||
* Common choice of object emitting | |||
* @param ctx emitter context | |||
* @param obj object to print | |||
* @param first flag to mark the first element | |||
* @param print_key print key of an object | |||
* @param compact compact output | |||
*/ | |||
static void | |||
ucl_emitter_common_elt (struct ucl_emitter_context *ctx, | |||
const ucl_object_t *obj, bool first, bool print_key, bool compact) | |||
{ | |||
const struct ucl_emitter_functions *func = ctx->func; | |||
bool flag; | |||
struct ucl_object_userdata *ud; | |||
const char *ud_out = ""; | |||
if (ctx->id != UCL_EMIT_CONFIG && !first) { | |||
if (compact) { | |||
func->ucl_emitter_append_character (',', 1, func->ud); | |||
} | |||
else { | |||
if (ctx->id == UCL_EMIT_YAML && ctx->indent == 0) { | |||
func->ucl_emitter_append_len ("\n", 1, func->ud); | |||
} else { | |||
func->ucl_emitter_append_len (",\n", 2, func->ud); | |||
} | |||
} | |||
} | |||
ucl_add_tabs (func, ctx->indent, compact); | |||
switch (obj->type) { | |||
case UCL_INT: | |||
ucl_emitter_print_key (print_key, ctx, obj, compact); | |||
func->ucl_emitter_append_int (ucl_object_toint (obj), func->ud); | |||
ucl_emitter_finish_object (ctx, obj, compact, !print_key); | |||
break; | |||
case UCL_FLOAT: | |||
case UCL_TIME: | |||
ucl_emitter_print_key (print_key, ctx, obj, compact); | |||
func->ucl_emitter_append_double (ucl_object_todouble (obj), func->ud); | |||
ucl_emitter_finish_object (ctx, obj, compact, !print_key); | |||
break; | |||
case UCL_BOOLEAN: | |||
ucl_emitter_print_key (print_key, ctx, obj, compact); | |||
flag = ucl_object_toboolean (obj); | |||
if (flag) { | |||
func->ucl_emitter_append_len ("true", 4, func->ud); | |||
} | |||
else { | |||
func->ucl_emitter_append_len ("false", 5, func->ud); | |||
} | |||
ucl_emitter_finish_object (ctx, obj, compact, !print_key); | |||
break; | |||
case UCL_STRING: | |||
ucl_emitter_print_key (print_key, ctx, obj, compact); | |||
if (ctx->id == UCL_EMIT_CONFIG && ucl_maybe_long_string (obj)) { | |||
ucl_elt_string_write_multiline (obj->value.sv, obj->len, ctx); | |||
} | |||
else { | |||
ucl_elt_string_write_json (obj->value.sv, obj->len, ctx); | |||
} | |||
ucl_emitter_finish_object (ctx, obj, compact, !print_key); | |||
break; | |||
case UCL_NULL: | |||
ucl_emitter_print_key (print_key, ctx, obj, compact); | |||
func->ucl_emitter_append_len ("null", 4, func->ud); | |||
ucl_emitter_finish_object (ctx, obj, compact, !print_key); | |||
break; | |||
case UCL_OBJECT: | |||
ucl_emitter_common_start_object (ctx, obj, print_key, compact); | |||
ucl_emitter_common_end_object (ctx, obj, compact); | |||
break; | |||
case UCL_ARRAY: | |||
ucl_emitter_common_start_array (ctx, obj, print_key, compact); | |||
ucl_emitter_common_end_array (ctx, obj, compact); | |||
break; | |||
case UCL_USERDATA: | |||
ud = (struct ucl_object_userdata *)obj; | |||
ucl_emitter_print_key (print_key, ctx, obj, compact); | |||
if (ud->emitter) { | |||
ud_out = ud->emitter (obj->value.ud); | |||
if (ud_out == NULL) { | |||
ud_out = "null"; | |||
} | |||
} | |||
ucl_elt_string_write_json (ud_out, strlen (ud_out), ctx); | |||
ucl_emitter_finish_object (ctx, obj, compact, !print_key); | |||
break; | |||
} | |||
} | |||
/* | |||
* Specific standard implementations of the emitter functions | |||
*/ | |||
#define UCL_EMIT_TYPE_IMPL(type, compact) \ | |||
static void ucl_emit_ ## type ## _elt (struct ucl_emitter_context *ctx, \ | |||
const ucl_object_t *obj, bool first, bool print_key) { \ | |||
ucl_emitter_common_elt (ctx, obj, first, print_key, (compact)); \ | |||
} \ | |||
static void ucl_emit_ ## type ## _start_obj (struct ucl_emitter_context *ctx, \ | |||
const ucl_object_t *obj, bool print_key) { \ | |||
ucl_emitter_common_start_object (ctx, obj, print_key, (compact)); \ | |||
} \ | |||
static void ucl_emit_ ## type## _start_array (struct ucl_emitter_context *ctx, \ | |||
const ucl_object_t *obj, bool print_key) { \ | |||
ucl_emitter_common_start_array (ctx, obj, print_key, (compact)); \ | |||
} \ | |||
static void ucl_emit_ ##type## _end_object (struct ucl_emitter_context *ctx, \ | |||
const ucl_object_t *obj) { \ | |||
ucl_emitter_common_end_object (ctx, obj, (compact)); \ | |||
} \ | |||
static void ucl_emit_ ##type## _end_array (struct ucl_emitter_context *ctx, \ | |||
const ucl_object_t *obj) { \ | |||
ucl_emitter_common_end_array (ctx, obj, (compact)); \ | |||
} | |||
UCL_EMIT_TYPE_IMPL(json, false) | |||
UCL_EMIT_TYPE_IMPL(json_compact, true) | |||
UCL_EMIT_TYPE_IMPL(config, false) | |||
UCL_EMIT_TYPE_IMPL(yaml, false) | |||
unsigned char * | |||
ucl_object_emit (const ucl_object_t *obj, enum ucl_emitter emit_type) | |||
{ | |||
unsigned char *res = NULL; | |||
struct ucl_emitter_functions *func; | |||
if (obj == NULL) { | |||
return NULL; | |||
} | |||
func = ucl_object_emit_memory_funcs ((void **)&res); | |||
if (func != NULL) { | |||
ucl_object_emit_full (obj, emit_type, func); | |||
ucl_object_emit_funcs_free (func); | |||
} | |||
return res; | |||
} | |||
bool | |||
ucl_object_emit_full (const ucl_object_t *obj, enum ucl_emitter emit_type, | |||
struct ucl_emitter_functions *emitter) | |||
{ | |||
const struct ucl_emitter_context *ctx; | |||
struct ucl_emitter_context my_ctx; | |||
bool res = false; | |||
ctx = ucl_emit_get_standard_context (emit_type); | |||
if (ctx != NULL) { | |||
memcpy (&my_ctx, ctx, sizeof (my_ctx)); | |||
my_ctx.func = emitter; | |||
my_ctx.indent = 0; | |||
my_ctx.top = obj; | |||
my_ctx.ops->ucl_emitter_write_elt (&my_ctx, obj, true, false); | |||
res = true; | |||
} | |||
return res; | |||
} |
@@ -0,0 +1,165 @@ | |||
/* Copyright (c) 2014, Vsevolod Stakhov | |||
* All rights reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions are met: | |||
* * Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* * Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in the | |||
* documentation and/or other materials provided with the distribution. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY | |||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY | |||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
#ifdef HAVE_CONFIG_H | |||
#include "config.h" | |||
#endif | |||
#include "ucl.h" | |||
#include "ucl_internal.h" | |||
#include "ucl_chartable.h" | |||
struct ucl_emitter_streamline_stack { | |||
bool is_array; | |||
bool empty; | |||
const ucl_object_t *obj; | |||
struct ucl_emitter_streamline_stack *next; | |||
}; | |||
struct ucl_emitter_context_streamline { | |||
/* Inherited from the main context */ | |||
const char *name; | |||
int id; | |||
const struct ucl_emitter_functions *func; | |||
const struct ucl_emitter_operations *ops; | |||
unsigned int ident; | |||
const ucl_object_t *top; | |||
/* Streamline specific fields */ | |||
struct ucl_emitter_streamline_stack *containers; | |||
}; | |||
#define TO_STREAMLINE(ctx) (struct ucl_emitter_context_streamline *)(ctx) | |||
struct ucl_emitter_context* | |||
ucl_object_emit_streamline_new (const ucl_object_t *obj, | |||
enum ucl_emitter emit_type, | |||
struct ucl_emitter_functions *emitter) | |||
{ | |||
const struct ucl_emitter_context *ctx; | |||
struct ucl_emitter_context_streamline *sctx; | |||
ctx = ucl_emit_get_standard_context (emit_type); | |||
if (ctx == NULL) { | |||
return NULL; | |||
} | |||
sctx = calloc (1, sizeof (*sctx)); | |||
if (sctx == NULL) { | |||
return NULL; | |||
} | |||
memcpy (sctx, ctx, sizeof (*ctx)); | |||
sctx->func = emitter; | |||
sctx->top = obj; | |||
ucl_object_emit_streamline_start_container ((struct ucl_emitter_context *)sctx, | |||
obj); | |||
return (struct ucl_emitter_context *)sctx; | |||
} | |||
void | |||
ucl_object_emit_streamline_start_container (struct ucl_emitter_context *ctx, | |||
const ucl_object_t *obj) | |||
{ | |||
struct ucl_emitter_context_streamline *sctx = TO_STREAMLINE(ctx); | |||
struct ucl_emitter_streamline_stack *st, *top; | |||
bool print_key = false; | |||
/* Check top object presence */ | |||
if (sctx->top == NULL) { | |||
sctx->top = obj; | |||
} | |||
top = sctx->containers; | |||
st = malloc (sizeof (*st)); | |||
if (st != NULL) { | |||
if (top != NULL && !top->is_array) { | |||
print_key = true; | |||
} | |||
st->empty = true; | |||
st->obj = obj; | |||
if (obj != NULL && obj->type == UCL_ARRAY) { | |||
st->is_array = true; | |||
sctx->ops->ucl_emitter_start_array (ctx, obj, print_key); | |||
} | |||
else { | |||
st->is_array = false; | |||
sctx->ops->ucl_emitter_start_object (ctx, obj, print_key); | |||
} | |||
LL_PREPEND (sctx->containers, st); | |||
} | |||
} | |||
void | |||
ucl_object_emit_streamline_add_object ( | |||
struct ucl_emitter_context *ctx, const ucl_object_t *obj) | |||
{ | |||
struct ucl_emitter_context_streamline *sctx = TO_STREAMLINE(ctx); | |||
bool is_array = false, is_first = false; | |||
if (sctx->containers != NULL) { | |||
if (sctx->containers->is_array) { | |||
is_array = true; | |||
} | |||
if (sctx->containers->empty) { | |||
is_first = true; | |||
sctx->containers->empty = false; | |||
} | |||
} | |||
sctx->ops->ucl_emitter_write_elt (ctx, obj, is_first, !is_array); | |||
} | |||
void | |||
ucl_object_emit_streamline_end_container (struct ucl_emitter_context *ctx) | |||
{ | |||
struct ucl_emitter_context_streamline *sctx = TO_STREAMLINE(ctx); | |||
struct ucl_emitter_streamline_stack *st; | |||
if (sctx->containers != NULL) { | |||
st = sctx->containers; | |||
if (st->is_array) { | |||
sctx->ops->ucl_emitter_end_array (ctx, st->obj); | |||
} | |||
else { | |||
sctx->ops->ucl_emitter_end_object (ctx, st->obj); | |||
} | |||
sctx->containers = st->next; | |||
free (st); | |||
} | |||
} | |||
void | |||
ucl_object_emit_streamline_finish (struct ucl_emitter_context *ctx) | |||
{ | |||
struct ucl_emitter_context_streamline *sctx = TO_STREAMLINE(ctx); | |||
while (sctx->containers != NULL) { | |||
ucl_object_emit_streamline_end_container (ctx); | |||
} | |||
free (sctx); | |||
} |
@@ -0,0 +1,487 @@ | |||
/* Copyright (c) 2014, Vsevolod Stakhov | |||
* All rights reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions are met: | |||
* * Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* * Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in the | |||
* documentation and/or other materials provided with the distribution. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY | |||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY | |||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
#ifdef HAVE_CONFIG_H | |||
#include "config.h" | |||
#endif | |||
#include "ucl.h" | |||
#include "ucl_internal.h" | |||
#include "ucl_chartable.h" | |||
#ifdef HAVE_FLOAT_H | |||
#include <float.h> | |||
#endif | |||
#ifdef HAVE_MATH_H | |||
#include <math.h> | |||
#endif | |||
extern const struct ucl_emitter_operations ucl_standartd_emitter_ops[]; | |||
static const struct ucl_emitter_context ucl_standard_emitters[] = { | |||
[UCL_EMIT_JSON] = { | |||
.name = "json", | |||
.id = UCL_EMIT_JSON, | |||
.func = NULL, | |||
.ops = &ucl_standartd_emitter_ops[UCL_EMIT_JSON] | |||
}, | |||
[UCL_EMIT_JSON_COMPACT] = { | |||
.name = "json_compact", | |||
.id = UCL_EMIT_JSON_COMPACT, | |||
.func = NULL, | |||
.ops = &ucl_standartd_emitter_ops[UCL_EMIT_JSON_COMPACT] | |||
}, | |||
[UCL_EMIT_CONFIG] = { | |||
.name = "config", | |||
.id = UCL_EMIT_CONFIG, | |||
.func = NULL, | |||
.ops = &ucl_standartd_emitter_ops[UCL_EMIT_CONFIG] | |||
}, | |||
[UCL_EMIT_YAML] = { | |||
.name = "yaml", | |||
.id = UCL_EMIT_YAML, | |||
.func = NULL, | |||
.ops = &ucl_standartd_emitter_ops[UCL_EMIT_YAML] | |||
} | |||
}; | |||
/** | |||
* Get standard emitter context for a specified emit_type | |||
* @param emit_type type of emitter | |||
* @return context or NULL if input is invalid | |||
*/ | |||
const struct ucl_emitter_context * | |||
ucl_emit_get_standard_context (enum ucl_emitter emit_type) | |||
{ | |||
if (emit_type >= UCL_EMIT_JSON && emit_type <= UCL_EMIT_YAML) { | |||
return &ucl_standard_emitters[emit_type]; | |||
} | |||
return NULL; | |||
} | |||
/** | |||
* Serialise string | |||
* @param str string to emit | |||
* @param buf target buffer | |||
*/ | |||
void | |||
ucl_elt_string_write_json (const char *str, size_t size, | |||
struct ucl_emitter_context *ctx) | |||
{ | |||
const char *p = str, *c = str; | |||
size_t len = 0; | |||
const struct ucl_emitter_functions *func = ctx->func; | |||
func->ucl_emitter_append_character ('"', 1, func->ud); | |||
while (size) { | |||
if (ucl_test_character (*p, UCL_CHARACTER_JSON_UNSAFE)) { | |||
if (len > 0) { | |||
func->ucl_emitter_append_len (c, len, func->ud); | |||
} | |||
switch (*p) { | |||
case '\n': | |||
func->ucl_emitter_append_len ("\\n", 2, func->ud); | |||
break; | |||
case '\r': | |||
func->ucl_emitter_append_len ("\\r", 2, func->ud); | |||
break; | |||
case '\b': | |||
func->ucl_emitter_append_len ("\\b", 2, func->ud); | |||
break; | |||
case '\t': | |||
func->ucl_emitter_append_len ("\\t", 2, func->ud); | |||
break; | |||
case '\f': | |||
func->ucl_emitter_append_len ("\\f", 2, func->ud); | |||
break; | |||
case '\\': | |||
func->ucl_emitter_append_len ("\\\\", 2, func->ud); | |||
break; | |||
case '"': | |||
func->ucl_emitter_append_len ("\\\"", 2, func->ud); | |||
break; | |||
} | |||
len = 0; | |||
c = ++p; | |||
} | |||
else { | |||
p ++; | |||
len ++; | |||
} | |||
size --; | |||
} | |||
if (len > 0) { | |||
func->ucl_emitter_append_len (c, len, func->ud); | |||
} | |||
func->ucl_emitter_append_character ('"', 1, func->ud); | |||
} | |||
void | |||
ucl_elt_string_write_multiline (const char *str, size_t size, | |||
struct ucl_emitter_context *ctx) | |||
{ | |||
const struct ucl_emitter_functions *func = ctx->func; | |||
func->ucl_emitter_append_len ("<<EOD\n", sizeof ("<<EOD\n") - 1, func->ud); | |||
func->ucl_emitter_append_len (str, size, func->ud); | |||
func->ucl_emitter_append_len ("\nEOD", sizeof ("\nEOD") - 1, func->ud); | |||
} | |||
/* | |||
* Generic utstring output | |||
*/ | |||
static int | |||
ucl_utstring_append_character (unsigned char c, size_t len, void *ud) | |||
{ | |||
UT_string *buf = ud; | |||
if (len == 1) { | |||
utstring_append_c (buf, c); | |||
} | |||
else { | |||
utstring_reserve (buf, len + 1); | |||
memset (&buf->d[buf->i], c, len); | |||
buf->i += len; | |||
buf->d[buf->i] = '\0'; | |||
} | |||
return 0; | |||
} | |||
static int | |||
ucl_utstring_append_len (const unsigned char *str, size_t len, void *ud) | |||
{ | |||
UT_string *buf = ud; | |||
utstring_append_len (buf, str, len); | |||
return 0; | |||
} | |||
static int | |||
ucl_utstring_append_int (int64_t val, void *ud) | |||
{ | |||
UT_string *buf = ud; | |||
utstring_printf (buf, "%jd", (intmax_t)val); | |||
return 0; | |||
} | |||
static int | |||
ucl_utstring_append_double (double val, void *ud) | |||
{ | |||
UT_string *buf = ud; | |||
const double delta = 0.0000001; | |||
if (val == (double)(int)val) { | |||
utstring_printf (buf, "%.1lf", val); | |||
} | |||
else if (fabs (val - (double)(int)val) < delta) { | |||
/* Write at maximum precision */ | |||
utstring_printf (buf, "%.*lg", DBL_DIG, val); | |||
} | |||
else { | |||
utstring_printf (buf, "%lf", val); | |||
} | |||
return 0; | |||
} | |||
/* | |||
* Generic file output | |||
*/ | |||
static int | |||
ucl_file_append_character (unsigned char c, size_t len, void *ud) | |||
{ | |||
FILE *fp = ud; | |||
while (len --) { | |||
fputc (c, fp); | |||
} | |||
return 0; | |||
} | |||
static int | |||
ucl_file_append_len (const unsigned char *str, size_t len, void *ud) | |||
{ | |||
FILE *fp = ud; | |||
fwrite (str, len, 1, fp); | |||
return 0; | |||
} | |||
static int | |||
ucl_file_append_int (int64_t val, void *ud) | |||
{ | |||
FILE *fp = ud; | |||
fprintf (fp, "%jd", (intmax_t)val); | |||
return 0; | |||
} | |||
static int | |||
ucl_file_append_double (double val, void *ud) | |||
{ | |||
FILE *fp = ud; | |||
const double delta = 0.0000001; | |||
if (val == (double)(int)val) { | |||
fprintf (fp, "%.1lf", val); | |||
} | |||
else if (fabs (val - (double)(int)val) < delta) { | |||
/* Write at maximum precision */ | |||
fprintf (fp, "%.*lg", DBL_DIG, val); | |||
} | |||
else { | |||
fprintf (fp, "%lf", val); | |||
} | |||
return 0; | |||
} | |||
/* | |||
* Generic file descriptor writing functions | |||
*/ | |||
static int | |||
ucl_fd_append_character (unsigned char c, size_t len, void *ud) | |||
{ | |||
int fd = *(int *)ud; | |||
unsigned char *buf; | |||
if (len == 1) { | |||
return write (fd, &c, 1); | |||
} | |||
else { | |||
buf = malloc (len); | |||
if (buf == NULL) { | |||
/* Fallback */ | |||
while (len --) { | |||
if (write (fd, &c, 1) == -1) { | |||
return -1; | |||
} | |||
} | |||
} | |||
else { | |||
memset (buf, c, len); | |||
if (write (fd, buf, len) == -1) { | |||
free(buf); | |||
return -1; | |||
} | |||
free (buf); | |||
} | |||
} | |||
return 0; | |||
} | |||
static int | |||
ucl_fd_append_len (const unsigned char *str, size_t len, void *ud) | |||
{ | |||
int fd = *(int *)ud; | |||
return write (fd, str, len); | |||
} | |||
static int | |||
ucl_fd_append_int (int64_t val, void *ud) | |||
{ | |||
int fd = *(int *)ud; | |||
char intbuf[64]; | |||
snprintf (intbuf, sizeof (intbuf), "%jd", (intmax_t)val); | |||
return write (fd, intbuf, strlen (intbuf)); | |||
} | |||
static int | |||
ucl_fd_append_double (double val, void *ud) | |||
{ | |||
int fd = *(int *)ud; | |||
const double delta = 0.0000001; | |||
char nbuf[64]; | |||
if (val == (double)(int)val) { | |||
snprintf (nbuf, sizeof (nbuf), "%.1lf", val); | |||
} | |||
else if (fabs (val - (double)(int)val) < delta) { | |||
/* Write at maximum precision */ | |||
snprintf (nbuf, sizeof (nbuf), "%.*lg", DBL_DIG, val); | |||
} | |||
else { | |||
snprintf (nbuf, sizeof (nbuf), "%lf", val); | |||
} | |||
return write (fd, nbuf, strlen (nbuf)); | |||
} | |||
struct ucl_emitter_functions* | |||
ucl_object_emit_memory_funcs (void **pmem) | |||
{ | |||
struct ucl_emitter_functions *f; | |||
UT_string *s; | |||
f = calloc (1, sizeof (*f)); | |||
if (f != NULL) { | |||
f->ucl_emitter_append_character = ucl_utstring_append_character; | |||
f->ucl_emitter_append_double = ucl_utstring_append_double; | |||
f->ucl_emitter_append_int = ucl_utstring_append_int; | |||
f->ucl_emitter_append_len = ucl_utstring_append_len; | |||
f->ucl_emitter_free_func = free; | |||
utstring_new (s); | |||
f->ud = s; | |||
*pmem = s->d; | |||
s->pd = pmem; | |||
} | |||
return f; | |||
} | |||
struct ucl_emitter_functions* | |||
ucl_object_emit_file_funcs (FILE *fp) | |||
{ | |||
struct ucl_emitter_functions *f; | |||
f = calloc (1, sizeof (*f)); | |||
if (f != NULL) { | |||
f->ucl_emitter_append_character = ucl_file_append_character; | |||
f->ucl_emitter_append_double = ucl_file_append_double; | |||
f->ucl_emitter_append_int = ucl_file_append_int; | |||
f->ucl_emitter_append_len = ucl_file_append_len; | |||
f->ucl_emitter_free_func = NULL; | |||
f->ud = fp; | |||
} | |||
return f; | |||
} | |||
struct ucl_emitter_functions* | |||
ucl_object_emit_fd_funcs (int fd) | |||
{ | |||
struct ucl_emitter_functions *f; | |||
int *ip; | |||
f = calloc (1, sizeof (*f)); | |||
if (f != NULL) { | |||
ip = malloc (sizeof (fd)); | |||
if (ip == NULL) { | |||
free (f); | |||
return NULL; | |||
} | |||
memcpy (ip, &fd, sizeof (fd)); | |||
f->ucl_emitter_append_character = ucl_fd_append_character; | |||
f->ucl_emitter_append_double = ucl_fd_append_double; | |||
f->ucl_emitter_append_int = ucl_fd_append_int; | |||
f->ucl_emitter_append_len = ucl_fd_append_len; | |||
f->ucl_emitter_free_func = free; | |||
f->ud = ip; | |||
} | |||
return f; | |||
} | |||
void | |||
ucl_object_emit_funcs_free (struct ucl_emitter_functions *f) | |||
{ | |||
if (f != NULL) { | |||
if (f->ucl_emitter_free_func != NULL) { | |||
f->ucl_emitter_free_func (f->ud); | |||
} | |||
free (f); | |||
} | |||
} | |||
unsigned char * | |||
ucl_object_emit_single_json (const ucl_object_t *obj) | |||
{ | |||
UT_string *buf = NULL; | |||
unsigned char *res = NULL; | |||
if (obj == NULL) { | |||
return NULL; | |||
} | |||
utstring_new (buf); | |||
if (buf != NULL) { | |||
switch (obj->type) { | |||
case UCL_OBJECT: | |||
ucl_utstring_append_len ("object", 6, buf); | |||
break; | |||
case UCL_ARRAY: | |||
ucl_utstring_append_len ("array", 5, buf); | |||
break; | |||
case UCL_INT: | |||
ucl_utstring_append_int (obj->value.iv, buf); | |||
break; | |||
case UCL_FLOAT: | |||
case UCL_TIME: | |||
ucl_utstring_append_double (obj->value.dv, buf); | |||
break; | |||
case UCL_NULL: | |||
ucl_utstring_append_len ("null", 4, buf); | |||
break; | |||
case UCL_BOOLEAN: | |||
if (obj->value.iv) { | |||
ucl_utstring_append_len ("true", 4, buf); | |||
} | |||
else { | |||
ucl_utstring_append_len ("false", 5, buf); | |||
} | |||
break; | |||
case UCL_STRING: | |||
ucl_utstring_append_len (obj->value.sv, obj->len, buf); | |||
break; | |||
case UCL_USERDATA: | |||
ucl_utstring_append_len ("userdata", 8, buf); | |||
break; | |||
} | |||
res = utstring_body (buf); | |||
free (buf); | |||
} | |||
return res; | |||
} | |||
#define LONG_STRING_LIMIT 80 | |||
bool | |||
ucl_maybe_long_string (const ucl_object_t *obj) | |||
{ | |||
if (obj->len > LONG_STRING_LIMIT || (obj->flags & UCL_OBJECT_MULTILINE)) { | |||
/* String is long enough, so search for newline characters in it */ | |||
if (memchr (obj->value.sv, '\n', obj->len) != NULL) { | |||
return true; | |||
} | |||
} | |||
return false; | |||
} |
@@ -0,0 +1,353 @@ | |||
/* Copyright (c) 2013, Vsevolod Stakhov | |||
* All rights reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions are met: | |||
* * Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* * Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in the | |||
* documentation and/or other materials provided with the distribution. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY | |||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY | |||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
#include "ucl_internal.h" | |||
#include "ucl_hash.h" | |||
#include "khash.h" | |||
#include "kvec.h" | |||
struct ucl_hash_elt { | |||
const ucl_object_t *obj; | |||
size_t ar_idx; | |||
}; | |||
struct ucl_hash_struct { | |||
void *hash; | |||
kvec_t(const ucl_object_t *) ar; | |||
bool caseless; | |||
}; | |||
static inline uint32_t | |||
ucl_hash_func (const ucl_object_t *o) | |||
{ | |||
return XXH32 (o->key, o->keylen, 0xdeadbeef); | |||
} | |||
static inline int | |||
ucl_hash_equal (const ucl_object_t *k1, const ucl_object_t *k2) | |||
{ | |||
if (k1->keylen == k2->keylen) { | |||
return strncmp (k1->key, k2->key, k1->keylen) == 0; | |||
} | |||
return 0; | |||
} | |||
KHASH_INIT (ucl_hash_node, const ucl_object_t *, struct ucl_hash_elt, 1, | |||
ucl_hash_func, ucl_hash_equal) | |||
static inline uint32_t | |||
ucl_hash_caseless_func (const ucl_object_t *o) | |||
{ | |||
void *xxh = XXH32_init (0xdeadbeef); | |||
char hash_buf[64], *c; | |||
const char *p; | |||
ssize_t remain = o->keylen; | |||
p = o->key; | |||
c = &hash_buf[0]; | |||
while (remain > 0) { | |||
*c++ = tolower (*p++); | |||
if (c - &hash_buf[0] == sizeof (hash_buf)) { | |||
XXH32_update (xxh, hash_buf, sizeof (hash_buf)); | |||
c = &hash_buf[0]; | |||
} | |||
remain --; | |||
} | |||
if (c - &hash_buf[0] != 0) { | |||
XXH32_update (xxh, hash_buf, c - &hash_buf[0]); | |||
} | |||
return XXH32_digest (xxh); | |||
} | |||
static inline int | |||
ucl_hash_caseless_equal (const ucl_object_t *k1, const ucl_object_t *k2) | |||
{ | |||
if (k1->keylen == k2->keylen) { | |||
return strncasecmp (k1->key, k2->key, k1->keylen) == 0; | |||
} | |||
return 0; | |||
} | |||
KHASH_INIT (ucl_hash_caseless_node, const ucl_object_t *, struct ucl_hash_elt, 1, | |||
ucl_hash_caseless_func, ucl_hash_caseless_equal) | |||
ucl_hash_t* | |||
ucl_hash_create (bool ignore_case) | |||
{ | |||
ucl_hash_t *new; | |||
new = UCL_ALLOC (sizeof (ucl_hash_t)); | |||
if (new != NULL) { | |||
kv_init (new->ar); | |||
new->caseless = ignore_case; | |||
if (ignore_case) { | |||
khash_t(ucl_hash_caseless_node) *h = kh_init (ucl_hash_caseless_node); | |||
new->hash = (void *)h; | |||
} | |||
else { | |||
khash_t(ucl_hash_node) *h = kh_init (ucl_hash_node); | |||
new->hash = (void *)h; | |||
} | |||
} | |||
return new; | |||
} | |||
void ucl_hash_destroy (ucl_hash_t* hashlin, ucl_hash_free_func *func) | |||
{ | |||
const ucl_object_t *cur, *tmp; | |||
if (hashlin == NULL) { | |||
return; | |||
} | |||
if (func != NULL) { | |||
/* Iterate over the hash first */ | |||
khash_t(ucl_hash_node) *h = (khash_t(ucl_hash_node) *) | |||
hashlin->hash; | |||
khiter_t k; | |||
for (k = kh_begin (h); k != kh_end (h); ++k) { | |||
if (kh_exist (h, k)) { | |||
cur = (kh_value (h, k)).obj; | |||
while (cur != NULL) { | |||
tmp = cur->next; | |||
func (__DECONST (ucl_object_t *, cur)); | |||
cur = tmp; | |||
} | |||
} | |||
} | |||
} | |||
if (hashlin->caseless) { | |||
khash_t(ucl_hash_caseless_node) *h = (khash_t(ucl_hash_caseless_node) *) | |||
hashlin->hash; | |||
kh_destroy (ucl_hash_caseless_node, h); | |||
} | |||
else { | |||
khash_t(ucl_hash_node) *h = (khash_t(ucl_hash_node) *) | |||
hashlin->hash; | |||
kh_destroy (ucl_hash_node, h); | |||
} | |||
kv_destroy (hashlin->ar); | |||
UCL_FREE (sizeof (*hashlin), hashlin); | |||
} | |||
void | |||
ucl_hash_insert (ucl_hash_t* hashlin, const ucl_object_t *obj, | |||
const char *key, unsigned keylen) | |||
{ | |||
khiter_t k; | |||
int ret; | |||
struct ucl_hash_elt *elt; | |||
if (hashlin == NULL) { | |||
return; | |||
} | |||
if (hashlin->caseless) { | |||
khash_t(ucl_hash_caseless_node) *h = (khash_t(ucl_hash_caseless_node) *) | |||
hashlin->hash; | |||
k = kh_put (ucl_hash_caseless_node, h, obj, &ret); | |||
if (ret > 0) { | |||
elt = &kh_value (h, k); | |||
kv_push (const ucl_object_t *, hashlin->ar, obj); | |||
elt->obj = obj; | |||
elt->ar_idx = kv_size (hashlin->ar) - 1; | |||
} | |||
} | |||
else { | |||
khash_t(ucl_hash_node) *h = (khash_t(ucl_hash_node) *) | |||
hashlin->hash; | |||
k = kh_put (ucl_hash_node, h, obj, &ret); | |||
if (ret > 0) { | |||
elt = &kh_value (h, k); | |||
kv_push (const ucl_object_t *, hashlin->ar, obj); | |||
elt->obj = obj; | |||
elt->ar_idx = kv_size (hashlin->ar) - 1; | |||
} | |||
} | |||
} | |||
void ucl_hash_replace (ucl_hash_t* hashlin, const ucl_object_t *old, | |||
const ucl_object_t *new) | |||
{ | |||
khiter_t k; | |||
int ret; | |||
struct ucl_hash_elt elt, *pelt; | |||
if (hashlin == NULL) { | |||
return; | |||
} | |||
if (hashlin->caseless) { | |||
khash_t(ucl_hash_caseless_node) *h = (khash_t(ucl_hash_caseless_node) *) | |||
hashlin->hash; | |||
k = kh_put (ucl_hash_caseless_node, h, old, &ret); | |||
if (ret == 0) { | |||
elt = kh_value (h, k); | |||
kh_del (ucl_hash_caseless_node, h, k); | |||
k = kh_put (ucl_hash_caseless_node, h, new, &ret); | |||
pelt = &kh_value (h, k); | |||
pelt->obj = new; | |||
pelt->ar_idx = elt.ar_idx; | |||
kv_A (hashlin->ar, elt.ar_idx) = new; | |||
} | |||
} | |||
else { | |||
khash_t(ucl_hash_node) *h = (khash_t(ucl_hash_node) *) | |||
hashlin->hash; | |||
k = kh_put (ucl_hash_node, h, old, &ret); | |||
if (ret == 0) { | |||
elt = kh_value (h, k); | |||
kh_del (ucl_hash_node, h, k); | |||
k = kh_put (ucl_hash_node, h, new, &ret); | |||
pelt = &kh_value (h, k); | |||
pelt->obj = new; | |||
pelt->ar_idx = elt.ar_idx; | |||
kv_A (hashlin->ar, elt.ar_idx) = new; | |||
} | |||
} | |||
} | |||
struct ucl_hash_real_iter { | |||
const ucl_object_t **cur; | |||
const ucl_object_t **end; | |||
}; | |||
const void* | |||
ucl_hash_iterate (ucl_hash_t *hashlin, ucl_hash_iter_t *iter) | |||
{ | |||
struct ucl_hash_real_iter *it = (struct ucl_hash_real_iter *)(*iter); | |||
const ucl_object_t *ret = NULL; | |||
if (hashlin == NULL) { | |||
return NULL; | |||
} | |||
if (it == NULL) { | |||
it = UCL_ALLOC (sizeof (*it)); | |||
it->cur = &hashlin->ar.a[0]; | |||
it->end = it->cur + hashlin->ar.n; | |||
} | |||
if (it->cur < it->end) { | |||
ret = *it->cur++; | |||
} | |||
else { | |||
UCL_FREE (sizeof (*it), it); | |||
*iter = NULL; | |||
return NULL; | |||
} | |||
*iter = it; | |||
return ret; | |||
} | |||
bool | |||
ucl_hash_iter_has_next (ucl_hash_t *hashlin, ucl_hash_iter_t iter) | |||
{ | |||
struct ucl_hash_real_iter *it = (struct ucl_hash_real_iter *)(iter); | |||
return it->cur < it->end - 1; | |||
} | |||
const ucl_object_t* | |||
ucl_hash_search (ucl_hash_t* hashlin, const char *key, unsigned keylen) | |||
{ | |||
khiter_t k; | |||
const ucl_object_t *ret = NULL; | |||
ucl_object_t search; | |||
struct ucl_hash_elt *elt; | |||
search.key = key; | |||
search.keylen = keylen; | |||
if (hashlin == NULL) { | |||
return NULL; | |||
} | |||
if (hashlin->caseless) { | |||
khash_t(ucl_hash_caseless_node) *h = (khash_t(ucl_hash_caseless_node) *) | |||
hashlin->hash; | |||
k = kh_get (ucl_hash_caseless_node, h, &search); | |||
if (k != kh_end (h)) { | |||
elt = &kh_value (h, k); | |||
ret = elt->obj; | |||
} | |||
} | |||
else { | |||
khash_t(ucl_hash_node) *h = (khash_t(ucl_hash_node) *) | |||
hashlin->hash; | |||
k = kh_get (ucl_hash_node, h, &search); | |||
if (k != kh_end (h)) { | |||
elt = &kh_value (h, k); | |||
ret = elt->obj; | |||
} | |||
} | |||
return ret; | |||
} | |||
void | |||
ucl_hash_delete (ucl_hash_t* hashlin, const ucl_object_t *obj) | |||
{ | |||
khiter_t k; | |||
struct ucl_hash_elt *elt; | |||
if (hashlin == NULL) { | |||
return; | |||
} | |||
if (hashlin->caseless) { | |||
khash_t(ucl_hash_caseless_node) *h = (khash_t(ucl_hash_caseless_node) *) | |||
hashlin->hash; | |||
k = kh_get (ucl_hash_caseless_node, h, obj); | |||
if (k != kh_end (h)) { | |||
elt = &kh_value (h, k); | |||
kv_A (hashlin->ar, elt->ar_idx) = NULL; | |||
kh_del (ucl_hash_caseless_node, h, k); | |||
} | |||
} | |||
else { | |||
khash_t(ucl_hash_node) *h = (khash_t(ucl_hash_node) *) | |||
hashlin->hash; | |||
k = kh_get (ucl_hash_node, h, obj); | |||
if (k != kh_end (h)) { | |||
elt = &kh_value (h, k); | |||
kv_A (hashlin->ar, elt->ar_idx) = NULL; | |||
kh_del (ucl_hash_node, h, k); | |||
} | |||
} | |||
} |
@@ -0,0 +1,93 @@ | |||
/* Copyright (c) 2013, Vsevolod Stakhov | |||
* All rights reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions are met: | |||
* * Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* * Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in the | |||
* documentation and/or other materials provided with the distribution. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY | |||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY | |||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
#ifndef __UCL_HASH_H | |||
#define __UCL_HASH_H | |||
#include "ucl.h" | |||
/******************************************************************************/ | |||
struct ucl_hash_node_s; | |||
typedef struct ucl_hash_node_s ucl_hash_node_t; | |||
typedef int ucl_hash_cmp_func (const void* void_a, const void* void_b); | |||
typedef void ucl_hash_free_func (void *ptr); | |||
typedef void* ucl_hash_iter_t; | |||
/** | |||
* Linear chained hashtable. | |||
*/ | |||
struct ucl_hash_struct; | |||
typedef struct ucl_hash_struct ucl_hash_t; | |||
/** | |||
* Initializes the hashtable. | |||
*/ | |||
ucl_hash_t* ucl_hash_create (bool ignore_case); | |||
/** | |||
* Deinitializes the hashtable. | |||
*/ | |||
void ucl_hash_destroy (ucl_hash_t* hashlin, ucl_hash_free_func *func); | |||
/** | |||
* Inserts an element in the the hashtable. | |||
*/ | |||
void ucl_hash_insert (ucl_hash_t* hashlin, const ucl_object_t *obj, const char *key, | |||
unsigned keylen); | |||
/** | |||
* Replace element in the hash | |||
*/ | |||
void ucl_hash_replace (ucl_hash_t* hashlin, const ucl_object_t *old, | |||
const ucl_object_t *new); | |||
/** | |||
* Delete an element from the the hashtable. | |||
*/ | |||
void ucl_hash_delete (ucl_hash_t* hashlin, const ucl_object_t *obj); | |||
/** | |||
* Searches an element in the hashtable. | |||
*/ | |||
const ucl_object_t* ucl_hash_search (ucl_hash_t* hashlin, const char *key, | |||
unsigned keylen); | |||
/** | |||
* Iterate over hash table | |||
* @param hashlin hash | |||
* @param iter iterator (must be NULL on first iteration) | |||
* @return the next object | |||
*/ | |||
const void* ucl_hash_iterate (ucl_hash_t *hashlin, ucl_hash_iter_t *iter); | |||
/** | |||
* Check whether an iterator has next element | |||
*/ | |||
bool ucl_hash_iter_has_next (ucl_hash_t *hashlin, ucl_hash_iter_t iter); | |||
#endif |
@@ -0,0 +1,399 @@ | |||
/* Copyright (c) 2013, Vsevolod Stakhov | |||
* All rights reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions are met: | |||
* * Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* * Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in the | |||
* documentation and/or other materials provided with the distribution. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY | |||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY | |||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
#ifndef UCL_INTERNAL_H_ | |||
#define UCL_INTERNAL_H_ | |||
#ifdef HAVE_CONFIG_H | |||
#include "config.h" | |||
#else | |||
/* Help embedded builds */ | |||
#define HAVE_SYS_TYPES_H | |||
#define HAVE_SYS_MMAN_H | |||
#define HAVE_SYS_STAT_H | |||
#define HAVE_SYS_PARAM_H | |||
#define HAVE_LIMITS_H | |||
#define HAVE_FCNTL_H | |||
#define HAVE_ERRNO_H | |||
#define HAVE_UNISTD_H | |||
#define HAVE_CTYPE_H | |||
#define HAVE_STDIO_H | |||
#define HAVE_STRING_H | |||
#define HAVE_FLOAT_H | |||
#define HAVE_LIBGEN_H | |||
#define HAVE_MATH_H | |||
#define HAVE_STDBOOL_H | |||
#define HAVE_STDINT_H | |||
#define HAVE_STDARG_H | |||
#ifndef _WIN32 | |||
# define HAVE_REGEX_H | |||
#endif | |||
#endif | |||
#ifdef HAVE_SYS_TYPES_H | |||
#include <sys/types.h> | |||
#endif | |||
#ifdef HAVE_SYS_MMAN_H | |||
# ifndef _WIN32 | |||
# include <sys/mman.h> | |||
# endif | |||
#endif | |||
#ifdef HAVE_SYS_STAT_H | |||
#include <sys/stat.h> | |||
#endif | |||
#ifdef HAVE_SYS_PARAM_H | |||
#include <sys/param.h> | |||
#endif | |||
#ifdef HAVE_LIMITS_H | |||
#include <limits.h> | |||
#endif | |||
#ifdef HAVE_FCNTL_H | |||
#include <fcntl.h> | |||
#endif | |||
#ifdef HAVE_ERRNO_H | |||
#include <errno.h> | |||
#endif | |||
#ifdef HAVE_UNISTD_H | |||
#include <unistd.h> | |||
#endif | |||
#ifdef HAVE_CTYPE_H | |||
#include <ctype.h> | |||
#endif | |||
#ifdef HAVE_STDIO_H | |||
#include <stdio.h> | |||
#endif | |||
#ifdef HAVE_STRING_H | |||
#include <string.h> | |||
#endif | |||
#include "utlist.h" | |||
#include "utstring.h" | |||
#include "uthash.h" | |||
#include "ucl.h" | |||
#include "ucl_hash.h" | |||
#include "xxhash.h" | |||
#ifdef HAVE_OPENSSL | |||
#include <openssl/evp.h> | |||
#endif | |||
#ifndef __DECONST | |||
#define __DECONST(type, var) ((type)(uintptr_t)(const void *)(var)) | |||
#endif | |||
/** | |||
* @file rcl_internal.h | |||
* Internal structures and functions of UCL library | |||
*/ | |||
#define UCL_MAX_RECURSION 16 | |||
#define UCL_TRASH_KEY 0 | |||
#define UCL_TRASH_VALUE 1 | |||
enum ucl_parser_state { | |||
UCL_STATE_INIT = 0, | |||
UCL_STATE_OBJECT, | |||
UCL_STATE_ARRAY, | |||
UCL_STATE_KEY, | |||
UCL_STATE_VALUE, | |||
UCL_STATE_AFTER_VALUE, | |||
UCL_STATE_ARRAY_VALUE, | |||
UCL_STATE_SCOMMENT, | |||
UCL_STATE_MCOMMENT, | |||
UCL_STATE_MACRO_NAME, | |||
UCL_STATE_MACRO, | |||
UCL_STATE_ERROR | |||
}; | |||
enum ucl_character_type { | |||
UCL_CHARACTER_DENIED = 0, | |||
UCL_CHARACTER_KEY = 1, | |||
UCL_CHARACTER_KEY_START = 1 << 1, | |||
UCL_CHARACTER_WHITESPACE = 1 << 2, | |||
UCL_CHARACTER_WHITESPACE_UNSAFE = 1 << 3, | |||
UCL_CHARACTER_VALUE_END = 1 << 4, | |||
UCL_CHARACTER_VALUE_STR = 1 << 5, | |||
UCL_CHARACTER_VALUE_DIGIT = 1 << 6, | |||
UCL_CHARACTER_VALUE_DIGIT_START = 1 << 7, | |||
UCL_CHARACTER_ESCAPE = 1 << 8, | |||
UCL_CHARACTER_KEY_SEP = 1 << 9, | |||
UCL_CHARACTER_JSON_UNSAFE = 1 << 10, | |||
UCL_CHARACTER_UCL_UNSAFE = 1 << 11 | |||
}; | |||
struct ucl_macro { | |||
char *name; | |||
ucl_macro_handler handler; | |||
void* ud; | |||
UT_hash_handle hh; | |||
}; | |||
struct ucl_stack { | |||
ucl_object_t *obj; | |||
struct ucl_stack *next; | |||
int level; | |||
}; | |||
struct ucl_chunk { | |||
const unsigned char *begin; | |||
const unsigned char *end; | |||
const unsigned char *pos; | |||
size_t remain; | |||
unsigned int line; | |||
unsigned int column; | |||
unsigned priority; | |||
struct ucl_chunk *next; | |||
}; | |||
#ifdef HAVE_OPENSSL | |||
struct ucl_pubkey { | |||
EVP_PKEY *key; | |||
struct ucl_pubkey *next; | |||
}; | |||
#else | |||
struct ucl_pubkey { | |||
struct ucl_pubkey *next; | |||
}; | |||
#endif | |||
struct ucl_variable { | |||
char *var; | |||
char *value; | |||
size_t var_len; | |||
size_t value_len; | |||
struct ucl_variable *prev, *next; | |||
}; | |||
struct ucl_parser { | |||
enum ucl_parser_state state; | |||
enum ucl_parser_state prev_state; | |||
unsigned int recursion; | |||
int flags; | |||
ucl_object_t *top_obj; | |||
ucl_object_t *cur_obj; | |||
char *cur_file; | |||
struct ucl_macro *macroes; | |||
struct ucl_stack *stack; | |||
struct ucl_chunk *chunks; | |||
struct ucl_pubkey *keys; | |||
struct ucl_variable *variables; | |||
ucl_variable_handler var_handler; | |||
void *var_data; | |||
UT_string *err; | |||
}; | |||
struct ucl_object_userdata { | |||
ucl_object_t obj; | |||
ucl_userdata_dtor dtor; | |||
ucl_userdata_emitter emitter; | |||
}; | |||
/** | |||
* Unescape json string inplace | |||
* @param str | |||
*/ | |||
size_t ucl_unescape_json_string (char *str, size_t len); | |||
/** | |||
* Handle include macro | |||
* @param data include data | |||
* @param len length of data | |||
* @param ud user data | |||
* @param err error ptr | |||
* @return | |||
*/ | |||
bool ucl_include_handler (const unsigned char *data, size_t len, | |||
const ucl_object_t *args, void* ud); | |||
bool ucl_try_include_handler (const unsigned char *data, size_t len, | |||
const ucl_object_t *args, void* ud); | |||
/** | |||
* Handle includes macro | |||
* @param data include data | |||
* @param len length of data | |||
* @param ud user data | |||
* @param err error ptr | |||
* @return | |||
*/ | |||
bool ucl_includes_handler (const unsigned char *data, size_t len, | |||
const ucl_object_t *args, void* ud); | |||
size_t ucl_strlcpy (char *dst, const char *src, size_t siz); | |||
size_t ucl_strlcpy_unsafe (char *dst, const char *src, size_t siz); | |||
size_t ucl_strlcpy_tolower (char *dst, const char *src, size_t siz); | |||
#ifdef __GNUC__ | |||
static inline void | |||
ucl_create_err (UT_string **err, const char *fmt, ...) | |||
__attribute__ (( format( printf, 2, 3) )); | |||
#endif | |||
static inline void | |||
ucl_create_err (UT_string **err, const char *fmt, ...) | |||
{ | |||
if (*err == NULL) { | |||
utstring_new (*err); | |||
va_list ap; | |||
va_start (ap, fmt); | |||
utstring_printf_va (*err, fmt, ap); | |||
va_end (ap); | |||
} | |||
} | |||
/** | |||
* Check whether a given string contains a boolean value | |||
* @param obj object to set | |||
* @param start start of a string | |||
* @param len length of a string | |||
* @return true if a string is a boolean value | |||
*/ | |||
static inline bool | |||
ucl_maybe_parse_boolean (ucl_object_t *obj, const unsigned char *start, size_t len) | |||
{ | |||
const char *p = (const char *)start; | |||
bool ret = false, val = false; | |||
if (len == 5) { | |||
if ((p[0] == 'f' || p[0] == 'F') && strncasecmp (p, "false", 5) == 0) { | |||
ret = true; | |||
val = false; | |||
} | |||
} | |||
else if (len == 4) { | |||
if ((p[0] == 't' || p[0] == 'T') && strncasecmp (p, "true", 4) == 0) { | |||
ret = true; | |||
val = true; | |||
} | |||
} | |||
else if (len == 3) { | |||
if ((p[0] == 'y' || p[0] == 'Y') && strncasecmp (p, "yes", 3) == 0) { | |||
ret = true; | |||
val = true; | |||
} | |||
else if ((p[0] == 'o' || p[0] == 'O') && strncasecmp (p, "off", 3) == 0) { | |||
ret = true; | |||
val = false; | |||
} | |||
} | |||
else if (len == 2) { | |||
if ((p[0] == 'n' || p[0] == 'N') && strncasecmp (p, "no", 2) == 0) { | |||
ret = true; | |||
val = false; | |||
} | |||
else if ((p[0] == 'o' || p[0] == 'O') && strncasecmp (p, "on", 2) == 0) { | |||
ret = true; | |||
val = true; | |||
} | |||
} | |||
if (ret) { | |||
obj->type = UCL_BOOLEAN; | |||
obj->value.iv = val; | |||
} | |||
return ret; | |||
} | |||
/** | |||
* Check numeric string | |||
* @param obj object to set if a string is numeric | |||
* @param start start of string | |||
* @param end end of string | |||
* @param pos position where parsing has stopped | |||
* @param allow_double allow parsing of floating point values | |||
* @return 0 if string is numeric and error code (EINVAL or ERANGE) in case of conversion error | |||
*/ | |||
int ucl_maybe_parse_number (ucl_object_t *obj, | |||
const char *start, const char *end, const char **pos, | |||
bool allow_double, bool number_bytes, bool allow_time); | |||
static inline const ucl_object_t * | |||
ucl_hash_search_obj (ucl_hash_t* hashlin, ucl_object_t *obj) | |||
{ | |||
return (const ucl_object_t *)ucl_hash_search (hashlin, obj->key, obj->keylen); | |||
} | |||
static inline ucl_hash_t * ucl_hash_insert_object (ucl_hash_t *hashlin, | |||
const ucl_object_t *obj, | |||
bool ignore_case) UCL_WARN_UNUSED_RESULT; | |||
static inline ucl_hash_t * | |||
ucl_hash_insert_object (ucl_hash_t *hashlin, | |||
const ucl_object_t *obj, | |||
bool ignore_case) | |||
{ | |||
if (hashlin == NULL) { | |||
hashlin = ucl_hash_create (ignore_case); | |||
} | |||
ucl_hash_insert (hashlin, obj, obj->key, obj->keylen); | |||
return hashlin; | |||
} | |||
/** | |||
* Get standard emitter context for a specified emit_type | |||
* @param emit_type type of emitter | |||
* @return context or NULL if input is invalid | |||
*/ | |||
const struct ucl_emitter_context * | |||
ucl_emit_get_standard_context (enum ucl_emitter emit_type); | |||
/** | |||
* Serialize string as JSON string | |||
* @param str string to emit | |||
* @param buf target buffer | |||
*/ | |||
void ucl_elt_string_write_json (const char *str, size_t size, | |||
struct ucl_emitter_context *ctx); | |||
/** | |||
* Write multiline string using `EOD` as string terminator | |||
* @param str | |||
* @param size | |||
* @param ctx | |||
*/ | |||
void ucl_elt_string_write_multiline (const char *str, size_t size, | |||
struct ucl_emitter_context *ctx); | |||
/** | |||
* Emit a single object to string | |||
* @param obj | |||
* @return | |||
*/ | |||
unsigned char * ucl_object_emit_single_json (const ucl_object_t *obj); | |||
/** | |||
* Check whether a specified string is long and should be likely printed in | |||
* multiline mode | |||
* @param obj | |||
* @return | |||
*/ | |||
bool ucl_maybe_long_string (const ucl_object_t *obj); | |||
#endif /* UCL_INTERNAL_H_ */ |
@@ -0,0 +1,475 @@ | |||
/* | |||
xxHash - Fast Hash algorithm | |||
Copyright (C) 2012-2013, Yann Collet. | |||
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) | |||
Redistribution and use in source and binary forms, with or without | |||
modification, are permitted provided that the following conditions are | |||
met: | |||
* Redistributions of source code must retain the above copyright | |||
notice, this list of conditions and the following disclaimer. | |||
* Redistributions in binary form must reproduce the above | |||
copyright notice, this list of conditions and the following disclaimer | |||
in the documentation and/or other materials provided with the | |||
distribution. | |||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
You can contact the author at : | |||
- xxHash source repository : http://code.google.com/p/xxhash/ | |||
*/ | |||
//************************************** | |||
// Tuning parameters | |||
//************************************** | |||
// Unaligned memory access is automatically enabled for "common" CPU, such as x86. | |||
// For others CPU, the compiler will be more cautious, and insert extra code to ensure aligned access is respected. | |||
// If you know your target CPU supports unaligned memory access, you want to force this option manually to improve performance. | |||
// You can also enable this parameter if you know your input data will always be aligned (boundaries of 4, for U32). | |||
#if defined(__ARM_FEATURE_UNALIGNED) || defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) | |||
# define XXH_USE_UNALIGNED_ACCESS 1 | |||
#endif | |||
// XXH_ACCEPT_NULL_INPUT_POINTER : | |||
// If the input pointer is a null pointer, xxHash default behavior is to trigger a memory access error, since it is a bad pointer. | |||
// When this option is enabled, xxHash output for null input pointers will be the same as a null-length input. | |||
// This option has a very small performance cost (only measurable on small inputs). | |||
// By default, this option is disabled. To enable it, uncomment below define : | |||
//#define XXH_ACCEPT_NULL_INPUT_POINTER 1 | |||
// XXH_FORCE_NATIVE_FORMAT : | |||
// By default, xxHash library provides endian-independant Hash values, based on little-endian convention. | |||
// Results are therefore identical for little-endian and big-endian CPU. | |||
// This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format. | |||
// Should endian-independance be of no importance for your application, you may set the #define below to 1. | |||
// It will improve speed for Big-endian CPU. | |||
// This option has no impact on Little_Endian CPU. | |||
#define XXH_FORCE_NATIVE_FORMAT 0 | |||
//************************************** | |||
// Compiler Specific Options | |||
//************************************** | |||
// Disable some Visual warning messages | |||
#ifdef _MSC_VER // Visual Studio | |||
# pragma warning(disable : 4127) // disable: C4127: conditional expression is constant | |||
#endif | |||
#ifdef _MSC_VER // Visual Studio | |||
# define forceinline static __forceinline | |||
#else | |||
# ifdef __GNUC__ | |||
# define forceinline static inline __attribute__((always_inline)) | |||
# else | |||
# define forceinline static inline | |||
# endif | |||
#endif | |||
//************************************** | |||
// Includes & Memory related functions | |||
//************************************** | |||
#include "xxhash.h" | |||
// Modify the local functions below should you wish to use some other memory related routines | |||
// for malloc(), free() | |||
#include <stdlib.h> | |||
forceinline void* XXH_malloc(size_t s) { return malloc(s); } | |||
forceinline void XXH_free (void* p) { free(p); } | |||
// for memcpy() | |||
#include <string.h> | |||
forceinline void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcpy(dest,src,size); } | |||
//************************************** | |||
// Basic Types | |||
//************************************** | |||
#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L // C99 | |||
# include <stdint.h> | |||
typedef uint8_t BYTE; | |||
typedef uint16_t U16; | |||
typedef uint32_t U32; | |||
typedef int32_t S32; | |||
typedef uint64_t U64; | |||
#else | |||
typedef unsigned char BYTE; | |||
typedef unsigned short U16; | |||
typedef unsigned int U32; | |||
typedef signed int S32; | |||
typedef unsigned long long U64; | |||
#endif | |||
#if defined(__GNUC__) && !defined(XXH_USE_UNALIGNED_ACCESS) | |||
# define _PACKED __attribute__ ((packed)) | |||
#else | |||
# define _PACKED | |||
#endif | |||
#if !defined(XXH_USE_UNALIGNED_ACCESS) && !defined(__GNUC__) | |||
# ifdef __IBMC__ | |||
# pragma pack(1) | |||
# else | |||
# pragma pack(push, 1) | |||
# endif | |||
#endif | |||
typedef struct _U32_S { U32 v; } _PACKED U32_S; | |||
#if !defined(XXH_USE_UNALIGNED_ACCESS) && !defined(__GNUC__) | |||
# pragma pack(pop) | |||
#endif | |||
#define A32(x) (((U32_S *)(x))->v) | |||
//*************************************** | |||
// Compiler-specific Functions and Macros | |||
//*************************************** | |||
#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) | |||
// Note : although _rotl exists for minGW (GCC under windows), performance seems poor | |||
#if defined(_MSC_VER) | |||
# define XXH_rotl32(x,r) _rotl(x,r) | |||
#else | |||
# define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r))) | |||
#endif | |||
#if defined(_MSC_VER) // Visual Studio | |||
# define XXH_swap32 _byteswap_ulong | |||
#elif GCC_VERSION >= 403 | |||
# define XXH_swap32 __builtin_bswap32 | |||
#else | |||
static inline U32 XXH_swap32 (U32 x) { | |||
return ((x << 24) & 0xff000000 ) | | |||
((x << 8) & 0x00ff0000 ) | | |||
((x >> 8) & 0x0000ff00 ) | | |||
((x >> 24) & 0x000000ff );} | |||
#endif | |||
//************************************** | |||
// Constants | |||
//************************************** | |||
#define PRIME32_1 2654435761U | |||
#define PRIME32_2 2246822519U | |||
#define PRIME32_3 3266489917U | |||
#define PRIME32_4 668265263U | |||
#define PRIME32_5 374761393U | |||
//************************************** | |||
// Architecture Macros | |||
//************************************** | |||
typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess; | |||
#ifndef XXH_CPU_LITTLE_ENDIAN // It is possible to define XXH_CPU_LITTLE_ENDIAN externally, for example using a compiler switch | |||
static const int one = 1; | |||
# define XXH_CPU_LITTLE_ENDIAN (*(char*)(&one)) | |||
#endif | |||
//************************************** | |||
// Macros | |||
//************************************** | |||
#define XXH_STATIC_ASSERT(c) { enum { XXH_static_assert = 1/(!!(c)) }; } // use only *after* variable declarations | |||
//**************************** | |||
// Memory reads | |||
//**************************** | |||
typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment; | |||
forceinline U32 XXH_readLE32_align(const U32* ptr, XXH_endianess endian, XXH_alignment align) | |||
{ | |||
if (align==XXH_unaligned) | |||
return endian==XXH_littleEndian ? A32(ptr) : XXH_swap32(A32(ptr)); | |||
else | |||
return endian==XXH_littleEndian ? *ptr : XXH_swap32(*ptr); | |||
} | |||
forceinline U32 XXH_readLE32(const U32* ptr, XXH_endianess endian) { return XXH_readLE32_align(ptr, endian, XXH_unaligned); } | |||
//**************************** | |||
// Simple Hash Functions | |||
//**************************** | |||
forceinline U32 XXH32_endian_align(const void* input, int len, U32 seed, XXH_endianess endian, XXH_alignment align) | |||
{ | |||
const BYTE* p = (const BYTE*)input; | |||
const BYTE* const bEnd = p + len; | |||
U32 h32; | |||
#ifdef XXH_ACCEPT_NULL_INPUT_POINTER | |||
if (p==NULL) { len=0; p=(const BYTE*)(size_t)16; } | |||
#endif | |||
if (len>=16) | |||
{ | |||
const BYTE* const limit = bEnd - 16; | |||
U32 v1 = seed + PRIME32_1 + PRIME32_2; | |||
U32 v2 = seed + PRIME32_2; | |||
U32 v3 = seed + 0; | |||
U32 v4 = seed - PRIME32_1; | |||
do | |||
{ | |||
v1 += XXH_readLE32_align((const U32*)p, endian, align) * PRIME32_2; v1 = XXH_rotl32(v1, 13); v1 *= PRIME32_1; p+=4; | |||
v2 += XXH_readLE32_align((const U32*)p, endian, align) * PRIME32_2; v2 = XXH_rotl32(v2, 13); v2 *= PRIME32_1; p+=4; | |||
v3 += XXH_readLE32_align((const U32*)p, endian, align) * PRIME32_2; v3 = XXH_rotl32(v3, 13); v3 *= PRIME32_1; p+=4; | |||
v4 += XXH_readLE32_align((const U32*)p, endian, align) * PRIME32_2; v4 = XXH_rotl32(v4, 13); v4 *= PRIME32_1; p+=4; | |||
} while (p<=limit); | |||
h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18); | |||
} | |||
else | |||
{ | |||
h32 = seed + PRIME32_5; | |||
} | |||
h32 += (U32) len; | |||
while (p<=bEnd-4) | |||
{ | |||
h32 += XXH_readLE32_align((const U32*)p, endian, align) * PRIME32_3; | |||
h32 = XXH_rotl32(h32, 17) * PRIME32_4 ; | |||
p+=4; | |||
} | |||
while (p<bEnd) | |||
{ | |||
h32 += (*p) * PRIME32_5; | |||
h32 = XXH_rotl32(h32, 11) * PRIME32_1 ; | |||
p++; | |||
} | |||
h32 ^= h32 >> 15; | |||
h32 *= PRIME32_2; | |||
h32 ^= h32 >> 13; | |||
h32 *= PRIME32_3; | |||
h32 ^= h32 >> 16; | |||
return h32; | |||
} | |||
U32 XXH32(const void* input, int len, U32 seed) | |||
{ | |||
#if 0 | |||
// Simple version, good for code maintenance, but unfortunately slow for small inputs | |||
void* state = XXH32_init(seed); | |||
XXH32_update(state, input, len); | |||
return XXH32_digest(state); | |||
#else | |||
XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; | |||
# if !defined(XXH_USE_UNALIGNED_ACCESS) | |||
if (!(((size_t)input) & 3)) // Input is aligned, let's leverage the speed advantage | |||
{ | |||
if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) | |||
return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned); | |||
else | |||
return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned); | |||
} | |||
# endif | |||
if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) | |||
return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned); | |||
else | |||
return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned); | |||
#endif | |||
} | |||
//**************************** | |||
// Advanced Hash Functions | |||
//**************************** | |||
struct XXH_state32_t | |||
{ | |||
U64 total_len; | |||
U32 seed; | |||
U32 v1; | |||
U32 v2; | |||
U32 v3; | |||
U32 v4; | |||
int memsize; | |||
char memory[16]; | |||
}; | |||
int XXH32_sizeofState(void) | |||
{ | |||
XXH_STATIC_ASSERT(XXH32_SIZEOFSTATE >= sizeof(struct XXH_state32_t)); // A compilation error here means XXH32_SIZEOFSTATE is not large enough | |||
return sizeof(struct XXH_state32_t); | |||
} | |||
XXH_errorcode XXH32_resetState(void* state_in, U32 seed) | |||
{ | |||
struct XXH_state32_t * state = (struct XXH_state32_t *) state_in; | |||
state->seed = seed; | |||
state->v1 = seed + PRIME32_1 + PRIME32_2; | |||
state->v2 = seed + PRIME32_2; | |||
state->v3 = seed + 0; | |||
state->v4 = seed - PRIME32_1; | |||
state->total_len = 0; | |||
state->memsize = 0; | |||
return XXH_OK; | |||
} | |||
void* XXH32_init (U32 seed) | |||
{ | |||
void* state = XXH_malloc (sizeof(struct XXH_state32_t)); | |||
XXH32_resetState(state, seed); | |||
return state; | |||
} | |||
forceinline XXH_errorcode XXH32_update_endian (void* state_in, const void* input, int len, XXH_endianess endian) | |||
{ | |||
struct XXH_state32_t * state = (struct XXH_state32_t *) state_in; | |||
const BYTE* p = (const BYTE*)input; | |||
const BYTE* const bEnd = p + len; | |||
#ifdef XXH_ACCEPT_NULL_INPUT_POINTER | |||
if (input==NULL) return XXH_ERROR; | |||
#endif | |||
state->total_len += len; | |||
if (state->memsize + len < 16) // fill in tmp buffer | |||
{ | |||
XXH_memcpy(state->memory + state->memsize, input, len); | |||
state->memsize += len; | |||
return XXH_OK; | |||
} | |||
if (state->memsize) // some data left from previous update | |||
{ | |||
XXH_memcpy(state->memory + state->memsize, input, 16-state->memsize); | |||
{ | |||
const U32* p32 = (const U32*)state->memory; | |||
state->v1 += XXH_readLE32(p32, endian) * PRIME32_2; state->v1 = XXH_rotl32(state->v1, 13); state->v1 *= PRIME32_1; p32++; | |||
state->v2 += XXH_readLE32(p32, endian) * PRIME32_2; state->v2 = XXH_rotl32(state->v2, 13); state->v2 *= PRIME32_1; p32++; | |||
state->v3 += XXH_readLE32(p32, endian) * PRIME32_2; state->v3 = XXH_rotl32(state->v3, 13); state->v3 *= PRIME32_1; p32++; | |||
state->v4 += XXH_readLE32(p32, endian) * PRIME32_2; state->v4 = XXH_rotl32(state->v4, 13); state->v4 *= PRIME32_1; p32++; | |||
} | |||
p += 16-state->memsize; | |||
state->memsize = 0; | |||
} | |||
if (p <= bEnd-16) | |||
{ | |||
const BYTE* const limit = bEnd - 16; | |||
U32 v1 = state->v1; | |||
U32 v2 = state->v2; | |||
U32 v3 = state->v3; | |||
U32 v4 = state->v4; | |||
do | |||
{ | |||
v1 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v1 = XXH_rotl32(v1, 13); v1 *= PRIME32_1; p+=4; | |||
v2 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v2 = XXH_rotl32(v2, 13); v2 *= PRIME32_1; p+=4; | |||
v3 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v3 = XXH_rotl32(v3, 13); v3 *= PRIME32_1; p+=4; | |||
v4 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v4 = XXH_rotl32(v4, 13); v4 *= PRIME32_1; p+=4; | |||
} while (p<=limit); | |||
state->v1 = v1; | |||
state->v2 = v2; | |||
state->v3 = v3; | |||
state->v4 = v4; | |||
} | |||
if (p < bEnd) | |||
{ | |||
XXH_memcpy(state->memory, p, bEnd-p); | |||
state->memsize = (int)(bEnd-p); | |||
} | |||
return XXH_OK; | |||
} | |||
XXH_errorcode XXH32_update (void* state_in, const void* input, int len) | |||
{ | |||
XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; | |||
if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) | |||
return XXH32_update_endian(state_in, input, len, XXH_littleEndian); | |||
else | |||
return XXH32_update_endian(state_in, input, len, XXH_bigEndian); | |||
} | |||
forceinline U32 XXH32_intermediateDigest_endian (void* state_in, XXH_endianess endian) | |||
{ | |||
struct XXH_state32_t * state = (struct XXH_state32_t *) state_in; | |||
const BYTE * p = (const BYTE*)state->memory; | |||
BYTE* bEnd = (BYTE*)state->memory + state->memsize; | |||
U32 h32; | |||
if (state->total_len >= 16) | |||
{ | |||
h32 = XXH_rotl32(state->v1, 1) + XXH_rotl32(state->v2, 7) + XXH_rotl32(state->v3, 12) + XXH_rotl32(state->v4, 18); | |||
} | |||
else | |||
{ | |||
h32 = state->seed + PRIME32_5; | |||
} | |||
h32 += (U32) state->total_len; | |||
while (p<=bEnd-4) | |||
{ | |||
h32 += XXH_readLE32((const U32*)p, endian) * PRIME32_3; | |||
h32 = XXH_rotl32(h32, 17) * PRIME32_4; | |||
p+=4; | |||
} | |||
while (p<bEnd) | |||
{ | |||
h32 += (*p) * PRIME32_5; | |||
h32 = XXH_rotl32(h32, 11) * PRIME32_1; | |||
p++; | |||
} | |||
h32 ^= h32 >> 15; | |||
h32 *= PRIME32_2; | |||
h32 ^= h32 >> 13; | |||
h32 *= PRIME32_3; | |||
h32 ^= h32 >> 16; | |||
return h32; | |||
} | |||
U32 XXH32_intermediateDigest (void* state_in) | |||
{ | |||
XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; | |||
if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) | |||
return XXH32_intermediateDigest_endian(state_in, XXH_littleEndian); | |||
else | |||
return XXH32_intermediateDigest_endian(state_in, XXH_bigEndian); | |||
} | |||
U32 XXH32_digest (void* state_in) | |||
{ | |||
U32 h32 = XXH32_intermediateDigest(state_in); | |||
XXH_free(state_in); | |||
return h32; | |||
} |
@@ -0,0 +1,164 @@ | |||
/* | |||
xxHash - Fast Hash algorithm | |||
Header File | |||
Copyright (C) 2012-2013, Yann Collet. | |||
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) | |||
Redistribution and use in source and binary forms, with or without | |||
modification, are permitted provided that the following conditions are | |||
met: | |||
* Redistributions of source code must retain the above copyright | |||
notice, this list of conditions and the following disclaimer. | |||
* Redistributions in binary form must reproduce the above | |||
copyright notice, this list of conditions and the following disclaimer | |||
in the documentation and/or other materials provided with the | |||
distribution. | |||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
You can contact the author at : | |||
- xxHash source repository : http://code.google.com/p/xxhash/ | |||
*/ | |||
/* Notice extracted from xxHash homepage : | |||
xxHash is an extremely fast Hash algorithm, running at RAM speed limits. | |||
It also successfully passes all tests from the SMHasher suite. | |||
Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 Duo @3GHz) | |||
Name Speed Q.Score Author | |||
xxHash 5.4 GB/s 10 | |||
CrapWow 3.2 GB/s 2 Andrew | |||
MumurHash 3a 2.7 GB/s 10 Austin Appleby | |||
SpookyHash 2.0 GB/s 10 Bob Jenkins | |||
SBox 1.4 GB/s 9 Bret Mulvey | |||
Lookup3 1.2 GB/s 9 Bob Jenkins | |||
SuperFastHash 1.2 GB/s 1 Paul Hsieh | |||
CityHash64 1.05 GB/s 10 Pike & Alakuijala | |||
FNV 0.55 GB/s 5 Fowler, Noll, Vo | |||
CRC32 0.43 GB/s 9 | |||
MD5-32 0.33 GB/s 10 Ronald L. Rivest | |||
SHA1-32 0.28 GB/s 10 | |||
Q.Score is a measure of quality of the hash function. | |||
It depends on successfully passing SMHasher test set. | |||
10 is a perfect score. | |||
*/ | |||
#pragma once | |||
#if defined (__cplusplus) | |||
extern "C" { | |||
#endif | |||
//**************************** | |||
// Type | |||
//**************************** | |||
typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode; | |||
//**************************** | |||
// Simple Hash Functions | |||
//**************************** | |||
unsigned int XXH32 (const void* input, int len, unsigned int seed); | |||
/* | |||
XXH32() : | |||
Calculate the 32-bits hash of sequence of length "len" stored at memory address "input". | |||
The memory between input & input+len must be valid (allocated and read-accessible). | |||
"seed" can be used to alter the result predictably. | |||
This function successfully passes all SMHasher tests. | |||
Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s | |||
Note that "len" is type "int", which means it is limited to 2^31-1. | |||
If your data is larger, use the advanced functions below. | |||
*/ | |||
//**************************** | |||
// Advanced Hash Functions | |||
//**************************** | |||
void* XXH32_init (unsigned int seed); | |||
XXH_errorcode XXH32_update (void* state, const void* input, int len); | |||
unsigned int XXH32_digest (void* state); | |||
/* | |||
These functions calculate the xxhash of an input provided in several small packets, | |||
as opposed to an input provided as a single block. | |||
It must be started with : | |||
void* XXH32_init() | |||
The function returns a pointer which holds the state of calculation. | |||
This pointer must be provided as "void* state" parameter for XXH32_update(). | |||
XXH32_update() can be called as many times as necessary. | |||
The user must provide a valid (allocated) input. | |||
The function returns an error code, with 0 meaning OK, and any other value meaning there is an error. | |||
Note that "len" is type "int", which means it is limited to 2^31-1. | |||
If your data is larger, it is recommended to chunk your data into blocks | |||
of size for example 2^30 (1GB) to avoid any "int" overflow issue. | |||
Finally, you can end the calculation anytime, by using XXH32_digest(). | |||
This function returns the final 32-bits hash. | |||
You must provide the same "void* state" parameter created by XXH32_init(). | |||
Memory will be freed by XXH32_digest(). | |||
*/ | |||
int XXH32_sizeofState(void); | |||
XXH_errorcode XXH32_resetState(void* state, unsigned int seed); | |||
#define XXH32_SIZEOFSTATE 48 | |||
typedef struct { long long ll[(XXH32_SIZEOFSTATE+(sizeof(long long)-1))/sizeof(long long)]; } XXH32_stateSpace_t; | |||
/* | |||
These functions allow user application to make its own allocation for state. | |||
XXH32_sizeofState() is used to know how much space must be allocated for the xxHash 32-bits state. | |||
Note that the state must be aligned to access 'long long' fields. Memory must be allocated and referenced by a pointer. | |||
This pointer must then be provided as 'state' into XXH32_resetState(), which initializes the state. | |||
For static allocation purposes (such as allocation on stack, or freestanding systems without malloc()), | |||
use the structure XXH32_stateSpace_t, which will ensure that memory space is large enough and correctly aligned to access 'long long' fields. | |||
*/ | |||
unsigned int XXH32_intermediateDigest (void* state); | |||
/* | |||
This function does the same as XXH32_digest(), generating a 32-bit hash, | |||
but preserve memory context. | |||
This way, it becomes possible to generate intermediate hashes, and then continue feeding data with XXH32_update(). | |||
To free memory context, use XXH32_digest(), or free(). | |||
*/ | |||
//**************************** | |||
// Deprecated function names | |||
//**************************** | |||
// The following translations are provided to ease code transition | |||
// You are encouraged to no longer this function names | |||
#define XXH32_feed XXH32_update | |||
#define XXH32_result XXH32_digest | |||
#define XXH32_getIntermediateResult XXH32_intermediateDigest | |||
#if defined (__cplusplus) | |||
} | |||
#endif |
@@ -56,11 +56,6 @@ ENDMACRO(AddModules MLIST WLIST) | |||
# Contrib software | |||
ADD_SUBDIRECTORY(cdb) | |||
ADD_SUBDIRECTORY(ucl/cmake) | |||
SET(SLAVE_BUILD 1) | |||
ADD_SUBDIRECTORY(rdns) | |||
UNSET(SLAVE_BUILD) | |||
# Rspamd core components | |||
ADD_SUBDIRECTORY(lua) | |||
ADD_SUBDIRECTORY(libcryptobox) |