Compare commits

...

33 Commits

Author SHA1 Message Date
ddb3a60dcc Experimental support for building as C++
So far with the following caveats:
 - Triggers -Wc99-designator
 - Compound literals are non-standard.
 - The setjmp/longjmp in the configuration parser might be an issue.
 - Perhaps others.

It does not seem to be a good idea to use this library for C++ at all.
Much of what it does is directly replaced by the STL.
2020-10-22 02:19:59 +02:00
53bcebc2f0 Split out utf8_validate_cp(), adhere to RFC 3629 2020-10-21 05:20:20 +02:00
b08cf6c29f Reject overlong UTF-8 sequences 2020-10-21 05:08:59 +02:00
69101eb155 Fix optional arguments in --help output
An equals sign is necessary.
2020-10-13 21:27:46 +02:00
9d14562f7e Improve the UTF-8 API
We need to be able to detect partial sequences.
2020-10-12 22:56:22 +02:00
9b72304963 Fix a memory leak in mpd_client_parse_line() 2020-10-12 02:07:15 +02:00
1cd9ba8d97 Import configuration test from degesch 2020-10-12 02:07:15 +02:00
7e5b6c5343 Fix crashes in the config parser
It had a duality between not requiring null-terminated input
and relying on it, depending on where you looked.
2020-10-12 02:07:14 +02:00
c2c5031538 Add remaining fuzzing entry points
Closes #1
2020-10-12 02:07:07 +02:00
df3f53bd5c Add a basic fuzzing framework using libFuzzer
Updates #1
2020-10-11 20:04:34 +02:00
e029aae1d3 Import xwrite(), cstr_set(), resolve_..._template()
From degesch and json-rpc-shell.
2020-10-10 04:31:52 +02:00
b9457c321f Rename cstr_transform() argument
It does not always have to be tolower().
2020-10-10 04:30:19 +02:00
2201becca4 Mark some issues 2020-10-10 04:29:41 +02:00
7023c51347 Get rid of CMake dev warnings 2020-10-02 06:47:34 +02:00
d21f8466b5 Bump copyright years 2020-10-02 06:43:16 +02:00
7f919025ee Add iscntrl_ascii()
It's too easy to miss the DEL character.
2020-10-02 06:31:46 +02:00
1a76b2032e Add a slogan of sorts 2020-08-01 14:03:23 +02:00
722ef65c1f Name change 2020-08-01 14:02:25 +02:00
317dfcb6e2 Improve setjmp safety in config parser 2020-04-19 07:02:13 +02:00
bca7167d03 Fix the SCGI parser and tests 2018-10-18 06:34:16 +02:00
3e4e4e5103 Allow aborting the FastCGI protocol parser 2018-10-18 04:08:47 +02:00
9494e8e2af Add some comments 2018-10-11 21:02:45 +02:00
8ffe20c0e8 Add missing include for "struct iovec" 2018-06-24 06:09:40 +02:00
bb30c7d86e Remove .travis.yml
We don't depend on any proprietary services no longer.  I'll have to
make my own replacements with blackjack and hookers.  Until then,
the file stays in the commit log as an example.
2018-06-21 23:58:24 +02:00
47ef2ae5bd Update README 2018-06-21 23:58:03 +02:00
69800a6afb Relicense to 0BSD, update mail address
I've come to the conclusion that copyright mostly just stands in the way
of software development.  In my jurisdiction I cannot give up my own
copyright and 0BSD seems to be the closest thing to public domain.

The updated mail address, also used in my author/committer lines,
is shorter and looks nicer.  People rarely interact anyway.
2018-06-21 23:57:25 +02:00
fe1035633a Describe syntax of advanced configuration w/ PEG 2018-04-19 00:09:46 +02:00
da75b6f735 siphash: silence fall-through warnings 2017-09-26 19:08:13 +02:00
199c56e141 Little improvements 2017-07-24 03:46:06 +02:00
6e9217e5d0 MPD client: +mpd_client_send_command_raw() 2017-06-26 03:35:05 +02:00
3835b6e499 Improve simple_config_update_from_file()
- considerably shorter
 - catch file read errors as we should
 - better error messages, now including the filename
 - disallow empty keys as they are never used
 - allow whitespace before start of comment

NUL characters stop processing now, though.  If anyone cares.
2017-06-22 20:42:44 +02:00
bf534010cb _init() -> _make() where possible 2017-06-22 20:42:44 +02:00
7b0d7a19e5 Cleanup 2017-06-14 23:28:44 +02:00
16 changed files with 1049 additions and 610 deletions

View File

@@ -1,24 +0,0 @@
language: c
notifications:
irc:
channels: "irc.janouch.name#dev"
use_notice: true
skip_join: true
template:
- "%{repository_name}#%{build_number} on %{branch}: %{message}"
- " %{compare_url}"
- " %{build_url}"
on_success: change
on_failure: always
compiler:
- clang
- gcc
before_install:
- sudo apt-get update -qq
before_script:
- mkdir build
- cd build
script:
- cmake .. -DCMAKE_INSTALL_PREFIX=/usr
- make
- ctest -V

View File

@@ -2,10 +2,11 @@ project (liberty C)
cmake_minimum_required (VERSION 2.8.5)
# Moar warnings
if ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUC)
if ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUCC)
# -Wunused-function is pretty annoying here, as everything is static
set (CMAKE_C_FLAGS "-std=c99 -Wall -Wextra -Wno-unused-function")
endif ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUC)
set (wdisabled "-Wno-unused-function -Wno-implicit-fallthrough")
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Wall -Wextra ${wdisabled}")
endif ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUCC)
# Dependencies
set (CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
@@ -34,15 +35,8 @@ foreach (extra iconv rt)
endif (extra_lib_${extra})
endforeach (extra)
# Generate a configuration file
# TODO: actualy use the configuration file for something; so far we allow
# for direct inclusion without running this CMakeLists.txt
configure_file (${PROJECT_SOURCE_DIR}/liberty-config.h.in
${PROJECT_BINARY_DIR}/liberty-config.h)
include_directories (${PROJECT_SOURCE_DIR} ${PROJECT_BINARY_DIR})
set (common_sources ${PROJECT_BINARY_DIR}/liberty-config.h)
# Build some unit tests
include_directories (${PROJECT_SOURCE_DIR})
enable_testing ()
foreach (name liberty proto)
add_executable (test-${name} tests/${name}.c ${common_sources})

View File

@@ -1,8 +1,7 @@
Copyright (c) 2014 - 2017, Přemysl Janouch <p.janouch@gmail.com>
Copyright (c) 2014 - 2020, Přemysl Eric Janouch <p@janouch.name>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF

View File

@@ -19,18 +19,13 @@ I'm not testing them at all, with the exception of OpenBSD.
Contributing and Support
------------------------
Use this project's GitHub to report any bugs, request features, or submit pull
requests. If you want to discuss this project, or maybe just hang out with
the developer, feel free to join me at irc://irc.janouch.name, channel #dev.
Use https://git.janouch.name/p/liberty to report any bugs, request features,
or submit pull requests. `git send-email` is tolerated. If you want to discuss
the project, feel free to join me at ircs://irc.janouch.name, channel #dev.
Bitcoin donations: 12r5uEWEgcHC46xd64tt3hHt9EUvYYDHe9
Bitcoin donations are accepted at: 12r5uEWEgcHC46xd64tt3hHt9EUvYYDHe9
License
-------
'liberty' is written by Přemysl Janouch <p.janouch@gmail.com>.
You may use the software under the terms of the ISC license, the text of which
is included within the package, or, at your option, you may relicense the work
under the MIT or the Modified BSD License, as listed at the following site:
http://www.gnu.org/licenses/license-list.html
This software is released under the terms of the 0BSD license, the text of which
is included within the package along with the list of authors.

View File

@@ -1,17 +1,17 @@
# Public Domain
find_package (PkgConfig REQUIRED)
pkg_check_modules (NCURSESW QUIET ncursesw)
pkg_check_modules (Ncursesw QUIET ncursesw)
# OpenBSD doesn't provide a pkg-config file
set (required_vars NCURSESW_LIBRARIES)
if (NOT NCURSESW_FOUND)
find_library (NCURSESW_LIBRARIES NAMES ncursesw)
find_path (NCURSESW_INCLUDE_DIRS ncurses.h)
list (APPEND required_vars NCURSESW_INCLUDE_DIRS)
endif (NOT NCURSESW_FOUND)
set (required_vars Ncursesw_LIBRARIES)
if (NOT Ncursesw_FOUND)
find_library (Ncursesw_LIBRARIES NAMES ncursesw)
find_path (Ncursesw_INCLUDE_DIRS ncurses.h)
list (APPEND required_vars Ncursesw_INCLUDE_DIRS)
endif (NOT Ncursesw_FOUND)
include (FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS (NCURSESW DEFAULT_MSG ${required_vars})
FIND_PACKAGE_HANDLE_STANDARD_ARGS (Ncursesw DEFAULT_MSG ${required_vars})
mark_as_advanced (NCURSESW_LIBRARIES NCURSESW_INCLUDE_DIRS)
mark_as_advanced (Ncursesw_LIBRARIES Ncursesw_INCLUDE_DIRS)

View File

@@ -1,10 +1,10 @@
# Public Domain
find_path (UNISTRING_INCLUDE_DIRS unistr.h)
find_library (UNISTRING_LIBRARIES NAMES unistring libunistring)
find_path (Unistring_INCLUDE_DIRS unistr.h)
find_library (Unistring_LIBRARIES NAMES unistring libunistring)
include (FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS (UNISTRING DEFAULT_MSG
UNISTRING_INCLUDE_DIRS UNISTRING_LIBRARIES)
FIND_PACKAGE_HANDLE_STANDARD_ARGS (Unistring DEFAULT_MSG
Unistring_INCLUDE_DIRS Unistring_LIBRARIES)
mark_as_advanced (UNISTRING_LIBRARIES UNISTRING_INCLUDE_DIRS)
mark_as_advanced (Unistring_LIBRARIES Unistring_INCLUDE_DIRS)

18
fuzz Executable file
View File

@@ -0,0 +1,18 @@
#!/bin/sh
# I'm not sure how to make maximum use of this invention
# Make sure to have llvm-symbolizer installed
clang -g -fsanitize=address,undefined,fuzzer -fno-sanitize-recover=all \
tests/fuzz.c -o fuzz-executor
fuzz () {
echo "`tput bold`-- Fuzzing $1`tput sgr0`"
mkdir -p /tmp/corpus-$1
./fuzz-executor -test=$1 -artifact_prefix=$1- \
-max_total_time=600 -timeout=1 /tmp/corpus-$1
}
if [ $# -gt 0 ]; then
for test in "$@"; do fuzz $test; done
else
for test in $(./fuzz-executor); do fuzz $test; done
fi

View File

View File

@@ -1,11 +1,10 @@
/*
* liberty-proto.c: the ultimate C unlibrary: protocols
*
* Copyright (c) 2014 - 2016, Přemysl Janouch <p.janouch@gmail.com>
* Copyright (c) 2014 - 2016, Přemysl Eric Janouch <p@janouch.name>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
* purpose with or without fee is hereby granted.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
@@ -34,9 +33,7 @@ struct irc_message
static char *
irc_unescape_message_tag (const char *value)
{
struct str s;
str_init (&s);
struct str s = str_make ();
bool escape = false;
for (const char *p = value; *p; p++)
{
@@ -63,8 +60,7 @@ irc_unescape_message_tag (const char *value)
static void
irc_parse_message_tags (const char *tags, struct str_map *out)
{
struct strv v;
strv_init (&v);
struct strv v = strv_make ();
cstr_split (tags, ";", true, &v);
for (size_t i = 0; i < v.len; i++)
@@ -78,19 +74,16 @@ irc_parse_message_tags (const char *tags, struct str_map *out)
else
str_map_set (out, key, xstrdup (""));
}
strv_free (&v);
}
static void
irc_parse_message (struct irc_message *msg, const char *line)
{
str_map_init (&msg->tags);
msg->tags.free = free;
msg->tags = str_map_make (free);
msg->prefix = NULL;
msg->command = NULL;
strv_init (&msg->params);
msg->params = strv_make ();
// IRC 3.2 message tags
if (*line == '@')
@@ -220,6 +213,7 @@ irc_fnmatch (const char *pattern, const char *string)
char x_pattern[pattern_size], x_string[string_size];
irc_strxfrm (x_pattern, pattern, pattern_size);
irc_strxfrm (x_string, string, string_size);
// FIXME: this supports [], which is not mentioned in RFC 2812
return fnmatch (x_pattern, x_string, 0);
}
@@ -288,14 +282,15 @@ struct http_tokenizer
struct str string; ///< "token" / "quoted-string" content
};
static void
http_tokenizer_init (struct http_tokenizer *self, const char *input, size_t len)
static struct http_tokenizer
http_tokenizer_make (const char *input, size_t len)
{
memset (self, 0, sizeof *self);
self->input = (const unsigned char *) input;
self->input_len = len;
str_init (&self->string);
return (struct http_tokenizer)
{
.input = (const unsigned char *) input,
.input_len = len,
.string = str_make (),
};
}
static void
@@ -428,8 +423,8 @@ http_parse_media_type (const char *media_type,
char **type, char **subtype, struct str_map *parameters)
{
bool result = false;
struct http_tokenizer t;
http_tokenizer_init (&t, media_type, strlen (media_type));
struct http_tokenizer t =
http_tokenizer_make (media_type, strlen (media_type));
if (http_tokenizer_next (&t, true) != HTTP_T_TOKEN)
goto end;
@@ -490,8 +485,7 @@ http_parse_upgrade (const char *upgrade, struct http_protocol **out)
struct http_protocol *list = NULL;
struct http_protocol *tail = NULL;
struct http_tokenizer t;
http_tokenizer_init (&t, upgrade, strlen (upgrade));
struct http_tokenizer t = http_tokenizer_make (upgrade, strlen (upgrade));
enum {
STATE_PROTOCOL_NAME,
@@ -513,7 +507,7 @@ http_parse_upgrade (const char *upgrade, struct http_protocol **out)
case HTTP_T_WHITESPACE:
break;
case HTTP_T_TOKEN:
proto = xcalloc (1, sizeof *proto);
proto = (struct http_protocol *) xcalloc (1, sizeof *proto);
proto->name = xstrdup (t.string.str);
LIST_APPEND_WITH_TAIL (list, tail, proto);
state = STATE_SLASH;
@@ -618,16 +612,16 @@ struct scgi_parser
void *user_data; ///< User data passed to callbacks
};
static void
scgi_parser_init (struct scgi_parser *self)
static struct scgi_parser
scgi_parser_make (void)
{
memset (self, 0, sizeof *self);
str_init (&self->input);
str_map_init (&self->headers);
self->headers.free = free;
str_init (&self->name);
str_init (&self->value);
return (struct scgi_parser)
{
.input = str_make (),
.headers = str_map_make (free),
.name = str_make (),
.value = str_make (),
};
}
static void
@@ -670,10 +664,11 @@ scgi_parser_push (struct scgi_parser *self,
if (digit == ':')
{
self->state = SCGI_READING_NAME;
str_remove_slice (&self->input, 0, 1);
break;
}
if (digit < '0' || digit >= '9')
if (digit < '0' || digit > '9')
return error_set (e, "invalid header netstring");
size_t new_len = self->headers_len * 10 + (digit - '0');
@@ -706,6 +701,7 @@ scgi_parser_push (struct scgi_parser *self,
self->state = SCGI_READING_VALUE;
str_remove_slice (&self->input, 0, 1);
self->headers_len--;
break;
}
case SCGI_READING_VALUE:
@@ -728,12 +724,13 @@ scgi_parser_push (struct scgi_parser *self,
self->name.str, str_steal (&self->value));
str_reset (&self->name);
str_init (&self->value);
self->value = str_make ();
self->state = SCGI_READING_NAME;
}
str_remove_slice (&self->input, 0, 1);
self->headers_len--;
break;
}
case SCGI_READING_CONTENT:
@@ -798,7 +795,8 @@ enum fcgi_protocol_status
struct fcgi_parser;
typedef void (*fcgi_message_fn)
/// Message handler, returns false if further processing should be stopped
typedef bool (*fcgi_message_fn)
(const struct fcgi_parser *parser, void *user_data);
enum fcgi_parser_state
@@ -827,12 +825,11 @@ struct fcgi_parser
void *user_data; ///< User data
};
static void
fcgi_parser_init (struct fcgi_parser *self)
static struct fcgi_parser
fcgi_parser_make (void)
{
memset (self, 0, sizeof *self);
str_init (&self->input);
str_init (&self->content);
return (struct fcgi_parser)
{ .input = str_make (), .content = str_make () };
}
static void
@@ -845,8 +842,8 @@ fcgi_parser_free (struct fcgi_parser *self)
static void
fcgi_parser_unpack_header (struct fcgi_parser *self)
{
struct msg_unpacker unpacker;
msg_unpacker_init (&unpacker, self->input.str, self->input.len);
struct msg_unpacker unpacker =
msg_unpacker_make (self->input.str, self->input.len);
bool success = true;
uint8_t reserved;
@@ -861,7 +858,7 @@ fcgi_parser_unpack_header (struct fcgi_parser *self)
str_remove_slice (&self->input, 0, unpacker.offset);
}
static void
static bool
fcgi_parser_push (struct fcgi_parser *self, const void *data, size_t len)
{
// This could be made considerably faster for high-throughput applications
@@ -873,14 +870,14 @@ fcgi_parser_push (struct fcgi_parser *self, const void *data, size_t len)
{
case FCGI_READING_HEADER:
if (self->input.len < FCGI_HEADER_LEN)
return;
return true;
fcgi_parser_unpack_header (self);
self->state = FCGI_READING_CONTENT;
break;
case FCGI_READING_CONTENT:
if (self->input.len < self->content_length)
return;
return true;
// Move an appropriate part of the input buffer to the content buffer
str_reset (&self->content);
@@ -890,10 +887,11 @@ fcgi_parser_push (struct fcgi_parser *self, const void *data, size_t len)
break;
case FCGI_READING_PADDING:
if (self->input.len < self->padding_length)
return;
return true;
// Call the callback to further process the message
self->on_message (self, self->user_data);
if (!self->on_message (self, self->user_data))
return false;
// Remove the padding from the input buffer
str_remove_slice (&self->input, 0, self->padding_length);
@@ -928,11 +926,10 @@ struct fcgi_nv_parser
char *value; ///< The current value, 0-terminated
};
static void
fcgi_nv_parser_init (struct fcgi_nv_parser *self)
static struct fcgi_nv_parser
fcgi_nv_parser_make (void)
{
memset (self, 0, sizeof *self);
str_init (&self->input);
return (struct fcgi_nv_parser) { .input = str_make () };
}
static void
@@ -951,8 +948,8 @@ fcgi_nv_parser_push (struct fcgi_nv_parser *self, const void *data, size_t len)
while (true)
{
struct msg_unpacker unpacker;
msg_unpacker_init (&unpacker, self->input.str, self->input.len);
struct msg_unpacker unpacker =
msg_unpacker_make (self->input.str, self->input.len);
switch (self->state)
{
@@ -1005,7 +1002,7 @@ fcgi_nv_parser_push (struct fcgi_nv_parser *self, const void *data, size_t len)
if (self->input.len < self->name_len)
return;
self->name = xmalloc (self->name_len + 1);
self->name = (char *) xmalloc (self->name_len + 1);
self->name[self->name_len] = '\0';
memcpy (self->name, self->input.str, self->name_len);
str_remove_slice (&self->input, 0, self->name_len);
@@ -1015,7 +1012,7 @@ fcgi_nv_parser_push (struct fcgi_nv_parser *self, const void *data, size_t len)
if (self->input.len < self->value_len)
return;
self->value = xmalloc (self->value_len + 1);
self->value = (char *) xmalloc (self->value_len + 1);
self->value[self->value_len] = '\0';
memcpy (self->value, self->input.str, self->value_len);
str_remove_slice (&self->input, 0, self->value_len);
@@ -1049,12 +1046,11 @@ fcgi_nv_convert_len (size_t len, struct str *output)
static void
fcgi_nv_convert (struct str_map *map, struct str *output)
{
struct str_map_iter iter;
str_map_iter_init (&iter, map);
struct str_map_iter iter = str_map_iter_make (map);
while (str_map_iter_next (&iter))
{
const char *name = iter.link->key;
const char *value = iter.link->data;
const char *value = (const char *) iter.link->data;
size_t name_len = iter.link->key_length;
size_t value_len = strlen (value);
@@ -1089,8 +1085,7 @@ ws_encode_response_key (const char *key)
SHA1 ((unsigned char *) response_key, strlen (response_key), hash);
free (response_key);
struct str base64;
str_init (&base64);
struct str base64 = str_make ();
base64_encode (hash, sizeof hash, &base64);
return str_steal (&base64);
}
@@ -1168,11 +1163,10 @@ struct ws_parser
void *user_data; ///< User data for callbacks
};
static void
ws_parser_init (struct ws_parser *self)
static struct ws_parser
ws_parser_make (void)
{
memset (self, 0, sizeof *self);
str_init (&self->input);
return (struct ws_parser) { .input = str_make () };
}
static void
@@ -1200,8 +1194,10 @@ ws_parser_unmask (char *payload, uint64_t len, uint32_t mask)
{
case 3:
payload[end + 2] ^= (mask >> 8) & 0xFF;
// Fall-through
case 2:
payload[end + 1] ^= (mask >> 16) & 0xFF;
// Fall-through
case 1:
payload[end ] ^= (mask >> 24) & 0xFF;
}
@@ -1213,8 +1209,8 @@ ws_parser_push (struct ws_parser *self, const void *data, size_t len)
bool success = false;
str_append_data (&self->input, data, len);
struct msg_unpacker unpacker;
msg_unpacker_init (&unpacker, self->input.str, self->input.len);
struct msg_unpacker unpacker =
msg_unpacker_make (self->input.str, self->input.len);
while (true)
switch (self->state)
@@ -1231,7 +1227,7 @@ ws_parser_push (struct ws_parser *self, const void *data, size_t len)
self->reserved_1 = (u8 >> 6) & 1;
self->reserved_2 = (u8 >> 5) & 1;
self->reserved_3 = (u8 >> 4) & 1;
self->opcode = u8 & 15;
self->opcode = (enum ws_opcode) (u8 & 15);
(void) msg_unpacker_u8 (&unpacker, &u8);
self->is_masked = (u8 >> 7) & 1;
@@ -1275,7 +1271,7 @@ ws_parser_push (struct ws_parser *self, const void *data, size_t len)
case WS_PARSER_PAYLOAD:
// Move the buffer so that payload data is at the front
str_remove_slice (&self->input, 0, unpacker.offset);
msg_unpacker_init (&unpacker, self->input.str, self->input.len);
unpacker = msg_unpacker_make (self->input.str, self->input.len);
if (self->input.len < self->payload_len)
goto need_data;
@@ -1418,21 +1414,19 @@ struct mpd_client
static void mpd_client_reset (struct mpd_client *self);
static void mpd_client_destroy_connector (struct mpd_client *self);
static void
mpd_client_init (struct mpd_client *self, struct poller *poller)
static struct mpd_client
mpd_client_make (struct poller *poller)
{
memset (self, 0, sizeof *self);
self->poller = poller;
self->socket = -1;
str_init (&self->read_buffer);
str_init (&self->write_buffer);
strv_init (&self->data);
poller_fd_init (&self->socket_event, poller, -1);
poller_timer_init (&self->timeout_timer, poller);
return (struct mpd_client)
{
.poller = poller,
.socket = -1,
.read_buffer = str_make (),
.write_buffer = str_make (),
.socket_event = poller_fd_make (poller, -1),
.timeout_timer = poller_timer_make (poller),
.data = strv_make (),
};
}
static void
@@ -1470,7 +1464,7 @@ mpd_client_reset (struct mpd_client *self)
{
// Get rid of all pending tasks to release resources etc.
strv_reset (&self->data);
struct mpd_response aborted = { .message_text = "Disconnected" };
struct mpd_response aborted = { .message_text = (char *) "Disconnected" };
while (self->tasks)
mpd_client_dispatch (self, &aborted);
@@ -1530,7 +1524,8 @@ mpd_client_parse_response (const char *p, struct mpd_response *response)
if (errno != 0 || end == p)
return false;
p = end;
if (*p++ != ']' || *p++ != ' ' || *p++ != '{' || !(end = strchr (p, '}')))
if (*p++ != ']' || *p++ != ' ' || *p++ != '{'
|| !(end = (char *) strchr (p, '}')))
return false;
response->current_command = xstrndup (p, end - p);
@@ -1573,13 +1568,12 @@ mpd_client_parse_line (struct mpd_client *self, const char *line)
if (!strcmp (line, "list_OK"))
strv_append_owned (&self->data, NULL);
else if (mpd_client_parse_response (line, &response))
{
mpd_client_dispatch (self, &response);
free (response.current_command);
free (response.message_text);
}
else
strv_append (&self->data, line);
free (response.current_command);
free (response.message_text);
return true;
}
@@ -1631,7 +1625,7 @@ mpd_client_on_ready (const struct pollfd *pfd, void *user_data)
{
(void) pfd;
struct mpd_client *self = user_data;
struct mpd_client *self = (struct mpd_client *) user_data;
if (socket_io_try_read (self->socket, &self->read_buffer) != SOCKET_IO_OK
|| !mpd_client_process_input (self)
|| socket_io_try_write (self->socket, &self->write_buffer) != SOCKET_IO_OK)
@@ -1683,7 +1677,8 @@ mpd_client_add_task
// later flushed if an early ACK or OK arrives).
hard_assert (!self->in_list);
struct mpd_client_task *task = xcalloc (1, sizeof *self);
struct mpd_client_task *task =
(struct mpd_client_task *) xcalloc (1, sizeof *self);
task->callback = cb;
task->user_data = user_data;
LIST_APPEND_WITH_TAIL (self->tasks, self->tasks_tail, task);
@@ -1694,8 +1689,9 @@ mpd_client_add_task
static void mpd_client_send_command
(struct mpd_client *self, const char *command, ...) ATTRIBUTE_SENTINEL;
/// Avoid calling this method directly if you don't want things to explode
static void
mpd_client_send_commandv (struct mpd_client *self, char **commands)
mpd_client_send_command_raw (struct mpd_client *self, const char *raw)
{
// Automatically interrupt idle mode
if (self->idling)
@@ -1707,35 +1703,37 @@ mpd_client_send_commandv (struct mpd_client *self, char **commands)
mpd_client_send_command (self, "noidle", NULL);
}
struct str line;
str_init (&line);
for (; *commands; commands++)
{
if (line.len)
str_append_c (&line, ' ');
if (mpd_client_must_quote (*commands))
mpd_client_quote (*commands, &line);
else
str_append (&line, *commands);
}
if (self->on_io_hook)
self->on_io_hook (self->user_data, true, line.str);
self->on_io_hook (self->user_data, true, raw);
str_append_c (&line, '\n');
str_append_str (&self->write_buffer, &line);
str_free (&line);
str_append (&self->write_buffer, raw);
str_append_c (&self->write_buffer, '\n');
mpd_client_update_poller (self);
}
static void
mpd_client_send_commandv (struct mpd_client *self, char **fields)
{
struct str line = str_make ();
for (; *fields; fields++)
{
if (line.len)
str_append_c (&line, ' ');
if (mpd_client_must_quote (*fields))
mpd_client_quote (*fields, &line);
else
str_append (&line, *fields);
}
mpd_client_send_command_raw (self, line.str);
str_free (&line);
}
static void
mpd_client_send_command (struct mpd_client *self, const char *command, ...)
{
struct strv v;
strv_init (&v);
struct strv v = strv_make ();
va_list ap;
va_start (ap, command);
@@ -1749,6 +1747,8 @@ mpd_client_send_command (struct mpd_client *self, const char *command, ...)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/// "On success for all commands, OK is returned. If a command fails, no more
/// commands are executed and the appropriate ACK error is returned"
static void
mpd_client_list_begin (struct mpd_client *self)
{
@@ -1796,7 +1796,7 @@ mpd_client_on_idle_return (const struct mpd_response *response,
{
(void) response;
struct mpd_client *self = user_data;
struct mpd_client *self = (struct mpd_client *) user_data;
unsigned subsystems = 0;
for (size_t i = 0; i < data->len; i++)
{
@@ -1819,7 +1819,7 @@ static void mpd_client_idle (struct mpd_client *self, unsigned subsystems);
static void
mpd_client_on_timeout (void *user_data)
{
struct mpd_client *self = user_data;
struct mpd_client *self = (struct mpd_client *) user_data;
// Abort and immediately restore the current idle so that MPD doesn't
// disconnect us, even though the documentation says this won't happen.
@@ -1835,9 +1835,7 @@ mpd_client_idle (struct mpd_client *self, unsigned subsystems)
{
hard_assert (!self->in_list);
struct strv v;
strv_init (&v);
struct strv v = strv_make ();
strv_append (&v, "idle");
for (size_t i = 0; i < N_ELEMENTS (mpd_subsystem_names); i++)
if (subsystems & (1 << i))
@@ -1864,7 +1862,7 @@ mpd_client_finish_connection (struct mpd_client *self, int socket)
self->socket = socket;
self->state = MPD_CONNECTED;
poller_fd_init (&self->socket_event, self->poller, self->socket);
self->socket_event = poller_fd_make (self->poller, self->socket);
self->socket_event.dispatcher = mpd_client_on_ready;
self->socket_event.user_data = self;
@@ -1889,7 +1887,7 @@ mpd_client_destroy_connector (struct mpd_client *self)
static void
mpd_client_on_connector_failure (void *user_data)
{
struct mpd_client *self = user_data;
struct mpd_client *self = (struct mpd_client *) user_data;
mpd_client_destroy_connector (self);
mpd_client_fail (self);
}
@@ -1900,7 +1898,7 @@ mpd_client_on_connector_connected
{
(void) host;
struct mpd_client *self = user_data;
struct mpd_client *self = (struct mpd_client *) user_data;
mpd_client_destroy_connector (self);
mpd_client_finish_connection (self, socket);
}
@@ -1944,7 +1942,8 @@ mpd_client_connect (struct mpd_client *self, const char *address,
if (strchr (address, '/'))
return mpd_client_connect_unix (self, address, e);
struct connector *connector = xmalloc (sizeof *connector);
struct connector *connector =
(struct connector *) xmalloc (sizeof *connector);
connector_init (connector, self->poller);
self->connector = connector;

View File

@@ -1,11 +1,10 @@
/*
* liberty-tui.c: the ultimate C unlibrary: TUI
*
* Copyright (c) 2016 - 2017, Přemysl Janouch <p.janouch@gmail.com>
* Copyright (c) 2016 - 2017, Přemysl Eric Janouch <p@janouch.name>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
* purpose with or without fee is hereby granted.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
@@ -61,8 +60,7 @@ struct attrs
static struct attrs
attrs_decode (const char *value)
{
struct strv v;
strv_init (&v);
struct strv v = strv_make ();
cstr_split (value, " ", true, &v);
int colors = 0;
@@ -110,11 +108,12 @@ struct row_buffer
int total_width; ///< Total width of all characters
};
static void
row_buffer_init (struct row_buffer *self)
static struct row_buffer
row_buffer_make (void)
{
memset (self, 0, sizeof *self);
ARRAY_INIT_SIZED (self->chars, 256);
struct row_buffer self = {};
ARRAY_INIT_SIZED (self.chars, 256);
return self;
}
static void

797
liberty.c

File diff suppressed because it is too large Load Diff

View File

@@ -14,7 +14,7 @@ fakeroot sh -e <<-EOF
url = $url
builddate = \`date -u +%s\`
packager = $author
size = \`du -sb --apparent-size | cut -f1\`
size = \`du -sb | cut -f1\`
arch = $arch
END
cd "$wd" && tar cJf "../$target" .PKGINFO *

View File

@@ -61,13 +61,13 @@ siphash (const unsigned char key[16], const unsigned char *m, size_t len)
switch (len - blocks)
{
case 7: last7 |= (uint64_t) m[i + 6] << 48;
case 6: last7 |= (uint64_t) m[i + 5] << 40;
case 5: last7 |= (uint64_t) m[i + 4] << 32;
case 4: last7 |= (uint64_t) m[i + 3] << 24;
case 3: last7 |= (uint64_t) m[i + 2] << 16;
case 2: last7 |= (uint64_t) m[i + 1] << 8;
case 1: last7 |= (uint64_t) m[i + 0] ;
case 7: last7 |= (uint64_t) m[i + 6] << 48; // Fall-through
case 6: last7 |= (uint64_t) m[i + 5] << 40; // Fall-through
case 5: last7 |= (uint64_t) m[i + 4] << 32; // Fall-through
case 4: last7 |= (uint64_t) m[i + 3] << 24; // Fall-through
case 3: last7 |= (uint64_t) m[i + 2] << 16; // Fall-through
case 2: last7 |= (uint64_t) m[i + 1] << 8; // Fall-through
case 1: last7 |= (uint64_t) m[i + 0] ; // Fall-through
default:;
};
v3 ^= last7;

297
tests/fuzz.c Normal file
View File

@@ -0,0 +1,297 @@
/*
* tests/fuzz.c
*
* Copyright (c) 2020, Přemysl Eric Janouch <p@janouch.name>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#define PROGRAM_NAME "fuzz"
#define PROGRAM_VERSION "0"
#define LIBERTY_WANT_SSL
// The MPD client is a full wrapper and needs the network
#define LIBERTY_WANT_POLLER
#define LIBERTY_WANT_ASYNC
#define LIBERTY_WANT_PROTO_IRC
#define LIBERTY_WANT_PROTO_HTTP
#define LIBERTY_WANT_PROTO_SCGI
#define LIBERTY_WANT_PROTO_FASTCGI
#define LIBERTY_WANT_PROTO_WS
#define LIBERTY_WANT_PROTO_MPD
#include "../liberty.c"
#include "../liberty-tui.c"
static bool
app_is_character_in_locale (ucs4_t ch)
{
return ch < 128;
}
// --- UTF-8 -------------------------------------------------------------------
static void
test_utf8_validate (const uint8_t *data, size_t size)
{
utf8_validate ((const char *) data, size);
}
// --- Base 64 -----------------------------------------------------------------
static void
test_base64_decode (const uint8_t *data, size_t size)
{
struct str wrap = str_make ();
str_append_data (&wrap, data, size);
struct str out = str_make ();
base64_decode (wrap.str, true /* ignore_ws */, &out);
str_free (&out);
str_free (&wrap);
}
// --- IRC ---------------------------------------------------------------------
static void
test_irc_parse_message (const uint8_t *data, size_t size)
{
struct str wrap = str_make ();
str_append_data (&wrap, data, size);
struct irc_message msg;
irc_parse_message (&msg, wrap.str);
irc_free_message (&msg);
str_free (&wrap);
}
// --- HTTP --------------------------------------------------------------------
static void
test_http_parse_media_type (const uint8_t *data, size_t size)
{
struct str wrap = str_make ();
str_append_data (&wrap, data, size);
char *type = NULL;
char *subtype = NULL;
struct str_map parameters = str_map_make (free);
http_parse_media_type (wrap.str, &type, &subtype, &parameters);
free (type);
free (subtype);
str_map_free (&parameters);
str_free (&wrap);
}
static void
test_http_parse_upgrade (const uint8_t *data, size_t size)
{
struct str wrap = str_make ();
str_append_data (&wrap, data, size);
struct http_protocol *protocols = NULL;
http_parse_upgrade (wrap.str, &protocols);
LIST_FOR_EACH (struct http_protocol, iter, protocols)
http_protocol_destroy (iter);
str_free (&wrap);
}
// --- SCGI --------------------------------------------------------------------
static bool
test_scgi_parser_on_headers_read (void *user_data)
{
(void) user_data;
return true;
}
static bool
test_scgi_parser_on_content (void *user_data, const void *data, size_t len)
{
(void) user_data;
(void) data;
(void) len;
return true;
}
static void
test_scgi_parser_push (const uint8_t *data, size_t size)
{
struct scgi_parser parser = scgi_parser_make ();
parser.on_headers_read = test_scgi_parser_on_headers_read;
parser.on_content = test_scgi_parser_on_content;
scgi_parser_push (&parser, data, size, NULL);
scgi_parser_free (&parser);
}
// --- WebSockets --------------------------------------------------------------
static bool
test_ws_parser_on_frame_header (void *user_data, const struct ws_parser *self)
{
(void) user_data;
(void) self;
return true;
}
static bool
test_ws_parser_on_frame (void *user_data, const struct ws_parser *self)
{
(void) user_data;
(void) self;
return true;
}
static void
test_ws_parser_push (const uint8_t *data, size_t size)
{
struct ws_parser parser = ws_parser_make ();
parser.on_frame_header = test_ws_parser_on_frame_header;
parser.on_frame = test_ws_parser_on_frame;
ws_parser_push (&parser, data, size);
ws_parser_free (&parser);
}
// --- FastCGI -----------------------------------------------------------------
static bool
test_fcgi_parser_on_message (const struct fcgi_parser *parser, void *user_data)
{
(void) parser;
(void) user_data;
return true;
}
static void
test_fcgi_parser_push (const uint8_t *data, size_t size)
{
struct fcgi_parser parser = fcgi_parser_make ();
parser.on_message = test_fcgi_parser_on_message;
fcgi_parser_push (&parser, data, size);
fcgi_parser_free (&parser);
}
static void
test_fcgi_nv_parser_push (const uint8_t *data, size_t size)
{
struct str_map values = str_map_make (free);
struct fcgi_nv_parser nv_parser = fcgi_nv_parser_make ();
nv_parser.output = &values;
fcgi_nv_parser_push (&nv_parser, data, size);
fcgi_nv_parser_free (&nv_parser);
str_map_free (&values);
}
// --- Config ------------------------------------------------------------------
static void
test_config_item_parse (const uint8_t *data, size_t size)
{
struct config_item *item =
config_item_parse ((const char *) data, size, false, NULL);
if (item)
config_item_destroy (item);
}
// --- TUI ---------------------------------------------------------------------
static void
test_attrs_decode (const uint8_t *data, size_t size)
{
struct str wrap = str_make ();
str_append_data (&wrap, data, size);
attrs_decode (wrap.str);
str_free (&wrap);
}
// --- MPD ---------------------------------------------------------------------
static void
test_mpd_client_process_input (const uint8_t *data, size_t size)
{
struct poller poller;
poller_init (&poller);
struct mpd_client mpd = mpd_client_make (&poller);
str_append_data (&mpd.read_buffer, data, size);
mpd_client_process_input (&mpd);
mpd_client_free (&mpd);
poller_free (&poller);
}
// --- Main --------------------------------------------------------------------
typedef void (*fuzz_test_fn) (const uint8_t *data, size_t size);
static fuzz_test_fn generator = NULL;
void
LLVMFuzzerTestOneInput (const uint8_t *data, size_t size)
{
generator (data, size);
}
int
LLVMFuzzerInitialize (int *argcp, char ***argvp)
{
struct str_map targets = str_map_make (NULL);
#define REGISTER(name) str_map_set (&targets, #name, test_ ## name);
REGISTER (utf8_validate)
REGISTER (base64_decode)
REGISTER (irc_parse_message)
REGISTER (http_parse_media_type)
REGISTER (http_parse_upgrade)
REGISTER (scgi_parser_push)
REGISTER (ws_parser_push)
REGISTER (fcgi_parser_push)
REGISTER (fcgi_nv_parser_push)
REGISTER (config_item_parse)
REGISTER (attrs_decode)
REGISTER (mpd_client_process_input)
char **argv = *argvp, *option = "-test=", *name = NULL;
for (int i = 1; i < *argcp; i++)
if (!strncmp (argv[i], option, strlen (option)))
{
name = argv[i] + strlen (option);
memmove (argv + i, argv + i + 1, (*argcp - i) * sizeof *argv);
(*argcp)--;
}
if (!name)
{
struct str_map_iter iter = str_map_iter_make (&targets);
while (str_map_iter_next (&iter))
printf ("%s\n", iter.link->key);
exit (EXIT_FAILURE);
}
if (!(generator = str_map_find (&targets, name)))
{
fprintf (stderr, "Unknown test: %s\n", name);
exit (EXIT_FAILURE);
}
str_map_free (&targets);
return 0;
}

View File

@@ -1,11 +1,10 @@
/*
* tests/liberty.c
*
* Copyright (c) 2015 - 2016, Přemysl Janouch <p.janouch@gmail.com>
* Copyright (c) 2015 - 2016, Přemysl Eric Janouch <p@janouch.name>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
* purpose with or without fee is hereby granted.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
@@ -158,9 +157,7 @@ test_list_with_tail (void)
static void
test_strv (void)
{
struct strv v;
strv_init (&v);
struct strv v = strv_make ();
strv_append_owned (&v, xstrdup ("xkcd"));
strv_reset (&v);
@@ -168,8 +165,7 @@ test_strv (void)
{ "123", "456", "a", "bc", "def", "ghij", "klmno", "pqrstu" };
// Add the first two items via another vector
struct strv w;
strv_init (&w);
struct strv w = strv_make ();
strv_append_args (&w, a[0], a[1], NULL);
strv_append_vector (&v, w.vector);
strv_free (&w);
@@ -196,15 +192,13 @@ test_str (void)
{
uint8_t x[] = { 0x12, 0x34, 0x56, 0x78, 0x11, 0x22, 0x33, 0x44 };
struct str s;
str_init (&s);
struct str s = str_make ();
str_reserve (&s, MEGA);
str_append_data (&s, x, sizeof x);
str_remove_slice (&s, 4, 4);
soft_assert (s.len == 4);
struct str t;
str_init (&t);
struct str t = str_make ();
str_append_str (&t, &s);
str_append (&t, "abc");
str_append_c (&t, 'd');
@@ -265,10 +259,8 @@ static void
test_str_map (void)
{
// Put two reference counted objects in the map under case-insensitive keys
struct str_map m;
str_map_init (&m);
struct str_map m = str_map_make (free_counter);
m.key_xfrm = tolower_ascii_strxfrm;
m.free = free_counter;
int *a = make_counter ();
int *b = make_counter ();
@@ -282,8 +274,7 @@ test_str_map (void)
soft_assert (str_map_find (&m, "DEFghi") == b);
// Check that we can iterate over both of them
struct str_map_iter iter;
str_map_iter_init (&iter, &m);
struct str_map_iter iter = str_map_iter_make (&m);
bool met_a = false;
bool met_b = false;
@@ -310,8 +301,7 @@ test_str_map (void)
free_counter (b);
// Iterator test with a high number of items
str_map_init (&m);
m.free = free;
m = str_map_make (free);
for (size_t i = 0; i < 100 * 100; i++)
{
@@ -319,8 +309,7 @@ test_str_map (void)
str_map_set (&m, x, x);
}
struct str_map_unset_iter unset_iter;
str_map_unset_iter_init (&unset_iter, &m);
struct str_map_unset_iter unset_iter = str_map_unset_iter_make (&m);
while ((str_map_unset_iter_next (&unset_iter)))
{
unsigned long x;
@@ -337,14 +326,19 @@ test_str_map (void)
static void
test_utf8 (void)
{
const char valid [] = "2H₂ + O₂ ⇌ 2H₂O, R = 4.7 kΩ, ⌀ 200 mm";
const char invalid[] = "\xf0\x90\x28\xbc";
soft_assert ( utf8_validate (valid, sizeof valid));
soft_assert (!utf8_validate (invalid, sizeof invalid));
const char *full = "\xc5\x99", *partial = full, *empty = full;
soft_assert (utf8_decode (&full, 2) == 0x0159);
soft_assert (utf8_decode (&partial, 1) == -2);
soft_assert (utf8_decode (&empty, 0) == -1);
struct utf8_iter iter;
utf8_iter_init (&iter, "fóọ");
const char valid[] = "2H₂ + O₂ ⇌ 2H₂O, R = 4.7 kΩ, ⌀ 200 mm";
const char invalid_1[] = "\xf0\x90\x28\xbc";
const char invalid_2[] = "\xc0\x80";
soft_assert ( utf8_validate (valid, sizeof valid));
soft_assert (!utf8_validate (invalid_1, sizeof invalid_1));
soft_assert (!utf8_validate (invalid_2, sizeof invalid_2));
struct utf8_iter iter = utf8_iter_make ("fóọ");
size_t ch_len;
hard_assert (utf8_iter_next (&iter, &ch_len) == 'f' && ch_len == 1);
hard_assert (utf8_iter_next (&iter, &ch_len) == 0x00F3 && ch_len == 2);
@@ -358,8 +352,8 @@ test_base64 (void)
for (size_t i = 0; i < N_ELEMENTS (data); i++)
data[i] = i;
struct str encoded; str_init (&encoded);
struct str decoded; str_init (&decoded);
struct str encoded = str_make ();
struct str decoded = str_make ();
base64_encode (data, sizeof data, &encoded);
soft_assert (base64_decode (encoded.str, false, &decoded));
@@ -430,9 +424,9 @@ test_async (void)
{
struct test_async_data data;
memset (&data, 0, sizeof data);
async_manager_init (&data.manager);
data.manager = async_manager_make ();
async_init (&data.busyloop, &data.manager);
data.busyloop = async_make (&data.manager);
data.busyloop.execute = on_busyloop_execute;
data.busyloop.destroy = on_busyloop_destroy;
async_run (&data.busyloop);
@@ -542,7 +536,7 @@ test_connector_fixture_init
// Make it so that we immediately accept all connections
poller_init (&self->poller);
poller_fd_init (&self->listening_event, &self->poller, self->listening_fd);
self->listening_event = poller_fd_make (&self->poller, self->listening_fd);
self->listening_event.dispatcher = test_connector_on_client;
self->listening_event.user_data = (poller_fd_fn) self;
poller_fd_set (&self->listening_event, POLLIN);
@@ -617,6 +611,78 @@ test_connector (const void *user_data, struct test_connector_fixture *self)
connector_free (&connector);
}
// --- Configuration -----------------------------------------------------------
static void
on_test_config_foo_change (struct config_item *item)
{
*(bool *) item->user_data = item->value.boolean;
}
static bool
test_config_validate_nonnegative
(const struct config_item *item, struct error **e)
{
if (item->type == CONFIG_ITEM_NULL)
return true;
hard_assert (item->type == CONFIG_ITEM_INTEGER);
if (item->value.integer >= 0)
return true;
error_set (e, "must be non-negative");
return false;
}
static struct config_schema g_config_test[] =
{
{ .name = "foo",
.comment = "baz",
.type = CONFIG_ITEM_BOOLEAN,
.default_ = "off",
.on_change = on_test_config_foo_change },
{ .name = "bar",
.type = CONFIG_ITEM_INTEGER,
.validate = test_config_validate_nonnegative,
.default_ = "1" },
{ .name = "foobar",
.type = CONFIG_ITEM_STRING,
.default_ = "\"qux\\x01\"" },
{}
};
static void
test_config_load (struct config_item *subtree, void *user_data)
{
config_schema_apply_to_object (g_config_test, subtree, user_data);
}
static void
test_config (void)
{
struct config config = config_make ();
bool b = true;
config_register_module (&config, "top", test_config_load, &b);
config_load (&config, config_item_object ());
config_schema_call_changed (config.root);
hard_assert (b == false);
struct config_item *invalid = config_item_integer (-1);
hard_assert (!config_item_set_from (config_item_get (config.root,
"top.bar", NULL), invalid, NULL));
config_item_destroy (invalid);
struct str s = str_make ();
config_item_write (config.root, true, &s);
struct config_item *parsed = config_item_parse (s.str, s.len, false, NULL);
hard_assert (parsed);
config_item_destroy (parsed);
str_free (&s);
config_free (&config);
}
// --- Main --------------------------------------------------------------------
int
@@ -635,6 +701,7 @@ main (int argc, char *argv[])
test_add_simple (&test, "/utf-8", NULL, test_utf8);
test_add_simple (&test, "/base64", NULL, test_base64);
test_add_simple (&test, "/async", NULL, test_async);
test_add_simple (&test, "/config", NULL, test_config);
test_add (&test, "/connector", struct test_connector_fixture, NULL,
test_connector_fixture_init,

View File

@@ -1,11 +1,10 @@
/*
* tests/proto.c
*
* Copyright (c) 2015, Přemysl Janouch <p.janouch@gmail.com>
* Copyright (c) 2015, Přemysl Eric Janouch <p@janouch.name>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
* purpose with or without fee is hereby granted.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
@@ -43,8 +42,7 @@ test_irc (void)
irc_parse_message (&msg, "@first=a\\:\\s\\r\\n\\\\;2nd "
":srv hi there :good m8 :how are you?");
struct str_map_iter iter;
str_map_iter_init (&iter, &msg.tags);
struct str_map_iter iter = str_map_iter_make (&msg.tags);
soft_assert (msg.tags.len == 2);
char *value;
@@ -79,8 +77,7 @@ test_irc (void)
static void
test_http_parser (void)
{
struct str_map parameters;
str_map_init (&parameters);
struct str_map parameters = str_map_make (free);
parameters.key_xfrm = tolower_ascii_strxfrm;
char *type = NULL;
@@ -91,9 +88,11 @@ test_http_parser (void)
soft_assert (!strcasecmp_ascii (subtype, "html"));
soft_assert (parameters.len == 1);
soft_assert (!strcmp (str_map_find (&parameters, "charset"), "utf-8"));
free (type);
free (subtype);
str_map_free (&parameters);
struct http_protocol *protocols;
struct http_protocol *protocols = NULL;
soft_assert (http_parse_upgrade ("websocket, HTTP/2.0, , ", &protocols));
soft_assert (!strcmp (protocols->name, "websocket"));
@@ -108,10 +107,20 @@ test_http_parser (void)
http_protocol_destroy (iter);
}
struct scgi_fixture
{
struct scgi_parser parser;
bool seen_headers;
bool seen_content;
};
static bool
test_scgi_parser_on_headers_read (void *user_data)
{
struct scgi_parser *parser = user_data;
struct scgi_fixture *fixture = user_data;
struct scgi_parser *parser = &fixture->parser;
fixture->seen_headers = true;
soft_assert (parser->headers.len == 4);
soft_assert (!strcmp (str_map_find (&parser->headers,
"CONTENT_LENGTH"), "27"));
@@ -127,7 +136,9 @@ test_scgi_parser_on_headers_read (void *user_data)
static bool
test_scgi_parser_on_content (void *user_data, const void *data, size_t len)
{
(void) user_data;
struct scgi_fixture *fixture = user_data;
fixture->seen_content = true;
soft_assert (!strncmp (data, "What is the answer to life?", len));
return true;
}
@@ -135,11 +146,12 @@ test_scgi_parser_on_content (void *user_data, const void *data, size_t len)
static void
test_scgi_parser (void)
{
struct scgi_parser parser;
scgi_parser_init (&parser);
parser.on_headers_read = test_scgi_parser_on_headers_read;
parser.on_content = test_scgi_parser_on_content;
parser.user_data = &parser;
struct scgi_fixture fixture = { scgi_parser_make(), false, false };
struct scgi_parser *parser = &fixture.parser;
parser->on_headers_read = test_scgi_parser_on_headers_read;
parser->on_content = test_scgi_parser_on_content;
parser->user_data = &fixture;
// This is an example straight from the specification
const char example[] =
@@ -151,8 +163,9 @@ test_scgi_parser (void)
","
"What is the answer to life?";
soft_assert (scgi_parser_push (&parser, example, sizeof example, NULL));
scgi_parser_free (&parser);
soft_assert (scgi_parser_push (parser, example, sizeof example, NULL));
soft_assert (fixture.seen_headers && fixture.seen_content);
scgi_parser_free (parser);
}
static bool
@@ -181,8 +194,7 @@ test_websockets (void)
soft_assert (!strcmp (accept, "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="));
free (accept);
struct ws_parser parser;
ws_parser_init (&parser);
struct ws_parser parser = ws_parser_make ();
parser.on_frame_header = test_websockets_on_frame_header;
parser.on_frame = test_websockets_on_frame;
parser.user_data = &parser;