Compare commits

..

29 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
15 changed files with 783 additions and 243 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,11 +2,11 @@ project (liberty C)
cmake_minimum_required (VERSION 2.8.5) cmake_minimum_required (VERSION 2.8.5)
# Moar warnings # 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 # -Wunused-function is pretty annoying here, as everything is static
set (wdisabled "-Wno-unused-function -Wno-implicit-fallthrough") set (wdisabled "-Wno-unused-function -Wno-implicit-fallthrough")
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Wall -Wextra ${wdisabled}") set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Wall -Wextra ${wdisabled}")
endif ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUC) endif ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUCC)
# Dependencies # Dependencies
set (CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) set (CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)

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 Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above purpose with or without fee is hereby granted.
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 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 Contributing and Support
------------------------ ------------------------
Use this project's GitHub to report any bugs, request features, or submit pull Use https://git.janouch.name/p/liberty to report any bugs, request features,
requests. If you want to discuss this project, or maybe just hang out with or submit pull requests. `git send-email` is tolerated. If you want to discuss
the developer, feel free to join me at irc://irc.janouch.name, channel #dev. the project, feel free to join me at ircs://irc.janouch.name, channel #dev.
Bitcoin donations: 12r5uEWEgcHC46xd64tt3hHt9EUvYYDHe9 Bitcoin donations are accepted at: 12r5uEWEgcHC46xd64tt3hHt9EUvYYDHe9
License License
------- -------
'liberty' is written by Přemysl Janouch <p.janouch@gmail.com>. 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.
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

View File

@@ -1,17 +1,17 @@
# Public Domain # Public Domain
find_package (PkgConfig REQUIRED) find_package (PkgConfig REQUIRED)
pkg_check_modules (NCURSESW QUIET ncursesw) pkg_check_modules (Ncursesw QUIET ncursesw)
# OpenBSD doesn't provide a pkg-config file # OpenBSD doesn't provide a pkg-config file
set (required_vars NCURSESW_LIBRARIES) set (required_vars Ncursesw_LIBRARIES)
if (NOT NCURSESW_FOUND) if (NOT Ncursesw_FOUND)
find_library (NCURSESW_LIBRARIES NAMES ncursesw) find_library (Ncursesw_LIBRARIES NAMES ncursesw)
find_path (NCURSESW_INCLUDE_DIRS ncurses.h) find_path (Ncursesw_INCLUDE_DIRS ncurses.h)
list (APPEND required_vars NCURSESW_INCLUDE_DIRS) list (APPEND required_vars Ncursesw_INCLUDE_DIRS)
endif (NOT NCURSESW_FOUND) endif (NOT Ncursesw_FOUND)
include (FindPackageHandleStandardArgs) 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 # Public Domain
find_path (UNISTRING_INCLUDE_DIRS unistr.h) find_path (Unistring_INCLUDE_DIRS unistr.h)
find_library (UNISTRING_LIBRARIES NAMES unistring libunistring) find_library (Unistring_LIBRARIES NAMES unistring libunistring)
include (FindPackageHandleStandardArgs) include (FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS (UNISTRING DEFAULT_MSG FIND_PACKAGE_HANDLE_STANDARD_ARGS (Unistring DEFAULT_MSG
UNISTRING_INCLUDE_DIRS UNISTRING_LIBRARIES) 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

@@ -1,11 +1,10 @@
/* /*
* liberty-proto.c: the ultimate C unlibrary: protocols * 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 * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted.
* copyright notice and this permission notice appear in all copies.
* *
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
@@ -214,6 +213,7 @@ irc_fnmatch (const char *pattern, const char *string)
char x_pattern[pattern_size], x_string[string_size]; char x_pattern[pattern_size], x_string[string_size];
irc_strxfrm (x_pattern, pattern, pattern_size); irc_strxfrm (x_pattern, pattern, pattern_size);
irc_strxfrm (x_string, string, string_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); return fnmatch (x_pattern, x_string, 0);
} }
@@ -507,7 +507,7 @@ http_parse_upgrade (const char *upgrade, struct http_protocol **out)
case HTTP_T_WHITESPACE: case HTTP_T_WHITESPACE:
break; break;
case HTTP_T_TOKEN: case HTTP_T_TOKEN:
proto = xcalloc (1, sizeof *proto); proto = (struct http_protocol *) xcalloc (1, sizeof *proto);
proto->name = xstrdup (t.string.str); proto->name = xstrdup (t.string.str);
LIST_APPEND_WITH_TAIL (list, tail, proto); LIST_APPEND_WITH_TAIL (list, tail, proto);
state = STATE_SLASH; state = STATE_SLASH;
@@ -664,10 +664,11 @@ scgi_parser_push (struct scgi_parser *self,
if (digit == ':') if (digit == ':')
{ {
self->state = SCGI_READING_NAME; self->state = SCGI_READING_NAME;
str_remove_slice (&self->input, 0, 1);
break; break;
} }
if (digit < '0' || digit >= '9') if (digit < '0' || digit > '9')
return error_set (e, "invalid header netstring"); return error_set (e, "invalid header netstring");
size_t new_len = self->headers_len * 10 + (digit - '0'); size_t new_len = self->headers_len * 10 + (digit - '0');
@@ -700,6 +701,7 @@ scgi_parser_push (struct scgi_parser *self,
self->state = SCGI_READING_VALUE; self->state = SCGI_READING_VALUE;
str_remove_slice (&self->input, 0, 1); str_remove_slice (&self->input, 0, 1);
self->headers_len--;
break; break;
} }
case SCGI_READING_VALUE: case SCGI_READING_VALUE:
@@ -728,6 +730,7 @@ scgi_parser_push (struct scgi_parser *self,
} }
str_remove_slice (&self->input, 0, 1); str_remove_slice (&self->input, 0, 1);
self->headers_len--;
break; break;
} }
case SCGI_READING_CONTENT: case SCGI_READING_CONTENT:
@@ -792,7 +795,8 @@ enum fcgi_protocol_status
struct fcgi_parser; 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); (const struct fcgi_parser *parser, void *user_data);
enum fcgi_parser_state enum fcgi_parser_state
@@ -854,7 +858,7 @@ fcgi_parser_unpack_header (struct fcgi_parser *self)
str_remove_slice (&self->input, 0, unpacker.offset); 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) fcgi_parser_push (struct fcgi_parser *self, const void *data, size_t len)
{ {
// This could be made considerably faster for high-throughput applications // This could be made considerably faster for high-throughput applications
@@ -866,14 +870,14 @@ fcgi_parser_push (struct fcgi_parser *self, const void *data, size_t len)
{ {
case FCGI_READING_HEADER: case FCGI_READING_HEADER:
if (self->input.len < FCGI_HEADER_LEN) if (self->input.len < FCGI_HEADER_LEN)
return; return true;
fcgi_parser_unpack_header (self); fcgi_parser_unpack_header (self);
self->state = FCGI_READING_CONTENT; self->state = FCGI_READING_CONTENT;
break; break;
case FCGI_READING_CONTENT: case FCGI_READING_CONTENT:
if (self->input.len < self->content_length) if (self->input.len < self->content_length)
return; return true;
// Move an appropriate part of the input buffer to the content buffer // Move an appropriate part of the input buffer to the content buffer
str_reset (&self->content); str_reset (&self->content);
@@ -883,10 +887,11 @@ fcgi_parser_push (struct fcgi_parser *self, const void *data, size_t len)
break; break;
case FCGI_READING_PADDING: case FCGI_READING_PADDING:
if (self->input.len < self->padding_length) if (self->input.len < self->padding_length)
return; return true;
// Call the callback to further process the message // 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 // Remove the padding from the input buffer
str_remove_slice (&self->input, 0, self->padding_length); str_remove_slice (&self->input, 0, self->padding_length);
@@ -997,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) if (self->input.len < self->name_len)
return; return;
self->name = xmalloc (self->name_len + 1); self->name = (char *) xmalloc (self->name_len + 1);
self->name[self->name_len] = '\0'; self->name[self->name_len] = '\0';
memcpy (self->name, self->input.str, self->name_len); memcpy (self->name, self->input.str, self->name_len);
str_remove_slice (&self->input, 0, self->name_len); str_remove_slice (&self->input, 0, self->name_len);
@@ -1007,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) if (self->input.len < self->value_len)
return; return;
self->value = xmalloc (self->value_len + 1); self->value = (char *) xmalloc (self->value_len + 1);
self->value[self->value_len] = '\0'; self->value[self->value_len] = '\0';
memcpy (self->value, self->input.str, self->value_len); memcpy (self->value, self->input.str, self->value_len);
str_remove_slice (&self->input, 0, self->value_len); str_remove_slice (&self->input, 0, self->value_len);
@@ -1045,7 +1050,7 @@ fcgi_nv_convert (struct str_map *map, struct str *output)
while (str_map_iter_next (&iter)) while (str_map_iter_next (&iter))
{ {
const char *name = iter.link->key; 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 name_len = iter.link->key_length;
size_t value_len = strlen (value); size_t value_len = strlen (value);
@@ -1189,8 +1194,10 @@ ws_parser_unmask (char *payload, uint64_t len, uint32_t mask)
{ {
case 3: case 3:
payload[end + 2] ^= (mask >> 8) & 0xFF; payload[end + 2] ^= (mask >> 8) & 0xFF;
// Fall-through
case 2: case 2:
payload[end + 1] ^= (mask >> 16) & 0xFF; payload[end + 1] ^= (mask >> 16) & 0xFF;
// Fall-through
case 1: case 1:
payload[end ] ^= (mask >> 24) & 0xFF; payload[end ] ^= (mask >> 24) & 0xFF;
} }
@@ -1220,7 +1227,7 @@ ws_parser_push (struct ws_parser *self, const void *data, size_t len)
self->reserved_1 = (u8 >> 6) & 1; self->reserved_1 = (u8 >> 6) & 1;
self->reserved_2 = (u8 >> 5) & 1; self->reserved_2 = (u8 >> 5) & 1;
self->reserved_3 = (u8 >> 4) & 1; self->reserved_3 = (u8 >> 4) & 1;
self->opcode = u8 & 15; self->opcode = (enum ws_opcode) (u8 & 15);
(void) msg_unpacker_u8 (&unpacker, &u8); (void) msg_unpacker_u8 (&unpacker, &u8);
self->is_masked = (u8 >> 7) & 1; self->is_masked = (u8 >> 7) & 1;
@@ -1416,9 +1423,9 @@ mpd_client_make (struct poller *poller)
.socket = -1, .socket = -1,
.read_buffer = str_make (), .read_buffer = str_make (),
.write_buffer = str_make (), .write_buffer = str_make (),
.data = strv_make (),
.socket_event = poller_fd_make (poller, -1), .socket_event = poller_fd_make (poller, -1),
.timeout_timer = poller_timer_make (poller), .timeout_timer = poller_timer_make (poller),
.data = strv_make (),
}; };
} }
@@ -1457,7 +1464,7 @@ mpd_client_reset (struct mpd_client *self)
{ {
// Get rid of all pending tasks to release resources etc. // Get rid of all pending tasks to release resources etc.
strv_reset (&self->data); strv_reset (&self->data);
struct mpd_response aborted = { .message_text = "Disconnected" }; struct mpd_response aborted = { .message_text = (char *) "Disconnected" };
while (self->tasks) while (self->tasks)
mpd_client_dispatch (self, &aborted); mpd_client_dispatch (self, &aborted);
@@ -1517,7 +1524,8 @@ mpd_client_parse_response (const char *p, struct mpd_response *response)
if (errno != 0 || end == p) if (errno != 0 || end == p)
return false; return false;
p = end; p = end;
if (*p++ != ']' || *p++ != ' ' || *p++ != '{' || !(end = strchr (p, '}'))) if (*p++ != ']' || *p++ != ' ' || *p++ != '{'
|| !(end = (char *) strchr (p, '}')))
return false; return false;
response->current_command = xstrndup (p, end - p); response->current_command = xstrndup (p, end - p);
@@ -1560,13 +1568,12 @@ mpd_client_parse_line (struct mpd_client *self, const char *line)
if (!strcmp (line, "list_OK")) if (!strcmp (line, "list_OK"))
strv_append_owned (&self->data, NULL); strv_append_owned (&self->data, NULL);
else if (mpd_client_parse_response (line, &response)) else if (mpd_client_parse_response (line, &response))
{
mpd_client_dispatch (self, &response); mpd_client_dispatch (self, &response);
free (response.current_command);
free (response.message_text);
}
else else
strv_append (&self->data, line); strv_append (&self->data, line);
free (response.current_command);
free (response.message_text);
return true; return true;
} }
@@ -1618,7 +1625,7 @@ mpd_client_on_ready (const struct pollfd *pfd, void *user_data)
{ {
(void) pfd; (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 if (socket_io_try_read (self->socket, &self->read_buffer) != SOCKET_IO_OK
|| !mpd_client_process_input (self) || !mpd_client_process_input (self)
|| socket_io_try_write (self->socket, &self->write_buffer) != SOCKET_IO_OK) || socket_io_try_write (self->socket, &self->write_buffer) != SOCKET_IO_OK)
@@ -1670,7 +1677,8 @@ mpd_client_add_task
// later flushed if an early ACK or OK arrives). // later flushed if an early ACK or OK arrives).
hard_assert (!self->in_list); 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->callback = cb;
task->user_data = user_data; task->user_data = user_data;
LIST_APPEND_WITH_TAIL (self->tasks, self->tasks_tail, task); LIST_APPEND_WITH_TAIL (self->tasks, self->tasks_tail, task);
@@ -1739,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 static void
mpd_client_list_begin (struct mpd_client *self) mpd_client_list_begin (struct mpd_client *self)
{ {
@@ -1786,7 +1796,7 @@ mpd_client_on_idle_return (const struct mpd_response *response,
{ {
(void) response; (void) response;
struct mpd_client *self = user_data; struct mpd_client *self = (struct mpd_client *) user_data;
unsigned subsystems = 0; unsigned subsystems = 0;
for (size_t i = 0; i < data->len; i++) for (size_t i = 0; i < data->len; i++)
{ {
@@ -1809,7 +1819,7 @@ static void mpd_client_idle (struct mpd_client *self, unsigned subsystems);
static void static void
mpd_client_on_timeout (void *user_data) 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 // Abort and immediately restore the current idle so that MPD doesn't
// disconnect us, even though the documentation says this won't happen. // disconnect us, even though the documentation says this won't happen.
@@ -1877,7 +1887,7 @@ mpd_client_destroy_connector (struct mpd_client *self)
static void static void
mpd_client_on_connector_failure (void *user_data) 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_destroy_connector (self);
mpd_client_fail (self); mpd_client_fail (self);
} }
@@ -1888,7 +1898,7 @@ mpd_client_on_connector_connected
{ {
(void) host; (void) host;
struct mpd_client *self = user_data; struct mpd_client *self = (struct mpd_client *) user_data;
mpd_client_destroy_connector (self); mpd_client_destroy_connector (self);
mpd_client_finish_connection (self, socket); mpd_client_finish_connection (self, socket);
} }
@@ -1932,7 +1942,8 @@ mpd_client_connect (struct mpd_client *self, const char *address,
if (strchr (address, '/')) if (strchr (address, '/'))
return mpd_client_connect_unix (self, address, e); 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); connector_init (connector, self->poller);
self->connector = connector; self->connector = connector;

View File

@@ -1,11 +1,10 @@
/* /*
* liberty-tui.c: the ultimate C unlibrary: TUI * 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 * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted.
* copyright notice and this permission notice appear in all copies.
* *
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF

406
liberty.c
View File

@@ -1,11 +1,10 @@
/* /*
* liberty.c: the ultimate C unlibrary * liberty.c: the ultimate C unlibrary
* *
* Copyright (c) 2014 - 2016, 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 * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted.
* copyright notice and this permission notice appear in all copies.
* *
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
@@ -38,6 +37,7 @@
#include <sys/wait.h> #include <sys/wait.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/uio.h>
#include <fcntl.h> #include <fcntl.h>
#include <poll.h> #include <poll.h>
#include <signal.h> #include <signal.h>
@@ -117,6 +117,10 @@ extern char **environ;
#define CONTAINER_OF(pointer, type, member) \ #define CONTAINER_OF(pointer, type, member) \
((type *) ((char *) pointer - offsetof (type, member))) ((type *) ((char *) pointer - offsetof (type, member)))
const char *liberty =
"They who can give up essential liberty to obtain a little "
"temporary safety deserve neither liberty nor safety.";
// --- Logging ----------------------------------------------------------------- // --- Logging -----------------------------------------------------------------
static void static void
@@ -284,7 +288,7 @@ xreallocarray (void *o, size_t n, size_t m)
static char * static char *
xstrdup (const char *s) xstrdup (const char *s)
{ {
return strcpy (xmalloc (strlen (s) + 1), s); return strcpy ((char *) xmalloc (strlen (s) + 1), s);
} }
static char * static char *
@@ -294,7 +298,7 @@ xstrndup (const char *s, size_t n)
if (n > size) if (n > size)
n = size; n = size;
char *copy = xmalloc (n + 1); char *copy = (char *) xmalloc (n + 1);
memcpy (copy, s, n); memcpy (copy, s, n);
copy[n] = '\0'; copy[n] = '\0';
return copy; return copy;
@@ -307,14 +311,15 @@ xstrndup (const char *s, size_t n)
#define ARRAY(type, name) type *name; size_t name ## _len, name ## _alloc; #define ARRAY(type, name) type *name; size_t name ## _len, name ## _alloc;
#define ARRAY_INIT_SIZED(a, n) \ #define ARRAY_INIT_SIZED(a, n) \
BLOCK_START \ BLOCK_START \
(a) = xcalloc (sizeof *(a), (a ## _alloc) = (n)); \ (a) = (type *) xcalloc (sizeof *(a), (a ## _alloc) = (n)); \
(a ## _len) = 0; \ (a ## _len) = 0; \
BLOCK_END BLOCK_END
#define ARRAY_INIT(a) ARRAY_INIT_SIZED (a, 16) #define ARRAY_INIT(a) ARRAY_INIT_SIZED (a, 16)
#define ARRAY_RESERVE(a, n) \ #define ARRAY_RESERVE(a, n) \
BLOCK_START \ BLOCK_START \
while ((a ## _alloc) - (a ## _len) < n) \ while ((a ## _alloc) - (a ## _len) < n) \
(a) = xreallocarray ((a), sizeof *(a), (a ## _alloc) <<= 1); \ (a) = (type *) xreallocarray ((a), \
sizeof *(a), (a ## _alloc) <<= 1); \
BLOCK_END BLOCK_END
// --- Double-linked list helpers ---------------------------------------------- // --- Double-linked list helpers ----------------------------------------------
@@ -389,7 +394,7 @@ strv_make (void)
struct strv self; struct strv self;
self.alloc = 4; self.alloc = 4;
self.len = 0; self.len = 0;
self.vector = xcalloc (sizeof *self.vector, self.alloc); self.vector = (char **) xcalloc (sizeof *self.vector, self.alloc);
return self; return self;
} }
@@ -416,7 +421,7 @@ strv_append_owned (struct strv *self, char *s)
{ {
self->vector[self->len] = s; self->vector[self->len] = s;
if (++self->len >= self->alloc) if (++self->len >= self->alloc)
self->vector = xreallocarray (self->vector, self->vector = (char **) xreallocarray (self->vector,
sizeof *self->vector, (self->alloc <<= 1)); sizeof *self->vector, (self->alloc <<= 1));
self->vector[self->len] = NULL; self->vector[self->len] = NULL;
} }
@@ -489,7 +494,7 @@ str_make (void)
struct str self; struct str self;
self.alloc = 16; self.alloc = 16;
self.len = 0; self.len = 0;
self.str = strcpy (xmalloc (self.alloc), ""); self.str = strcpy ((char *) xmalloc (self.alloc), "");
return self; return self;
} }
@@ -526,7 +531,7 @@ str_reserve (struct str *self, size_t n)
while (new_alloc <= self->len + n) while (new_alloc <= self->len + n)
new_alloc <<= 1; new_alloc <<= 1;
if (new_alloc != self->alloc) if (new_alloc != self->alloc)
self->str = xrealloc (self->str, (self->alloc = new_alloc)); self->str = (char *) xrealloc (self->str, (self->alloc = new_alloc));
} }
static void static void
@@ -605,7 +610,7 @@ str_remove_slice (struct str *self, size_t start, size_t length)
// Shrink the string if the allocation becomes way too large // Shrink the string if the allocation becomes way too large
if (self->alloc >= STR_SHRINK_THRESHOLD && self->len < (self->alloc >> 2)) if (self->alloc >= STR_SHRINK_THRESHOLD && self->len < (self->alloc >> 2))
self->str = xrealloc (self->str, self->alloc >>= 2); self->str = (char *) xrealloc (self->str, self->alloc >>= 2);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -619,7 +624,11 @@ str_pack_u8 (struct str *self, uint8_t x)
static void static void
str_pack_u16 (struct str *self, uint16_t x) str_pack_u16 (struct str *self, uint16_t x)
{ {
uint8_t tmp[2] = { x >> 8, x }; uint8_t tmp[2] =
{
(uint8_t) (x >> 8),
(uint8_t) x
};
str_append_data (self, tmp, sizeof tmp); str_append_data (self, tmp, sizeof tmp);
} }
@@ -627,7 +636,13 @@ static void
str_pack_u32 (struct str *self, uint32_t x) str_pack_u32 (struct str *self, uint32_t x)
{ {
uint32_t u = x; uint32_t u = x;
uint8_t tmp[4] = { u >> 24, u >> 16, u >> 8, u }; uint8_t tmp[4] =
{
(uint8_t) (u >> 24),
(uint8_t) (u >> 16),
(uint8_t) (u >> 8),
(uint8_t) u
};
str_append_data (self, tmp, sizeof tmp); str_append_data (self, tmp, sizeof tmp);
} }
@@ -635,7 +650,16 @@ static void
str_pack_u64 (struct str *self, uint64_t x) str_pack_u64 (struct str *self, uint64_t x)
{ {
uint8_t tmp[8] = uint8_t tmp[8] =
{ x >> 56, x >> 48, x >> 40, x >> 32, x >> 24, x >> 16, x >> 8, x }; {
(uint8_t) (x >> 56),
(uint8_t) (x >> 48),
(uint8_t) (x >> 40),
(uint8_t) (x >> 32),
(uint8_t) (x >> 24),
(uint8_t) (x >> 16),
(uint8_t) (x >> 8),
(uint8_t) x
};
str_append_data (self, tmp, sizeof tmp); str_append_data (self, tmp, sizeof tmp);
} }
@@ -669,8 +693,8 @@ error_set (struct error **e, const char *message, ...)
hard_assert (size >= 0); hard_assert (size >= 0);
struct error *tmp = xmalloc (sizeof *tmp); struct error *tmp = (struct error *) xmalloc (sizeof *tmp);
tmp->message = xmalloc (size + 1); tmp->message = (char *) xmalloc (size + 1);
va_start (ap, message); va_start (ap, message);
size = vsnprintf (tmp->message, size + 1, message, ap); size = vsnprintf (tmp->message, size + 1, message, ap);
@@ -728,6 +752,21 @@ set_blocking (int fd, bool blocking)
return prev; return prev;
} }
static bool
xwrite (int fd, const char *data, size_t len, struct error **e)
{
size_t written = 0;
while (written < len)
{
ssize_t res = write (fd, data + written, len - written);
if (res >= 0)
written += res;
else if (errno != EINTR)
return error_set (e, "%s", strerror (errno));
}
return true;
}
static void static void
xclose (int fd) xclose (int fd)
{ {
@@ -760,7 +799,7 @@ random_bytes (void *output, size_t len, struct error **e)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static unsigned char g_siphash_key[16] = "SipHash 2-4 key!"; static unsigned char g_siphash_key[16] = "SipHash 2-4 key" /* \0 */;
static inline void static inline void
siphash_wrapper_randomize (void) siphash_wrapper_randomize (void)
@@ -772,7 +811,7 @@ siphash_wrapper_randomize (void)
static inline uint64_t static inline uint64_t
siphash_wrapper (const void *m, size_t len) siphash_wrapper (const void *m, size_t len)
{ {
return siphash (g_siphash_key, m, len); return siphash (g_siphash_key, (const unsigned char *) m, len);
} }
// --- String hash map --------------------------------------------------------- // --- String hash map ---------------------------------------------------------
@@ -814,7 +853,7 @@ str_map_make (str_map_free_fn free)
self.len = 0; self.len = 0;
self.free = free; self.free = free;
self.key_xfrm = NULL; self.key_xfrm = NULL;
self.map = xcalloc (self.alloc, sizeof *self.map); self.map = (struct str_map_link **) xcalloc (self.alloc, sizeof *self.map);
self.shrink_lock = false; self.shrink_lock = false;
return self; return self;
} }
@@ -870,7 +909,8 @@ str_map_resize (struct str_map *self, size_t new_size)
size_t mask = new_size - 1; size_t mask = new_size - 1;
self->alloc = new_size; self->alloc = new_size;
self->map = xcalloc (self->alloc, sizeof *self->map); self->map =
(struct str_map_link **) xcalloc (self->alloc, sizeof *self->map);
for (i = 0; i < old_size; i++) for (i = 0; i < old_size; i++)
{ {
struct str_map_link *iter = old_map[i], *next_iter; struct str_map_link *iter = old_map[i], *next_iter;
@@ -940,7 +980,8 @@ str_map_set_real (struct str_map *self, const char *key, void *value)
// Link in a new element for the given <key, value> pair // Link in a new element for the given <key, value> pair
size_t key_length = strlen (key); size_t key_length = strlen (key);
struct str_map_link *link = xmalloc (sizeof *link + key_length + 1); struct str_map_link *link =
(struct str_map_link *) xmalloc (sizeof *link + key_length + 1);
link->data = value; link->data = value;
link->key_length = key_length; link->key_length = key_length;
memcpy (link->key, key, key_length + 1); memcpy (link->key, key, key_length + 1);
@@ -1093,7 +1134,7 @@ struct async
LIST_HEADER (struct async) LIST_HEADER (struct async)
struct async_manager *manager; ///< Our manager object struct async_manager *manager; ///< Our manager object
// "cancelled" may not be accesed or modified by the worker thread // "cancelled" may not be accessed or modified by the worker thread
pthread_t worker; ///< Worker thread ID pthread_t worker; ///< Worker thread ID
bool started; ///< Worker thread ID is valid bool started; ///< Worker thread ID is valid
@@ -1143,7 +1184,7 @@ async_cancel (struct async *self)
static void static void
async_cleanup (void *user_data) async_cleanup (void *user_data)
{ {
struct async *self = user_data; struct async *self = (struct async *) user_data;
hard_assert (!pthread_mutex_lock (&self->manager->lock)); hard_assert (!pthread_mutex_lock (&self->manager->lock));
LIST_UNLINK (self->manager->running, self); LIST_UNLINK (self->manager->running, self);
@@ -1159,7 +1200,7 @@ async_routine (void *user_data)
{ {
// Beware that we mustn't trigger any cancellation point before we set up // Beware that we mustn't trigger any cancellation point before we set up
// the cleanup handler, otherwise we'd need to disable it first // the cleanup handler, otherwise we'd need to disable it first
struct async *self = user_data; struct async *self = (struct async *) user_data;
pthread_cleanup_push (async_cleanup, self); pthread_cleanup_push (async_cleanup, self);
self->execute (self); self->execute (self);
@@ -1354,6 +1395,7 @@ struct poller_idle
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// The heap could definitely be made faster but we'll prefer simplicity
struct poller_timers struct poller_timers
{ {
struct poller_timer **heap; ///< Min-heap of timers struct poller_timer **heap; ///< Min-heap of timers
@@ -1367,7 +1409,8 @@ poller_timers_make (void)
struct poller_timers self; struct poller_timers self;
self.alloc = POLLER_MIN_ALLOC; self.alloc = POLLER_MIN_ALLOC;
self.len = 0; self.len = 0;
self.heap = xmalloc (self.alloc * sizeof *self.heap); self.heap =
(struct poller_timer **) xmalloc (self.alloc * sizeof *self.heap);
return self; return self;
} }
@@ -1477,7 +1520,7 @@ poller_timers_set (struct poller_timers *self, struct poller_timer *timer)
} }
if (self->len == self->alloc) if (self->len == self->alloc)
self->heap = xreallocarray (self->heap, self->heap = (struct poller_timer **) xreallocarray (self->heap,
self->alloc <<= 1, sizeof *self->heap); self->alloc <<= 1, sizeof *self->heap);
self->heap[self->len] = timer; self->heap[self->len] = timer;
timer->index = self->len; timer->index = self->len;
@@ -1554,9 +1597,10 @@ poller_init (struct poller *self)
self->len = 0; self->len = 0;
self->alloc = POLLER_MIN_ALLOC; self->alloc = POLLER_MIN_ALLOC;
self->fds = xcalloc (self->alloc, sizeof *self->fds); self->fds = (struct poller_fd **) xcalloc (self->alloc, sizeof *self->fds);
self->dummy = xcalloc (self->alloc, sizeof *self->dummy); self->dummy = (int *) xcalloc (self->alloc, sizeof *self->dummy);
self->revents = xcalloc (self->alloc, sizeof *self->revents); self->revents = (struct epoll_event *)
xcalloc (self->alloc, sizeof *self->revents);
self->revents_len = 0; self->revents_len = 0;
poller_common_init (&self->common, self); poller_common_init (&self->common, self);
@@ -1569,7 +1613,7 @@ poller_free (struct poller *self)
{ {
struct poller_fd *fd = self->fds[i]; struct poller_fd *fd = self->fds[i];
hard_assert (epoll_ctl (self->epoll_fd, hard_assert (epoll_ctl (self->epoll_fd,
EPOLL_CTL_DEL, fd->fd, (void *) "") != -1); EPOLL_CTL_DEL, fd->fd, (struct epoll_event *) "") != -1);
} }
poller_common_free (&self->common); poller_common_free (&self->common);
@@ -1589,11 +1633,11 @@ poller_ensure_space (struct poller *self)
self->alloc <<= 1; self->alloc <<= 1;
hard_assert (self->alloc != 0); hard_assert (self->alloc != 0);
self->revents = xreallocarray self->revents = (struct epoll_event *) xreallocarray
(self->revents, sizeof *self->revents, self->alloc); (self->revents, sizeof *self->revents, self->alloc);
self->fds = xreallocarray self->fds = (struct poller_fd **) xreallocarray
(self->fds, sizeof *self->fds, self->alloc); (self->fds, sizeof *self->fds, self->alloc);
self->dummy = xreallocarray self->dummy = (int *) xreallocarray
(self->dummy, sizeof *self->dummy, self->alloc); (self->dummy, sizeof *self->dummy, self->alloc);
} }
@@ -1645,8 +1689,12 @@ poller_set (struct poller *self, struct poller_fd *fd)
static int static int
poller_compare_fds (const void *ax, const void *bx) poller_compare_fds (const void *ax, const void *bx)
{ {
const struct epoll_event *ay = ax, *by = bx; const struct epoll_event
struct poller_fd *a = ay->data.ptr, *b = by->data.ptr; *ay = (const struct epoll_event *) ax,
*by = (const struct epoll_event *) bx;
struct poller_fd
*a = (struct poller_fd *) ay->data.ptr,
*b = (struct poller_fd *) by->data.ptr;
return a->fd - b->fd; return a->fd - b->fd;
} }
@@ -1657,7 +1705,7 @@ poller_remove_from_dispatch (struct poller *self, const struct poller_fd *fd)
return; return;
struct epoll_event key = { .data.ptr = (void *) fd }, *fd_event; struct epoll_event key = { .data.ptr = (void *) fd }, *fd_event;
if ((fd_event = bsearch (&key, self->revents, if ((fd_event = (struct epoll_event *) bsearch (&key, self->revents,
self->revents_len, sizeof *self->revents, poller_compare_fds))) self->revents_len, sizeof *self->revents, poller_compare_fds)))
{ {
fd_event->events = -1; fd_event->events = -1;
@@ -1680,7 +1728,7 @@ poller_remove_at_index (struct poller *self, size_t index)
poller_remove_from_dispatch (self, fd); poller_remove_from_dispatch (self, fd);
if (!fd->closed) if (!fd->closed)
hard_assert (epoll_ctl (self->epoll_fd, hard_assert (epoll_ctl (self->epoll_fd,
EPOLL_CTL_DEL, fd->fd, (void *) "") != -1); EPOLL_CTL_DEL, fd->fd, (struct epoll_event *) "") != -1);
if (index != --self->len) if (index != --self->len)
{ {
@@ -1716,7 +1764,7 @@ poller_run (struct poller *self)
if (revents->events == (uint32_t) -1) if (revents->events == (uint32_t) -1)
continue; continue;
struct poller_fd *fd = revents->data.ptr; struct poller_fd *fd = (struct poller_fd *) revents->data.ptr;
hard_assert (fd->index != -1); hard_assert (fd->index != -1);
struct pollfd pfd; struct pollfd pfd;
@@ -1759,8 +1807,9 @@ poller_init (struct poller *self)
self->len = 0; self->len = 0;
self->alloc = POLLER_MIN_ALLOC; self->alloc = POLLER_MIN_ALLOC;
self->fds = xcalloc (self->alloc, sizeof *self->fds); self->fds = (struct poller_fd **) xcalloc (self->alloc, sizeof *self->fds);
self->revents = xcalloc (self->alloc, sizeof *self->revents); self->revents = (struct kevent *)
xcalloc (self->alloc, sizeof *self->revents);
self->revents_len = 0; self->revents_len = 0;
poller_common_init (&self->common, self); poller_common_init (&self->common, self);
} }
@@ -1783,9 +1832,9 @@ poller_ensure_space (struct poller *self)
self->alloc <<= 1; self->alloc <<= 1;
hard_assert (self->alloc != 0); hard_assert (self->alloc != 0);
self->revents = xreallocarray self->revents = (struct kevent *) xreallocarray
(self->revents, sizeof *self->revents, self->alloc); (self->revents, sizeof *self->revents, self->alloc);
self->fds = xreallocarray self->fds = (struct poller_fd **) xreallocarray
(self->fds, sizeof *self->fds, self->alloc); (self->fds, sizeof *self->fds, self->alloc);
} }
@@ -1985,8 +2034,9 @@ poller_init (struct poller *self)
{ {
self->alloc = POLLER_MIN_ALLOC; self->alloc = POLLER_MIN_ALLOC;
self->len = 0; self->len = 0;
self->fds = xcalloc (self->alloc, sizeof *self->fds); self->fds = (struct pollfd **) xcalloc (self->alloc, sizeof *self->fds);
self->fds_data = xcalloc (self->alloc, sizeof *self->fds_data); self->fds_data = (struct poller_fd **)
xcalloc (self->alloc, sizeof *self->fds_data);
poller_common_init (&self->common, self); poller_common_init (&self->common, self);
self->dispatch_next = -1; self->dispatch_next = -1;
} }
@@ -2006,8 +2056,9 @@ poller_ensure_space (struct poller *self)
return; return;
self->alloc <<= 1; self->alloc <<= 1;
self->fds = xreallocarray (self->fds, sizeof *self->fds, self->alloc); self->fds = (struct pollfd *)
self->fds_data = xreallocarray xreallocarray (self->fds, sizeof *self->fds, self->alloc);
self->fds_data = (struct poller_fd **) xreallocarray
(self->fds_data, sizeof *self->fds_data, self->alloc); (self->fds_data, sizeof *self->fds_data, self->alloc);
} }
@@ -2291,7 +2342,8 @@ static struct async_getaddrinfo *
async_getaddrinfo (struct async_manager *manager, async_getaddrinfo (struct async_manager *manager,
const char *host, const char *service, const struct addrinfo *hints) const char *host, const char *service, const struct addrinfo *hints)
{ {
struct async_getaddrinfo *self = xcalloc (1, sizeof *self); struct async_getaddrinfo *self =
(struct async_getaddrinfo *) xcalloc (1, sizeof *self);
self->async = async_make (manager); self->async = async_make (manager);
if (host) self->host = xstrdup (host); if (host) self->host = xstrdup (host);
@@ -2355,10 +2407,11 @@ static struct async_getnameinfo *
async_getnameinfo (struct async_manager *manager, async_getnameinfo (struct async_manager *manager,
const struct sockaddr *sa, socklen_t sa_len, int flags) const struct sockaddr *sa, socklen_t sa_len, int flags)
{ {
struct async_getnameinfo *self = xcalloc (1, sizeof *self); struct async_getnameinfo *self =
(struct async_getnameinfo *) xcalloc (1, sizeof *self);
self->async = async_make (manager); self->async = async_make (manager);
self->address = memcpy (xmalloc (sa_len), sa, sa_len); self->address = (struct sockaddr *) memcpy (xmalloc (sa_len), sa, sa_len);
self->address_len = sa_len; self->address_len = sa_len;
self->flags = flags; self->flags = flags;
@@ -2523,7 +2576,12 @@ struct msg_unpacker
static struct msg_unpacker static struct msg_unpacker
msg_unpacker_make (const void *data, size_t len) msg_unpacker_make (const void *data, size_t len)
{ {
return (struct msg_unpacker) { .data = data, .len = len, .offset = 0 }; return (struct msg_unpacker)
{
.data = (const char *) data,
.offset = 0,
.len = len
};
} }
static size_t static size_t
@@ -2612,8 +2670,16 @@ msg_writer_flush (struct msg_writer *self, size_t *len)
{ {
// Update the message length // Update the message length
uint64_t x = self->buf.len; uint64_t x = self->buf.len;
uint8_t tmp[8] = uint8_t tmp[8] = {
{ x >> 56, x >> 48, x >> 40, x >> 32, x >> 24, x >> 16, x >> 8, x }; (uint8_t) (x >> 56),
(uint8_t) (x >> 48),
(uint8_t) (x >> 40),
(uint8_t) (x >> 32),
(uint8_t) (x >> 24),
(uint8_t) (x >> 16),
(uint8_t) (x >> 8),
(uint8_t) x
};
memcpy (self->buf.str, tmp, sizeof tmp); memcpy (self->buf.str, tmp, sizeof tmp);
*len = x; *len = x;
@@ -2669,6 +2735,12 @@ strncasecmp_ascii (const char *a, const char *b, size_t n)
return 0; return 0;
} }
static bool
iscntrl_ascii (int c)
{
return (c >= 0 && c < 32) || c == 0x7f;
}
static bool static bool
isalpha_ascii (int c) isalpha_ascii (int c)
{ {
@@ -2697,63 +2769,71 @@ isspace_ascii (int c)
// --- UTF-8 ------------------------------------------------------------------- // --- UTF-8 -------------------------------------------------------------------
/// Return a pointer to the next UTF-8 character, or NULL on error /// Return the value of the UTF-8 character at `*s` and advance the pointer
static const char * /// to the next one. Returns -2 if there is only a partial but possibly valid
utf8_next (const char *s, size_t len, int32_t *codepoint) /// character sequence, or -1 on other errors. Either way, `*s` is untouched.
static int32_t
utf8_decode (const char **s, size_t len)
{ {
// End of string, we go no further // End of string, we go no further
if (!len) if (!len)
return NULL; return -1;
// Find out how long the sequence is (0 for ASCII) // Find out how long the sequence is (0 for ASCII)
unsigned mask = 0x80; unsigned mask = 0x80;
unsigned sequence_len = 0; unsigned sequence_len = 0;
const uint8_t *p = (const uint8_t *) s; const uint8_t *p = (const uint8_t *) *s, *end = p + len;
while ((*p & mask) == mask) while ((*p & mask) == mask)
{ {
// Invalid start of sequence // Invalid start of sequence
if (mask == 0xFE) if (mask == 0xFE)
return NULL; return -1;
mask |= mask >> 1; mask |= mask >> 1;
sequence_len++; sequence_len++;
} }
// In the middle of a character or the input is too short // In the middle of a character
if (sequence_len == 1 || sequence_len > len) if (sequence_len == 1)
return NULL; return -1;
// Check the rest of the sequence // Check the rest of the sequence
uint32_t cp = *p++ & ~mask; uint32_t cp = *p++ & ~mask;
// Overlong sequence (possibly MUTF-8, not supported)
if (!cp && sequence_len)
return -1;
while (sequence_len && --sequence_len) while (sequence_len && --sequence_len)
{ {
if (p == end)
return -2;
if ((*p & 0xC0) != 0x80) if ((*p & 0xC0) != 0x80)
return NULL; return -1;
cp = cp << 6 | (*p++ & 0x3F); cp = cp << 6 | (*p++ & 0x3F);
} }
if (codepoint) *s = (const char *) p;
*codepoint = cp; return cp;
return (const char *) p; }
static inline bool
utf8_validate_cp (int32_t cp)
{
// RFC 3629, CESU-8 not allowed
return cp >= 0 && cp <= 0x10FFFF && (cp < 0xD800 || cp > 0xDFFF);
} }
/// Very rough UTF-8 validation, just makes sure codepoints can be iterated /// Very rough UTF-8 validation, just makes sure codepoints can be iterated
static bool static bool
utf8_validate (const char *s, size_t len) utf8_validate (const char *s, size_t len)
{ {
const char *next; const char *end = s + len;
while (len)
{
int32_t codepoint; int32_t codepoint;
// TODO: better validations while ((codepoint = utf8_decode (&s, end - s)) >= 0
if (!(next = utf8_next (s, len, &codepoint)) && utf8_validate_cp (codepoint))
|| codepoint > 0x10FFFF) ;
return false; return s == end;
len -= next - s;
s = next;
}
return true;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -2777,12 +2857,12 @@ utf8_iter_next (struct utf8_iter *self, size_t *len)
return -1; return -1;
const char *old = self->s; const char *old = self->s;
int32_t codepoint; int32_t codepoint = utf8_decode (&self->s, self->len);
if (!soft_assert ((self->s = utf8_next (old, self->len, &codepoint)))) if (!soft_assert (codepoint >= 0))
{ {
// Invalid UTF-8 // Invalid UTF-8
self->len = 0; self->len = 0;
return -1; return codepoint;
} }
size_t advance = self->s - old; size_t advance = self->s - old;
@@ -2878,7 +2958,7 @@ base64_encode (const void *data, size_t len, struct str *output)
const char *alphabet = const char *alphabet =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const uint8_t *p = data; const uint8_t *p = (const uint8_t *) data;
size_t n_groups = len / 3; size_t n_groups = len / 3;
size_t tail = len - n_groups * 3; size_t tail = len - n_groups * 3;
uint32_t group; uint32_t group;
@@ -2914,6 +2994,13 @@ base64_encode (const void *data, size_t len, struct str *output)
// --- Utilities --------------------------------------------------------------- // --- Utilities ---------------------------------------------------------------
static void
cstr_set (char **s, char *new_)
{
free (*s);
*s = new_;
}
static void static void
cstr_split (const char *s, const char *delimiters, bool ignore_empty, cstr_split (const char *s, const char *delimiters, bool ignore_empty,
struct strv *out) struct strv *out)
@@ -2943,10 +3030,10 @@ cstr_strip_in_place (char *s, const char *stripped_chars)
} }
static void static void
cstr_transform (char *s, int (*tolower) (int c)) cstr_transform (char *s, int (*xform) (int c))
{ {
for (; *s; s++) for (; *s; s++)
*s = tolower (*s); *s = xform (*s);
} }
static char * static char *
@@ -2989,10 +3076,11 @@ iconv_xstrdup (iconv_t conv, char *in, size_t in_len, size_t *out_len)
char *buf, *buf_ptr; char *buf, *buf_ptr;
size_t out_left, buf_alloc; size_t out_left, buf_alloc;
buf = buf_ptr = xmalloc (out_left = buf_alloc = 64); buf = buf_ptr = (char *) xmalloc (out_left = buf_alloc = 64);
char *in_ptr = in; char *in_ptr = in;
if (in_len == (size_t) -1) if (in_len == (size_t) -1)
// XXX: out_len will be one character longer than the string!
in_len = strlen (in) + 1; in_len = strlen (in) + 1;
while (iconv (conv, (char **) &in_ptr, &in_len, while (iconv (conv, (char **) &in_ptr, &in_len,
@@ -3004,7 +3092,7 @@ iconv_xstrdup (iconv_t conv, char *in, size_t in_len, size_t *out_len)
return NULL; return NULL;
} }
out_left += buf_alloc; out_left += buf_alloc;
char *new_buf = xrealloc (buf, buf_alloc <<= 1); char *new_buf = (char *) xrealloc (buf, buf_alloc <<= 1);
buf_ptr += new_buf - buf; buf_ptr += new_buf - buf;
buf = new_buf; buf = new_buf;
} }
@@ -3085,8 +3173,8 @@ lock_pid_file (const char *path, struct error **e)
struct flock lock = struct flock lock =
{ {
.l_type = F_WRLCK, .l_type = F_WRLCK,
.l_start = 0,
.l_whence = SEEK_SET, .l_whence = SEEK_SET,
.l_start = 0,
.l_len = 0, .l_len = 0,
}; };
if (fcntl (fd, F_SETLK, &lock)) if (fcntl (fd, F_SETLK, &lock))
@@ -3252,16 +3340,8 @@ resolve_relative_data_filename (const char *filename)
} }
static char * static char *
resolve_relative_runtime_filename (const char *filename) resolve_relative_runtime_filename_finish (struct str path)
{ {
struct str path = str_make ();
const char *runtime_dir = getenv ("XDG_RUNTIME_DIR");
if (runtime_dir && *runtime_dir == '/')
str_append (&path, runtime_dir);
else
get_xdg_home_dir (&path, "XDG_DATA_HOME", ".local/share");
str_append_printf (&path, "/%s/%s", PROGRAM_NAME, filename);
// Try to create the file's ancestors; // Try to create the file's ancestors;
// typically the user will want to immediately create a file in there // typically the user will want to immediately create a file in there
const char *last_slash = strrchr (path.str, '/'); const char *last_slash = strrchr (path.str, '/');
@@ -3274,6 +3354,41 @@ resolve_relative_runtime_filename (const char *filename)
return str_steal (&path); return str_steal (&path);
} }
static char *
resolve_relative_runtime_filename (const char *filename)
{
struct str path = str_make ();
const char *runtime_dir = getenv ("XDG_RUNTIME_DIR");
if (runtime_dir && *runtime_dir == '/')
str_append (&path, runtime_dir);
else
get_xdg_home_dir (&path, "XDG_DATA_HOME", ".local/share");
str_append_printf (&path, "/%s/%s", PROGRAM_NAME, filename);
return resolve_relative_runtime_filename_finish (path);
}
/// This differs from resolve_relative_runtime_filename() in that we expect
/// the filename to be something like a pattern for mkstemp(), so the resulting
/// path can reside in a system-wide directory with no risk of a conflict.
/// However, we have to take care about permissions. Do we even need this?
static char *
resolve_relative_runtime_template (const char *template_)
{
struct str path = str_make ();
const char *runtime_dir = getenv ("XDG_RUNTIME_DIR");
const char *tmpdir = getenv ("TMPDIR");
if (runtime_dir && *runtime_dir == '/')
str_append_printf (&path, "%s/%s", runtime_dir, PROGRAM_NAME);
else if (tmpdir && *tmpdir == '/')
str_append_printf (&path, "%s/%s.%d", tmpdir, PROGRAM_NAME, geteuid ());
else
str_append_printf (&path, "/tmp/%s.%d", PROGRAM_NAME, geteuid ());
str_append_printf (&path, "/%s", template_);
return resolve_relative_runtime_filename_finish (path);
}
static char * static char *
try_expand_tilde (const char *filename) try_expand_tilde (const char *filename)
{ {
@@ -3292,9 +3407,9 @@ try_expand_tilde (const char *filename)
struct passwd pwd, *success = NULL; struct passwd pwd, *success = NULL;
char *user = xstrndup (filename, until_slash); char *user = xstrndup (filename, until_slash);
char *buf = xmalloc (buf_len); char *buf = (char *) xmalloc (buf_len);
while (getpwnam_r (user, &pwd, buf, buf_len, &success) == ERANGE) while (getpwnam_r (user, &pwd, buf, buf_len, &success) == ERANGE)
buf = xrealloc (buf, buf_len <<= 1); buf = (char *) xrealloc (buf, buf_len <<= 1);
free (user); free (user);
char *result = NULL; char *result = NULL;
@@ -3370,7 +3485,7 @@ xssl_get_error (SSL *ssl, int result, const char **error_info)
static regex_t * static regex_t *
regex_compile (const char *regex, int flags, struct error **e) regex_compile (const char *regex, int flags, struct error **e)
{ {
regex_t *re = xmalloc (sizeof *re); regex_t *re = (regex_t *) xmalloc (sizeof *re);
int err = regcomp (re, regex, flags); int err = regcomp (re, regex, flags);
if (!err) if (!err)
return re; return re;
@@ -3386,7 +3501,7 @@ regex_compile (const char *regex, int flags, struct error **e)
static void static void
regex_free (void *regex) regex_free (void *regex)
{ {
regfree (regex); regfree ((regex_t *) regex);
free (regex); free (regex);
} }
@@ -3405,7 +3520,7 @@ static bool
regex_cache_match (struct str_map *cache, const char *regex, int flags, regex_cache_match (struct str_map *cache, const char *regex, int flags,
const char *s, struct error **e) const char *s, struct error **e)
{ {
regex_t *re = str_map_find (cache, regex); regex_t *re = (regex_t *) str_map_find (cache, regex);
if (!re) if (!re)
{ {
re = regex_compile (regex, flags, e); re = regex_compile (regex, flags, e);
@@ -3675,7 +3790,7 @@ opt_handler_make (int argc, char **argv,
self.opts = opts; self.opts = opts;
self.opts_len = len; self.opts_len = len;
self.options = xcalloc (len + 1, sizeof *self.options); self.options = (struct option *) xcalloc (len + 1, sizeof *self.options);
struct str opt_string = str_make (); struct str opt_string = str_make ();
for (size_t i = 0; i < len; i++) for (size_t i = 0; i < len; i++)
@@ -3726,7 +3841,7 @@ opt_handler_usage (const struct opt_handler *self, FILE *stream)
str_append_printf (&row, "--%s", opt->long_name); str_append_printf (&row, "--%s", opt->long_name);
if (opt->arg_hint) if (opt->arg_hint)
str_append_printf (&row, (opt->flags & OPT_OPTIONAL_ARG) str_append_printf (&row, (opt->flags & OPT_OPTIONAL_ARG)
? " [%s]" : " %s", opt->arg_hint); ? "[=%s]" : " %s", opt->arg_hint);
// TODO: keep the indent if there are multiple lines // TODO: keep the indent if there are multiple lines
if (row.len + 2 <= OPT_USAGE_ALIGNMENT_COLUMN) if (row.len + 2 <= OPT_USAGE_ALIGNMENT_COLUMN)
@@ -3853,7 +3968,7 @@ test_add_internal (struct test *self, const char *name, size_t fixture_size,
hard_assert (test != NULL); hard_assert (test != NULL);
hard_assert (name != NULL); hard_assert (name != NULL);
struct test_unit *unit = xcalloc (1, sizeof *unit); struct test_unit *unit = (struct test_unit *) xcalloc (1, sizeof *unit);
unit->name = xstrdup (name); unit->name = xstrdup (name);
unit->fixture_size = fixture_size; unit->fixture_size = fixture_size;
unit->user_data = user_data; unit->user_data = user_data;
@@ -4006,7 +4121,8 @@ struct connector_target
static struct connector_target * static struct connector_target *
connector_target_new (void) connector_target_new (void)
{ {
struct connector_target *self = xcalloc (1, sizeof *self); struct connector_target *self =
(struct connector_target *) xcalloc (1, sizeof *self);
return self; return self;
} }
@@ -4219,7 +4335,7 @@ connector_free (struct connector *self)
static void static void
connector_on_getaddrinfo (int err, struct addrinfo *results, void *user_data) connector_on_getaddrinfo (int err, struct addrinfo *results, void *user_data)
{ {
struct connector_target *self = user_data; struct connector_target *self = (struct connector_target *) user_data;
if (err) if (err)
{ {
@@ -4337,6 +4453,32 @@ socket_io_try_write (int socket_fd, struct str *wb)
// This is a more powerful configuration format, adding key-value maps and // This is a more powerful configuration format, adding key-value maps and
// simplifying item validation and dynamic handling of changes. All strings // simplifying item validation and dynamic handling of changes. All strings
// must be encoded in UTF-8. // must be encoded in UTF-8.
//
// The syntax is roughly described by the following parsing expression grammar:
//
// config = entries eof # as if there were implicit curly braces around
// entries = (newline* pair)* newline*
// pair = key newline* lws '=' newline* value (&endobj / newline / eof)
// key = string / !null !boolean lws [A-Za-z_][0-9A-Za-z_]*
// value = object / string / integer / null / boolean
//
// object = lws '{' entries endobj
// endobj = lws '}'
//
// string = lws '"' ('\\' escape / ![\\"] char)* '"'
// char = [\0-\177] # or any Unicode codepoint in the UTF-8 encoding
// escape = [\\"abfnrtv] / [xX][0-9A-Fa-f][0-9A-Fa-f]? / [0-7][0-7]?[0-7]?
//
// integer = lws '-'? [0-9]+ # whatever strtoll() accepts on your system
// null = lws 'null'
// boolean = lws 'yes' / lws 'YES' / lws 'no' / lws 'NO'
// / lws 'on' / lws 'ON' / lws 'off' / lws 'OFF'
// / lws 'true' / lws 'TRUE' / lws 'false' / lws 'FALSE'
//
// newline = lws comment? '\n'
// eof = lws comment? !.
// lws = [ \t\r]* # linear whitespace (plus CR as it is insignificant)
// comment = '#' (!'\n' .)*
enum config_item_type enum config_item_type
{ {
@@ -4446,7 +4588,8 @@ config_item_move (struct config_item *self, struct config_item *source)
static struct config_item * static struct config_item *
config_item_new (enum config_item_type type) config_item_new (enum config_item_type type)
{ {
struct config_item *self = xcalloc (1, sizeof *self); struct config_item *self =
(struct config_item *) xcalloc (1, sizeof *self);
self->type = type; self->type = type;
return self; return self;
} }
@@ -4586,7 +4729,8 @@ config_item_get (struct config_item *self, const char *path, struct error **e)
const char *key = v.vector[i]; const char *key = v.vector[i];
if (!*key) if (!*key)
error_set (e, "empty path element"); error_set (e, "empty path element");
else if (!(self = str_map_find (&self->value.object, key))) else if (!(self = (struct config_item *)
str_map_find (&self->value.object, key)))
error_set (e, "`%s' not found in object", key); error_set (e, "`%s' not found in object", key);
else if (++i == v.len) else if (++i == v.len)
result = self; result = self;
@@ -4717,7 +4861,7 @@ config_item_write_object_innards
struct str_map_iter iter = str_map_iter_make (&object->value.object); struct str_map_iter iter = str_map_iter_make (&object->value.object);
struct config_item *value; struct config_item *value;
while ((value = str_map_iter_next (&iter))) while ((value = (struct config_item *) str_map_iter_next (&iter)))
config_item_write_kv_pair (self, iter.link->key, value); config_item_write_kv_pair (self, iter.link->key, value);
} }
@@ -5001,18 +5145,21 @@ config_tokenizer_next (struct config_tokenizer *self, struct error **e)
return CONFIG_T_STRING; return CONFIG_T_STRING;
} }
char *end; // Our input doesn't need to be NUL-terminated but we want to use strtoll()
char buf[48] = "", *end = buf;
size_t buf_len = MIN (sizeof buf - 1, self->len);
errno = 0; errno = 0;
self->integer = strtoll (self->p, &end, 10); self->integer = strtoll (strncpy (buf, self->p, buf_len), &end, 10);
if (errno == ERANGE) if (errno == ERANGE)
{ {
config_tokenizer_error (self, e, "integer out of range"); config_tokenizer_error (self, e, "integer out of range");
return CONFIG_T_ABORT; return CONFIG_T_ABORT;
} }
if (end != self->p) if (end != buf)
{ {
self->len -= end - self->p; self->len -= end - buf;
self->p = end; self->p += end - buf;
return CONFIG_T_INTEGER; return CONFIG_T_INTEGER;
} }
@@ -5025,7 +5172,7 @@ config_tokenizer_next (struct config_tokenizer *self, struct error **e)
str_reset (&self->string); str_reset (&self->string);
do do
str_append_c (&self->string, config_tokenizer_advance (self)); str_append_c (&self->string, config_tokenizer_advance (self));
while (config_tokenizer_is_word_char (*self->p)); while (self->len && config_tokenizer_is_word_char (*self->p));
if (!strcmp (self->string.str, "null")) if (!strcmp (self->string.str, "null"))
return CONFIG_T_NULL; return CONFIG_T_NULL;
@@ -5228,7 +5375,9 @@ static struct config_item *
config_item_parse (const char *script, size_t len, config_item_parse (const char *script, size_t len,
bool single_value_only, struct error **e) bool single_value_only, struct error **e)
{ {
struct config_parser parser = config_parser_make (script, len); volatile struct config_parser parser = config_parser_make (script, len);
struct config_parser *volatile self = (struct config_parser *) &parser;
struct config_item *volatile object = NULL; struct config_item *volatile object = NULL;
jmp_buf err; jmp_buf err;
@@ -5250,13 +5399,13 @@ config_item_parse (const char *script, size_t len,
// This is really only intended for in-program configuration // This is really only intended for in-program configuration
// and telling the line number would look awkward // and telling the line number would look awkward
parser.tokenizer.report_line = false; parser.tokenizer.report_line = false;
object = config_parser_parse_value (&parser, err); object = config_parser_parse_value (self, err);
} }
else else
object = config_parser_parse_object (&parser, err); object = config_parser_parse_object (self, err);
config_parser_expect (&parser, CONFIG_T_ABORT, err); config_parser_expect (self, CONFIG_T_ABORT, err);
end: end:
config_parser_free (&parser); config_parser_free (self);
return object; return object;
} }
@@ -5278,11 +5427,11 @@ config_read_from_file (const char *filename, struct error **e)
{ {
struct config_item *root = NULL; struct config_item *root = NULL;
struct error *error = NULL;
struct str data = str_make (); struct str data = str_make ();
if (!read_file (filename, &data, e)) if (!read_file (filename, &data, e))
goto end; goto end;
struct error *error = NULL;
if (!(root = config_item_parse (data.str, data.len, false, &error))) if (!(root = config_item_parse (data.str, data.len, false, &error)))
{ {
error_set (e, "parse error in `%s': %s", filename, error->message); error_set (e, "parse error in `%s': %s", filename, error->message);
@@ -5302,12 +5451,12 @@ config_schema_initialize_item (struct config_schema *schema,
struct error **e) struct error **e)
{ {
hard_assert (parent->type == CONFIG_ITEM_OBJECT); hard_assert (parent->type == CONFIG_ITEM_OBJECT);
struct config_item *item = struct config_item *item = (struct config_item *)
str_map_find (&parent->value.object, schema->name); str_map_find (&parent->value.object, schema->name);
struct error *error = NULL;
if (item) if (item)
{ {
struct error *error = NULL;
item->user_data = user_data; item->user_data = user_data;
if (config_item_validate_by_schema (item, schema, &error)) if (config_item_validate_by_schema (item, schema, &error))
goto keep_current; goto keep_current;
@@ -5315,9 +5464,9 @@ config_schema_initialize_item (struct config_schema *schema,
error_set (warning, "resetting configuration item " error_set (warning, "resetting configuration item "
"`%s' to default: %s", schema->name, error->message); "`%s' to default: %s", schema->name, error->message);
error_free (error); error_free (error);
error = NULL;
} }
struct error *error = NULL;
if (schema->default_) if (schema->default_)
item = config_item_parse item = config_item_parse
(schema->default_, strlen (schema->default_), true, &error); (schema->default_, strlen (schema->default_), true, &error);
@@ -5380,7 +5529,7 @@ config_schema_call_changed (struct config_item *item)
{ {
struct str_map_iter iter = str_map_iter_make (&item->value.object); struct str_map_iter iter = str_map_iter_make (&item->value.object);
struct config_item *child; struct config_item *child;
while ((child = str_map_iter_next (&iter))) while ((child = (struct config_item *) str_map_iter_next (&iter)))
config_schema_call_changed (child); config_schema_call_changed (child);
} }
else if (item->schema && item->schema->on_change) else if (item->schema && item->schema->on_change)
@@ -5433,7 +5582,8 @@ static void
config_register_module (struct config *self, config_register_module (struct config *self,
const char *name, config_module_load_fn loader, void *user_data) const char *name, config_module_load_fn loader, void *user_data)
{ {
struct config_module *module = xcalloc (1, sizeof *module); struct config_module *module =
(struct config_module *) xcalloc (1, sizeof *module);
module->name = xstrdup (name); module->name = xstrdup (name);
module->loader = loader; module->loader = loader;
module->user_data = user_data; module->user_data = user_data;
@@ -5451,9 +5601,9 @@ config_load (struct config *self, struct config_item *root)
struct str_map_iter iter = str_map_iter_make (&self->modules); struct str_map_iter iter = str_map_iter_make (&self->modules);
struct config_module *module; struct config_module *module;
while ((module = str_map_iter_next (&iter))) while ((module = (struct config_module *) str_map_iter_next (&iter)))
{ {
struct config_item *subtree = str_map_find struct config_item *subtree = (struct config_item *) str_map_find
(&root->value.object, module->name); (&root->value.object, module->name);
// Silently fix inputs that only a lunatic user could create // Silently fix inputs that only a lunatic user could create
if (!subtree || subtree->type != CONFIG_ITEM_OBJECT) if (!subtree || subtree->type != CONFIG_ITEM_OBJECT)

View File

@@ -14,7 +14,7 @@ fakeroot sh -e <<-EOF
url = $url url = $url
builddate = \`date -u +%s\` builddate = \`date -u +%s\`
packager = $author packager = $author
size = \`du -sb --apparent-size | cut -f1\` size = \`du -sb | cut -f1\`
arch = $arch arch = $arch
END END
cd "$wd" && tar cJf "../$target" .PKGINFO * 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) switch (len - blocks)
{ {
case 7: last7 |= (uint64_t) m[i + 6] << 48; case 7: last7 |= (uint64_t) m[i + 6] << 48; // Fall-through
case 6: last7 |= (uint64_t) m[i + 5] << 40; case 6: last7 |= (uint64_t) m[i + 5] << 40; // Fall-through
case 5: last7 |= (uint64_t) m[i + 4] << 32; case 5: last7 |= (uint64_t) m[i + 4] << 32; // Fall-through
case 4: last7 |= (uint64_t) m[i + 3] << 24; case 4: last7 |= (uint64_t) m[i + 3] << 24; // Fall-through
case 3: last7 |= (uint64_t) m[i + 2] << 16; case 3: last7 |= (uint64_t) m[i + 2] << 16; // Fall-through
case 2: last7 |= (uint64_t) m[i + 1] << 8; case 2: last7 |= (uint64_t) m[i + 1] << 8; // Fall-through
case 1: last7 |= (uint64_t) m[i + 0] ; case 1: last7 |= (uint64_t) m[i + 0] ; // Fall-through
default:; default:;
}; };
v3 ^= last7; 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 * 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 * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted.
* copyright notice and this permission notice appear in all copies.
* *
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
@@ -327,10 +326,17 @@ test_str_map (void)
static void static void
test_utf8 (void) test_utf8 (void)
{ {
const char valid [] = "2H₂ + O₂ ⇌ 2H₂O, R = 4.7 kΩ, ⌀ 200 mm"; const char *full = "\xc5\x99", *partial = full, *empty = full;
const char invalid[] = "\xf0\x90\x28\xbc"; soft_assert (utf8_decode (&full, 2) == 0x0159);
soft_assert (utf8_decode (&partial, 1) == -2);
soft_assert (utf8_decode (&empty, 0) == -1);
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 (valid, sizeof valid));
soft_assert (!utf8_validate (invalid, sizeof invalid)); 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óọ"); struct utf8_iter iter = utf8_iter_make ("fóọ");
size_t ch_len; size_t ch_len;
@@ -605,6 +611,78 @@ test_connector (const void *user_data, struct test_connector_fixture *self)
connector_free (&connector); 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 -------------------------------------------------------------------- // --- Main --------------------------------------------------------------------
int int
@@ -623,6 +701,7 @@ main (int argc, char *argv[])
test_add_simple (&test, "/utf-8", NULL, test_utf8); test_add_simple (&test, "/utf-8", NULL, test_utf8);
test_add_simple (&test, "/base64", NULL, test_base64); test_add_simple (&test, "/base64", NULL, test_base64);
test_add_simple (&test, "/async", NULL, test_async); 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_add (&test, "/connector", struct test_connector_fixture, NULL,
test_connector_fixture_init, test_connector_fixture_init,

View File

@@ -1,11 +1,10 @@
/* /*
* tests/proto.c * 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 * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted.
* copyright notice and this permission notice appear in all copies.
* *
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
@@ -78,7 +77,7 @@ test_irc (void)
static void static void
test_http_parser (void) test_http_parser (void)
{ {
struct str_map parameters = str_map_make (NULL); struct str_map parameters = str_map_make (free);
parameters.key_xfrm = tolower_ascii_strxfrm; parameters.key_xfrm = tolower_ascii_strxfrm;
char *type = NULL; char *type = NULL;
@@ -89,9 +88,11 @@ test_http_parser (void)
soft_assert (!strcasecmp_ascii (subtype, "html")); soft_assert (!strcasecmp_ascii (subtype, "html"));
soft_assert (parameters.len == 1); soft_assert (parameters.len == 1);
soft_assert (!strcmp (str_map_find (&parameters, "charset"), "utf-8")); soft_assert (!strcmp (str_map_find (&parameters, "charset"), "utf-8"));
free (type);
free (subtype);
str_map_free (&parameters); 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 (http_parse_upgrade ("websocket, HTTP/2.0, , ", &protocols));
soft_assert (!strcmp (protocols->name, "websocket")); soft_assert (!strcmp (protocols->name, "websocket"));
@@ -106,10 +107,20 @@ test_http_parser (void)
http_protocol_destroy (iter); http_protocol_destroy (iter);
} }
struct scgi_fixture
{
struct scgi_parser parser;
bool seen_headers;
bool seen_content;
};
static bool static bool
test_scgi_parser_on_headers_read (void *user_data) 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 (parser->headers.len == 4);
soft_assert (!strcmp (str_map_find (&parser->headers, soft_assert (!strcmp (str_map_find (&parser->headers,
"CONTENT_LENGTH"), "27")); "CONTENT_LENGTH"), "27"));
@@ -125,7 +136,9 @@ test_scgi_parser_on_headers_read (void *user_data)
static bool static bool
test_scgi_parser_on_content (void *user_data, const void *data, size_t len) 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)); soft_assert (!strncmp (data, "What is the answer to life?", len));
return true; return true;
} }
@@ -133,10 +146,12 @@ test_scgi_parser_on_content (void *user_data, const void *data, size_t len)
static void static void
test_scgi_parser (void) test_scgi_parser (void)
{ {
struct scgi_parser parser = scgi_parser_make (); struct scgi_fixture fixture = { scgi_parser_make(), false, false };
parser.on_headers_read = test_scgi_parser_on_headers_read; struct scgi_parser *parser = &fixture.parser;
parser.on_content = test_scgi_parser_on_content;
parser.user_data = &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 // This is an example straight from the specification
const char example[] = const char example[] =
@@ -148,8 +163,9 @@ test_scgi_parser (void)
"," ","
"What is the answer to life?"; "What is the answer to life?";
soft_assert (scgi_parser_push (&parser, example, sizeof example, NULL)); soft_assert (scgi_parser_push (parser, example, sizeof example, NULL));
scgi_parser_free (&parser); soft_assert (fixture.seen_headers && fixture.seen_content);
scgi_parser_free (parser);
} }
static bool static bool