1133 lines
28 KiB
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;
|
|
}
|