1
0
Fork 0
Univerxel/deps/picoquic/util.c

1133 lines
28 KiB
C

/*
* Author: Christian Huitema
* Copyright (c) 2017, Private Octopus, Inc.
* All rights reserved.
*
* Permission to use, copy, modify, and 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.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* clang-format off */
/* Simple set of utilities */
#ifdef _WINDOWS
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <WinSock2.h>
#include <Ws2def.h>
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <pthread.h>
#include <unistd.h>
#endif
#include "picoquic_internal.h"
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include "picoquic_utils.h"
/* clang-format on */
char* picoquic_string_create(const char* original, size_t len)
{
size_t allocated = len + 1;
char * str = NULL;
/* tests to protect against integer overflow */
if (allocated > 0) {
str = (char*)malloc(allocated);
if (str != NULL) {
if (original == NULL || len == 0) {
str[0] = 0;
}
else if (allocated > len) {
memcpy(str, original, len);
str[allocated - 1] = 0;
}
else {
/* This could happen only in case of integer overflow */
free(str);
str = NULL;
}
}
}
return str;
}
char* picoquic_string_duplicate(const char* original)
{
char* str = NULL;
if (original != NULL) {
size_t len = strlen(original);
str = picoquic_string_create(original, len);
}
return str;
}
char* picoquic_string_free(char* str)
{
if (str != NULL) {
free(str);
}
return NULL;
}
char* picoquic_strip_endofline(char* buf, size_t bufmax, char const* line)
{
for (size_t i = 0; i < bufmax; i++) {
int c = line[i];
if (c == 0 || c == '\r' || c == '\n') {
buf[i] = 0;
break;
}
else {
buf[i] = (char) c;
}
}
buf[bufmax - 1] = 0;
return buf;
}
static FILE* debug_out = NULL;
static int debug_suspended = 0;
void debug_set_stream(FILE *F)
{
debug_out = F;
}
void debug_printf(const char* fmt, ...)
{
if (debug_suspended == 0 && debug_out != NULL) {
va_list args;
va_start(args, fmt);
vfprintf(debug_out, fmt, args);
va_end(args);
}
}
void debug_dump(const void * x, int len)
{
if (debug_suspended == 0 && debug_out != NULL) {
uint8_t * bytes = (uint8_t *)x;
for (int i = 0; i < len;) {
fprintf(debug_out, "%04x: ", (int)i);
for (int j = 0; j < 16 && i < len; j++, i++) {
fprintf(debug_out, "%02x ", bytes[i]);
}
fprintf(debug_out, "\n");
}
}
}
void debug_printf_push_stream(FILE* f)
{
if (debug_out) {
fprintf(stderr, "Nested err out not supported\n");
exit(1);
}
debug_out = f;
}
void debug_printf_pop_stream(void)
{
if (debug_out == NULL) {
fprintf(stderr, "No current err out\n");
exit(1);
}
debug_out = NULL;
}
void debug_printf_suspend(void)
{
debug_suspended = 1;
}
void debug_printf_resume(void)
{
debug_suspended = 0;
}
int debug_printf_reset(int suspended)
{
int ret = debug_suspended;
debug_suspended = suspended;
return ret;
}
int picoquic_sprintf(char* buf, size_t buf_len, size_t * nb_chars, const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
#ifdef _WINDOWS
int res = vsnprintf_s(buf, buf_len, _TRUNCATE, fmt, args);
#else
int res = vsnprintf(buf, buf_len, fmt, args);
#endif
va_end(args);
if (nb_chars != NULL) {
*nb_chars = res;
}
// vsnprintf returns <0 for errors and >=0 for nb of characters required.
// We return 0 when printing was successful.
return res >= 0 ? ((size_t)res >= buf_len) : res;
}
int picoquic_print_connection_id_hexa(char* buf, size_t buf_len, const picoquic_connection_id_t * cnxid)
{
static const char hex_to_char[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
if (buf_len < ((size_t)cnxid->id_len) * 2u + 1u) {
return -1;
}
for (unsigned i = 0; i < cnxid->id_len; i++) {
buf[i * 2u] = hex_to_char[cnxid->id[i] >> 4];
buf[i * 2u + 1u] = hex_to_char[cnxid->id[i] & 0x0f];
}
buf[cnxid->id_len * 2u] = 0;
return 0;
}
int picoquic_parse_hexa_digit(char x) {
int ret = -1;
if (x >= '0' && x <= '9') {
ret = x - '0';
}
else if (x >= 'A' && x <= 'F') {
ret = x - 'A' + 10;
}
else if (x >= 'a' && x <= 'f') {
ret = x - 'a' + 10;
}
return ret;
}
size_t picoquic_parse_hexa(char const * hex_input, size_t input_length, uint8_t * bin_output, size_t output_max)
{
size_t ret = 0;
if (input_length > 0 && (input_length & 1) == 0 && 2*output_max >= input_length) {
size_t offset = 0;
while (offset < input_length) {
int a = picoquic_parse_hexa_digit(hex_input[offset++]);
int b = picoquic_parse_hexa_digit(hex_input[offset++]);
if (a < 0 || b < 0) {
ret = 0;
break;
}
else {
bin_output[ret++] = (uint8_t)((a << 4) | b);
}
}
}
return ret;
}
uint8_t picoquic_parse_connection_id_hexa(char const * hex_input, size_t input_length, picoquic_connection_id_t * cnx_id)
{
memset(cnx_id, 0, sizeof(picoquic_connection_id_t));
cnx_id->id_len = (uint8_t) picoquic_parse_hexa(hex_input, input_length, cnx_id->id, 18);
if (cnx_id->id_len == 0) {
memset(cnx_id, 0, sizeof(picoquic_connection_id_t));
}
return (cnx_id->id_len);
}
uint8_t picoquic_create_packet_header_cnxid_lengths(uint8_t dest_len, uint8_t srce_len)
{
uint8_t ret;
ret = (dest_len < 4) ? 0 : (dest_len - 3);
ret <<= 4;
ret |= (srce_len < 4) ? 0 : (srce_len - 3);
return ret;
}
uint8_t picoquic_format_connection_id(uint8_t* bytes, size_t bytes_max, picoquic_connection_id_t cnx_id)
{
uint8_t copied = cnx_id.id_len;
if (copied > bytes_max || copied == 0) {
copied = 0;
} else {
memcpy(bytes, cnx_id.id, copied);
}
return copied;
}
uint8_t picoquic_parse_connection_id(const uint8_t * bytes, uint8_t len, picoquic_connection_id_t * cnx_id)
{
if (len <= PICOQUIC_CONNECTION_ID_MAX_SIZE) {
cnx_id->id_len = len;
memcpy(cnx_id->id, bytes, len);
} else {
len = 0;
cnx_id->id_len = 0;
}
return len;
}
const picoquic_connection_id_t picoquic_null_connection_id = {
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0 };
int picoquic_is_connection_id_null(const picoquic_connection_id_t * cnx_id)
{
return (cnx_id->id_len == 0) ? 1 : 0;
}
int picoquic_compare_connection_id(const picoquic_connection_id_t * cnx_id1, const picoquic_connection_id_t * cnx_id2)
{
int ret = -1;
if (cnx_id1->id_len == cnx_id2->id_len) {
ret = memcmp(cnx_id1->id, cnx_id2->id, cnx_id1->id_len);
}
return ret;
}
/* Hash connection ids for picohash_table's */
uint64_t picoquic_connection_id_hash(const picoquic_connection_id_t * cid)
{
uint64_t val64 = 0;
for (size_t i = 0; i < cid->id_len; i++) {
val64 += val64 << 8;
val64 += cid->id[i];
}
return val64;
}
uint64_t picoquic_val64_connection_id(picoquic_connection_id_t cnx_id)
{
uint64_t val64 = 0;
if (cnx_id.id_len < 8)
{
for (size_t i = 0; i < cnx_id.id_len; i++) {
val64 <<= 8;
val64 |= cnx_id.id[i];
}
for (size_t i = cnx_id.id_len; i < 8; i++) {
val64 <<= 8;
}
} else {
for (size_t i = 0; i < 8; i++) {
val64 <<= 8;
val64 |= cnx_id.id[i];
}
}
return val64;
}
void picoquic_set64_connection_id(picoquic_connection_id_t * cnx_id, uint64_t val64)
{
for (int i = 7; i >= 0; i--) {
cnx_id->id[i] = (uint8_t)(val64 & 0xFF);
val64 >>= 8;
}
for (size_t i = 8; i < sizeof(cnx_id->id); i++) {
cnx_id->id[i] = 0;
}
cnx_id->id_len = 8;
}
uint64_t picoquic_hash_addr(const struct sockaddr* addr)
{
uint64_t h;
if (addr->sa_family == AF_INET) {
struct sockaddr_in* a4 = (struct sockaddr_in*)addr;
h = picohash_bytes((uint8_t*)&a4->sin_addr , 4);
h += 128ull * a4->sin_port;
}
else {
struct sockaddr_in6* a6 = (struct sockaddr_in6*)addr;
h = picohash_bytes((uint8_t*)& a6->sin6_addr, 16);
h += 128ull * a6->sin6_port;
}
return h;
}
int picoquic_compare_addr(const struct sockaddr * expected, const struct sockaddr * actual)
{
int ret = -1;
if (expected->sa_family == actual->sa_family) {
if (expected->sa_family == AF_INET) {
struct sockaddr_in * ex = (struct sockaddr_in *)expected;
struct sockaddr_in * ac = (struct sockaddr_in *)actual;
if (ex->sin_port == ac->sin_port &&
#ifdef _WINDOWS
ex->sin_addr.S_un.S_addr == ac->sin_addr.S_un.S_addr) {
#else
ex->sin_addr.s_addr == ac->sin_addr.s_addr){
#endif
ret = 0;
}
} else {
struct sockaddr_in6 * ex = (struct sockaddr_in6 *)expected;
struct sockaddr_in6 * ac = (struct sockaddr_in6 *)actual;
if (ex->sin6_port == ac->sin6_port &&
memcmp(&ex->sin6_addr, &ac->sin6_addr, 16) == 0) {
ret = 0;
}
}
}
return ret;
}
int picoquic_addr_length(const struct sockaddr* addr)
{
int len = 0;
if (addr->sa_family == AF_INET) {
len = (int)sizeof(struct sockaddr_in);
} else if (addr->sa_family == AF_INET6) {
len = (int)sizeof(struct sockaddr_in6);
}
return len;
}
/* Copy a sockaddr to a storage value, and return the copied address length */
void picoquic_store_addr(struct sockaddr_storage * stored_addr, const struct sockaddr * addr)
{
int len = 0;
if (addr == NULL || (len = picoquic_addr_length(addr)) == 0) {
stored_addr->ss_family = 0;
}
else {
memcpy(stored_addr, addr, len);
}
}
/* Return a pointer to the IP address and IP length in a sockaddr */
void picoquic_get_ip_addr(struct sockaddr * addr, uint8_t ** ip_addr, uint8_t * ip_addr_len)
{
if (addr->sa_family == AF_INET) {
*ip_addr = (uint8_t *)&((struct sockaddr_in *)addr)->sin_addr;
*ip_addr_len = 4;
}
else if(addr->sa_family == AF_INET6) {
*ip_addr = (uint8_t *)&((struct sockaddr_in6 *)addr)->sin6_addr;
*ip_addr_len = 16;
}
else {
*ip_addr = NULL;
*ip_addr_len = 0;
}
}
/* Store a test address */
int picoquic_store_text_addr(struct sockaddr_storage* stored_addr, const char* ip_address_text, uint16_t port)
{
int ret = 0;
struct sockaddr_in* ipv4_addr = (struct sockaddr_in*)stored_addr;
struct sockaddr_in6* ipv6_addr = (struct sockaddr_in6*)stored_addr;
/* get the IP address of the server */
memset(stored_addr, 0, sizeof(struct sockaddr_storage));
if (inet_pton(AF_INET, ip_address_text, &ipv4_addr->sin_addr) == 1) {
/* Valid IPv4 address */
ipv4_addr->sin_family = AF_INET;
ipv4_addr->sin_port = htons((unsigned short)port);
}
else if (inet_pton(AF_INET6, ip_address_text, &ipv6_addr->sin6_addr) == 1) {
/* Valid IPv6 address */
ipv6_addr->sin6_family = AF_INET6;
ipv6_addr->sin6_port = htons((unsigned short)port);
}
else {
ret = -1;
}
return ret;
}
/* Get text string for address and port */
char const* picoquic_addr_text(struct sockaddr* addr, char* text, size_t text_size)
{
char addr_buffer[128];
char const* addr_text;
char const* ret_text = "?:?";
switch (addr->sa_family) {
case AF_INET:
addr_text = inet_ntop(AF_INET,
(const void*)(&((struct sockaddr_in*)addr)->sin_addr),
addr_buffer, sizeof(addr_buffer));
if (picoquic_sprintf(text, text_size, NULL, "%s:%d", addr_text, ((struct sockaddr_in*) addr)->sin_port) == 0) {
ret_text = text;
}
break;
case AF_INET6:
addr_text = inet_ntop(AF_INET6,
(const void*)(&((struct sockaddr_in6*)addr)->sin6_addr),
addr_buffer, sizeof(addr_buffer));
if (picoquic_sprintf(text, text_size, NULL, "[%s]:%d", addr_text, ((struct sockaddr_in6*) addr)->sin6_port) == 0) {
ret_text = text;
}
default:
break;
}
return ret_text;
}
/* Return a directory path based on solution dir and file name */
char const* picoquic_solution_dir = NULL;
void picoquic_set_solution_dir(char const* solution_dir)
{
picoquic_solution_dir = solution_dir;
}
int picoquic_get_input_path(char * target_file_path, size_t file_path_max, const char * solution_path, const char * file_name)
{
if (solution_path == NULL) {
solution_path = PICOQUIC_DEFAULT_SOLUTION_DIR;
}
const char * separator = PICOQUIC_FILE_SEPARATOR;
size_t solution_path_length = strlen(solution_path);
if (solution_path_length != 0 && solution_path[solution_path_length - 1] == separator[0]) {
separator = "";
}
int ret = picoquic_sprintf(target_file_path, file_path_max, NULL, "%s%s%s",
solution_path, separator, file_name);
return ret;
}
/* Safely open files in a portable way */
FILE * picoquic_file_open_ex(char const * file_name, char const * flags, int * last_err)
{
FILE * F;
#ifdef _WINDOWS
errno_t err = fopen_s(&F, file_name, flags);
if (err != 0){
if (last_err != NULL) {
*last_err = err;
}
if (F != NULL) {
fclose(F);
F = NULL;
}
}
#else
F = fopen(file_name, flags);
if (F == NULL && last_err != NULL) {
*last_err = errno;
}
#endif
return F;
}
FILE* picoquic_file_open(char const* file_name, char const* flags)
{
return picoquic_file_open_ex(file_name, flags, NULL);
}
/* Safely close files in a portable way */
FILE * picoquic_file_close(FILE * F)
{
if (F != NULL) {
(void)fclose(F);
}
return NULL;
}
/* Safely delete file in a portable way */
int picoquic_file_delete(char const * file_name, int * last_err)
{
int ret;
#ifdef _WINDOWS
ret = _unlink(file_name);
if (last_err != NULL && ret != 0) {
*last_err = errno;
}
#else
ret = unlink(file_name);
if (last_err != NULL && ret != 0) {
*last_err = errno;
}
#endif
return ret;
}
/* Skip and decode function.
* These functions return NULL in case of a failure (insufficient buffer).
*/
const uint8_t* picoquic_frames_fixed_skip(const uint8_t* bytes, const uint8_t* bytes_max, size_t size)
{
return (bytes += size) <= bytes_max ? bytes : NULL;
}
const uint8_t* picoquic_frames_varint_skip(const uint8_t* bytes, const uint8_t* bytes_max)
{
return bytes < bytes_max ? picoquic_frames_fixed_skip(bytes, bytes_max, VARINT_LEN(bytes)) : NULL;
}
/* Parse a varint. In case of an error, *n64 is unchanged, and NULL is returned */
const uint8_t* picoquic_frames_varint_decode(const uint8_t* bytes, const uint8_t* bytes_max, uint64_t* n64)
{
uint8_t length;
if (bytes < bytes_max && bytes + (length = (uint8_t)VARINT_LEN(bytes)) <= bytes_max) {
uint64_t v = *bytes++ & 0x3F;
while (--length > 0) {
v <<= 8;
v += *bytes++;
}
*n64 = v;
}
else {
bytes = NULL;
}
return bytes;
}
const uint8_t* picoquic_frames_varlen_decode(const uint8_t* bytes, const uint8_t* bytes_max, size_t* n)
{
uint64_t len = 0;
bytes = picoquic_frames_varint_decode(bytes, bytes_max, &len);
*n = (size_t)len;
return (*n == len) ? bytes : NULL;
}
const uint8_t* picoquic_frames_uint8_decode(const uint8_t* bytes, const uint8_t* bytes_max, uint8_t* n)
{
if (bytes < bytes_max) {
*n = *bytes++;
}
else {
bytes = NULL;
}
return bytes;
}
const uint8_t* picoquic_frames_uint16_decode(const uint8_t* bytes, const uint8_t* bytes_max, uint16_t* n)
{
if (bytes + sizeof(*n) <= bytes_max) {
*n = PICOPARSE_16(bytes);
bytes += sizeof(*n);
}
else {
bytes = NULL;
}
return bytes;
}
const uint8_t* picoquic_frames_uint32_decode(const uint8_t* bytes, const uint8_t* bytes_max, uint32_t* n)
{
if (bytes + sizeof(*n) <= bytes_max) {
*n = PICOPARSE_32(bytes);
bytes += sizeof(*n);
}
else {
bytes = NULL;
}
return bytes;
}
const uint8_t* picoquic_frames_uint64_decode(const uint8_t* bytes, const uint8_t* bytes_max, uint64_t* n)
{
if (bytes + sizeof(*n) <= bytes_max) {
*n = PICOPARSE_64(bytes);
bytes += sizeof(*n);
}
else {
bytes = NULL;
}
return bytes;
}
const uint8_t* picoquic_frames_length_data_skip(const uint8_t* bytes, const uint8_t* bytes_max)
{
uint64_t length;
if ((bytes = picoquic_frames_varint_decode(bytes, bytes_max, &length)) != NULL) {
bytes = picoquic_frames_fixed_skip(bytes, bytes_max, (size_t)length);
}
return bytes;
}
const uint8_t* picoquic_frames_cid_decode(const uint8_t* bytes, const uint8_t* bytes_max, picoquic_connection_id_t* cid)
{
bytes = picoquic_frames_uint8_decode(bytes, bytes_max, &cid->id_len);
if (cid->id_len > PICOQUIC_CONNECTION_ID_MAX_SIZE ||
bytes + cid->id_len > bytes_max) {
bytes = NULL;
}
else {
memset(cid->id, 0, sizeof(cid->id));
memcpy(cid->id, bytes, cid->id_len);
bytes += cid->id_len;
}
return bytes;
}
/* Encoding functions of the form uint8_t * picoquic_frame_XXX_encode(uint8_t * bytes, uint8_t * bytes-max, ...)
*/
uint8_t* picoquic_frames_varint_encode(uint8_t* bytes, const uint8_t* bytes_max, uint64_t n64)
{
if (n64 < 16384) {
if (n64 < 64) {
if (bytes + 1 <= bytes_max) {
*bytes++ = (uint8_t)(n64);
}
else {
bytes = NULL;
}
}
else {
if (bytes + 2 <= bytes_max) {
*bytes++ = (uint8_t)((n64 >> 8) | 0x40);
*bytes++ = (uint8_t)(n64);
}
else {
bytes = NULL;
}
}
}
else if (n64 < 1073741824) {
if (bytes + 4 <= bytes_max) {
*bytes++ = (uint8_t)((n64 >> 24) | 0x80);
*bytes++ = (uint8_t)(n64 >> 16);
*bytes++ = (uint8_t)(n64 >> 8);
*bytes++ = (uint8_t)(n64);
}
else {
bytes = NULL;
}
}
else {
if (bytes + 8 <= bytes_max) {
*bytes++ = (uint8_t)((n64 >> 56) | 0xC0);
*bytes++ = (uint8_t)(n64 >> 48);
*bytes++ = (uint8_t)(n64 >> 40);
*bytes++ = (uint8_t)(n64 >> 32);
*bytes++ = (uint8_t)(n64 >> 24);
*bytes++ = (uint8_t)(n64 >> 16);
*bytes++ = (uint8_t)(n64 >> 8);
*bytes++ = (uint8_t)(n64);
}
else {
bytes = NULL;
}
}
return bytes;
}
uint8_t* picoquic_frames_varlen_encode(uint8_t* bytes, const uint8_t* bytes_max, size_t n)
{
return picoquic_frames_varint_encode(bytes, bytes_max, n);
}
uint8_t* picoquic_frames_uint8_encode(uint8_t* bytes, const uint8_t* bytes_max, uint8_t n)
{
if (bytes + sizeof(n) > bytes_max) {
bytes = NULL;
}
else {
*bytes++ = n;
}
return (bytes);
}
uint8_t* picoquic_frames_uint16_encode(uint8_t* bytes, const uint8_t* bytes_max, uint16_t n)
{
if (bytes + sizeof(n) > bytes_max) {
bytes = NULL;
}
else {
*bytes++ = (uint8_t)(n >> 8);
*bytes++ = (uint8_t)n;
}
return (bytes);
}
uint8_t* picoquic_frames_uint24_encode(uint8_t* bytes, const uint8_t* bytes_max, uint32_t n)
{
if (bytes + 3 > bytes_max) {
bytes = NULL;
}
else {
*bytes++ = (uint8_t)(n >> 16);
*bytes++ = (uint8_t)(n >> 8);
*bytes++ = (uint8_t)n;
}
return (bytes);
}
uint8_t* picoquic_frames_uint32_encode(uint8_t* bytes, const uint8_t* bytes_max, uint32_t n)
{
if (bytes + sizeof(n) > bytes_max) {
bytes = NULL;
}
else {
*bytes++ = (uint8_t)(n >> 24);
*bytes++ = (uint8_t)(n >> 16);
*bytes++ = (uint8_t)(n >> 8);
*bytes++ = (uint8_t)n;
}
return (bytes);
}
uint8_t* picoquic_frames_uint64_encode(uint8_t* bytes, const uint8_t* bytes_max, uint64_t n)
{
if (bytes + sizeof(n) > bytes_max) {
bytes = NULL;
}
else {
*bytes++ = (uint8_t)(n >> 56);
*bytes++ = (uint8_t)(n >> 48);
*bytes++ = (uint8_t)(n >> 40);
*bytes++ = (uint8_t)(n >> 32);
*bytes++ = (uint8_t)(n >> 24);
*bytes++ = (uint8_t)(n >> 16);
*bytes++ = (uint8_t)(n >> 8);
*bytes++ = (uint8_t)n;
}
return (bytes);
}
uint8_t* picoquic_frames_length_data_encode(uint8_t* bytes, const uint8_t* bytes_max, size_t l, const uint8_t* v)
{
if ((bytes = picoquic_frames_varlen_encode(bytes, bytes_max, l)) != NULL &&
(bytes + l) <= bytes_max) {
memcpy(bytes, v, l);
bytes += l;
}
else {
bytes = NULL;
}
return bytes;
}
uint8_t* picoquic_frames_cid_encode(uint8_t* bytes, const uint8_t* bytes_max, const picoquic_connection_id_t* cid)
{
return picoquic_frames_length_data_encode(bytes, bytes_max, cid->id_len, cid->id);
}
/* Constant time memory comparison. This is only required now for
* the comparison of 16 bytes long stateless reset secrets, so we have
* only minimal requriements for performance, and privilege portability.
*
* Returns uint64_t value so the code works for arbitrary length.
* Value is zero if strings match.
*/
int picoquic_constant_time_memcmp(const uint8_t* x, const uint8_t* y, size_t l)
{
uint64_t ret = 0;
while (l > 0) {
ret += (*x++ ^ *y++);
l--;
}
return (ret == 0)?0:-1;
}
/* Minimal support for threads.
*/
#ifndef _WINDOWS
static void picoquic_set_abs_delay(struct timespec* ts, uint64_t microsec_wait) {
clock_gettime(CLOCK_REALTIME, ts);
ts->tv_sec += (unsigned long)(microsec_wait / 1000000);
ts->tv_nsec += (unsigned long)((microsec_wait % 1000000)*1000);
if (ts->tv_nsec > 1000000000) {
ts->tv_sec++;
ts->tv_nsec -= 1000000000;
}
}
#endif
int picoquic_create_thread(picoquic_thread_t * thread, picoquic_thread_fn thread_fn, void * arg)
{
#ifdef _WINDOWS
int ret = 0;
*thread = CreateThread(NULL, 0, thread_fn, arg, 0, NULL);
if (*thread == NULL) {
ret = GetLastError();
}
#else
int ret = pthread_create(thread, NULL, thread_fn, arg);
#endif
return ret;
}
void picoquic_delete_thread(picoquic_thread_t * thread)
{
#ifdef _WINDOWS
/* Wait until background thread has terminated, or timeout in milliseconds */
if (WaitForMultipleObjects(1, thread, TRUE, 1000) == WAIT_TIMEOUT) {
/* if soft wait fails, then hard cancel */
TerminateThread(*thread, 0);
}
/* Close the thread handle */
CloseHandle(*thread);
*thread = NULL;
#else
if (pthread_join(*thread, NULL) != 0) {
(void)pthread_cancel(*thread);
}
#endif
}
int picoquic_create_mutex(picoquic_mutex_t * mutex)
{
#ifdef _WINDOWS
int ret = 0;
*mutex = CreateMutex(NULL, FALSE, NULL);
if (*mutex == NULL) {
ret = -1;
}
#else
int ret = pthread_mutex_init(mutex, NULL);
#endif
return ret;
}
int picoquic_delete_mutex(picoquic_mutex_t* mutex)
{
#ifdef _WINDOWS
int ret = 0;
CloseHandle(*mutex);
*mutex = NULL;
#else
int ret = pthread_mutex_destroy(mutex);
#endif
return ret;
}
int picoquic_lock_mutex(picoquic_mutex_t * mutex)
{
#ifdef _WINDOWS
int ret = 0;
DWORD w_ret = WaitForSingleObject(*mutex, INFINITE);
if (w_ret != WAIT_OBJECT_0) {
ret = -1;
}
#else
int ret = pthread_mutex_lock(mutex);
#endif
return ret;
}
int picoquic_unlock_mutex(picoquic_mutex_t * mutex)
{
#ifdef _WINDOWS
int ret = 0;
if (!ReleaseMutex(*mutex)) {
ret = -1;
}
#else
int ret = pthread_mutex_unlock(mutex);
#endif
return ret;
}
int picoquic_create_event(picoquic_event_t* event)
{
#ifdef _WINDOWS
int ret = 0;
*event = CreateEvent(NULL, TRUE, FALSE, NULL);
if (*event == NULL) {
ret = -1;
}
#else
int ret;
memset(event, 0, sizeof(picoquic_event_t));
ret = pthread_mutex_init(&event->mutex, NULL);
if (ret == 0) {
ret = pthread_cond_init(&event->cond, NULL);
}
#endif
return ret;
}
void picoquic_delete_event(picoquic_event_t* event)
{
#ifdef _WINDOWS
CloseHandle(*event);
*event = NULL;
#else
(void)pthread_mutex_destroy(&event->mutex);
(void)pthread_cond_destroy(&event->cond);
memset(event, 0, sizeof(picoquic_event_t));
#endif
}
int picoquic_signal_event(picoquic_event_t* event)
{
#ifdef _WINDOWS
int ret = 0;
if (!SetEvent(*event)){
ret = -1;
}
#else
int ret;
(void)pthread_mutex_lock(&event->mutex);
ret = pthread_cond_broadcast(&event->cond);
(void)pthread_mutex_unlock(&event->mutex);
#endif
return ret;
}
int picoquic_wait_for_event(picoquic_event_t* event, uint64_t microsec_wait)
{
#ifdef _WINDOWS
int ret = 0;
DWORD dwWaitResult;
DWORD millisec_wait = (microsec_wait == UINT64_MAX) ? INFINITE : (DWORD)(microsec_wait / 1000);
dwWaitResult = WaitForSingleObject(*event, millisec_wait);
if (dwWaitResult == WAIT_OBJECT_0) {
(void)ResetEvent(*event);
} else {
ret = -1;
}
#else
int ret;
(void)pthread_mutex_lock(&event->mutex);
if (microsec_wait == UINT64_MAX) {
ret = pthread_cond_wait(&event->cond, &event->mutex);
}
else {
struct timespec abstime;
picoquic_set_abs_delay(&abstime, microsec_wait);
ret = pthread_cond_timedwait(&event->cond, &event->mutex, &abstime);
}
(void)pthread_mutex_unlock(&event->mutex);
#endif
return ret;
}
/* Pseudo random generation suitable for tests. Guaranties that the
* same seed will produce the same sequence, allows for specific
* random sequence for a given test.
* Adapted from http://xoroshiro.di.unimi.it/splitmix64.c,
* Written in 2015 by Sebastiano Vigna (vigna@acm.org) */
uint64_t picoquic_test_random(uint64_t* random_context)
{
uint64_t z;
*random_context += 0x9e3779b97f4a7c15;
z = *random_context;
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ull;
z = (z ^ (z >> 27)) * 0x94d049bb133111ebull;
return z ^ (z >> 31);
}
void picoquic_test_random_bytes(uint64_t* random_context, uint8_t* bytes, size_t bytes_max)
{
size_t byte_index = 0;
while (byte_index < bytes_max) {
uint64_t v = picoquic_test_random(random_context);
for (int i = 0; i < 8 && byte_index < bytes_max; i++) {
bytes[byte_index++] = v & 0xFF;
v >>= 8;
}
}
}
uint64_t picoquic_test_uniform_random(uint64_t* random_context, uint64_t rnd_max)
{
uint64_t rnd = 0;
if (rnd_max > 0) {
uint64_t rnd_min = ((uint64_t)((int64_t)-1)) % rnd_max;
do {
rnd = picoquic_test_random(random_context);
} while (rnd < rnd_min);
rnd %= rnd_max;
}
return rnd;
}
double picoquic_test_gauss_random(uint64_t* random_context)
{
double dx = 0;
/* Sum of 12 variables in [0..1], provides
* average = 6.0, stdev = 3.0 */
for (int i = 0; i < 12; i++) {
double d;
uint64_t r = picoquic_test_random(random_context);
r ^= r >> 17;
r ^= r >> 34;
d = (double)(r & 0x1ffff) + 0.5;
d /= (double)(0x20000);
dx += d;
}
dx -= 6.0;
return dx;
}