Browse Source

Move ucl and rdns to contrib.

tags/0.9.0
Vsevolod Stakhov 9 years ago
parent
commit
6e121a026f
44 changed files with 16541 additions and 14 deletions
  1. 0
    6
      .gitmodules
  2. 4
    3
      CMakeLists.txt
  3. 10
    0
      contrib/librdns/CMakeLists.txt
  4. 154
    0
      contrib/librdns/compression.c
  5. 45
    0
      contrib/librdns/compression.h
  6. 472
    0
      contrib/librdns/curve.c
  7. 262
    0
      contrib/librdns/dns_private.h
  8. 53
    0
      contrib/librdns/logger.c
  9. 42
    0
      contrib/librdns/logger.h
  10. 271
    0
      contrib/librdns/packet.c
  11. 71
    0
      contrib/librdns/packet.h
  12. 419
    0
      contrib/librdns/parse.c
  13. 65
    0
      contrib/librdns/parse.h
  14. 297
    0
      contrib/librdns/punycode.c
  15. 59
    0
      contrib/librdns/punycode.h
  16. 372
    0
      contrib/librdns/rdns.h
  17. 75
    0
      contrib/librdns/rdns_curve.h
  18. 232
    0
      contrib/librdns/rdns_ev.h
  19. 231
    0
      contrib/librdns/rdns_event.h
  20. 71
    0
      contrib/librdns/ref.h
  21. 782
    0
      contrib/librdns/resolver.c
  22. 257
    0
      contrib/librdns/upstream.h
  23. 523
    0
      contrib/librdns/util.c
  24. 62
    0
      contrib/librdns/util.h
  25. 18
    0
      contrib/libucl/CMakeLists.txt
  26. 627
    0
      contrib/libucl/khash.h
  27. 103
    0
      contrib/libucl/kvec.h
  28. 820
    0
      contrib/libucl/lua_ucl.c
  29. 69
    0
      contrib/libucl/lua_ucl.h
  30. 212
    0
      contrib/libucl/tree.h
  31. 1144
    0
      contrib/libucl/ucl.h
  32. 267
    0
      contrib/libucl/ucl_chartable.h
  33. 511
    0
      contrib/libucl/ucl_emitter.c
  34. 165
    0
      contrib/libucl/ucl_emitter_streamline.c
  35. 487
    0
      contrib/libucl/ucl_emitter_utils.c
  36. 353
    0
      contrib/libucl/ucl_hash.c
  37. 93
    0
      contrib/libucl/ucl_hash.h
  38. 399
    0
      contrib/libucl/ucl_internal.h
  39. 2222
    0
      contrib/libucl/ucl_parser.c
  40. 1015
    0
      contrib/libucl/ucl_schema.c
  41. 2568
    0
      contrib/libucl/ucl_util.c
  42. 475
    0
      contrib/libucl/xxhash.c
  43. 164
    0
      contrib/libucl/xxhash.h
  44. 0
    5
      src/CMakeLists.txt

+ 0
- 6
.gitmodules View File

@@ -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

+ 4
- 3
CMakeLists.txt View File

@@ -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)

+ 10
- 0
contrib/librdns/CMakeLists.txt View File

@@ -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})

+ 154
- 0
contrib/librdns/compression.c View File

@@ -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;
}

+ 45
- 0
contrib/librdns/compression.h View File

@@ -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_ */

+ 472
- 0
contrib/librdns/curve.c View File

@@ -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

+ 262
- 0
contrib/librdns/dns_private.h View File

@@ -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_ */

+ 53
- 0
contrib/librdns/logger.c View File

@@ -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);
}
}

+ 42
- 0
contrib/librdns/logger.h View File

@@ -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_ */

+ 271
- 0
contrib/librdns/packet.c View File

@@ -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;
}

+ 71
- 0
contrib/librdns/packet.h View File

@@ -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_ */

+ 419
- 0
contrib/librdns/parse.c View File

@@ -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;
}

+ 65
- 0
contrib/librdns/parse.h View File

@@ -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_ */

+ 297
- 0
contrib/librdns/punycode.c View File

@@ -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;
}

+ 59
- 0
contrib/librdns/punycode.h View File

@@ -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_ */

+ 372
- 0
contrib/librdns/rdns.h View File

@@ -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

+ 75
- 0
contrib/librdns/rdns_curve.h View File

@@ -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_ */

+ 232
- 0
contrib/librdns/rdns_ev.h View File

@@ -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_ */

+ 231
- 0
contrib/librdns/rdns_event.h View File

@@ -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_ */

+ 71
- 0
contrib/librdns/ref.h View File

@@ -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_ */

+ 782
- 0
contrib/librdns/resolver.c View File

@@ -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;
}
}

+ 257
- 0
contrib/librdns/upstream.h View File

@@ -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_ */

+ 523
- 0
contrib/librdns/util.c View File

@@ -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;
}

+ 62
- 0
contrib/librdns/util.h View File

@@ -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_ */

+ 18
- 0
contrib/libucl/CMakeLists.txt View File

@@ -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")

+ 627
- 0
contrib/libucl/khash.h View File

@@ -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 */

+ 103
- 0
contrib/libucl/kvec.h View File

@@ -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

+ 820
- 0
contrib/libucl/lua_ucl.c View File

@@ -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;
}

+ 69
- 0
contrib/libucl/lua_ucl.h View File

@@ -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_ */

+ 212
- 0
contrib/libucl/tree.h View File

@@ -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 */

+ 1144
- 0
contrib/libucl/ucl.h
File diff suppressed because it is too large
View File


+ 267
- 0
contrib/libucl/ucl_chartable.h View File

@@ -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_ */

+ 511
- 0
contrib/libucl/ucl_emitter.c View File

@@ -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;
}

+ 165
- 0
contrib/libucl/ucl_emitter_streamline.c View File

@@ -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);
}

+ 487
- 0
contrib/libucl/ucl_emitter_utils.c View File

@@ -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;
}

+ 353
- 0
contrib/libucl/ucl_hash.c View File

@@ -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);
}
}
}

+ 93
- 0
contrib/libucl/ucl_hash.h View File

@@ -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

+ 399
- 0
contrib/libucl/ucl_internal.h View File

@@ -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_ */

+ 2222
- 0
contrib/libucl/ucl_parser.c
File diff suppressed because it is too large
View File


+ 1015
- 0
contrib/libucl/ucl_schema.c
File diff suppressed because it is too large
View File


+ 2568
- 0
contrib/libucl/ucl_util.c
File diff suppressed because it is too large
View File


+ 475
- 0
contrib/libucl/xxhash.c View File

@@ -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;
}

+ 164
- 0
contrib/libucl/xxhash.h View File

@@ -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

+ 0
- 5
src/CMakeLists.txt View File

@@ -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)

Loading…
Cancel
Save