LSQUIC Client: Initial release

This commit is contained in:
Dmitri Tikhonov 2017-09-22 17:00:03 -04:00
commit 50aadb33c7
183 changed files with 99199 additions and 0 deletions

89
CMakeLists.txt Normal file
View File

@ -0,0 +1,89 @@
# Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE.
cmake_minimum_required(VERSION 2.8)
project(lsquic)
# We prefer clang
IF(NOT ("${CMAKE_C_COMPILER}" MATCHES "ccc-analyzer" OR
"${CMAKE_C_COMPILER}" MATCHES "gcc" OR
"${CMAKE_C_COMPILER}" MATCHES "afl-gcc"))
FIND_PROGRAM(CLANG "clang")
IF(CLANG)
SET(CMAKE_C_COMPILER "${CLANG}")
ENDIF()
ENDIF()
# By default, we compile in development mode. To compile production code,
# pass -DDEVEL_MODE=0 to cmake (before that, `make clean' and remove any
# cmake cache files).
#
IF(NOT DEFINED DEVEL_MODE)
SET(DEVEL_MODE 1)
ENDIF()
MESSAGE(STATUS "DEVEL_MODE: ${DEVEL_MODE}")
SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -Wall -Wextra -Wno-unused-parameter")
SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -fno-omit-frame-pointer")
IF(CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9)
SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -Wno-missing-field-initializers")
ENDIF()
IF(DEVEL_MODE EQUAL 1)
SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -O0 -g3")
# -Werror is used to force us to fix warnings early.
SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -Werror")
IF(CMAKE_C_COMPILER MATCHES "clang")
SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -fsanitize=address")
ENDIF()
# Uncomment to enable fault injection testing via libfiu:
#SET (MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -DFIU_ENABLE=1")
#SET (FIULIB "fiu")
ELSE()
SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -O3 -g0")
SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -DNDEBUG")
# Comment out the following line to compile out debug messages:
#SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -DLSQUIC_LOWEST_LOG_LEVEL=LSQ_LOG_INFO")
ENDIF()
IF(MY_CMAKE_FLAGS MATCHES "fsanitize=address")
MESSAGE(STATUS "AddressSanitizer is ON")
ELSE()
MESSAGE(STATUS "AddressSanitizer is OFF")
ENDIF()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${MY_CMAKE_FLAGS} $ENV{EXTRA_CFLAGS}")
MESSAGE(STATUS "Compiler flags: ${CMAKE_C_FLAGS}")
SET(CMAKE_INCLUDE_CURRENT_DIR ON)
include_directories( include )
include_directories( ${BORINGSSL_INCLUDE} )
link_directories( ${BORINGSSL_LIB} )
IF(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
# Find libevent on FreeBSD:
include_directories( /usr/local/include )
link_directories( /usr/local/lib )
ENDIF()
add_executable(http_client test/http_client.c test/prog.c test/test_common.c test/test_cert.c)
target_link_libraries(http_client lsquic event pthread libssl.a libcrypto.a libdecrepit.a ${FIULIB} z m)
add_subdirectory(src)
IF(DEVEL_MODE EQUAL 1)
# Our test framework relies on assertions, only compile if assertions are
# enabled.
#
add_subdirectory(test)
enable_testing()
ENDIF()
ADD_CUSTOM_TARGET(docs doxygen dox.cfg)

134
EXAMPLES.txt Normal file
View File

@ -0,0 +1,134 @@
# Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE.
LSQUIC Examples
===============
test/http_client.c demonstrates how to use HTTP features of QUIC.
Usage Examples
--------------
Fetch Google's home page:
./http_client -H www.google.com -s 74.125.22.106:443 -p /
In the example above, -H specifies the domain; it is also used as the value
of SNI paramater in the handshake.
POST a file to calculate its CRC32 checksum:
./http_client -H www.litespeedtech.com -s 127.0.0.1:443 \
-p /cgi-bin/crc32.cgi -P file-256M -M POST
HTTP/1.1 200 OK
content-type: text/plain
date: Fri, 09 Jun 2017 08:40:45 GMT
server: LiteSpeed
alt-svc: quic=":443"; v="35,37"
CRC32: 2A0E7DBB
This is a good way to check that the payload gets to the other side
correctly. The CGI script is:
#!/usr/bin/perl
use String::CRC32;
printf "Content-type: text/plain\r\n\r\nCRC32: %X\n", crc32(*STDIN)
On the command line, I do
alias crc32="perl -MString::CRC32 -e'printf qq(%X\n), crc32(<>)'"
To submit several requests concurrently, one can use -n and -r options:
./http_client -H www.litespeedtech.com -s 127.0.0.1:443 \
-p /cgi-bin/crc32.cgi -P file-256M -M POST -n 3 -r 10
This will open three parallel connections which will make ten POST
requests together.
To perform load testing, it is good to mix sending and receiving data:
for i in {1..100}; do
./http_client $COMMON_OPTS -p /cgi-bin/crc32.cgi -P file-256M \
-M POST >out-post.$i &
./http_client $COMMON_OPTS -p /docs/file-256M >out-get.$i &
sleep 1
done
If you don't want to create a hundred 256-megabyte out-get.* files, use -K
flag to discard output.
Control QUIC Settings via -o Flag
---------------------------------
Most of the settings in struct lsquic_engine_settings can be controlled
via -o flag. With exception of es_versions, which is a bit mask, other
es_* options can be mapped to corresponding -o value via s/^es_//:
es_cfcw => -o cwcf=12345
es_max_streams_in => -o max_streams_in=123
And so on.
The code to set options via -o flag lives in set_engine_option(). It is good
to update this function at the same time as member fields are added to struct
lsquic_engine_settings.
Control LSQUIC Behavior via Environment Variables
-------------------------------------------------
LSQUIC_CUBIC_SHIFT_EPOCH
This environment variable determines whether cubic epoch is shifted
when sender is application-limited.
This is a leftover from the time when application-limited behavior was
implemented and is only available in debug builds. By default, the
epoch is shifted.
LSQUIC_PACER_INTERTICK
Number of microsecods to use as constant intertick time in lieu of the
pacer's dynamic intertick time approximation.
Only available in debug builds.
Control Network-Related Stuff
-----------------------------
-D Set `do not fragment' flag on outgoing UDP packets.
-z BYTES Maximum size of outgoing UDP packets. The default is 1370
bytes for IPv4 socket and 1350 bytes for IPv6 socket.
-S opt=val Socket options. Supported options:
sndbuf=12345 # Sets SO_SNDBUF
rcvbuf=12345 # Sets SO_RCVBUF
More Compilation Options
------------------------
-DFULL_CONN_STATS=1
Track some statistics about full connection -- packets in, sent, delayed,
stream payload per packet size ratio, and some others -- and print them
at NOTICE level when connection is destroyed.
This is useful when performing network testing and especially analyzing
the effects of changing send buffer size (see -S sndbuf= in the previous
section).
-DLSQUIC_PACKINTS_SANITY_CHECK=1
Turn on sanity checking for packet interval code. The packet interval
code, shared by both send and receive history modules, contained a bug
which prompted me to add a checking function.
-DLSQUIC_SEND_STATS=0
Turn off statistics collection performed by the send controller: number
of packets sent, resent, and delayed.
-DLSQUIC_LOWEST_LOG_LEVEL=LSQ_LOG_WARN
If you want to go even faster: compile out some log levels entirely.

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 LiteSpeed Technologies Inc
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

32
LICENSE.chrome Normal file
View File

@ -0,0 +1,32 @@
A few parts of LiteSpeed QUIC library are based on proto-quic. That
code is covered by this additional license:
------------------------------
Copyright 2015 The Chromium Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

40
README.md Normal file
View File

@ -0,0 +1,40 @@
LiteSpeed QUIC (LSQUIC) Client Library README
=============================================
Description
-----------
LiteSpeed QUIC (LSQUIC) Client Library is an open-source implementation
of QUIC functionality for clients. It is released in the hope to speed
the adoption of QUIC. Most of the code in this distribution is used in
our own products: LiteSpeed Web Server and ADC. We think it is free of
major problems. Nevertheless, do not hesitate to report bugs back to us.
Even better, send us fixes and improvements!
Currently supported QUIC versions are Q035, Q037, Q038, Q039, and Q040.
Support for newer versions will be added soon after they are released.
The version(s) specified by IETF QUIC WG will be added once the IETF
version of the protocol settles down a little.
Documentation
-------------
The documentation for this module is admittedly sparse. The API is
documented in include/lsquic.h. If you have doxygen, you can run
`doxygen dox.cfg' or `make docs'. The example program is
test/http_client.c: a bare-bones, but working, QUIC client. Have a look
in EXAMPLES.txt to see how it can be used.
Building
--------
To build LSQUIC, you need CMake and BoringSSL. The example program
uses libevent to provide the event loop. In short:
cmake -DBORINGSSL_INCLUDE=/some/dir -DBORINGSSL_LIB=/some/other/dir .
Have fun,
LiteSpeed QUIC Team.
Copyright (c) 2017 LiteSpeed Technologies Inc

2427
dox.cfg Normal file

File diff suppressed because it is too large Load Diff

883
include/lsquic.h Normal file
View File

@ -0,0 +1,883 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef __LSQUIC_H__
#define __LSQUIC_H__
/**
* @file
* public API for using liblsquic is defined in this file.
*
*/
#include <stdarg.h>
#include <lsquic_types.h>
#include <sys/uio.h>
#include <sys/types.h>
#include <time.h>
struct iovec;
struct sockaddr;
#ifdef __cplusplus
extern "C" {
#endif
/**
* Engine flags:
*/
/** Server mode */
#define LSENG_SERVER (1 << 0)
/** Treat stream 3 as headers stream and, in general, behave like the
* regular QUIC.
*/
#define LSENG_HTTP (1 << 1)
#define LSENG_HTTP_SERVER (LSENG_SERVER|LSENG_HTTP)
/**
* This is a list of QUIC versions that we know of. List of supported
* versions is in LSQUIC_SUPPORTED_VERSIONS.
*/
enum lsquic_version
{
/** Q035. This is the first version to be supported by LSQUIC. */
LSQVER_035,
/**
* Q037. This version is like Q035, except the way packet hashes are
* generated is different for clients and servers. In addition, new
* option NSTP (no STOP_WAITING frames) is rumored to be supported at
* some point in the future.
*/
LSQVER_037,
/**
* Q038. Based on Q037, supports PADDING frames in the middle of packet
* and NSTP (no STOP_WAITING frames) option.
*/
LSQVER_038,
/**
* Q039. Switch to big endian. Do not ack acks. Send connection level
* WINDOW_UPDATE frame every 20 sent packets which do not contain
* retransmittable frames.
*/
LSQVER_039,
/**
* Q040. RST_STREAM, ACK and STREAM frames match IETF format.
*/
LSQVER_040,
N_LSQVER
};
/**
* We currently support versions 35, 37, 38, 39, and 40.
* @see lsquic_version
*/
#define LSQUIC_SUPPORTED_VERSIONS ((1 << LSQVER_035) | (1 << LSQVER_037) | \
(1 << LSQVER_038) | (1 << LSQVER_039) | (1 << LSQVER_040))
#define LSQUIC_EXPERIMENTAL_VERSIONS ((1 << LSQVER_040))
/**
* @struct lsquic_stream_if
* @brief The definition of callback functions call by lsquic_stream to
* process events.
*
*/
struct lsquic_stream_if {
/**
* Use @ref lsquic_conn_get_ctx to get back the context. It is
* OK for this function to return NULL.
*/
lsquic_conn_ctx_t *(*on_new_conn)(void *stream_if_ctx,
lsquic_conn_t *c);
/** This is called when our side received GOAWAY frame. After this,
* new streams should not be created. The callback is optional.
*/
void (*on_goaway_received)(lsquic_conn_t *c);
void (*on_conn_closed)(lsquic_conn_t *c);
/** If you need to initiate a connection, call lsquic_conn_make_stream().
* This will cause `on_new_stream' callback to be called when appropriate
* (this operation is delayed when maximum number of outgoing streams is
* reached).
*
* After `on_close' is called, the stream is no longer accessible.
*/
lsquic_stream_ctx_t *
(*on_new_stream)(void *stream_if_ctx, lsquic_stream_t *s);
void (*on_read) (lsquic_stream_t *s, lsquic_stream_ctx_t *h);
void (*on_write) (lsquic_stream_t *s, lsquic_stream_ctx_t *h);
void (*on_close) (lsquic_stream_t *s, lsquic_stream_ctx_t *h);
};
/**
* Minimum flow control window is set to 16 KB for both client and server.
* This means we can send up to this amount of data before handshake gets
* completed.
*/
#define LSQUIC_MIN_FCW (16 * 1024)
/* Each LSQUIC_DF_* value corresponds to es_* entry in
* lsquic_engine_settings below.
*/
/**
* By default, experimental versions are not included.
*/
#define LSQUIC_DF_VERSIONS (LSQUIC_SUPPORTED_VERSIONS & \
~LSQUIC_EXPERIMENTAL_VERSIONS)
#define LSQUIC_DF_CFCW_SERVER (3 * 1024 * 1024 / 2)
#define LSQUIC_DF_CFCW_CLIENT (15 * 1024 * 1024)
#define LSQUIC_DF_SFCW_SERVER (1 * 1024 * 1024)
#define LSQUIC_DF_SFCW_CLIENT (6 * 1024 * 1024)
#define LSQUIC_DF_MAX_STREAMS_IN 100
/**
* Default handshake timeout in microseconds.
*/
#define LSQUIC_DF_HANDSHAKE_TO (10 * 1000 * 1000)
#define LSQUIC_DF_IDLE_CONN_TO (30 * 1000 * 1000)
#define LSQUIC_DF_SILENT_CLOSE 1
/** Default value of maximum header list size. If set to non-zero value,
* SETTINGS_MAX_HEADER_LIST_SIZE will be sent to peer after handshake is
* completed (assuming the peer supports this setting frame type).
*/
#define LSQUIC_DF_MAX_HEADER_LIST_SIZE 0
/** Default value of UAID (user-agent ID). */
#define LSQUIC_DF_UA "LSQUIC"
#define LSQUIC_DF_STTL 86400
#define LSQUIC_DF_MAX_INCHOATE (1 * 1000 * 1000)
#define LSQUIC_DF_SUPPORT_SREJ_SERVER 1
#define LSQUIC_DF_SUPPORT_SREJ_CLIENT 0 /* TODO: client support */
/** Do not use NSTP by default */
#define LSQUIC_DF_SUPPORT_NSTP 0
#define LSQUIC_DF_SUPPORT_PUSH 1
#define LSQUIC_DF_SUPPORT_TCID0 1
/** By default, LSQUIC ignores Public Reset packets. */
#define LSQUIC_DF_HONOR_PRST 0
/** By default, infinite loop checks are turned on */
#define LSQUIC_DF_PROGRESS_CHECK 1000
/** By default, Pending RW Queue infinite loop checks are turned on: */
#define LSQUIC_DF_PENDRW_CHECK 10
/** By default, read/write events are dispatched in a loop */
#define LSQUIC_DF_RW_ONCE 0
/** By default, the threshold is not enabled */
#define LSQUIC_DF_PROC_TIME_THRESH 0
/** By default, packets are paced */
#define LSQUIC_DF_PACE_PACKETS 1
struct lsquic_engine_settings {
/**
* This is a bit mask wherein each bit corresponds to a value in
* enum lsquic_version. Client starts negotiating with the highest
* version and goes down. Server supports either of the versions
* specified here.
*
* @see lsquic_version
*/
unsigned es_versions;
/**
* Initial default CFCW.
*
* In server mode, per-connection values may be set lower than
* this if resources are scarce.
*
* Do not set es_cfcw and es_sfcw lower than @ref LSQUIC_MIN_FCW.
*
* @see es_max_cfcw
*/
unsigned es_cfcw;
/**
* Initial default SFCW.
*
* In server mode, per-connection values may be set lower than
* this if resources are scarce.
*
* Do not set es_cfcw and es_sfcw lower than @ref LSQUIC_MIN_FCW.
*
* @see es_max_sfcw
*/
unsigned es_sfcw;
/**
* This value is used to specify maximum allowed value CFCW is allowed
* to reach due to window auto-tuning. By default, this value is zero,
* which means that CFCW is not allowed to increase from its initial
* value.
*
* @see es_cfcw
*/
unsigned es_max_cfcw;
unsigned es_max_sfcw;
/** MIDS */
unsigned es_max_streams_in;
/**
* Handshake timeout in microseconds.
*
* For client, this can be set to an arbitrary value (zero turns the
* timeout off).
*
*/
unsigned long es_handshake_to;
/** ICSL in microseconds */
unsigned long es_idle_conn_to;
/** SCLS (silent close) */
int es_silent_close;
/**
* This corresponds to SETTINGS_MAX_HEADER_LIST_SIZE
* (RFC 7540, Section 6.5.2). 0 means no limit. Defaults
* to @ref LSQUIC_DF_MAX_HEADER_LIST_SIZE.
*/
unsigned es_max_header_list_size;
/** UAID -- User-Agent ID. Defaults to @ref LSQUIC_DF_UA. */
const char *es_ua;
uint32_t es_pdmd; /* One fixed value X509 */
uint32_t es_aead; /* One fixed value AESG */
uint32_t es_kexs; /* One fixed value C255 */
/**
* Support SREJ: for client side, this means supporting server's SREJ
* responses (this does not work yet) and for server side, this means
* generating SREJ instead of REJ when appropriate.
*/
int es_support_srej;
/**
* Setting this value to 0 means that
*
* For client:
* a) we send a SETTINGS frame to indicate that we do not support server
* push; and
* b) All incoming pushed streams get reset immediately.
* (For maximum effect, set es_max_streams_in to 0.)
*
*/
int es_support_push;
/**
* If set to true value, the server will not include connection ID in
* outgoing packets if client's CHLO specifies TCID=0.
*
* For client, this means including TCID=0 into CHLO message. TODO:
* this does not work yet.
*/
int es_support_tcid0;
/**
* Q037 and higher support "No STOP_WAITING frame" mode. When set, the
* client will send NSTP option in its Client Hello message and will not
* sent STOP_WAITING frames, while ignoring incoming STOP_WAITING frames,
* if any. Note that if the version negotiation happens to downgrade the
* client below Q037, this mode will *not* be used.
*
* This option does not affect the server, as it must support NSTP mode
* if it was specified by the client.
*/
int es_support_nstp;
/**
* If set to true value, the library will drop connections when it
* receives corresponding Public Reset packet. The default is to
* ignore these packets.
*/
int es_honor_prst;
/**
* A non-zero value enables internal checks that identify suspected
* infinite loops in user @ref on_read and @ref on_write callbacks
* and break them. An infinite loop may occur if user code keeps
* on performing the same operation without checking status, e.g.
* reading from a closed stream etc.
*
* The value of this parameter is as follows: should a callback return
* this number of times in a row without making progress (that is,
* reading, writing, or changing stream state), loop break will occur.
*
* The defaut value is @ref LSQUIC_DF_PROGRESS_CHECK.
*/
unsigned es_progress_check;
/**
* A non-zero value enables internal checks to identify suspected
* infinite loops in Pending RW Queue logic. The value of this
* setting is the number of times a connection on Pending RW Queue
* is allowed to be processed without making progress before it is
* banished from Pending RW Queue.
*
* Progress is considered to have happened if any of the following
* occurs:
* - User reads data, FIN, or new error (due to a reset) from a
* stream.
* - A new stream-related frame is packetized.
*
* The defaut value is @ref LSQUIC_DF_PENDRW_CHECK.
*/
unsigned es_pendrw_check;
/**
* A non-zero value make stream dispatch its read-write events once
* per call.
*
* When zero, read and write events are dispatched until the stream
* is no longer readable or writeable, respectively, or until the
* user signals unwillingness to read or write using
* @ref lsquic_stream_wantread() or @ref lsquic_stream_wantwrite()
* or shuts down the stream.
*
* The default value is @ref LSQUIC_DF_RW_ONCE.
*/
int es_rw_once;
/**
* If set, this value specifies that number of microseconds that
* functions @ref lsquic_engine_proc_all(),
* @ref lsquic_engine_process_conns_with_incoming(),
* @ref lsquic_engine_process_conns_to_tick(), and
* @ref lsquic_engine_process_conns_with_pend_rw() are allowed
* to spend before returning.
*
* This is not an exact science and the connections must make
* progress, so the deadline is checked after all connections get
* a chance to tick and at least one batch of packets is sent out.
*
* When processing function runs out of its time slice, immediate
* calls to @ref lsquic_engine_has_pend_rw() and
* @ref lsquic_engine_has_unsent_packets() return false.
*
* The default value is @ref LSQUIC_DF_PROC_TIME_THRESH.
*/
unsigned es_proc_time_thresh;
/**
* If set to true, packet pacing is implemented per connection.
*
* The default value is @ref LSQUIC_DF_PACE_PACKETS.
*/
int es_pace_packets;
};
/* Initialize `settings' to default values */
void
lsquic_engine_init_settings (struct lsquic_engine_settings *,
unsigned lsquic_engine_flags);
/**
* Check settings for errors.
*
* @param settings Settings struct.
*
* @param flags Engine flags.
*
* @param err_buf Optional pointer to buffer into which error string
* is written.
* @param err_buf_sz Size of err_buf. No more than this number of bytes
* will be written to err_buf, including the NUL byte.
*
* @retval 0 Settings have no errors.
* @retval -1 There are errors in settings.
*/
int
lsquic_engine_check_settings (const struct lsquic_engine_settings *settings,
unsigned lsquic_engine_flags,
char *err_buf, size_t err_buf_sz);
struct lsquic_out_spec
{
const unsigned char *buf;
size_t sz;
const struct sockaddr *local_sa;
const struct sockaddr *dest_sa;
void *peer_ctx;
};
/**
* Returns number of packets successfully sent out or -1 on error. -1 should
* only be returned if no packets were sent out.
*/
typedef int (*lsquic_packets_out_f)(
void *packets_out_ctx,
const struct lsquic_out_spec *out_spec,
unsigned n_packets_out
);
/**
* The packet out memory interface is used by LSQUIC to get buffers to
* which outgoing packets will be written before they are passed to
* ea_packets_out callback. pmi_release() is called at some point,
* usually after the packet is sent successfully, to return the buffer
* to the pool.
*
* If not specified, malloc() and free() are used.
*/
struct lsquic_packout_mem_if
{
void * (*pmi_allocate) (void *pmi_ctx, size_t sz);
void (*pmi_release) (void *pmi_ctx, void *obj);
};
/* TODO: describe this important data structure */
typedef struct lsquic_engine_api
{
const struct lsquic_engine_settings *ea_settings; /* Optional */
const struct lsquic_stream_if *ea_stream_if;
void *ea_stream_if_ctx;
lsquic_packets_out_f ea_packets_out;
void *ea_packets_out_ctx;
/**
* Memory interface is optional.
*/
const struct lsquic_packout_mem_if *ea_pmi;
void *ea_pmi_ctx;
} lsquic_engine_api_t;
/**
* Create new engine.
*
* @param lsquic_engine_flags A bitmask of @ref LSENG_SERVER and
* @ref LSENG_HTTP
*/
lsquic_engine_t *
lsquic_engine_new (unsigned lsquic_engine_flags,
const struct lsquic_engine_api *);
/**
* Create a client connection to peer identified by `peer_ctx'.
* If `max_packet_size' is set to zero, it is inferred based on `peer_sa':
* 1350 for IPv6 and 1370 for IPv4.
*/
int
lsquic_engine_connect (lsquic_engine_t *, const struct sockaddr *peer_sa,
void *peer_ctx, const char *hostname,
unsigned short max_packet_size);
/**
* Pass incoming packet to the QUIC engine. This function can be called
* more than once in a row. After you add one or more packets, call
* lsquic_engine_process_conns_with_incoming() to schedule output, if any.
*
* @retval 0 Packet was processed by a real connection.
*
* @retval -1 Some error occurred. Possible reasons are invalid packet
* size or failure to allocate memory.
*/
int
lsquic_engine_packet_in (lsquic_engine_t *,
const unsigned char *packet_in_data, size_t packet_in_size,
const struct sockaddr *sa_local, const struct sockaddr *sa_peer,
void *peer_ctx);
/**
* Process all connections. This function must be called often enough so
* that packets and connections do not expire.
*/
void
lsquic_engine_proc_all (lsquic_engine_t *engine);
/**
* Process connections that have incoming packets. Call this after adding
* one or more incoming packets using lsquic_engine_packet_in().
*/
void
lsquic_engine_process_conns_with_incoming (lsquic_engine_t *);
/**
* Process connections in Advisory Tick Time queue whose tick times are in
* the past.
*/
void
lsquic_engine_process_conns_to_tick (lsquic_engine_t *);
/**
* Returns true if engine has connections that have pending read or write
* events.
*
* Connections with pending read or write events are those that have at
* least one stream whose state changed outside of the regular callback
* mechanism. The simplest example is writing directly to the stream
* object when data comes in.
*
* A call to @ref lsquic_engine_proc_all,
* @ref lsquic_engine_process_conns_with_incoming,
* @ref lsquic_engine_process_conns_to_tick, or
* @ref lsquic_engine_process_conns_with_pend_rw removes processed connection
* from Pending RW queue.
*/
int
lsquic_engine_has_pend_rw (lsquic_engine_t *);
/**
* Process connections that have pending read or write events (@see
* lsquic_engine_has_pend_rw for description).
*/
void
lsquic_engine_process_conns_with_pend_rw (lsquic_engine_t *);
/**
* Returns true if engine has some unsent packets. This happens if
* @ref ea_packets_out() could not send everything out.
*/
int
lsquic_engine_has_unsent_packets (lsquic_engine_t *engine);
/**
* Send out as many unsent packets as possibe: until we are out of unsent
* packets or until @ref ea_packets_out() fails.
*/
void
lsquic_engine_send_unsent_packets (lsquic_engine_t *engine);
void
lsquic_engine_destroy (lsquic_engine_t *);
void lsquic_conn_make_stream(lsquic_conn_t *);
/** Return number of delayed streams currently pending */
unsigned
lsquic_conn_n_pending_streams (const lsquic_conn_t *);
/** Cancel `n' pending streams. Returns new number of pending streams. */
unsigned
lsquic_conn_cancel_pending_streams (lsquic_conn_t *, unsigned n);
/**
* Mark connection as going away: send GOAWAY frame and do not accept
* any more incoming streams, nor generate streams of our own.
*/
void
lsquic_conn_going_away(lsquic_conn_t *conn);
/**
* This forces connection close. on_conn_closed and on_close callbacks
* will be called.
*/
void lsquic_conn_close(lsquic_conn_t *conn);
int lsquic_stream_wantread(lsquic_stream_t *s, int is_want);
ssize_t lsquic_stream_read(lsquic_stream_t *s, void *buf, size_t len);
ssize_t lsquic_stream_readv(lsquic_stream_t *s, const struct iovec *,
int iovcnt);
int lsquic_stream_wantwrite(lsquic_stream_t *s, int is_want);
/**
* Return maximum number of bytes lsquic_stream_write() will write. This
* call is useful if you don't want to perform your own buffering.
*/
size_t lsquic_stream_write_avail (const lsquic_stream_t *s);
/**
* Write `len' bytes to the stream. Returns number of bytes written, which
* may be smaller that `len'. Use lsquic_stream_write_avail() to find out
* maximum size of `len'.
*/
ssize_t lsquic_stream_write(lsquic_stream_t *s, const void *buf, size_t len);
/**
* Returns 0 if `filename' was queued for writing, -1 on error. This
* function queues the size of the file as it was when the function was
* called. The stream will write at most this number of bytes to the
* peer. If the file grows, appended data is not used.
*/
int lsquic_stream_write_file(lsquic_stream_t *s, const char *filename);
ssize_t lsquic_stream_writev(lsquic_stream_t *s, const struct iovec *vec, int count);
/**
* Returns 0 if `fdSrc' was queued for writing, -1 on error. This
* function queues at most `size' bytes to be written. If the file shrinks,
* fewer bytes are written.
*/
int lsquic_stream_sendfile(lsquic_stream_t *s, int fdSrc, off_t off, size_t size);
int lsquic_stream_flush(lsquic_stream_t *s);
/**
* @typedef lsquic_http_header_t
* @brief HTTP header structure. Contains header name and value.
*
*/
typedef struct lsquic_http_header
{
struct iovec name;
struct iovec value;
} lsquic_http_header_t;
/**
* @typedef lsquic_http_headers_t
* @brief HTTP header list structure. Contains a list of HTTP headers in key/value pairs.
* used in API functions to pass headers.
*/
struct lsquic_http_headers
{
int count;
lsquic_http_header_t *headers;
};
int lsquic_stream_send_headers(lsquic_stream_t *s,
const lsquic_http_headers_t *h, int eos);
int lsquic_conn_is_push_enabled(lsquic_conn_t *c);
/** Possible values for how are 0, 1, and 2. See shutdown(2). */
int lsquic_stream_shutdown(lsquic_stream_t *s, int how);
int lsquic_stream_close(lsquic_stream_t *s);
/** Returns ID of the stream */
uint32_t
lsquic_stream_id (const lsquic_stream_t *s);
/**
* Returns stream ctx associated with the stream. (The context is what
* is returned by @ref on_new_stream callback).
*/
lsquic_stream_ctx_t *
lsquic_stream_get_ctx (const lsquic_stream_t *s);
/** Returns true if this is a pushed stream */
int
lsquic_stream_is_pushed (const lsquic_stream_t *s);
/**
* Refuse pushed stream. Call it from @ref on_new_stream.
*
* No need to call lsquic_stream_close() after this. on_close will be called.
*
* @see lsquic_stream_is_pushed
*/
int
lsquic_stream_refuse_push (lsquic_stream_t *s);
/**
* Get information associated with pushed stream:
*
* @param ref_stream_id Stream ID in response to which push promise was
* sent.
* @param headers Uncompressed request headers.
* @param headers_sz Size of uncompressed request headers, not counting
* the NUL byte.
*
* @retval 0 Success.
* @retval -1 This is not a pushed stream.
*/
int
lsquic_stream_push_info (const lsquic_stream_t *, uint32_t *ref_stream_id,
const char **headers, size_t *headers_sz);
/** Return current priority of the stream */
unsigned lsquic_stream_priority (const lsquic_stream_t *s);
/**
* Set stream priority. Valid priority values are 1 through 256, inclusive.
*
* @retval 0 Success.
* @retval -1 Priority value is invalid.
*/
int lsquic_stream_set_priority (lsquic_stream_t *s, unsigned priority);
/**
* Get a pointer to the connection object. Use it with lsquic_conn_*
* functions.
*/
lsquic_conn_t * lsquic_stream_conn(const lsquic_stream_t *s);
lsquic_stream_t *
lsquic_conn_get_stream_by_id (lsquic_conn_t *c, uint32_t stream_id);
/** Get connection ID */
lsquic_cid_t
lsquic_conn_id (const lsquic_conn_t *c);
int lsquic_conn_get_sockaddr(const lsquic_conn_t *c,
const struct sockaddr **local, const struct sockaddr **peer);
struct lsquic_logger_if {
int (*vprintf)(void *logger_ctx, const char *fmt, va_list args);
};
/**
* Enumerate timestamp styles supported by LSQUIC logger mechanism.
*/
enum lsquic_logger_timestamp_style {
/**
* No timestamp is generated.
*/
LLTS_NONE,
/**
* The timestamp consists of 24 hours, minutes, seconds, and
* milliseconds. Example: 13:43:46.671
*/
LLTS_HHMMSSMS,
/**
* Like above, plus date, e.g: 2017-03-21 13:43:46.671
*/
LLTS_YYYYMMDD_HHMMSSMS,
/**
* This is Chrome-like timestamp used by proto-quic. The timestamp
* includes month, date, hours, minutes, seconds, and microseconds.
*
* Example: 1223/104613.946956 (instead of 12/23 10:46:13.946956).
*
* This is to facilitate reading two logs side-by-side.
*/
LLTS_CHROMELIKE,
/**
* The timestamp consists of 24 hours, minutes, seconds, and
* microseconds. Example: 13:43:46.671123
*/
LLTS_HHMMSSUS,
N_LLTS
};
/**
* Call this if you want to do something with LSQUIC log messages, as they
* are thrown out by default.
*/
void lsquic_logger_init(const struct lsquic_logger_if *, void *logger_ctx,
enum lsquic_logger_timestamp_style);
/**
* Set log level for all LSQUIC modules. Acceptable values are debug, info,
* notice, warning, error, alert, emerg, crit (case-insensitive).
*
* @retval 0 Success.
* @retval -1 Failure: log_level is not valid.
*/
int
lsquic_set_log_level (const char *log_level);
/**
* E.g. "event=debug"
*/
int
lsquic_logger_lopt (const char *optarg);
/**
* Return the list of QUIC versions (as bitmask) this engine instance
* supports.
*/
unsigned lsquic_engine_quic_versions (const lsquic_engine_t *);
/**
* This is one of the flags that can be passed to @ref lsquic_global_init.
* Use it to initialize LSQUIC for use in client mode.
*/
#define LSQUIC_GLOBAL_CLIENT (1 << 0)
/**
* This is one of the flags that can be passed to @ref lsquic_global_init.
* Use it to initialize LSQUIC for use in server mode.
*/
#define LSQUIC_GLOBAL_SERVER (1 << 1)
/**
* Initialize LSQUIC. This must be called before any other LSQUIC function
* is called. Returns 0 on success and -1 on failure.
*
* @param flags This a bitmask of @ref LSQUIC_GLOBAL_CLIENT and
* @ref LSQUIC_GLOBAL_SERVER. At least one of these
* flags should be specified.
*
* @retval 0 Success.
* @retval -1 Initialization failed.
*
* @see LSQUIC_GLOBAL_CLIENT
* @see LSQUIC_GLOBAL_SERVER
*/
int
lsquic_global_init (int flags);
/**
* Clean up global state created by @ref lsquic_global_init. Should be
* called after all LSQUIC engine instances are gone.
*/
void
lsquic_global_cleanup (void);
/**
* Get QUIC version used by the connection.
*
* @see lsquic_version
*/
enum lsquic_version
lsquic_conn_quic_version (const lsquic_conn_t *c);
/** Translate string QUIC version to LSQUIC QUIC version representation */
enum lsquic_version
lsquic_str2ver (const char *str, size_t len);
/**
* Get user-supplied context associated with the connection.
*/
lsquic_conn_ctx_t *
lsquic_conn_get_ctx (const lsquic_conn_t *c);
/**
* Get peer context associated with the connection. should be UdpListener *.
*/
void *lsquic_conn_get_peer_ctx( const lsquic_conn_t *lconn);
/**
* Abort connection.
*/
void
lsquic_conn_abort (lsquic_conn_t *c);
/**
* Returns true if there is a connection on the Advisory Tick Time queue,
* false otherwise. If true, `diff' is set to the difference between
* the earliest advisory tick time and now. If the former is in the past,
* the value of `diff' is negative.
*/
int
lsquic_engine_earliest_adv_tick (lsquic_engine_t *engine, int *diff);
/**
* Return number of connections whose advisory tick time is before $now
* plus `from_now' delta. `from_now' can be negative.
*/
unsigned
lsquic_engine_count_attq (lsquic_engine_t *engine, int from_now);
#ifdef __cplusplus
}
#endif
#endif //__LSQUIC_H__

35
include/lsquic_types.h Normal file
View File

@ -0,0 +1,35 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef __LSQUIC_TYPES_H__
#define __LSQUIC_TYPES_H__
/**
* @file
* LSQUIC types.
*/
#include <stdint.h>
/**
* Connection ID
*/
typedef uint64_t lsquic_cid_t;
/** LSQUIC engine */
typedef struct lsquic_engine lsquic_engine_t;
/** Connection */
typedef struct lsquic_conn lsquic_conn_t;
/** Connection context. This is the return value of @ref on_new_conn. */
typedef struct lsquic_conn_ctx lsquic_conn_ctx_t;
/** Stream */
typedef struct lsquic_stream lsquic_stream_t;
/** Stream context. This is the return value of @ref on_new_stream. */
typedef struct lsquic_stream_ctx lsquic_stream_ctx_t;
/** HTTP headers */
typedef struct lsquic_http_headers lsquic_http_headers_t;
#endif

4
src/CMakeLists.txt Normal file
View File

@ -0,0 +1,4 @@
# Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE.
cmake_minimum_required(VERSION 2.8)
add_subdirectory(liblsquic)

View File

@ -0,0 +1,62 @@
# Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE.
SET(lsquic_STAT_SRCS
lsquic_alarmset.c
lsquic_conn.c
lsquic_full_conn.c
lsquic_chsk_stream.c
lsquic_engine.c
lsquic_parse_gquic_common.c
lsquic_parse_gquic_le.c
lsquic_parse_gquic_be.c
lsquic_parse_gquic_Q040.c
lsquic_packet_in.c
lsquic_packet_out.c
lsquic_crypto.c
lsquic_handshake.c
lsquic_logger.c
lsquic_malo.c
lsquic_mm.c
lsquic_rechist.c
lsquic_rtt.c
lsquic_send_ctl.c
lsquic_senhist.c
lsquic_cfcw.c
lsquic_sfcw.c
lsquic_stream.c
lsquic_util.c
lsquic_cubic.c
lsquic_set.c
lsquic_headers_stream.c
lsquic_frame_reader.c
lsquic_frame_writer.c
lsquic_crt_compress.c
lsquic_conn_hash.c
lsquic_eng_hist.c
lsquic_spi.c
lsquic_di_nocopy.c
lsquic_di_hash.c
lsquic_di_error.c
lsquic_global.c
lsquic_packet_common.c
lsquic_ev_log.c
lsquic_frame_common.c
lsquic_packints.c
lsquic_version.c
lsquic_pacer.c
lsquic_attq.c
lsquic_str.c
lsquic_arr.c
lsquic_hash.c
lsquic_hpack_common.c
lsquic_hpack_dec.c
lsquic_hpack_enc.c
lsquic_xxhash.c
lsquic_buf.c
)
add_library(lsquic STATIC ${lsquic_STAT_SRCS} )
link_directories(${PROJECT_SOURCE_DIR}/ssl/ /usr/local/lib)

View File

@ -0,0 +1,136 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/* Copyright (c) 2015 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE.chrome file.
*/
/* This file contains common certificates. It's designed to be #included in
* another file, in a namespace. */
/* Updated for C style, David Shue */
/*
* Replace "kDERCert" to "der_cert2_"
*
*/
#include <stdint.h>
#include "common_cert_set_2a.inc"
#include "common_cert_set_2b.inc"
#define common_certs2_num 54
static const unsigned char * const common_certs2[common_certs2_num] = {
der_cert2_0,
der_cert2_1,
der_cert2_2,
der_cert2_3,
der_cert2_4,
der_cert2_5,
der_cert2_6,
der_cert2_7,
der_cert2_8,
der_cert2_9,
der_cert2_10,
der_cert2_11,
der_cert2_12,
der_cert2_13,
der_cert2_14,
der_cert2_15,
der_cert2_16,
der_cert2_17,
der_cert2_18,
der_cert2_19,
der_cert2_20,
der_cert2_21,
der_cert2_22,
der_cert2_23,
der_cert2_24,
der_cert2_25,
der_cert2_26,
der_cert2_27,
der_cert2_28,
der_cert2_29,
der_cert2_30,
der_cert2_31,
der_cert2_32,
der_cert2_33,
der_cert2_34,
der_cert2_35,
der_cert2_36,
der_cert2_37,
der_cert2_38,
der_cert2_39,
der_cert2_40,
der_cert2_41,
der_cert2_42,
der_cert2_43,
der_cert2_44,
der_cert2_45,
der_cert2_46,
der_cert2_47,
der_cert2_48,
der_cert2_49,
der_cert2_50,
der_cert2_51,
der_cert2_52,
der_cert2_53,
};
static const size_t common_certs2_lens[common_certs2_num] = {
897,
911,
985,
1012,
1049,
1062,
1065,
1071,
1084,
1096,
1097,
1105,
1107,
1117,
1127,
1133,
1136,
1138,
1153,
1171,
1172,
1176,
1182,
1188,
1194,
1203,
1205,
1206,
1210,
1222,
1226,
1236,
1236,
1236,
1238,
1256,
1270,
1280,
1283,
1284,
1287,
1315,
1327,
1340,
1418,
1447,
1509,
1520,
1570,
1581,
1592,
1628,
1632,
1770,
};
#define common_certs2_hash UINT64_C(0xe81a92926081e801)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,126 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/* Copyright (c) 2015 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE.chrome file.
*/
/* Updated for C style, David Shue */
/*
* Replace "kDERCert" to "der_cert3_"
*
*/
#include "common_cert_set_3a.inc"
#include "common_cert_set_3b.inc"
#define common_certs3_num 52
static const unsigned char* const common_certs3[common_certs3_num] = {
der_cert3_0,
der_cert3_1,
der_cert3_2,
der_cert3_3,
der_cert3_4,
der_cert3_5,
der_cert3_6,
der_cert3_7,
der_cert3_8,
der_cert3_9,
der_cert3_10,
der_cert3_11,
der_cert3_12,
der_cert3_13,
der_cert3_14,
der_cert3_15,
der_cert3_16,
der_cert3_17,
der_cert3_18,
der_cert3_19,
der_cert3_20,
der_cert3_21,
der_cert3_22,
der_cert3_23,
der_cert3_24,
der_cert3_25,
der_cert3_26,
der_cert3_27,
der_cert3_28,
der_cert3_29,
der_cert3_30,
der_cert3_31,
der_cert3_32,
der_cert3_33,
der_cert3_34,
der_cert3_35,
der_cert3_36,
der_cert3_37,
der_cert3_38,
der_cert3_39,
der_cert3_40,
der_cert3_41,
der_cert3_42,
der_cert3_43,
der_cert3_44,
der_cert3_45,
der_cert3_46,
der_cert3_47,
der_cert3_48,
der_cert3_49,
der_cert3_50,
der_cert3_51,
};
static const size_t common_certs3_lens[common_certs3_num] = {
897,
911,
1012,
1049,
1065,
1096,
1097,
1101,
1105,
1105,
1107,
1117,
1127,
1133,
1136,
1138,
1139,
1145,
1149,
1153,
1167,
1172,
1174,
1174,
1176,
1188,
1194,
1196,
1203,
1205,
1206,
1208,
1209,
1210,
1222,
1227,
1236,
1236,
1238,
1283,
1284,
1287,
1298,
1315,
1327,
1340,
1357,
1418,
1447,
1509,
1513,
1632,
};
#define common_certs3_hash UINT64_C(0x918215a28680ed7e)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

38
src/liblsquic/fiu-local.h Normal file
View File

@ -0,0 +1,38 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/* libfiu - Fault Injection in Userspace
*
* This header, part of libfiu, is meant to be included in your project to
* avoid having libfiu as a mandatory build-time dependency.
*
* You can add it to your project, and #include it instead of fiu.h.
* The real fiu.h will be used only when FIU_ENABLE is defined.
*
* This header, as the rest of libfiu, is in the public domain.
*
* You can find more information about libfiu at
* http://blitiri.com.ar/p/libfiu.
*/
#ifndef _FIU_LOCAL_H
#define _FIU_LOCAL_H
/* Only define the stubs when fiu is disabled, otherwise use the real fiu.h
* header */
#ifndef FIU_ENABLE
#define fiu_init(flags) 0
#define fiu_fail(name) 0
#define fiu_failinfo() NULL
#define fiu_do_on(name, action)
#define fiu_exit_on(name)
#define fiu_return_on(name, retval)
#else
#include <fiu.h>
#endif /* FIU_ENABLE */
#endif /* _FIU_LOCAL_H */

View File

@ -0,0 +1,54 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_alarmset.c -- A set of alarms
*/
#include <assert.h>
#include <string.h>
#include "lsquic_types.h"
#include "lsquic_alarmset.h"
#define LSQUIC_LOGGER_MODULE LSQLM_ALARMSET
#define LSQUIC_LOG_CONN_ID alset->as_cid
#include "lsquic_logger.h"
void
lsquic_alarmset_init (lsquic_alarmset_t *alset, lsquic_cid_t cid)
{
alset->as_cid = cid;
alset->as_armed_set = 0;
}
void
lsquic_alarmset_init_alarm (lsquic_alarmset_t *alset, enum alarm_id al_id,
lsquic_alarm_cb_f callback, void *cb_ctx)
{
alset->as_alarms[ al_id ].callback = callback;
alset->as_alarms[ al_id ].cb_ctx = cb_ctx;
}
void
lsquic_alarmset_ring_expired (lsquic_alarmset_t *alset, lsquic_time_t now)
{
enum alarm_id_bit armed_set;
enum alarm_id al_id;
for (al_id = 0, armed_set = alset->as_armed_set;
al_id < MAX_LSQUIC_ALARMS && armed_set;
armed_set &= ~(1 << al_id), ++al_id)
if (armed_set & (1 << al_id))
{
if (alset->as_expiry[al_id] < now)
{
alset->as_armed_set &= ~(1 << al_id);
LSQ_INFO("ring expired alarm %d", al_id);
alset->as_alarms[al_id].callback(
alset->as_alarms[al_id].cb_ctx,
alset->as_expiry[al_id], now);
}
}
}

View File

@ -0,0 +1,70 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_alarmset.h -- A set of alarms
*/
#ifndef LSQUIC_ALARM_H
#define LSQUIC_ALARM_H 1
#include "lsquic_int_types.h"
typedef void (*lsquic_alarm_cb_f)(void *cb_ctx,
lsquic_time_t expiry, lsquic_time_t now);
typedef struct lsquic_alarm {
lsquic_alarm_cb_f callback;
void *cb_ctx;
} lsquic_alarm_t;
enum alarm_id {
AL_HANDSHAKE,
AL_RETX,
AL_ACK,
AL_PING,
AL_IDLE,
MAX_LSQUIC_ALARMS
};
enum alarm_id_bit {
ALBIT_HANDSHAKE = 1 << AL_HANDSHAKE,
ALBIT_RETX = 1 << AL_RETX,
ALBIT_ACK = 1 << AL_ACK,
ALBIT_PING = 1 << AL_PING,
ALBIT_IDLE = 1 << AL_IDLE,
};
typedef struct lsquic_alarmset {
enum alarm_id_bit as_armed_set;
lsquic_time_t as_expiry[MAX_LSQUIC_ALARMS];
lsquic_cid_t as_cid; /* Used for logging */
struct lsquic_alarm as_alarms[MAX_LSQUIC_ALARMS];
} lsquic_alarmset_t;
void
lsquic_alarmset_init (lsquic_alarmset_t *, lsquic_cid_t);
void
lsquic_alarmset_init_alarm (lsquic_alarmset_t *, enum alarm_id,
lsquic_alarm_cb_f, void *cb_ctx);
#define lsquic_alarmset_set(alarmset, al_id, exp) do { \
(alarmset)->as_armed_set |= 1 << (al_id); \
(alarmset)->as_expiry[al_id] = exp; \
} while (0)
#define lsquic_alarmset_unset(alarmset, al_id) do { \
(alarmset)->as_armed_set &= ~(1 << (al_id)); \
} while (0)
#define lsquic_alarmset_is_set(alarmset, al_id) \
((alarmset)->as_armed_set & (1 << (al_id)))
/* Timers "fire," alarms "ring." */
void
lsquic_alarmset_ring_expired (lsquic_alarmset_t *, lsquic_time_t now);
#endif

View File

@ -0,0 +1,46 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_arr.c
*/
#include "lsquic_arr.h"
int
lsquic_arr_push (struct lsquic_arr *arr, uintptr_t val)
{
uintptr_t *new_els;
unsigned n;
if (arr->off + arr->nelem < arr->nalloc)
{
arr->els[arr->off + arr->nelem] = val;
++arr->nelem;
return 0;
}
if (arr->off > arr->nalloc / 2)
{
memmove(arr->els, arr->els + arr->off,
sizeof(arr->els[0]) * arr->nelem);
arr->off = 0;
arr->els[arr->nelem] = val;
++arr->nelem;
return 0;
}
if (arr->nalloc)
n = arr->nalloc * 2;
else
n = 64;
new_els = malloc(n * sizeof(arr->els[0]));
if (!new_els)
return -1;
memcpy(new_els, arr->els + arr->off, sizeof(arr->els[0]) * arr->nelem);
free(arr->els);
arr->off = 0;
arr->els = new_els;
arr->nalloc = n;
arr->els[arr->off + arr->nelem] = val;
++arr->nelem;
return 0;
}

View File

@ -0,0 +1,64 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_arr.h -- Array
*/
#ifndef LSQUIC_ARR_H
#define LSQUIC_ARR_H 1
#include <assert.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
struct lsquic_arr
{
unsigned nalloc,
nelem,
off;
uintptr_t *els;
};
#define lsquic_arr_init(a) do { \
memset((a), 0, sizeof(*(a))); \
} while (0)
#define lsquic_arr_cleanup(a) do { \
free((a)->els); \
memset((a), 0, sizeof(*(a))); \
} while (0)
#define lsquic_arr_clear(a) do { \
(a)->off = 0; \
(a)->nelem = 0; \
} while (0)
#define lsquic_arr_get(a, i) ( \
assert((i) < (a)->nelem), \
(a)->els[(a)->off + (i)] \
)
#define lsquic_arr_shift(a) ( \
assert((a)->nelem > 0), \
(a)->nelem -= 1, \
(a)->els[(a)->off++] \
)
#define lsquic_arr_peek(a) ( \
assert((a)->nelem > 0), \
(a)->els[(a)->off] \
)
#define lsquic_arr_pop(a) ( \
assert((a)->nelem > 0), \
(a)->nelem -= 1, \
(a)->els[(a)->off + (a)->nelem] \
)
#define lsquic_arr_count(a) (+(a)->nelem)
int
lsquic_arr_push (struct lsquic_arr *, uintptr_t);
#endif

282
src/liblsquic/lsquic_attq.c Normal file
View File

@ -0,0 +1,282 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_attq.c -- Advisory Tick Time Queue
*
* This is a collection of connections kept in a binary heap, the top
* element having the minimum advsory time. To speed up removal, each
* element has an index it has in the heap array. The index is updated
* as elements are moved around in the array when heap is updated.
*/
#include <assert.h>
#include <stdlib.h>
#include "lsquic.h"
#include "lsquic_types.h"
#include "lsquic_int_types.h"
#include "lsquic_attq.h"
#include "lsquic_malo.h"
#include "lsquic_conn.h"
struct attq
{
struct malo *aq_elem_malo;
struct attq_elem **aq_heap;
lsquic_time_t aq_min;
unsigned aq_nelem;
unsigned aq_nalloc;
};
struct attq *
attq_create (void)
{
struct attq *q;
struct malo *malo;
malo = lsquic_malo_create(sizeof(struct attq_elem));
if (!malo)
return NULL;
q = calloc(1, sizeof(*q));
if (!q)
{
lsquic_malo_destroy(malo);
return NULL;
}
q->aq_elem_malo = malo;
return q;
}
void
attq_destroy (struct attq *q)
{
lsquic_malo_destroy(q->aq_elem_malo);
free(q->aq_heap);
free(q);
}
#define AE_PARENT(i) ((i - 1) / 2)
#define AE_LCHILD(i) (2 * i + 1)
#define AE_RCHILD(i) (2 * i + 2)
#ifndef NDEBUG
static void
attq_verify (struct attq *q)
{
unsigned i;
for (i = 0; i < q->aq_nelem; ++i)
{
assert(q->aq_heap[i]->ae_heap_idx == i);
if (AE_LCHILD(i) < q->aq_nelem)
assert(q->aq_heap[i]->ae_adv_time <=
q->aq_heap[AE_LCHILD(i)]->ae_adv_time);
if (AE_RCHILD(i) < q->aq_nelem)
assert(q->aq_heap[i]->ae_adv_time <=
q->aq_heap[AE_RCHILD(i)]->ae_adv_time);
}
}
#else
#define attq_verify(q)
#endif
int
attq_maybe_add (struct attq *q, struct lsquic_conn *conn,
lsquic_time_t advisory_time)
{
if (advisory_time < q->aq_min)
return 1;
else
return attq_add(q, conn, advisory_time);
}
static void
attq_swap (struct attq *q, unsigned a, unsigned b)
{
struct attq_elem *el;
el = q->aq_heap[ a ];
q->aq_heap[ a ] = q->aq_heap[ b ];
q->aq_heap[ b ] = el;
q->aq_heap[ a ]->ae_heap_idx = a;
q->aq_heap[ b ]->ae_heap_idx = b;
}
int
attq_add (struct attq *q, struct lsquic_conn *conn,
lsquic_time_t advisory_time)
{
struct attq_elem *el, **heap;
unsigned n, i;
if (q->aq_nelem >= q->aq_nalloc)
{
if (q->aq_nalloc > 0)
n = q->aq_nalloc * 2;
else
n = 8;
heap = realloc(q->aq_heap, n * sizeof(q->aq_heap[0]));
if (!heap)
return -1;
q->aq_heap = heap;
q->aq_nalloc = n;
}
el = lsquic_malo_get(q->aq_elem_malo);
if (!el)
return -1;
el->ae_adv_time = advisory_time;
/* The only place linkage between conn and attq_elem occurs: */
el->ae_conn = conn;
conn->cn_attq_elem = el;
el->ae_heap_idx = q->aq_nelem;
q->aq_heap[ q->aq_nelem++ ] = el;
i = q->aq_nelem - 1;
while (i > 0 && q->aq_heap[ AE_PARENT(i) ]->ae_adv_time >=
q->aq_heap[ i ]->ae_adv_time)
{
attq_swap(q, i, AE_PARENT(i));
i = AE_PARENT(i);
}
attq_verify(q);
return 0;
}
struct lsquic_conn *
attq_pop (struct attq *q, lsquic_time_t cutoff)
{
struct lsquic_conn *conn;
struct attq_elem *el;
if (q->aq_nelem == 0)
return NULL;
el = q->aq_heap[0];
if (el->ae_adv_time >= cutoff)
return NULL;
conn = el->ae_conn;
attq_remove(q, conn);
return conn;
}
static void
attq_heapify (struct attq *q, unsigned i)
{
unsigned smallest;
assert(i < q->aq_nelem);
if (AE_LCHILD(i) < q->aq_nelem)
{
if (q->aq_heap[ AE_LCHILD(i) ]->ae_adv_time <
q->aq_heap[ i ]->ae_adv_time)
smallest = AE_LCHILD(i);
else
smallest = i;
if (AE_RCHILD(i) < q->aq_nelem &&
q->aq_heap[ AE_RCHILD(i) ]->ae_adv_time <
q->aq_heap[ smallest ]->ae_adv_time)
smallest = AE_RCHILD(i);
}
else
smallest = i;
if (smallest != i)
{
attq_swap(q, smallest, i);
attq_heapify(q, smallest);
}
}
void
attq_remove (struct attq *q, struct lsquic_conn *conn)
{
struct attq_elem *el;
unsigned idx;
el = conn->cn_attq_elem;
idx = el->ae_heap_idx;
assert(q->aq_nelem > 0);
assert(q->aq_heap[idx] == el);
assert(conn->cn_attq_elem == el);
conn->cn_attq_elem = NULL;
lsquic_malo_put(el);
q->aq_heap[ idx ] = q->aq_heap[ --q->aq_nelem ];
q->aq_heap[ idx ]->ae_heap_idx = idx;
if (idx > 0 && q->aq_heap[ idx ]->ae_adv_time <
q->aq_heap[ AE_PARENT(idx) ]->ae_adv_time)
{
do
{
attq_swap(q, idx, AE_PARENT(idx));
idx = AE_PARENT(idx);
}
while (idx > 0 && q->aq_heap[ idx ]->ae_adv_time <
q->aq_heap[ AE_PARENT(idx) ]->ae_adv_time);
}
else if (q->aq_nelem > 1 && idx < q->aq_nelem)
attq_heapify(q, idx);
attq_verify(q);
}
unsigned
attq_count_before (struct attq *q, lsquic_time_t cutoff)
{
unsigned level, total_count, level_count, i, level_max;
total_count = 0;
for (i = 0, level = 0;; ++level)
{
level_count = 0;
level_max = i + (1U << level);
for ( ; i < level_max && i < q->aq_nelem; ++i)
level_count += q->aq_heap[i]->ae_adv_time < cutoff;
total_count += level_count;
if (level_count < (1U << level))
return total_count;
}
assert(0);
}
const lsquic_time_t *
attq_next_time (struct attq *q)
{
if (q->aq_nelem > 0)
return &q->aq_heap[0]->ae_adv_time;
else
return NULL;
}
lsquic_time_t
attq_set_min (struct attq *q, lsquic_time_t new_min)
{
lsquic_time_t prev_value;
prev_value = q->aq_min;
q->aq_min = new_min;
return prev_value;
}

View File

@ -0,0 +1,54 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_attq.h -- Advisory Tick Time Queue
*/
#ifndef LSQUIC_ATTQ_H
#define LSQUIC_ATTQ_H
struct attq;
struct lsquic_conn;
/* The extra level of indirection is done for speed: swapping heap elements
* does not need memory associated with lsquic_conn.
*/
struct attq_elem
{
struct lsquic_conn *ae_conn;
lsquic_time_t ae_adv_time;
unsigned ae_heap_idx;
};
struct attq *
attq_create (void);
void
attq_destroy (struct attq *);
/* Return 1 if advisory_time is too small, 0 on success, -1 on failure */
int
attq_maybe_add (struct attq *, struct lsquic_conn *,
lsquic_time_t advisory_time);
/* Return 0 on success, -1 on failure (malloc) */
int
attq_add (struct attq *, struct lsquic_conn *, lsquic_time_t advisory_time);
void
attq_remove (struct attq *, struct lsquic_conn *);
struct lsquic_conn *
attq_pop (struct attq *, lsquic_time_t cutoff);
unsigned
attq_count_before (struct attq *, lsquic_time_t cutoff);
const lsquic_time_t *
attq_next_time (struct attq *);
lsquic_time_t
attq_set_min (struct attq *, lsquic_time_t new_min);
#endif

View File

@ -0,0 +1,89 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_buf.c
*/
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include "lsquic_buf.h"
static int
lsquic_buf_reserve (struct lsquic_buf *buf, int size)
{
char *new_buf;
if (buf->bufend - buf->buf == size)
return 0;
new_buf = realloc(buf->buf, size);
if (new_buf != 0 || size == 0)
{
buf->end = new_buf + (buf->end - buf->buf);
buf->buf = new_buf;
buf->bufend = new_buf + size;
if (buf->end > buf->bufend)
buf->end = buf->bufend;
return 0;
}
else
return -1;
}
static int
lsquic_buf_grow (struct lsquic_buf *buf, int size)
{
size = ((size + 511) >> 9) << 9;
return lsquic_buf_reserve(buf, lsquic_buf_capacity(buf) + size);
}
struct lsquic_buf *
lsquic_buf_create (int size)
{
struct lsquic_buf *buf;
buf = calloc(1, sizeof(*buf));
if (!buf)
return NULL;
if (0 != lsquic_buf_reserve(buf, size))
{
free(buf);
return NULL;
}
return buf;
}
int
lsquic_buf_append (struct lsquic_buf *buf, const char *str, int size)
{
if (buf == NULL || size < 0)
{
errno = EINVAL;
return -1;
}
if (size == 0)
return 0;
if (size > lsquic_buf_avail(buf))
{
if (lsquic_buf_grow(buf, size - lsquic_buf_avail(buf)) != 0)
return -1;
}
memmove(buf->end, str, size);
buf->end += size;
return size;
}
void
lsquic_buf_destroy (struct lsquic_buf *buf)
{
free(buf->buf);
free(buf);
}

View File

@ -0,0 +1,35 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_buf.h
*/
#ifndef LSQUIC_BUF_H
#define LSQUIC_BUF_H 1
struct lsquic_buf
{
char *buf, *end, *bufend;
};
struct lsquic_buf *
lsquic_buf_create (int);
int
lsquic_buf_append (struct lsquic_buf *, const char *, int);
#define lsquic_buf_begin(buf_) ((buf_)->buf)
#define lsquic_buf_size(buf_) ((buf_)->end - (buf_)->buf)
#define lsquic_buf_avail(buf_) ((buf_)->bufend - (buf_)->end)
#define lsquic_buf_capacity(buf_) ((buf_)->bufend - (buf_)->buf)
#define lsquic_buf_clear(buf_) do { \
(buf_)->end = (buf_)->buf; \
} while (0)
void
lsquic_buf_destroy (struct lsquic_buf *);
#endif

113
src/liblsquic/lsquic_cfcw.c Normal file
View File

@ -0,0 +1,113 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#include <inttypes.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include "lsquic.h"
#include "lsquic_types.h"
#include "lsquic_int_types.h"
#include "lsquic_rtt.h"
#include "lsquic_conn_flow.h"
#include "lsquic_sfcw.h"
#include "lsquic_stream.h"
#include "lsquic_conn_public.h"
#include "lsquic_mm.h"
#include "lsquic_engine_public.h"
#include "lsquic_util.h"
#include "lsquic_conn.h"
#include "lsquic_ev_log.h"
#define LSQUIC_LOGGER_MODULE LSQLM_CFCW
#define LSQUIC_LOG_CONN_ID fc->cf_conn_pub->lconn->cn_cid
#include "lsquic_logger.h"
void
lsquic_cfcw_init (struct lsquic_cfcw *fc, struct lsquic_conn_public *cpub,
unsigned max_recv_window)
{
memset(fc, 0, sizeof(*fc));
fc->cf_max_recv_win = max_recv_window;
fc->cf_conn_pub = cpub;
(void) lsquic_cfcw_fc_offsets_changed(fc);
}
static void
cfcw_maybe_increase_max_window (struct lsquic_cfcw *fc)
{
unsigned new_max_window;
new_max_window = fc->cf_max_recv_win * 2;
/* Do not increase past explicitly specified maximum */
if (new_max_window > fc->cf_conn_pub->enpub->enp_settings.es_max_cfcw)
new_max_window = fc->cf_conn_pub->enpub->enp_settings.es_max_cfcw;
if (new_max_window > fc->cf_max_recv_win)
{
LSQ_DEBUG("max window increase %u -> %u", fc->cf_max_recv_win,
new_max_window);
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID,
"max CFCW increase %u -> %u", fc->cf_max_recv_win,
new_max_window);
fc->cf_max_recv_win = new_max_window;
}
else
LSQ_DEBUG("max window could use an increase, but we're stuck "
"at %u", fc->cf_max_recv_win);
}
int
lsquic_cfcw_fc_offsets_changed (struct lsquic_cfcw *fc)
{
lsquic_time_t now, since_last_update, srtt;
if (fc->cf_recv_off - fc->cf_read_off >= fc->cf_max_recv_win / 2)
return 0;
now = lsquic_time_now();
since_last_update = now - fc->cf_last_updated;
fc->cf_last_updated = now;
srtt = lsquic_rtt_stats_get_srtt(&fc->cf_conn_pub->rtt_stats);
if (since_last_update < srtt * 2)
cfcw_maybe_increase_max_window(fc);
fc->cf_recv_off = fc->cf_read_off + fc->cf_max_recv_win;
LSQ_DEBUG("recv_off changed: read_off: %"PRIu64"; recv_off: %"
PRIu64"", fc->cf_read_off, fc->cf_recv_off);
return 1;
}
int
lsquic_cfcw_incr_max_recv_off (struct lsquic_cfcw *fc, uint64_t incr)
{
if (fc->cf_max_recv_off + incr <= fc->cf_recv_off)
{
fc->cf_max_recv_off += incr;
LSQ_DEBUG("max_recv_off goes from %"PRIu64" to %"PRIu64"",
fc->cf_max_recv_off - incr, fc->cf_max_recv_off);
return 1;
}
else
{
LSQ_WARN("flow control violation: received at offset %"PRIu64", while "
"flow control receive offset is %"PRIu64,
fc->cf_max_recv_off + incr, fc->cf_recv_off);
return 0;
}
}
void
lsquic_cfcw_incr_read_off (struct lsquic_cfcw *fc, uint64_t incr)
{
fc->cf_read_off += incr;
LSQ_DEBUG("read_off goes from %"PRIu64" to %"PRIu64,
fc->cf_read_off - incr, fc->cf_read_off);
}

View File

@ -0,0 +1,189 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* Stream/crypto handshake adapter for the client side.
*
* The client composes CHLO, writes it to the stream, and wait for the
* server response, which it processes.
*/
#include <assert.h>
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include "lsquic_int_types.h"
#include "lsquic.h"
#include "lsquic_handshake.h"
#include "lsquic_chsk_stream.h"
#include "lsquic_ver_neg.h"
#include "lsquic_conn.h"
#include "lsquic_mm.h"
#define LSQUIC_LOGGER_MODULE LSQLM_HSK_ADAPTER
#define LSQUIC_LOG_CONN_ID lsquic_conn_id(c_hsk->lconn)
#include "lsquic_logger.h"
static lsquic_stream_ctx_t *
hsk_client_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream)
{
struct client_hsk_ctx *const c_hsk = stream_if_ctx;
LSQ_DEBUG("stream created");
lsquic_stream_wantwrite(stream, 1);
return (void *) c_hsk;
}
static void
hsk_client_on_read (lsquic_stream_t *stream, struct lsquic_stream_ctx *sh)
{
struct client_hsk_ctx *const c_hsk = (struct client_hsk_ctx *) sh;
ssize_t nread;
int s;
if (!c_hsk->buf_in)
{
c_hsk->buf_in = lsquic_mm_get_16k(c_hsk->mm);
if (!c_hsk->buf_in)
{
LSQ_WARN("could not get buffer: %s", strerror(errno));
lsquic_stream_wantread(stream, 0);
lsquic_conn_close(c_hsk->lconn);
return;
}
c_hsk->buf_sz = 16 * 1024;
c_hsk->buf_off = 0;
}
nread = lsquic_stream_read(stream, c_hsk->buf_in + c_hsk->buf_off,
c_hsk->buf_sz - c_hsk->buf_off);
if (nread <= 0)
{
if (nread < 0)
LSQ_INFO("Could not read from handshake stream: %s",
strerror(errno));
else
LSQ_INFO("Handshake stream closed (odd)");
lsquic_mm_put_16k(c_hsk->mm, c_hsk->buf_in);
c_hsk->buf_in = NULL;
lsquic_stream_wantread(stream, 0);
lsquic_conn_close(c_hsk->lconn);
return;
}
c_hsk->buf_off += nread;
s = handle_chlo_reply(c_hsk->lconn->cn_enc_session,
c_hsk->buf_in, c_hsk->buf_off);
LSQ_DEBUG("handle_chlo_reply returned %d", s);
switch (s)
{
case DATA_NOT_ENOUGH:
if (c_hsk->buf_off < c_hsk->buf_sz)
LSQ_INFO("not enough server response has arrived, continue "
"buffering");
else
{
LSQ_INFO("read in %u bytes of server response, and it is still "
"not enough: giving up", c_hsk->buf_off);
lsquic_mm_put_16k(c_hsk->mm, c_hsk->buf_in);
c_hsk->buf_in = NULL;
lsquic_stream_wantread(stream, 0);
lsquic_conn_close(c_hsk->lconn);
}
break;
case DATA_NO_ERROR:
lsquic_mm_put_16k(c_hsk->mm, c_hsk->buf_in);
c_hsk->buf_in = NULL;
lsquic_stream_wantread(stream, 0);
if (is_hs_done(c_hsk->lconn->cn_enc_session))
{
LSQ_DEBUG("handshake is complete, inform connection");
c_hsk->lconn->cn_if->ci_handshake_done(c_hsk->lconn);
}
else
{
LSQ_DEBUG("handshake not yet complete, will generate another "
"message");
lsquic_stream_wantwrite(stream, 1);
}
break;
default:
LSQ_WARN("handle_chlo_reply returned unknown value %d", s);
case DATA_FORMAT_ERROR:
LSQ_INFO("handle_chlo_reply returned an error");
break;
}
}
/* In this function, we assume that we can write the whole message in one
* shot. Otherwise, this is an error.
*/
static void
hsk_client_on_write (lsquic_stream_t *stream, struct lsquic_stream_ctx *sh)
{
struct client_hsk_ctx *const c_hsk = (struct client_hsk_ctx *) sh;
unsigned char *buf;
size_t len;
ssize_t nw;
lsquic_stream_wantwrite(stream, 0);
buf = lsquic_mm_get_4k(c_hsk->mm);
if (!buf)
{
LSQ_WARN("cannot allocate buffer: %s", strerror(errno));
lsquic_conn_close(c_hsk->lconn);
return;
}
len = 4 * 1024;
if (0 != gen_chlo(c_hsk->lconn->cn_enc_session, c_hsk->ver_neg->vn_ver,
buf, &len))
{
LSQ_WARN("cannot create CHLO message");
lsquic_mm_put_4k(c_hsk->mm, buf);
lsquic_conn_close(c_hsk->lconn);
return;
}
nw = lsquic_stream_write(stream, buf, len);
lsquic_mm_put_4k(c_hsk->mm, buf);
if (nw < 0)
LSQ_INFO("error writing to stream: %s", strerror(errno));
else if ((size_t) nw == len)
{
LSQ_INFO("wrote %zd bytes of CHLO to stream", nw);
lsquic_stream_flush(stream);
lsquic_stream_wantread(stream, 1);
}
else
LSQ_INFO("could only write %zd bytes to stream instead of %zd",
nw, len);
}
static void
hsk_client_on_close (lsquic_stream_t *stream, struct lsquic_stream_ctx *sh)
{
struct client_hsk_ctx *const c_hsk = (struct client_hsk_ctx *) sh;
if (c_hsk->buf_in)
lsquic_mm_put_16k(c_hsk->mm, c_hsk->buf_in);
LSQ_DEBUG("stream closed");
}
const struct lsquic_stream_if lsquic_client_hsk_stream_if =
{
.on_new_stream = hsk_client_on_new_stream,
.on_read = hsk_client_on_read,
.on_write = hsk_client_on_write,
.on_close = hsk_client_on_close,
};

View File

@ -0,0 +1,24 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* Stream/crypto handshake adapter for the client side.
*/
#ifndef LSQUIC_CHSK_STREAM_H
#define LSQUIC_CHSK_STREAM_H 1
struct lsquic_conn;
struct lsquic_mm;
struct ver_neg;
struct client_hsk_ctx {
struct lsquic_conn *lconn;
struct lsquic_mm *mm;
const struct ver_neg *ver_neg;
unsigned char *buf_in; /* Server response may have to be buffered */
unsigned buf_sz, /* Total number of bytes in `buf_in' */
buf_off; /* Number of bytes read into `buf_in' */
};
extern const struct lsquic_stream_if lsquic_client_hsk_stream_if;
#endif

148
src/liblsquic/lsquic_conn.c Normal file
View File

@ -0,0 +1,148 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#include <assert.h>
#include <inttypes.h>
#include <string.h>
#include "lsquic.h"
#include "lsquic_int_types.h"
#include "lsquic_conn.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_in.h"
#include "lsquic_handshake.h"
#include "lsquic_mm.h"
#include "lsquic_engine_public.h"
#include "lsquic_ev_log.h"
#include "lsquic_logger.h"
lsquic_cid_t
lsquic_conn_id (const lsquic_conn_t *lconn)
{
return lconn->cn_cid;
}
void *
lsquic_conn_get_peer_ctx( const lsquic_conn_t *lconn)
{
return lconn->cn_peer_ctx;
}
void
lsquic_conn_record_sockaddr (lsquic_conn_t *lconn,
const struct sockaddr *local, const struct sockaddr *peer)
{
assert(local->sa_family == peer->sa_family);
switch (local->sa_family)
{
case AF_INET:
lconn->cn_flags |= LSCONN_HAS_PEER_SA|LSCONN_HAS_LOCAL_SA;
memcpy(lconn->cn_local_addr, local, sizeof(struct sockaddr_in));
memcpy(lconn->cn_peer_addr, peer, sizeof(struct sockaddr_in));
break;
case AF_INET6:
lconn->cn_flags |= LSCONN_HAS_PEER_SA|LSCONN_HAS_LOCAL_SA;
memcpy(lconn->cn_local_addr, local, sizeof(struct sockaddr_in6));
memcpy(lconn->cn_peer_addr, peer, sizeof(struct sockaddr_in6));
break;
}
}
void
lsquic_conn_record_peer_sa (lsquic_conn_t *lconn, const struct sockaddr *peer)
{
switch (peer->sa_family)
{
case AF_INET:
lconn->cn_flags |= LSCONN_HAS_PEER_SA;
memcpy(lconn->cn_peer_addr, peer, sizeof(struct sockaddr_in));
break;
case AF_INET6:
lconn->cn_flags |= LSCONN_HAS_PEER_SA;
memcpy(lconn->cn_peer_addr, peer, sizeof(struct sockaddr_in6));
break;
}
}
int
lsquic_conn_get_sockaddr (const lsquic_conn_t *lconn,
const struct sockaddr **local, const struct sockaddr **peer)
{
if ((lconn->cn_flags & (LSCONN_HAS_PEER_SA|LSCONN_HAS_LOCAL_SA)) ==
(LSCONN_HAS_PEER_SA|LSCONN_HAS_LOCAL_SA))
{
*local = (struct sockaddr *) lconn->cn_local_addr;
*peer = (struct sockaddr *) lconn->cn_peer_addr;
return 0;
}
else
return -1;
}
int
lsquic_conn_copy_and_release_pi_data (const lsquic_conn_t *conn,
struct lsquic_engine_public *enpub, lsquic_packet_in_t *packet_in)
{
assert(!(packet_in->pi_flags & PI_OWN_DATA));
/* The size should be guarded in lsquic_engine_packet_in(): */
assert(packet_in->pi_data_sz <= QUIC_MAX_PACKET_SZ);
unsigned char *const copy = lsquic_mm_get_1370(&enpub->enp_mm);
if (!copy)
{
LSQ_WARN("cannot allocate memory to copy incoming packet data");
return -1;
}
memcpy(copy, packet_in->pi_data, packet_in->pi_data_sz);
packet_in->pi_data = copy;
packet_in->pi_flags |= PI_OWN_DATA;
return 0;
}
int
lsquic_conn_decrypt_packet (lsquic_conn_t *lconn,
struct lsquic_engine_public *enpub,
lsquic_packet_in_t *packet_in)
{
size_t header_len, data_len;
size_t out_len = 0;
unsigned char *copy = lsquic_mm_get_1370(&enpub->enp_mm);
if (!copy)
{
LSQ_WARN("cannot allocate memory to copy incoming packet data");
return -1;
}
header_len = packet_in->pi_header_sz;
data_len = packet_in->pi_data_sz - packet_in->pi_header_sz;
if (0 == lsquic_dec(lconn->cn_enc_session, lconn->cn_version, 0,
packet_in->pi_packno, packet_in->pi_data,
&header_len, data_len,
lsquic_packet_in_nonce(packet_in),
copy, 1370, &out_len))
{
assert(header_len + out_len <= 1370);
if (packet_in->pi_flags & PI_OWN_DATA)
lsquic_mm_put_1370(&enpub->enp_mm, packet_in->pi_data);
packet_in->pi_data = copy;
packet_in->pi_flags |= PI_OWN_DATA | PI_DECRYPTED;
packet_in->pi_header_sz = header_len;
packet_in->pi_data_sz = out_len + header_len;
EV_LOG_CONN_EVENT(lconn->cn_cid, "decrypted packet %"PRIu64,
packet_in->pi_packno);
return 0;
}
else
{
lsquic_mm_put_1370(&enpub->enp_mm, copy);
EV_LOG_CONN_EVENT(lconn->cn_cid, "could not decrypt packet %"PRIu64,
packet_in->pi_packno);
return -1;
}
}

130
src/liblsquic/lsquic_conn.h Normal file
View File

@ -0,0 +1,130 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_conn.h -- Connection interface
*
*/
#ifndef LSQUIC_CONN_H
#define LSQUIC_CONN_H
#include <sys/queue.h>
#include <sys/socket.h>
#include <netinet/in.h>
struct lsquic_conn;
struct lsquic_enc_session;
struct lsquic_engine_public;
struct lsquic_packet_out;
struct lsquic_packet_in;
struct sockaddr;
struct parse_funcs;
struct attq_elem;
enum lsquic_conn_flags {
LSCONN_HAS_INCOMING = (1 << 0),
LSCONN_HAS_OUTGOING = (1 << 1),
LSCONN_HASHED = (1 << 2),
LSCONN_HAS_PEER_SA = (1 << 4),
LSCONN_HAS_LOCAL_SA = (1 << 5),
LSCONN_HANDSHAKE_DONE = (1 << 6),
LSCONN_CLOSING = (1 << 7),
LSCONN_PEER_GOING_AWAY= (1 << 8),
LSCONN_TCID0 = (1 << 9),
LSCONN_VER_SET = (1 <<10), /* cn_version is set */
LSCONN_EVANESCENT = (1 <<11), /* evanescent connection */
LSCONN_RW_PENDING = (1 <<12),
LSCONN_COI_ACTIVE = (1 <<13),
LSCONN_COI_INACTIVE = (1 <<14),
LSCONN_SEND_BLOCKED = (1 <<15), /* Send connection blocked frame */
LSCONN_NEVER_PEND_RW = (1 <<17), /* Do not put onto Pending RW queue */
LSCONN_ATTQ = (1 <<19),
};
#define TICK_BIT_PROGRESS 3
/* A connection may have things to send and be closed at the same time.
*/
enum tick_st {
TICK_SEND = (1 << 0),
TICK_CLOSE = (1 << 1),
/* Progress was made (see @ref es_pendrw_check for definition of
* "progress.")
*/
TICK_PROGRESS= (1 << TICK_BIT_PROGRESS),
};
#define TICK_QUIET 0
struct conn_iface
{
enum tick_st
(*ci_tick) (struct lsquic_conn *, lsquic_time_t now);
void
(*ci_packet_in) (struct lsquic_conn *, struct lsquic_packet_in *);
struct lsquic_packet_out *
(*ci_next_packet_to_send) (struct lsquic_conn *);
void
(*ci_packet_sent) (struct lsquic_conn *, struct lsquic_packet_out *);
void
(*ci_packet_not_sent) (struct lsquic_conn *, struct lsquic_packet_out *);
void
(*ci_handshake_done) (struct lsquic_conn *);
int
(*ci_user_wants_read) (struct lsquic_conn *);
void
(*ci_destroy) (struct lsquic_conn *);
};
#define RW_HIST_BITS 6
typedef unsigned char rw_hist_idx_t;
struct lsquic_conn
{
void *cn_peer_ctx;
struct lsquic_enc_session *cn_enc_session;
lsquic_cid_t cn_cid;
STAILQ_ENTRY(lsquic_conn) cn_next_closed_conn;
TAILQ_ENTRY(lsquic_conn) cn_next_all,
cn_next_in,
cn_next_pend_rw,
cn_next_out,
cn_next_hash;
const struct conn_iface *cn_if;
const struct parse_funcs *cn_pf;
struct attq_elem *cn_attq_elem;
lsquic_time_t cn_last_sent;
enum lsquic_conn_flags cn_flags;
enum lsquic_version cn_version;
unsigned cn_noprogress_count;
unsigned cn_hash;
unsigned short cn_pack_size;
rw_hist_idx_t cn_rw_hist_idx;
unsigned char cn_rw_hist_buf[ 1 << RW_HIST_BITS ];
unsigned char cn_peer_addr[sizeof(struct sockaddr_in6)],
cn_local_addr[sizeof(struct sockaddr_in6)];
};
void
lsquic_conn_record_sockaddr (lsquic_conn_t *lconn, const struct sockaddr *local,
const struct sockaddr *peer);
void
lsquic_conn_record_peer_sa (lsquic_conn_t *lconn, const struct sockaddr *peer);
int
lsquic_conn_decrypt_packet (lsquic_conn_t *lconn,
struct lsquic_engine_public *, struct lsquic_packet_in *);
int
lsquic_conn_copy_and_release_pi_data (const lsquic_conn_t *conn,
struct lsquic_engine_public *, struct lsquic_packet_in *);
#define lsquic_conn_adv_time(c) ((c)->cn_attq_elem->ae_adv_time)
#endif

View File

@ -0,0 +1,74 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_conn_flow.h -- Connection flow control-related functions
*/
#ifndef LSQUIC_CONN_FLOW_H
#define LSQUIC_CONN_FLOW_H 1
struct lsquic_conn_public;
typedef struct lsquic_cfcw {
struct lsquic_conn_public
*cf_conn_pub;
uint64_t cf_max_recv_off; /* Largest offset observed (cumulative) */
uint64_t cf_recv_off; /* Flow control receive offset */
uint64_t cf_read_off; /* Number of bytes consumed (cumulative) */
lsquic_time_t cf_last_updated;
unsigned cf_max_recv_win; /* Maximum receive window */
} lsquic_cfcw_t;
struct lsquic_conn_cap {
uint64_t cc_sent; /* Number of bytes sent on connection */
uint64_t cc_max; /* Maximum cumulative number of bytes allowed
* to be sent on this connection.
*/
uint64_t cc_tosend; /* Number of bytes scheduled to be sent
* accross all streams.
*/
uint64_t cc_blocked; /* Last blocked offset used */
};
#define lsquic_conn_cap_init(cc, max) do { \
(cc)->cc_sent = 0; \
(cc)->cc_tosend = 0; \
(cc)->cc_max = max; \
} while (0)
#define lsquic_conn_cap_avail(cap) ( \
((cap)->cc_sent + (cap)->cc_tosend <= (cap)->cc_max) \
? \
((cap)->cc_max - (cap)->cc_sent - (cap)->cc_tosend) \
: \
0 \
)
void
lsquic_cfcw_init (lsquic_cfcw_t *, struct lsquic_conn_public *,
unsigned initial_max_recv_window);
/* If update is to be sent, updates max_recv_off and returns true. Note
* that if you call this function twice, the second call will return false.
*/
int
lsquic_cfcw_fc_offsets_changed (lsquic_cfcw_t *);
#define lsquic_cfcw_get_fc_recv_off(fc) (+(fc)->cf_recv_off)
#define lsquic_cfcw_get_max_recv_off(fc) (+(fc)->cf_max_recv_off)
#define lsquic_cfcw_get_max_recv_window(fc) (+(fc)->cf_max_recv_win)
/* Returns false if flow control violation is encountered */
int
lsquic_cfcw_incr_max_recv_off (lsquic_cfcw_t *, uint64_t);
/* Void because we do not expect the caller to make a mistake.
*/
void
lsquic_cfcw_incr_read_off (lsquic_cfcw_t *, uint64_t);
#endif

View File

@ -0,0 +1,171 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include "lsquic.h"
#include "lsquic_int_types.h"
#include "lsquic_conn.h"
#include "lsquic_conn_hash.h"
#include "lsquic_xxhash.h"
#define LSQUIC_LOGGER_MODULE LSQLM_CONN_HASH
#include "lsquic_logger.h"
#define n_buckets(nbits) (1U << (nbits))
#define conn_hash_mask(conn_hash) ((1 << (conn_hash)->ch_nbits) - 1)
#define conn_hash_bucket_no(conn_hash, hash) (hash & conn_hash_mask(conn_hash))
int
conn_hash_init (struct conn_hash *conn_hash, unsigned max_count)
{
unsigned n;
if (!max_count)
max_count = 1000000;
memset(conn_hash, 0, sizeof(*conn_hash));
conn_hash->ch_max_count = max_count;
conn_hash->ch_nbits = 1; /* Start small */
TAILQ_INIT(&conn_hash->ch_all);
conn_hash->ch_buckets = malloc(sizeof(conn_hash->ch_buckets[0]) *
n_buckets(conn_hash->ch_nbits));
if (!conn_hash->ch_buckets)
return -1;
for (n = 0; n < n_buckets(conn_hash->ch_nbits); ++n)
TAILQ_INIT(&conn_hash->ch_buckets[n]);
LSQ_INFO("initialized: max_count: %u", conn_hash->ch_max_count);
return 0;
}
void
conn_hash_cleanup (struct conn_hash *conn_hash)
{
free(conn_hash->ch_buckets);
}
struct lsquic_conn *
conn_hash_find (struct conn_hash *conn_hash, lsquic_cid_t cid, unsigned *hashp)
{
const unsigned hash = XXH32(&cid, sizeof(cid), (uintptr_t) conn_hash);
const unsigned buckno = conn_hash_bucket_no(conn_hash, hash);
struct lsquic_conn *lconn;
TAILQ_FOREACH(lconn, &conn_hash->ch_buckets[buckno], cn_next_hash)
if (lconn->cn_cid == cid)
return lconn;
if (hashp)
*hashp = hash;
return NULL;
}
static int
double_conn_hash_buckets (struct conn_hash *conn_hash)
{
struct lsquic_conn_head *new_buckets, *new[2];
struct lsquic_conn *lconn;
unsigned n, old_nbits;
int idx;
old_nbits = conn_hash->ch_nbits;
LSQ_INFO("doubling number of buckets to %u", n_buckets(old_nbits + 1));
new_buckets = malloc(sizeof(conn_hash->ch_buckets[0])
* n_buckets(old_nbits + 1));
if (!new_buckets)
{
LSQ_WARN("malloc failed: potential trouble ahead");
return -1;
}
for (n = 0; n < n_buckets(old_nbits); ++n)
{
new[0] = &new_buckets[n];
new[1] = &new_buckets[n + n_buckets(old_nbits)];
TAILQ_INIT(new[0]);
TAILQ_INIT(new[1]);
while ((lconn = TAILQ_FIRST(&conn_hash->ch_buckets[n])))
{
TAILQ_REMOVE(&conn_hash->ch_buckets[n], lconn, cn_next_hash);
idx = (lconn->cn_hash >> old_nbits) & 1;
TAILQ_INSERT_TAIL(new[idx], lconn, cn_next_hash);
}
}
free(conn_hash->ch_buckets);
conn_hash->ch_nbits = old_nbits + 1;
conn_hash->ch_buckets = new_buckets;
return 0;
}
int
conn_hash_add (struct conn_hash *conn_hash, struct lsquic_conn *lconn,
unsigned hash)
{
assert(hash == XXH32(&lconn->cn_cid, sizeof(lconn->cn_cid),
(uintptr_t) conn_hash));
if (conn_hash->ch_count >= conn_hash->ch_max_count)
return -1;
if (conn_hash->ch_count >=
n_buckets(conn_hash->ch_nbits) * CONN_HASH_MAX_PER_BUCKET &&
conn_hash->ch_nbits < sizeof(hash) * 8 - 1 &&
0 != double_conn_hash_buckets(conn_hash))
{
return -1;
}
const unsigned buckno = conn_hash_bucket_no(conn_hash, hash);
lconn->cn_hash = hash;
TAILQ_INSERT_TAIL(&conn_hash->ch_all, lconn, cn_next_all);
TAILQ_INSERT_TAIL(&conn_hash->ch_buckets[buckno], lconn, cn_next_hash);
++conn_hash->ch_count;
return 0;
}
int
conn_hash_add_new (struct conn_hash *conn_hash, struct lsquic_conn *lconn)
{
unsigned hash_val;
hash_val = XXH32(&lconn->cn_cid, sizeof(lconn->cn_cid),
(uintptr_t) conn_hash);
return conn_hash_add(conn_hash, lconn, hash_val);
}
void
conn_hash_remove (struct conn_hash *conn_hash, struct lsquic_conn *lconn)
{
const unsigned buckno = conn_hash_bucket_no(conn_hash, lconn->cn_hash);
TAILQ_REMOVE(&conn_hash->ch_all, lconn, cn_next_all);
TAILQ_REMOVE(&conn_hash->ch_buckets[buckno], lconn, cn_next_hash);
--conn_hash->ch_count;
}
void
conn_hash_reset_iter (struct conn_hash *conn_hash)
{
conn_hash->ch_next = TAILQ_FIRST(&conn_hash->ch_all);
}
struct lsquic_conn *
conn_hash_first (struct conn_hash *conn_hash)
{
conn_hash_reset_iter(conn_hash);
return conn_hash_next(conn_hash);
}
struct lsquic_conn *
conn_hash_next (struct conn_hash *conn_hash)
{
struct lsquic_conn *lconn = conn_hash->ch_next;
if (lconn)
conn_hash->ch_next = TAILQ_NEXT(lconn, cn_next_all);
return lconn;
}

View File

@ -0,0 +1,79 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_conn_hash.h -- A hash of connections
*/
#ifndef LSQUIC_MC_SET_H
#define LSQUIC_MC_SET_H
#include <sys/queue.h>
/* Once we reach this many per bucket on average, double the number of
* buckets and redistribute entries.
*
* This value should be a power of two for speed.
*/
#define CONN_HASH_MAX_PER_BUCKET 2
struct lsquic_conn;
TAILQ_HEAD(lsquic_conn_head, lsquic_conn);
struct conn_hash
{
struct lsquic_conn_head ch_all;
struct lsquic_conn_head *ch_buckets;
struct lsquic_conn *ch_next;
unsigned ch_count;
unsigned ch_nbits;
unsigned ch_max_count;
};
#define conn_hash_count(conn_hash) (+(conn_hash)->ch_count)
/* Returns -1 if malloc fails */
int
conn_hash_init (struct conn_hash *, unsigned max_count);
void
conn_hash_cleanup (struct conn_hash *);
/* If entry is not found, `hash' is set. Use it as argument to conn_hash_add.
* `hash' may be NULL.
*/
struct lsquic_conn *
conn_hash_find (struct conn_hash *conn_hash, lsquic_cid_t cid, unsigned *hash);
/* Returns -1 if limit has been reached or if malloc fails */
int
conn_hash_add (struct conn_hash *, struct lsquic_conn *, unsigned hash);
/* Returns -1 if limit has been reached or if malloc fails */
int
conn_hash_add_new (struct conn_hash *, struct lsquic_conn *);
void
conn_hash_remove (struct conn_hash *, struct lsquic_conn *);
/* Two ways to use the iterator:
* 1.
* for (conn = conn_hash_first(hash); conn;
* conn = conn_hash_next(hash))
* { ... }
*
* 2.
* conn_hash_reset_iter(hash);
* while ((conn = conn_hash_next(hash)))
* { ... }
*
*/
void
conn_hash_reset_iter (struct conn_hash *);
struct lsquic_conn *
conn_hash_first (struct conn_hash *);
struct lsquic_conn *
conn_hash_next (struct conn_hash *);
#endif

View File

@ -0,0 +1,35 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_conn_public.h -- Connection's "public interface"
*
* This structure is used to bundle things in connection that stream
* needs access to into a single object. This way, the space per
* stream object is one pointer instead of four or five.
*/
#ifndef LSQUIC_CONN_PUBLIC_H
#define LSQUIC_CONN_PUBLIC_H 1
struct lsquic_conn;
struct lsquic_engine_public;
struct lsquic_mm;
struct lsquic_stream;
struct headers_stream;
struct lsquic_send_ctl;
struct lsquic_conn_public {
struct lsquic_streams_tailq sending_streams,
rw_streams,
service_streams;
struct lsquic_cfcw cfcw;
struct lsquic_conn_cap conn_cap;
struct lsquic_rtt_stats rtt_stats;
struct lsquic_engine_public *enpub;
struct malo *packet_out_malo;
struct lsquic_conn *lconn;
struct lsquic_mm *mm;
struct headers_stream *hs;
struct lsquic_send_ctl *send_ctl;
};
#endif

View File

@ -0,0 +1,562 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#include <assert.h>
#include <stdbool.h>
#include <string.h>
#include <time.h>
#include <zlib.h>
#include <openssl/ssl.h>
#include "lsquic_int_types.h"
#include "lsquic_crypto.h"
#include "lsquic_crt_compress.h"
#include "lsquic_util.h"
#include "lsquic_str.h"
#include "common_cert_set_2.c"
#include "common_cert_set_3.c"
/*
* common_cert_sub_strings contains ~1500 bytes of common certificate substrings
* as a dictionary of zlib from the Alexa Top 5000 set.
*/
static const unsigned char common_cert_sub_strings[] = {
0x04, 0x02, 0x30, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04,
0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03,
0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30,
0x5f, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x04, 0x01,
0x06, 0x06, 0x0b, 0x60, 0x86, 0x48, 0x01, 0x86, 0xfd, 0x6d, 0x01, 0x07,
0x17, 0x01, 0x30, 0x33, 0x20, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65,
0x64, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x20, 0x53, 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, 0x34,
0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31,
0x32, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x53, 0x65, 0x72,
0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x30, 0x2d, 0x61, 0x69, 0x61, 0x2e,
0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
0x2f, 0x45, 0x2d, 0x63, 0x72, 0x6c, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73,
0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x45, 0x2e, 0x63, 0x65,
0x72, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x4a, 0x2e, 0x63,
0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73,
0x2f, 0x63, 0x70, 0x73, 0x20, 0x28, 0x63, 0x29, 0x30, 0x30, 0x09, 0x06,
0x03, 0x55, 0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x1d, 0x30, 0x0d,
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05,
0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x7b, 0x30, 0x1d, 0x06, 0x03, 0x55,
0x1d, 0x0e, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xd2,
0x6f, 0x64, 0x6f, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x2e,
0x63, 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16,
0x04, 0x14, 0xb4, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69,
0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x30, 0x0b, 0x06, 0x03,
0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x01, 0x30, 0x0d, 0x06, 0x09,
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30,
0x81, 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08,
0x13, 0x07, 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31, 0x13, 0x30,
0x11, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63, 0x6f, 0x74,
0x74, 0x73, 0x64, 0x61, 0x6c, 0x65, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03,
0x55, 0x04, 0x0a, 0x13, 0x11, 0x47, 0x6f, 0x44, 0x61, 0x64, 0x64, 0x79,
0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x33,
0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2a, 0x68, 0x74, 0x74,
0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79,
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74,
0x6f, 0x72, 0x79, 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03,
0x13, 0x27, 0x47, 0x6f, 0x20, 0x44, 0x61, 0x64, 0x64, 0x79, 0x20, 0x53,
0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66,
0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68,
0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55,
0x04, 0x05, 0x13, 0x08, 0x30, 0x37, 0x39, 0x36, 0x39, 0x32, 0x38, 0x37,
0x30, 0x1e, 0x17, 0x0d, 0x31, 0x31, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x05, 0xa0, 0x30, 0x0c,
0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00,
0x30, 0x1d, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff,
0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55,
0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
0x03, 0x02, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff,
0x04, 0x04, 0x03, 0x02, 0x05, 0xa0, 0x30, 0x33, 0x06, 0x03, 0x55, 0x1d,
0x1f, 0x04, 0x2c, 0x30, 0x2a, 0x30, 0x28, 0xa0, 0x26, 0xa0, 0x24, 0x86,
0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e,
0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
0x67, 0x64, 0x73, 0x31, 0x2d, 0x32, 0x30, 0x2a, 0x30, 0x28, 0x06, 0x08,
0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74,
0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65,
0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63,
0x70, 0x73, 0x30, 0x34, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17,
0x0d, 0x31, 0x33, 0x30, 0x35, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01,
0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a,
0x2f, 0x2f, 0x73, 0x30, 0x39, 0x30, 0x37, 0x06, 0x08, 0x2b, 0x06, 0x01,
0x05, 0x05, 0x07, 0x02, 0x30, 0x44, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04,
0x3d, 0x30, 0x3b, 0x30, 0x39, 0x06, 0x0b, 0x60, 0x86, 0x48, 0x01, 0x86,
0xf8, 0x45, 0x01, 0x07, 0x17, 0x06, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
0x55, 0x04, 0x06, 0x13, 0x02, 0x47, 0x42, 0x31, 0x1b, 0x53, 0x31, 0x17,
0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x65, 0x72,
0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31,
0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x56, 0x65,
0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74,
0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x3b, 0x30, 0x39,
0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x32, 0x54, 0x65, 0x72, 0x6d, 0x73,
0x20, 0x6f, 0x66, 0x20, 0x75, 0x73, 0x65, 0x20, 0x61, 0x74, 0x20, 0x68,
0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76,
0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
0x72, 0x70, 0x61, 0x20, 0x28, 0x63, 0x29, 0x30, 0x31, 0x10, 0x30, 0x0e,
0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x07, 0x53, 0x31, 0x13, 0x30, 0x11,
0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0a, 0x47, 0x31, 0x13, 0x30, 0x11,
0x06, 0x0b, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x3c, 0x02, 0x01,
0x03, 0x13, 0x02, 0x55, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04,
0x03, 0x14, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, 0x0f, 0x13, 0x14, 0x50,
0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x20, 0x4f, 0x72, 0x67, 0x61, 0x6e,
0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x12, 0x31, 0x21, 0x30,
0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x18, 0x44, 0x6f, 0x6d, 0x61,
0x69, 0x6e, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x20, 0x56,
0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x31, 0x14, 0x31, 0x31,
0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x28, 0x53, 0x65, 0x65,
0x20, 0x77, 0x77, 0x77, 0x2e, 0x72, 0x3a, 0x2f, 0x2f, 0x73, 0x65, 0x63,
0x75, 0x72, 0x65, 0x2e, 0x67, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53,
0x69, 0x67, 0x6e, 0x31, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x41,
0x2e, 0x63, 0x72, 0x6c, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e,
0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x45, 0x63, 0x72,
0x6c, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63,
0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x73, 0x64, 0x31, 0x1a,
0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x68, 0x74, 0x74, 0x70, 0x3a,
0x2f, 0x2f, 0x45, 0x56, 0x49, 0x6e, 0x74, 0x6c, 0x2d, 0x63, 0x63, 0x72,
0x74, 0x2e, 0x67, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x69, 0x63, 0x65, 0x72,
0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x31, 0x6f, 0x63, 0x73, 0x70, 0x2e,
0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
0x30, 0x39, 0x72, 0x61, 0x70, 0x69, 0x64, 0x73, 0x73, 0x6c, 0x2e, 0x63,
0x6f, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63,
0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72,
0x79, 0x2f, 0x30, 0x81, 0x80, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
0x07, 0x01, 0x01, 0x04, 0x74, 0x30, 0x72, 0x30, 0x24, 0x06, 0x08, 0x2b,
0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74,
0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6f, 0x64,
0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x4a, 0x06,
0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x3e, 0x68,
0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66,
0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64,
0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73,
0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x67, 0x64, 0x5f, 0x69, 0x6e, 0x74,
0x65, 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x2e, 0x63, 0x72,
0x74, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
0x80, 0x14, 0xfd, 0xac, 0x61, 0x32, 0x93, 0x6c, 0x45, 0xd6, 0xe2, 0xee,
0x85, 0x5f, 0x9a, 0xba, 0xe7, 0x76, 0x99, 0x68, 0xcc, 0xe7, 0x30, 0x27,
0x86, 0x29, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x86, 0x30,
0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x73,
};
#define common_certs_num 2
const common_cert_t common_cert_set[common_certs_num] = {
{common_certs2_num, common_certs2, common_certs2_lens, common_certs2_hash},
{common_certs3_num, common_certs3, common_certs3_lens, common_certs3_hash},
};
static lsquic_str_t *s_ccsbuf;
lsquic_str_t * get_common_certs_hash()
{
int i;
if (s_ccsbuf == NULL)
{
s_ccsbuf = lsquic_str_new(NULL, 0);
for (i=0 ;i<common_certs_num; ++i)
{
lsquic_str_append(s_ccsbuf, (const char *)&common_cert_set[i].hash, 8);
}
}
return s_ccsbuf;
}
/* return 0 found, -1 not found */
int get_common_cert(uint64_t hash, uint32_t index, lsquic_str_t *buf)
{
int i;
for (i = 0; i < common_certs_num; i++)
{
if (common_cert_set[i].hash == hash)
{
if (index < common_cert_set[i].num_certs)
{
lsquic_str_setto(buf, (const char *) common_cert_set[i].certs[index],
common_cert_set[i].lens[index]);
return 0;
}
break;
}
}
return -1;
}
/* result is written to dict */
static void
make_zlib_dict_for_entries(cert_entry_t *entries,
lsquic_str_t **certs, size_t certs_count,
lsquic_str_t *dict)
{
int i;
size_t zlib_dict_size = 0;
for (i = certs_count - 1; i >= 0; --i)
{
if (entries[i].type != ENTRY_COMPRESSED)
{
zlib_dict_size += lsquic_str_len(certs[i]);
}
}
// At the end of the dictionary is a block of common certificate substrings.
zlib_dict_size += sizeof(common_cert_sub_strings);
for (i = certs_count - 1; i >= 0; --i)
{
if (entries[i].type != ENTRY_COMPRESSED)
{
lsquic_str_append(dict, lsquic_str_buf(certs[i]), lsquic_str_len(certs[i]));
}
}
lsquic_str_append(dict, (const char *)common_cert_sub_strings, sizeof(common_cert_sub_strings));
assert((size_t)lsquic_str_len(dict) == zlib_dict_size);
}
void get_certs_hash(lsquic_str_t *certs, size_t certs_count, uint64_t *hashs)
{
size_t i;
for(i = 0; i < certs_count; ++i)
{
hashs[i] = fnv1a_64((const uint8_t *)lsquic_str_buf(&certs[i]), lsquic_str_len(&certs[i]));
}
}
size_t get_entries_size(cert_entry_t *entries, size_t entries_count)
{
size_t i;
size_t entries_size = 0;
for(i=0; i<entries_count; ++i)
{
entries_size++;
switch (entries[i].type)
{
case ENTRY_COMPRESSED:
break;
case ENTRY_CACHED:
entries_size += sizeof(uint64_t);
break;
case ENTRY_COMMON:
entries_size += sizeof(uint64_t) + sizeof(uint32_t);
break;
default:
break;
}
}
entries_size++; /* for end marker */
return entries_size;
}
void serialize_cert_entries(uint8_t* out, int *out_len, cert_entry_t *entries,
size_t entries_count)
{
size_t i;
uint8_t *start = out;
for(i=0; i<entries_count; ++i)
{
*out++ = (uint8_t)(entries[i].type);
switch (entries[i].type)
{
case ENTRY_COMPRESSED:
break;
case ENTRY_CACHED:
memcpy(out, &entries[i].hash, sizeof(uint64_t));
out += sizeof(uint64_t);
break;
case ENTRY_COMMON:
memcpy(out, &entries[i].set_hash, sizeof(uint64_t));
out += sizeof(uint64_t);
memcpy(out, &entries[i].index, sizeof(uint32_t));
out += sizeof(uint32_t);
break;
default:
break;
}
}
*out++ = 0; // end marker
*out_len = out - start;
}
int get_certs_count(lsquic_str_t *compressed_crt_buf)
{
char *in = lsquic_str_buf(compressed_crt_buf);
char *in_end = in + lsquic_str_len(compressed_crt_buf);
size_t idx = 0;
uint8_t type_byte;
for (;;)
{
if (in >= in_end)
return -1;
type_byte = in[0];
++in;
if (type_byte == 0)
break;
++idx;
switch(type_byte)
{
case ENTRY_COMPRESSED:
break;
case ENTRY_CACHED:
{
if (in_end - in < (int)sizeof(uint64_t))
return -1;
in += sizeof(uint64_t);
break;
}
case ENTRY_COMMON:
{
if (in_end - in < (int)(sizeof(uint64_t) + sizeof(uint32_t)))
return -1;
in += sizeof(uint64_t) + sizeof(uint32_t);
break;
}
default:
return -1;
}
}
return idx;
}
/* return 0: OK, -1, error */
static int parse_entries(const unsigned char **in_out, const unsigned char *const in_end,
lsquic_str_t *cached_certs, size_t cached_certs_count,
cert_entry_t *out_entries,
lsquic_str_t **out_certs, size_t *out_certs_count)
{
const unsigned char *in = *in_out;
size_t idx = 0;
uint64_t* cached_hashes;
cert_entry_t *entry;
lsquic_str_t *cert;
uint8_t type_byte;
int rv;
size_t i;
cached_hashes = NULL;
for (;;)
{
/* XXX potential invalid read */
type_byte = in[0];
++in;
if (type_byte == 0)
break;
entry = &out_entries[idx];
cert = out_certs[idx];
/* XXX This seems dangerous -- there is no guard that `idx' does not
* exceed `out_certs_count'.
*/
lsquic_str_d(cert);
++idx;
entry->type = type_byte;
switch (entry->type)
{
case ENTRY_COMPRESSED:
break;
case ENTRY_CACHED:
{
memcpy(&entry->hash, in, sizeof(uint64_t));
in += sizeof(uint64_t);
if (!cached_hashes)
{
cached_hashes = malloc(cached_certs_count * sizeof(uint64_t));;
if (!cached_hashes)
goto err;
get_certs_hash(cached_certs, cached_certs_count, cached_hashes);
}
for (i=0; i<cached_certs_count; ++i)
{
if (cached_hashes[i] == entry->hash)
{
lsquic_str_append(cert, lsquic_str_buf(&cached_certs[i]),
lsquic_str_len(&cached_certs[i]));
break;
}
}
/* XXX: return -1 if not found? Logic removed in
4fd7e76bc031ac637e76c7f0930aff53f5b71705 */
break;
}
case ENTRY_COMMON:
{
memcpy(&entry->set_hash, in, sizeof(uint64_t));
in += sizeof(uint64_t);
memcpy(&entry->index, in, sizeof(uint32_t));
in += sizeof(uint32_t);
if (0 == get_common_cert(entry->set_hash, entry->index, cert))
break;
else
goto err;
}
default:
goto err;
}
}
rv = 0;
*in_out = in;
*out_certs_count = idx;
cleanup:
free(cached_hashes);
return rv;
err:
rv = -1;
goto cleanup;
}
/* 0: ok */
int decompress_certs(const unsigned char *in, const unsigned char *in_end,
lsquic_str_t *cached_certs, size_t cached_certs_count,
lsquic_str_t **out_certs, size_t *out_certs_count)
{
int ret;
size_t i;
uint8_t* uncompressed_data, *uncompressed_data_buf;
lsquic_str_t *dict;
uint32_t uncompressed_size;
size_t count = *out_certs_count;
cert_entry_t *entries;
z_stream z;
assert(*out_certs_count > 0 && *out_certs_count < 10000
&& "Call get_certs_count() to get right certificates count first and make enough room for out_certs_count");
if (count == 0 || count > 10000)
return -1;
dict = lsquic_str_new(NULL, 0);
if (!dict)
return -1;
uncompressed_data_buf = NULL;
entries = malloc(count * sizeof(cert_entry_t));
if (!entries)
goto err;
ret = parse_entries(&in, in_end, cached_certs, cached_certs_count,
entries, out_certs, out_certs_count);
if (ret)
goto err;
/* re-assign count with real valus */
count = *out_certs_count;
if (in < in_end)
{
if (in_end - in < (int)sizeof(uint32_t))
goto err;
memcpy(&uncompressed_size, in, sizeof(uncompressed_size));
in += sizeof(uint32_t);
/* XXX Is 128 KB an arbitrary limit or is there a reason behind it? */
if (uncompressed_size > 128 * 1024)
goto err;
uncompressed_data_buf = uncompressed_data = malloc(uncompressed_size);
memset(&z, 0, sizeof(z));
z.next_out = uncompressed_data;
z.avail_out = uncompressed_size;
z.next_in = (unsigned char *) in;
z.avail_in = in_end - in;
if (Z_OK != inflateInit(&z))
goto err;
ret = inflate(&z, Z_FINISH);
if (ret == Z_NEED_DICT)
{
lsquic_str_d(dict);
make_zlib_dict_for_entries(entries, out_certs, count, dict);
if (Z_OK != inflateSetDictionary(&z, (const unsigned char *)lsquic_str_buf(dict), lsquic_str_len(dict)))
goto err;
ret = inflate(&z, Z_FINISH);
}
if (Z_STREAM_END != ret || z.avail_out > 0 || z.avail_in > 0)
goto err;
}
else
uncompressed_size = 0;
for (i = 0; i < count; i++)
{
switch (entries[i].type)
{
case ENTRY_COMPRESSED:
if (uncompressed_size < sizeof(uint32_t))
goto err;
lsquic_str_d(out_certs[i]);
uint32_t cert_len;
memcpy(&cert_len, uncompressed_data, sizeof(cert_len));
uncompressed_data += sizeof(uint32_t);
uncompressed_size -= sizeof(uint32_t);
if (uncompressed_size < cert_len)
goto err;
lsquic_str_append(out_certs[i], (const char *)uncompressed_data, cert_len);
uncompressed_data += cert_len;
uncompressed_size -= cert_len;
break;
case ENTRY_CACHED:
case ENTRY_COMMON:
default:
break;
}
}
cleanup:
lsquic_str_delete(dict);
free(entries);
if (uncompressed_data_buf)
inflateEnd(&z);
free(uncompressed_data_buf);
if (0 == uncompressed_size)
return 0;
else
return -1;
err:
uncompressed_size = 1; /* This triggers return -1 above */
goto cleanup;
}
void
lsquic_crt_cleanup (void)
{
if (s_ccsbuf)
{
lsquic_str_delete(s_ccsbuf);
s_ccsbuf = NULL;
}
}

View File

@ -0,0 +1,50 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef __LSQUIC_CRT_COMPRESS_H__
#define __LSQUIC_CRT_COMPRESS_H__
#include <stdint.h>
struct lsquic_str;
#ifdef __cplusplus
extern "C" {
#endif
enum entry_type {
END_OF_LIST = 0,
ENTRY_COMPRESSED = 1,
ENTRY_CACHED = 2,
ENTRY_COMMON = 3,
};
typedef struct cert_entry_st {
enum entry_type type;
uint32_t index;
uint64_t hash;
uint64_t set_hash;
} cert_entry_t;
typedef struct common_cert_st
{
size_t num_certs;
const unsigned char* const* certs;
const size_t* lens;
uint64_t hash;
} common_cert_t;
struct lsquic_str * get_common_certs_hash();
int get_certs_count(struct lsquic_str *compressed_crt_buf);
int decompress_certs(const unsigned char *in, const unsigned char *in_end,
struct lsquic_str *cached_certs, size_t cached_certs_count,
struct lsquic_str **out_certs,
size_t *out_certs_count);
void
lsquic_crt_cleanup (void);
#ifdef __cplusplus
}
#endif
#endif //__LSQUIC_CRT_COMPRESS_H__

View File

@ -0,0 +1,683 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#include <assert.h>
#include <string.h>
#include <openssl/ssl.h>
#include <openssl/crypto.h>
#include <openssl/stack.h>
#include <openssl/x509.h>
#include <openssl/rand.h>
#include <openssl/curve25519.h>
#include <zlib.h>
#include "lsquic_types.h"
#include "lsquic_crypto.h"
#include "lsquic_alarmset.h"
#include "lsquic_parse.h"
#include "lsquic_util.h"
#include "lsquic_str.h"
#define LSQUIC_LOGGER_MODULE LSQLM_CRYPTO
#include "lsquic_logger.h"
static const char s_hs_signature[] = "QUIC CHLO and server config signature";
static int crypto_inited = 0;
void rand_bytes(void *data, int len)
{
RAND_bytes(data, len);
}
uint64_t fnv1a_64(const uint8_t * data, int len)
{
uint64_t hash = UINT64_C(14695981039346656037);
const uint8_t *end = data + len;
while(data < end)
{
hash ^= *data;
hash *= UINT64_C(1099511628211);
++data;
}
return hash;
}
void fnv1a_64_s(const uint8_t * data, int len, char *md)
{
uint64_t hash = fnv1a_64(data, len);
memcpy(md, (void *)&hash, 8);
}
#if defined( __x86_64 )||defined( __x86_64__ )
static uint128 s_prime;
static uint128 s_init_hash;
static inline void make_uint128(uint128 *v, uint64_t hi, uint64_t lo)
{
*v = hi;
*v <<= 64;
*v += lo;
}
void fnv1a_inc(uint128 *hash, const uint8_t *data, int len)
{
const uint8_t* end = data + len;
while(data < end)
{
*hash = (*hash ^ (*data)) * s_prime;
++data;
}
}
uint128 fnv1a_128_2(const uint8_t * data1, int len1, const uint8_t *data2, int len2)
{
uint128 hash;
memcpy(&hash, &s_init_hash, 16);
fnv1a_inc(&hash, data1, len1);
if (data2)
fnv1a_inc(&hash, data2, len2);
return hash;
}
uint128 fnv1a_128_3(const uint8_t *data1, int len1,
const uint8_t *data2, int len2,
const uint8_t *data3, int len3)
{
uint128 hash;
memcpy(&hash, &s_init_hash, 16);
fnv1a_inc(&hash, data1, len1);
fnv1a_inc(&hash, data2, len2);
fnv1a_inc(&hash, data3, len3);
return hash;
}
void fnv1a_128_2_s(const uint8_t * data1, int len1, const uint8_t * data2, int len2, uint8_t *md)
{
uint128 hash = fnv1a_128_2(data1, len1, data2, len2);
memcpy(md, (void *)&hash, 16);
}
/* HS_PKT_HASH_LENGTH bytes of md */
void serialize_fnv128_short(uint128 v, uint8_t *md)
{
memcpy(md, (void *)&v, 12);
}
#else
uint128 *uint128_times(uint128 *v, const uint128 *factor)
{
uint64_t a96 = v->hi_ >> 32;
uint64_t a64 = v->hi_ & 0xffffffffu;
uint64_t a32 = v->lo_ >> 32;
uint64_t a00 = v->lo_ & 0xffffffffu;
uint64_t b96 = factor->hi_ >> 32;
uint64_t b64 = factor->hi_ & 0xffffffffu;
uint64_t b32 = factor->lo_ >> 32;
uint64_t b00 = factor->lo_ & 0xffffffffu;
uint64_t tmp, lolo;
// multiply [a96 .. a00] x [b96 .. b00]
// terms higher than c96 disappear off the high side
// terms c96 and c64 are safe to ignore carry bit
uint64_t c96 = a96 * b00 + a64 * b32 + a32 * b64 + a00 * b96;
uint64_t c64 = a64 * b00 + a32 * b32 + a00 * b64;
v->hi_ = (c96 << 32) + c64;
v->lo_ = 0;
tmp = a32 * b00;
v->hi_ += tmp >> 32;
v->lo_ += tmp << 32;
tmp = a00 * b32;
v->hi_ += tmp >> 32;
v->lo_ += tmp << 32;
tmp = a00 * b00;
lolo = v->lo_ + tmp;
if (lolo < v->lo_)
++v->hi_;
v->lo_ = lolo;
return v;
}
void fnv1a_inc(uint128 *hash, const uint8_t * data, int len)
{
static const uint128 kPrime = {16777216, 315};
const uint8_t* end = data + len;
while(data < end)
{
hash->lo_ = (hash->lo_ ^ (uint64_t)*data);
uint128_times(hash, &kPrime);
++data;
}
}
uint128 fnv1a_128_2(const uint8_t * data1, int len1, const uint8_t * data2, int len2)
{
uint128 hash = {UINT64_C(7809847782465536322), UINT64_C(7113472399480571277)};
fnv1a_inc(&hash, data1, len1);
if (data2)
fnv1a_inc(&hash, data2, len2);
return hash;
}
uint128 fnv1a_128_3(const uint8_t * data1, int len1,
const uint8_t * data2, int len2,
const uint8_t * data3, int len3)
{
uint128 hash = {UINT64_C(7809847782465536322), UINT64_C(7113472399480571277)};
fnv1a_inc(&hash, data1, len1);
fnv1a_inc(&hash, data2, len2);
fnv1a_inc(&hash, data3, len3);
return hash;
}
void fnv1a_128_2_s(const uint8_t * data1, int len1, const uint8_t * data2, int len2, uint8_t *md)
{
uint128 hash = fnv1a_128_2(data1, len1, data2, len2);
memcpy(md, (void *)&hash.lo_, 8);
memcpy(md + 8, (void *)&hash.hi_, 8);
}
/* HS_PKT_HASH_LENGTH bytes of md */
void serialize_fnv128_short(uint128 v, uint8_t *md)
{
assert(HS_PKT_HASH_LENGTH == 8 + 4);
memcpy(md, (void *)&v.lo_, 8);
memcpy(md + 8, (void *)&v.hi_, 4);
}
#endif
uint128 fnv1a_128(const uint8_t * data, int len)
{
return fnv1a_128_2(data, len , NULL, 0);
}
void fnv1a_128_s(const uint8_t * data, int len, uint8_t *md)
{
return fnv1a_128_2_s(data, len, NULL, 0, md);
}
/* packet data = header + MD + payload */
/* return 0 if OK */
int verify_hs_pkt(const uint8_t *pkg_data, size_t header_len, size_t pkg_len)
{
uint8_t md[HS_PKT_HASH_LENGTH];
uint128 hash;
if (pkg_len < header_len + HS_PKT_HASH_LENGTH)
return -1;
hash = fnv1a_128_2(pkg_data, header_len, pkg_data + header_len + HS_PKT_HASH_LENGTH,
pkg_len - header_len - HS_PKT_HASH_LENGTH);
serialize_fnv128_short(hash, md);
return memcmp(md, pkg_data + header_len, HS_PKT_HASH_LENGTH);
}
/* packet data = header + MD + payload, update the MD part */
int update_hs_pkt_hash(uint8_t *pkg_data, int header_len, int pkg_len)
{
uint8_t md[HS_PKT_HASH_LENGTH];
uint128 hash;
if (pkg_len < header_len + HS_PKT_HASH_LENGTH)
return -1;
hash = fnv1a_128_2(pkg_data, header_len, pkg_data + header_len + HS_PKT_HASH_LENGTH,
pkg_len - header_len - HS_PKT_HASH_LENGTH);
serialize_fnv128_short(hash, md);
memcpy(pkg_data + header_len, md, HS_PKT_HASH_LENGTH);
return 0;
}
int get_hs_pkt_hash_len()
{
return HS_PKT_HASH_LENGTH;
}
void sha256(const uint8_t *buf, int len, uint8_t *h)
{
SHA256_CTX ctx;
SHA256_Init(&ctx);
SHA256_Update(&ctx, buf, len);
SHA256_Final(h, &ctx);
}
/* base on rfc 5869 with sha256, prk is 32 bytes*/
void lshkdf_extract(const unsigned char *ikm, int ikm_len, const unsigned char *salt,
int salt_len, unsigned char *prk)
{
#ifndef NDEBUG
unsigned char *out;
unsigned int out_len;
out =
#endif
HMAC(EVP_sha256(), salt, salt_len, ikm, ikm_len, prk,
#ifndef NDEBUG
&out_len
#else
NULL
#endif
);
assert(out);
assert(out_len == 32);
}
int lshkdf_expand(const unsigned char *prk, const unsigned char *info, int info_len,
uint16_t c_key_len, uint8_t *c_key,
uint16_t s_key_len, uint8_t *s_key,
uint16_t c_key_iv_len, uint8_t *c_key_iv,
uint16_t s_key_iv_len, uint8_t *s_key_iv,
uint16_t sub_key_len, uint8_t *sub_key)
{
const int SHA256LEN = 32;
int L = c_key_len + s_key_len + c_key_iv_len + s_key_iv_len + sub_key_len;
int N = (L + SHA256LEN - 1) / SHA256LEN;
unsigned char *p_org;
uint8_t *buf;
unsigned char *p;
unsigned char T[SHA256LEN + 1];
int T_len = 0;
int i;
uint8_t *pb;
p_org = malloc(N * SHA256LEN);
if (!p_org)
return -1;
buf = malloc(SHA256LEN + info_len + 13);
if (!buf)
{
free(p_org);
return -1;
}
p = p_org;
for (i = 1; i <= N; ++i)
{
pb = buf;
if (T_len > 0)
{
memcpy(pb, T, T_len);
pb += T_len;
}
memcpy(pb, info, info_len);
pb += info_len;
*pb = i;
++pb;
HMAC(EVP_sha256(), prk, SHA256LEN, buf, pb - buf, T, NULL);
if (i != N)
T_len = SHA256LEN;
else
T_len = L - (N - 1) * SHA256LEN;
memcpy(p, T, T_len);
p += T_len;
}
free(buf);
p = p_org;
if (c_key_len)
{
memcpy(c_key, p, c_key_len);
p += c_key_len;
}
if (s_key_len)
{
memcpy(s_key, p, s_key_len);
p += s_key_len;
}
if (c_key_iv_len)
{
memcpy(c_key_iv, p, c_key_iv_len);
p += c_key_iv_len;
}
if (s_key_iv_len)
{
memcpy(s_key_iv, p, s_key_iv_len);
p += s_key_iv_len;
}
if (sub_key_len && sub_key)
{
memcpy(sub_key, p, sub_key_len);
p += sub_key_len;
}
free(p_org);
return 0;
}
int export_key_material_simple(unsigned char *ikm, uint32_t ikm_len,
unsigned char *salt, int salt_len,
char *label, uint32_t label_len,
const uint8_t *context, uint32_t context_len,
uint8_t *key, uint16_t key_len)
{
unsigned char prk[32];
int info_len;
uint8_t *info = NULL;
info = (uint8_t *)malloc(label_len + 1 + sizeof(uint32_t) + context_len);
if (!info)
return -1;
lshkdf_extract(ikm, ikm_len, salt, salt_len, prk);
memcpy(info, label, label_len);
info[label_len] = 0x00;
info_len = label_len + 1;
memcpy(info + info_len, &context_len, sizeof(uint32_t));
info_len += sizeof(uint32_t);
memcpy(info + info_len, context, context_len);
info_len += context_len;
lshkdf_expand(prk, info, info_len, key_len, key,
0, NULL, 0, NULL,0, NULL, 0, NULL);
free(info);
return 0;
}
int export_key_material(const unsigned char *ikm, uint32_t ikm_len,
const unsigned char *salt, int salt_len,
const unsigned char *context, uint32_t context_len,
uint16_t c_key_len, uint8_t *c_key,
uint16_t s_key_len, uint8_t *s_key,
uint16_t c_key_iv_len, uint8_t *c_key_iv,
uint16_t s_key_iv_len, uint8_t *s_key_iv,
uint8_t *sub_key)
{
unsigned char prk[32];
uint16_t sub_key_len = ikm_len;
lshkdf_extract(ikm, ikm_len, salt, salt_len, prk);
lshkdf_expand(prk, context, context_len, c_key_len, c_key,
s_key_len, s_key, c_key_iv_len, c_key_iv, s_key_iv_len,
s_key_iv, sub_key_len, sub_key);
return 0;
}
void c255_get_pub_key(unsigned char *priv_key, unsigned char pub_key[32])
{
X25519_public_from_private(pub_key, priv_key);
}
int c255_gen_share_key(unsigned char *priv_key, unsigned char *peer_pub_key, unsigned char *shared_key)
{
return X25519(shared_key, priv_key, peer_pub_key);
}
/* AEAD nonce is always zero */
/* return 0 for OK */
int aes_aead_enc(EVP_AEAD_CTX *key,
const uint8_t *ad, size_t ad_len,
const uint8_t *nonce, size_t nonce_len,
const uint8_t *plain, size_t plain_len,
uint8_t *cypher, size_t *cypher_len)
{
int ret = 0;
size_t max_out_len;
max_out_len = *cypher_len;//plain_len + EVP_AEAD_max_overhead(aead_);
assert(*cypher_len >= max_out_len);
LSQ_DEBUG("***aes_aead_enc data %s", get_bin_str(plain, plain_len, 40));
ret = EVP_AEAD_CTX_seal(key, cypher, cypher_len, max_out_len,
nonce, nonce_len, plain, plain_len, ad, ad_len);
// LSQ_DEBUG("***aes_aead_enc nonce: %s", get_bin_str(nonce, nonce_len));
// LSQ_DEBUG("***aes_aead_enc AD: %s", get_bin_str(ad, ad_len));
// LSQ_DEBUG("***aes_aead_enc return %d", (ret ? 0 : -1));
if (ret)
{
LSQ_DEBUG("***aes_aead_enc succeed, cypher content %s",
get_bin_str(cypher, *cypher_len, 40));
return 0;
}
else
{
LSQ_DEBUG("***aes_aead_enc failed.");
return -1;
}
}
/* return 0 for OK */
int aes_aead_dec(EVP_AEAD_CTX *key,
const uint8_t *ad, size_t ad_len,
const uint8_t *nonce, size_t nonce_len,
const uint8_t *cypher, size_t cypher_len,
uint8_t *plain, size_t *plain_len)
{
int ret = 0;
size_t max_out_len = *plain_len;
assert(max_out_len >= cypher_len);
LSQ_DEBUG("***aes_aead_dec data %s", get_bin_str(cypher, cypher_len, 40));
ret = EVP_AEAD_CTX_open(key, plain, plain_len, max_out_len,
nonce, nonce_len, cypher, cypher_len, ad, ad_len);
// LSQ_DEBUG("***aes_aead_dec nonce: %s", get_bin_str(nonce, nonce_len));
// LSQ_DEBUG("***aes_aead_dec AD: %s", get_bin_str(ad, ad_len));
// LSQ_DEBUG("***aes_aead_dec return %d", (ret ? 0 : -1));
if (ret)
{
LSQ_DEBUG("***aes_aead_dec succeed, plain content %s",
get_bin_str(plain, *plain_len, 20));
return 0;
}
else
{
LSQ_DEBUG("***aes_aead_dec failed.");
return -1;
}
}
/* aes 128, 16 bytes */
int aes_get_key_length()
{
return 16;
}
void gen_nonce_s(char *buf, int length)
{
rand_bytes(buf, length);
}
/* 32 bytes client nonce with 4 bytes tm, 8 bytes orbit */
void gen_nonce_c(unsigned char *buf, uint64_t orbit)
{
time_t tm = time(NULL);
unsigned char *p = buf;
memcpy(p, &tm, 4);
p += 4;
memcpy(p, &orbit, 8);
p += 8;
rand_bytes(p, 20);
p += 20;
}
EVP_PKEY *PEM_to_key(const char *buf, int len)
{
RSA *rsa = NULL;
EVP_PKEY *key = EVP_PKEY_new();
BIO *bio = BIO_new_mem_buf(buf, len);
if (!bio || !key)
return NULL;
rsa = PEM_read_bio_RSAPrivateKey(bio, &rsa, NULL, NULL);
if (!rsa)
return NULL;
EVP_PKEY_assign_RSA(key, rsa);
return key;
}
/* type 0 DER, 1: PEM */
X509 *bio_to_crt(const void *buf, int len, int type)
{
X509 *crt = NULL;
BIO *bio = BIO_new_mem_buf(buf, len);
if (bio == NULL)
return NULL;
if (type == 0)
crt = d2i_X509_bio(bio, NULL);
else
crt = PEM_read_bio_X509(bio, &crt, 0 , NULL);
BIO_free(bio);
return crt;
}
int read_rsa_priv_key(const uint8_t *buf, int len, EVP_PKEY *pkey)
{
RSA *rsa = RSA_private_key_from_bytes(buf, len);
if (!rsa)
return -1;
return EVP_PKEY_assign_RSA(pkey, rsa);
}
int gen_prof(const uint8_t *chlo_data, size_t chlo_data_len,
const uint8_t *scfg_data, uint32_t scfg_data_len,
const EVP_PKEY *priv_key, uint8_t *buf, size_t *buf_len)
{
uint8_t chlo_hash[32] = {0};
size_t chlo_hash_len = 32; /* SHA256 */
EVP_MD_CTX sign_context;
EVP_PKEY_CTX* pkey_ctx = NULL;
sha256(chlo_data, chlo_data_len, chlo_hash);
EVP_MD_CTX_init(&sign_context);
if (!EVP_DigestSignInit(&sign_context, &pkey_ctx, EVP_sha256(), NULL, (EVP_PKEY *)priv_key))
return -1;
EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING);
EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, -1);
if (!EVP_DigestSignUpdate(&sign_context, s_hs_signature, sizeof(s_hs_signature)) ||
!EVP_DigestSignUpdate(&sign_context, (const uint8_t*)(&chlo_hash_len), 4) ||
!EVP_DigestSignUpdate(&sign_context, chlo_hash, chlo_hash_len) ||
!EVP_DigestSignUpdate(&sign_context, scfg_data, scfg_data_len))
{
return -1;
}
size_t len = 0;
if (!EVP_DigestSignFinal(&sign_context, NULL, &len)) {
return -1;
}
if (len > *buf_len)
return -2;
if (buf)
EVP_DigestSignFinal(&sign_context, buf, buf_len);
EVP_MD_CTX_cleanup(&sign_context);
return 0;
}
int verify_cert(const char *buf, int len)
{
//X509_verify_cert();
return 0;
}
int verify_prof(const uint8_t *chlo_data, size_t chlo_data_len, lsquic_str_t * scfg,
const EVP_PKEY *pub_key, const uint8_t *buf, size_t len)
{
return verify_prof0(chlo_data, chlo_data_len,
(const uint8_t *)lsquic_str_buf(scfg),
lsquic_str_len(scfg), pub_key, buf, len);
}
/* -3 internal error, -1: verify failed, 0: Success */
int verify_prof0(const uint8_t *chlo_data, size_t chlo_data_len,
const uint8_t *scfg_data, uint32_t scfg_data_len,
const EVP_PKEY *pub_key, const uint8_t *buf, size_t len)
{
uint8_t chlo_hash[32] = {0};
size_t chlo_hash_len = 32; /* SHA256 */
EVP_MD_CTX sign_context;
EVP_PKEY_CTX* pkey_ctx = NULL;
int ret = 0;
EVP_MD_CTX_init(&sign_context);
sha256(chlo_data, chlo_data_len, chlo_hash);
// discarding const below to quiet compiler warning on call to ssl library code
if (!EVP_DigestVerifyInit(&sign_context, &pkey_ctx, EVP_sha256(), NULL, (EVP_PKEY *)pub_key))
return -4;
EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING);
EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, -1);
if (!EVP_DigestVerifyUpdate(&sign_context, s_hs_signature, sizeof(s_hs_signature)) ||
!EVP_DigestVerifyUpdate(&sign_context, (const uint8_t*)(&chlo_hash_len), 4) ||
!EVP_DigestVerifyUpdate(&sign_context, chlo_hash, chlo_hash_len) ||
!EVP_DigestVerifyUpdate(&sign_context, scfg_data, scfg_data_len))
{
return -3; /* set to -3, to avoid same as "not enough data" -2 */
}
ret = EVP_DigestVerifyFinal(&sign_context, buf, len);
EVP_MD_CTX_cleanup(&sign_context);
if (ret == 1)
return 0; //OK
else
return -1; //failed
}
void crypto_init(void *seed, int seed_len)
{
if (crypto_inited)
return ;
//SSL_library_init();
CRYPTO_library_init();
RAND_seed(seed, seed_len);
#if defined( __x86_64 )||defined( __x86_64__ )
make_uint128(&s_prime, 16777216, 315);
make_uint128(&s_init_hash, 7809847782465536322, 7113472399480571277);
#endif
/* MORE .... */
crypto_inited = 1;
}

View File

@ -0,0 +1,124 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef __LSQUIC_CRYPTO_H__
#define __LSQUIC_CRYPTO_H__
#include <stdint.h>
#define HS_PKT_HASH_LENGTH 12
#ifdef __cplusplus
extern "C" {
#endif
struct lsquic_str;
#if defined( __x86_64 )||defined( __x86_64__ )
typedef __uint128_t uint128;
#else
typedef struct uint128_st
{
uint64_t hi_;
uint64_t lo_;
} uint128;
#endif
void crypto_init();
/* XXX: why have a wrapper around RAND_bytes? */
void rand_bytes(void *data, int len);
int export_key_material_simple(unsigned char *ikm, uint32_t ikm_len,
unsigned char *salt, int salt_len,
char *label, uint32_t label_len,
const uint8_t *context, uint32_t context_len,
uint8_t *key, uint16_t key_len);
int export_key_material(const unsigned char *ikm, uint32_t ikm_len,
const unsigned char *salt, int salt_len,
const unsigned char *context, uint32_t context_len,
uint16_t c_key_len, uint8_t *c_key,
uint16_t s_key_len, uint8_t *s_key,
uint16_t c_key_iv_len, uint8_t *c_key_iv,
uint16_t s_key_iv_len, uint8_t *s_key_iv,
uint8_t *sub_key);
void c255_get_pub_key(unsigned char *priv_key, unsigned char pub_key[32]);
int c255_gen_share_key(unsigned char *priv_key, unsigned char *peer_pub_key, unsigned char *shared_key);
uint64_t fnv1a_64(const uint8_t * data, int len);
void fnv1a_64_s(const uint8_t * data, int len, char *md);
uint128 fnv1a_128(const uint8_t * data, int len);
void fnv1a_128_s(const uint8_t * data , int len, uint8_t *md);
uint128 fnv1a_128_2(const uint8_t * data1, int len1, const uint8_t * data2, int len2);
uint128 fnv1a_128_3(const uint8_t * data1, int len1,
const uint8_t * data2, int len2,
const uint8_t * data3, int len3);
void fnv1a_128_2_s(const uint8_t * data1, int len1, const uint8_t * data2, int len2, uint8_t *md);
void serialize_fnv128_short(uint128 v, uint8_t *md);
/* before session handshake complete */
int verify_hs_pkt(const uint8_t *pkg_data, size_t header_len, size_t pkg_len);
int update_hs_pkt_hash(uint8_t *pkg_data, int header_len, int pkg_len);
int get_hs_pkt_hash_len();
/*16 bytes of h outputted */
void sha256(const uint8_t *buf, int len, uint8_t *h);
/* Encrypt plaint text to cipher test */
int aes_aead_enc(EVP_AEAD_CTX *key,
const uint8_t *ad, size_t ad_len,
const uint8_t *nonce, size_t nonce_len,
const uint8_t *plain, size_t plain_len,
uint8_t *cypher, size_t *cypher_len);
int aes_aead_dec(EVP_AEAD_CTX *key,
const uint8_t *ad, size_t ad_len,
const uint8_t *nonce, size_t nonce_len,
const uint8_t *cypher, size_t cypher_len,
uint8_t *plain, size_t *plain_len);
int aes_get_key_length();
void gen_nonce_s(char *buf, int length);
/* 32 bytes client nonce with 4 bytes tm, 8 bytes orbit */
void gen_nonce_c(unsigned char *buf, uint64_t orbit);
EVP_PKEY *PEM_to_key(const char *buf, int len);
X509 *bio_to_crt(const void *buf, int len, int type);
int lshkdf_expand(const unsigned char *prk, const unsigned char *info, int info_len,
uint16_t c_key_len, uint8_t *c_key,
uint16_t s_key_len, uint8_t *s_key,
uint16_t c_key_iv_len, uint8_t *c_key_iv,
uint16_t s_key_iv_len, uint8_t *s_key_iv,
uint16_t sub_key_len, uint8_t *sub_key);
void lshkdf_extract(const unsigned char *ikm, int ikm_len, const unsigned char *salt,
int salt_len, unsigned char *prk);
int gen_prof(const uint8_t *chlo_data, size_t chlo_data_len,
const uint8_t *scfg_data, uint32_t scfg_data_len,
const EVP_PKEY *priv_key, uint8_t *buf, size_t *len);
int verify_prof0(const uint8_t *chlo_data, size_t chlo_data_len,
const uint8_t *scfg_data, uint32_t scfg_data_len,
const EVP_PKEY *pub_key, const uint8_t *buf, size_t len);
int verify_prof(const uint8_t *chlo_data, size_t chlo_data_len, struct lsquic_str * scfg,
const EVP_PKEY *pub_key, const uint8_t *buf, size_t len);
#ifdef __cplusplus
}
#endif
#endif //__LSQUIC_CRYPTO_H__

View File

@ -0,0 +1,198 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_cubic.c -- LSQUIC CUBIC implementation.
*/
#include <inttypes.h>
#include <math.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "lsquic_int_types.h"
#include "lsquic_types.h"
#include "lsquic_cubic.h"
#include "lsquic_util.h"
#define LSQUIC_LOGGER_MODULE LSQLM_CUBIC
#define LSQUIC_LOG_CONN_ID cubic->cu_cid
#include "lsquic_logger.h"
#define FAST_CONVERGENCE 1
#define BETA 205 /* 205/1024 */
#define C 410 /* 410/1024 */
#define TWO_MINUS_BETA_OVER_TWO 922 /* 922/1024 */
#define ONE_MINUS_BETA 819 /* 819/1024 */
#define ONE_OVER_C 2560 /* 2560/1024 */
static void
cubic_reset (struct lsquic_cubic *cubic)
{
memset(cubic, 0, offsetof(struct lsquic_cubic, cu_cid));
cubic->cu_cwnd = 32;
cubic->cu_last_max_cwnd = 32;
}
static void
cubic_update (struct lsquic_cubic *cubic, lsquic_time_t now)
{
lsquic_time_t delta_t, t, target;
unsigned tcp_cwnd;
if (0 == cubic->cu_epoch_start)
{
cubic->cu_epoch_start = now;
if (cubic->cu_cwnd < cubic->cu_last_max_cwnd)
{
cubic->cu_K = cbrt((cubic->cu_last_max_cwnd - cubic->cu_cwnd) *
ONE_OVER_C / 1024) * 1000000;
cubic->cu_origin_point = cubic->cu_last_max_cwnd;
}
else
{
cubic->cu_K = 0;
cubic->cu_origin_point = cubic->cu_cwnd;
}
}
else if ((cubic->cu_flags & CU_SHIFT_EPOCH) && cubic->cu_app_limited)
{
LSQ_DEBUG("increment epoch_start by %"PRIu64" microseconds", now - cubic->cu_app_limited);
cubic->cu_epoch_start += now - cubic->cu_app_limited;
}
delta_t = now + cubic->cu_min_delay - cubic->cu_epoch_start;
if (delta_t < cubic->cu_K)
{
t = cubic->cu_K - delta_t;
t /= 62500;
target = cubic->cu_origin_point - C * t * t * t / 1024 / 4096;
}
else
{
t = delta_t - cubic->cu_K;
t /= 62500;
target = cubic->cu_origin_point + C * t * t * t / 1024 / 4096;
if (cubic->cu_flags & CU_TCP_FRIENDLY)
{
tcp_cwnd = cubic->cu_last_max_cwnd * ONE_MINUS_BETA / 1024 +
(delta_t - cubic->cu_K) * C / 1024 / cubic->cu_min_delay;
if (tcp_cwnd > target)
target = tcp_cwnd;
}
}
if (target == 0)
target = 1;
cubic->cu_cwnd = target;
}
void
lsquic_cubic_init_ext (struct lsquic_cubic *cubic, lsquic_cid_t cid,
enum cubic_flags flags)
{
cubic_reset(cubic);
cubic->cu_ssthresh = 10000; /* Emulate "unbounded" slow start */
cubic->cu_cid = cid;
cubic->cu_flags = flags;
LSQ_DEBUG("%s(cubic, %"PRIu64", 0x%X)", __func__, cid, flags);
#ifndef NDEBUG
{
const char *shift;
shift = getenv("LSQUIC_CUBIC_SHIFT_EPOCH");
if (shift)
{
if (atoi(shift))
cubic->cu_flags |= CU_SHIFT_EPOCH;
else
cubic->cu_flags &= ~CU_SHIFT_EPOCH;
}
}
#endif
LSQ_INFO("initialized");
}
#define LOG_CWND(c) do { \
if (LSQ_LOG_ENABLED(LSQ_LOG_INFO)) { \
lsquic_time_t now = lsquic_time_now(); \
now -= now % 100000; \
if (now > (c)->cu_last_logged) { \
LSQ_INFO("CWND: %u", (c)->cu_cwnd); \
(c)->cu_last_logged = now; \
} \
} \
} while (0)
void
lsquic_cubic_ack (struct lsquic_cubic *cubic, lsquic_time_t now,
lsquic_time_t rtt, int app_limited)
{
LSQ_DEBUG("%s(cubic, %"PRIu64", %"PRIu64", %d)", __func__, now, rtt,
app_limited);
if (0 == cubic->cu_min_delay || rtt < cubic->cu_min_delay)
{
cubic->cu_min_delay = rtt;
LSQ_INFO("min_delay: %"PRIu64, rtt);
}
if (cubic->cu_cwnd <= cubic->cu_ssthresh)
{
++cubic->cu_cwnd;
LSQ_DEBUG("ACK: slow threshold, cwnd: %u", cubic->cu_cwnd);
}
else
{
if (app_limited)
{
if (cubic->cu_flags & CU_SHIFT_EPOCH)
{
if (0 == cubic->cu_app_limited)
{
cubic->cu_app_limited = now;
LSQ_DEBUG("set app_limited to %"PRIu64, now);
}
}
else
cubic->cu_epoch_start = 0;
}
else
{
cubic_update(cubic, now);
cubic->cu_app_limited = 0;
}
LSQ_DEBUG("ACK: cwnd: %u", cubic->cu_cwnd);
}
LOG_CWND(cubic);
}
void
lsquic_cubic_loss (struct lsquic_cubic *cubic)
{
LSQ_DEBUG("%s(cubic)", __func__);
cubic->cu_epoch_start = 0;
cubic->cu_app_limited = 0;
if (FAST_CONVERGENCE && cubic->cu_cwnd < cubic->cu_last_max_cwnd)
cubic->cu_last_max_cwnd = cubic->cu_cwnd * TWO_MINUS_BETA_OVER_TWO / 1024;
else
cubic->cu_last_max_cwnd = cubic->cu_cwnd;
cubic->cu_cwnd = cubic->cu_cwnd * ONE_MINUS_BETA / 1024;
cubic->cu_ssthresh = cubic->cu_cwnd;
LSQ_INFO("loss detected, last_max_cwnd: %u, cwnd: %u",
cubic->cu_last_max_cwnd, cubic->cu_cwnd);
LOG_CWND(cubic);
}
void
lsquic_cubic_timeout (struct lsquic_cubic *cubic)
{
LSQ_DEBUG("%s(cubic)", __func__);
cubic_reset(cubic);
cubic->cu_ssthresh = cubic->cu_cwnd;
LSQ_INFO("timeout, cwnd: %u", cubic->cu_cwnd);
LOG_CWND(cubic);
}

View File

@ -0,0 +1,49 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_cubic.h -- CUBIC congestion control protocol.
*/
#ifndef LSQUIC_CUBIC_H
#define LSQUIC_CUBIC_H 1
struct lsquic_cubic {
lsquic_time_t cu_min_delay;
lsquic_time_t cu_epoch_start;
lsquic_time_t cu_K;
lsquic_time_t cu_app_limited;
unsigned cu_origin_point;
unsigned cu_last_max_cwnd;
unsigned cu_cwnd;
unsigned cu_ssthresh;
lsquic_cid_t cu_cid; /* Used for logging */
enum cubic_flags {
CU_TCP_FRIENDLY = (1 << 0),
CU_SHIFT_EPOCH = (1 << 1),
} cu_flags;
lsquic_time_t cu_last_logged;
};
#define DEFAULT_CUBIC_FLAGS (CU_TCP_FRIENDLY|CU_SHIFT_EPOCH)
void
lsquic_cubic_init_ext (struct lsquic_cubic *, lsquic_cid_t, enum cubic_flags);
#define lsquic_cubic_init(cubic, cid) \
lsquic_cubic_init_ext(cubic, cid, DEFAULT_CUBIC_FLAGS)
void
lsquic_cubic_ack (struct lsquic_cubic *cubic, lsquic_time_t now,
lsquic_time_t rtt, int app_limited);
void
lsquic_cubic_loss (struct lsquic_cubic *cubic);
void
lsquic_cubic_timeout (struct lsquic_cubic *cubic);
#define lsquic_cubic_get_cwnd(c) (+(c)->cu_cwnd)
#define lsquic_cubic_in_slow_start(cubic) \
((cubic)->cu_cwnd < (cubic)->cu_ssthresh)
#endif

View File

@ -0,0 +1,81 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_data_in_if.h -- DATA in interface
*/
#ifndef LSQUIC_DATA_IN_IF_H
#define LSQUIC_DATA_IN_IF_H 1
struct data_frame;
struct data_in;
struct lsquic_conn_public;
struct stream_frame;
enum ins_frame
{
INS_FRAME_OK,
INS_FRAME_ERR,
INS_FRAME_DUP,
};
struct data_in_iface
{
void
(*di_destroy) (struct data_in *);
int
(*di_empty) (struct data_in *);
/* The caller releases control of stream frame. Do not reference it
* after the call.
*/
enum ins_frame
(*di_insert_frame) (struct data_in *, struct stream_frame *,
uint64_t read_offset);
struct data_frame *
(*di_get_frame) (struct data_in *, uint64_t read_offset);
void
(*di_frame_done) (struct data_in *, struct data_frame *);
/* Creates a new data_in object, feeds its stream frames to it, deletes
* itself and returns the new object.
*/
struct data_in *
(*di_switch_impl) (struct data_in *, uint64_t read_offset);
};
struct data_in
{
const struct data_in_iface *di_if;
enum {
/* If DI_SWITCH_IMPL is set, switching data_in implementation is
* recommended in order to get better performance for current
* incoming stream frame scenario. Check the value of this flag
* after calls to di_insert_frame() and di_frame_done().
*/
DI_SWITCH_IMPL = (1 << 0),
} di_flags;
};
struct data_in *
data_in_nocopy_new (struct lsquic_conn_public *, uint32_t stream_id);
struct data_in *
data_in_hash_new (struct lsquic_conn_public *, uint32_t stream_id,
uint64_t byteage);
enum ins_frame
data_in_hash_insert_data_frame (struct data_in *data_in,
const struct data_frame *data_frame, uint64_t read_offset);
struct data_in *
data_in_error_new ();
#endif

View File

@ -0,0 +1,81 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_di_error.c -- A placeholder when things go wrong
*
* This object is used in order to avoid dereferencing NULLs in stream.c
*/
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include "lsquic_data_in_if.h"
static const struct data_in_iface di_if_error;
static const struct data_in error_data_in = {
.di_if = &di_if_error,
.di_flags = 0,
};
struct data_in *
data_in_error_new (struct lsquic_conn_public *conn_pub)
{
return (struct data_in *) &error_data_in;
}
static void
error_di_destroy (struct data_in *data_in)
{
}
static enum ins_frame
error_di_insert_frame (struct data_in *data_in,
struct stream_frame *new_frame, uint64_t read_offset)
{
return INS_FRAME_ERR;
}
static struct data_frame *
error_di_get_frame (struct data_in *data_in, uint64_t read_offset)
{
return NULL;
}
static void
error_di_frame_done (struct data_in *data_in, struct data_frame *data_frame)
{
}
static int
error_di_empty (struct data_in *data_in)
{
return 1;
}
struct data_in *
error_di_switch_impl (struct data_in *data_in, uint64_t read_offset)
{
assert(0);
return data_in;
}
static const struct data_in_iface di_if_error = {
.di_destroy = error_di_destroy,
.di_empty = error_di_empty,
.di_frame_done = error_di_frame_done,
.di_get_frame = error_di_get_frame,
.di_insert_frame = error_di_insert_frame,
.di_switch_impl = error_di_switch_impl,
};

View File

@ -0,0 +1,617 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_di_hash.c -- Copy incoming data into a hash
*
* While this implementation copies the data, its memory use is limited,
* which makes it a good choice when we have a lot of stream frames
* coming in.
*
* Another difference is that it does not check for frame overlap, which
* is something that is present in Chrome, but it is not required by QUIC.
*/
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include "lsquic.h"
#include "lsquic_int_types.h"
#include "lsquic_types.h"
#include "lsquic_conn_flow.h"
#include "lsquic_packet_in.h"
#include "lsquic_rtt.h"
#include "lsquic_sfcw.h"
#include "lsquic_stream.h"
#include "lsquic_mm.h"
#include "lsquic_malo.h"
#include "lsquic_conn.h"
#include "lsquic_conn_public.h"
#include "lsquic_data_in_if.h"
#define LSQUIC_LOGGER_MODULE LSQLM_DI
#define LSQUIC_LOG_CONN_ID hdi->hdi_conn_pub->lconn->cn_cid
#define LSQUIC_LOG_STREAM_ID hdi->hdi_stream_id
#include "lsquic_logger.h"
#define N_DB_SETS 57
#define DB_DATA_SIZE (0x1000 - sizeof(TAILQ_ENTRY(data_block)) - \
sizeof(uint64_t) - N_DB_SETS * sizeof(uint64_t))
struct data_block
{
TAILQ_ENTRY(data_block) db_next;
uint64_t db_off;
uint64_t db_set[N_DB_SETS]; /* bit for each valid byte */
unsigned char db_data[DB_DATA_SIZE];
};
typedef char db_set_covers_all_db_data[(N_DB_SETS * 64 >= DB_DATA_SIZE) - 1];
typedef char db_set_no_waste[(N_DB_SETS * 64 - 64 <= DB_DATA_SIZE) - 1];
typedef char db_block_is_4K[(sizeof(struct data_block) == 0x1000) - 1];
TAILQ_HEAD(dblock_head, data_block);
static const struct data_in_iface di_if_hash;
struct hash_data_in
{
struct data_in hdi_data_in;
struct lsquic_conn_public *hdi_conn_pub;
uint64_t hdi_fin_off;
struct dblock_head *hdi_buckets;
struct data_block *hdi_last_block;
struct data_frame hdi_data_frame;
uint32_t hdi_stream_id;
unsigned hdi_count;
unsigned hdi_nbits;
enum {
HDI_FIN = (1 << 0),
} hdi_flags;
};
#define HDI_PTR(data_in) (struct hash_data_in *) \
((unsigned char *) (data_in) - offsetof(struct hash_data_in, hdi_data_in))
#define N_BUCKETS(n_bits) (1U << (n_bits))
#define BUCKNO(n_bits, off) ((off / DB_DATA_SIZE) & (N_BUCKETS(n_bits) - 1))
static unsigned
my_log2 /* silly name to suppress compiler warning */ (unsigned sz)
{
#if __GNUC__
unsigned clz = __builtin_clz(sz);
return 32 - clz;
#else
unsigned clz;
size_t y;
clz = 32;
y = sz >> 16; if (y) { clz -= 16; sz = y; }
y = sz >> 8; if (y) { clz -= 8; sz = y; }
y = sz >> 4; if (y) { clz -= 4; sz = y; }
y = sz >> 2; if (y) { clz -= 2; sz = y; }
y = sz >> 1; if (y) return 32 - clz + 1;
return 32 - clz + sz;
#endif
}
struct data_in *
data_in_hash_new (struct lsquic_conn_public *conn_pub, uint32_t stream_id,
uint64_t byteage)
{
struct hash_data_in *hdi;
unsigned n;
hdi = malloc(sizeof(*hdi));
if (!hdi)
return NULL;
hdi->hdi_data_in.di_if = &di_if_hash;
hdi->hdi_data_in.di_flags = 0;
hdi->hdi_conn_pub = conn_pub;
hdi->hdi_stream_id = stream_id;
hdi->hdi_fin_off = 0;
hdi->hdi_flags = 0;
hdi->hdi_last_block = NULL;
if (byteage >= DB_DATA_SIZE /* __builtin_clz is undefined if
argument is 0 */)
hdi->hdi_nbits = my_log2(byteage / DB_DATA_SIZE) + 2;
else
hdi->hdi_nbits = 3;
hdi->hdi_count = 0;
hdi->hdi_buckets = malloc(sizeof(hdi->hdi_buckets[0]) *
N_BUCKETS(hdi->hdi_nbits));
if (!hdi->hdi_buckets)
{
free(hdi);
return NULL;
}
for (n = 0; n < N_BUCKETS(hdi->hdi_nbits); ++n)
TAILQ_INIT(&hdi->hdi_buckets[n]);
return &hdi->hdi_data_in;
}
static void
hash_di_destroy (struct data_in *data_in)
{
struct hash_data_in *const hdi = HDI_PTR(data_in);
struct data_block *block;
unsigned n;
for (n = 0; n < N_BUCKETS(hdi->hdi_nbits); ++n)
{
while ((block = TAILQ_FIRST(&hdi->hdi_buckets[n])))
{
TAILQ_REMOVE(&hdi->hdi_buckets[n], block, db_next);
free(block);
}
}
free(hdi->hdi_buckets);
free(hdi);
}
static int
hash_grow (struct hash_data_in *hdi)
{
struct dblock_head *new_buckets, *new[2];
struct data_block *block;
unsigned n, old_nbits;
int idx;
old_nbits = hdi->hdi_nbits;
LSQ_DEBUG("doubling number of buckets to %u", N_BUCKETS(old_nbits + 1));
new_buckets = malloc(sizeof(hdi->hdi_buckets[0])
* N_BUCKETS(old_nbits + 1));
if (!new_buckets)
{
LSQ_WARN("malloc failed: potential trouble ahead");
return -1;
}
for (n = 0; n < N_BUCKETS(old_nbits); ++n)
{
new[0] = &new_buckets[n];
new[1] = &new_buckets[n + N_BUCKETS(old_nbits)];
TAILQ_INIT(new[0]);
TAILQ_INIT(new[1]);
while ((block = TAILQ_FIRST(&hdi->hdi_buckets[n])))
{
TAILQ_REMOVE(&hdi->hdi_buckets[n], block, db_next);
idx = (BUCKNO(old_nbits + 1, block->db_off) >> old_nbits) & 1;
TAILQ_INSERT_TAIL(new[idx], block, db_next);
}
}
free(hdi->hdi_buckets);
hdi->hdi_nbits = old_nbits + 1;
hdi->hdi_buckets = new_buckets;
return 0;
}
static int
hash_insert (struct hash_data_in *hdi, struct data_block *block)
{
unsigned buckno;
if (hdi->hdi_count >= N_BUCKETS(hdi->hdi_nbits) / 2 && 0 != hash_grow(hdi))
return -1;
buckno = BUCKNO(hdi->hdi_nbits, block->db_off);
TAILQ_INSERT_TAIL(&hdi->hdi_buckets[buckno], block, db_next);
++hdi->hdi_count;
return 0;
}
static struct data_block *
hash_find (const struct hash_data_in *hdi, uint64_t off)
{
struct data_block *block;
unsigned buckno;
buckno = BUCKNO(hdi->hdi_nbits, off);
TAILQ_FOREACH(block, &hdi->hdi_buckets[buckno], db_next)
if (off == block->db_off)
return block;
return NULL;
}
static void
hash_remove (struct hash_data_in *hdi, struct data_block *block)
{
unsigned buckno;
buckno = BUCKNO(hdi->hdi_nbits, block->db_off);
TAILQ_REMOVE(&hdi->hdi_buckets[buckno], block, db_next);
--hdi->hdi_count;
}
static struct data_block *
new_block (struct hash_data_in *hdi, uint64_t off)
{
struct data_block *block;
assert(0 == off % DB_DATA_SIZE);
block = malloc(sizeof(*block));
if (!block)
return NULL;
block->db_off = off;
if (0 != hash_insert(hdi, block))
{
free(block);
return NULL;
}
memset(block->db_set, 0, sizeof(block->db_set));
return block;
}
static unsigned
block_write (struct data_block *block, unsigned block_off,
const unsigned char *data, unsigned data_sz)
{
const unsigned char *begin, *end;
unsigned set, bit, n_full_sets, n;
uint64_t mask;
if (data_sz > DB_DATA_SIZE - block_off)
data_sz = DB_DATA_SIZE - block_off;
begin = data;
end = begin + data_sz;
set = block_off >> 6;
bit = block_off & 0x3F;
assert(set <= N_DB_SETS);
if (bit)
{
n = 64 - bit;
if (n > data_sz)
n = data_sz;
mask = ~((1ULL << bit ) - 1)
& ((1ULL << (bit + n - 1)) | ((1ULL << (bit + n - 1)) - 1));
block->db_set[ set ] |= mask;
memcpy(block->db_data + block_off, data, n);
data += n;
block_off += n;
++set;
}
n_full_sets = (end - data) >> 6;
if (n_full_sets)
{
memcpy(block->db_data + block_off, data, n_full_sets * 64);
data += n_full_sets * 64;
block_off += n_full_sets * 64;
memset(&block->db_set[ set ], 0xFF, n_full_sets * 8);
set += n_full_sets;
}
if (data < end)
{
assert(end - data < 64);
block->db_set[ set ] |= ((1ULL << (end - data)) - 1);
memcpy(block->db_data + block_off, data, end - data);
data = end;
}
assert(set <= N_DB_SETS);
return data - begin;
}
static int
has_bytes_after (const struct data_block *block, unsigned off)
{
unsigned bit, set;
int has;
set = off >> 6;
bit = off & 0x3F;
has = 0 != (block->db_set[ set ] >> bit);
++set;
for ( ; set < N_DB_SETS; ++set)
has += 0 != block->db_set[ set ];
return has > 0;
}
enum ins_frame
data_in_hash_insert_data_frame (struct data_in *data_in,
const struct data_frame *data_frame, uint64_t read_offset)
{
struct hash_data_in *const hdi = HDI_PTR(data_in);
struct data_block *block;
uint64_t key, off, diff, fin_off;
const unsigned char *data;
unsigned size, nw;
if (data_frame->df_offset + data_frame->df_size < read_offset)
return INS_FRAME_DUP;
if ((hdi->hdi_flags & HDI_FIN) &&
(
(data_frame->df_fin &&
data_frame->df_offset + data_frame->df_size != hdi->hdi_fin_off)
||
data_frame->df_offset + data_frame->df_size > hdi->hdi_fin_off
)
)
{
return INS_FRAME_ERR;
}
if (data_frame->df_offset < read_offset)
{
diff = read_offset - data_frame->df_offset;
assert(diff <= data_frame->df_size);
size = data_frame->df_size - diff;
off = data_frame->df_offset + diff;
data = data_frame->df_data + diff;
}
else
{
size = data_frame->df_size;
off = data_frame->df_offset;
data = data_frame->df_data;
}
key = off - (off % DB_DATA_SIZE);
do
{
block = hash_find(hdi, key);
if (!block)
{
block = new_block(hdi, key);
if (!block)
return INS_FRAME_ERR;
}
nw = block_write(block, off % DB_DATA_SIZE, data, size);
size -= nw;
off += nw;
data += nw;
key += DB_DATA_SIZE;
}
while (size > 0);
if (data_frame->df_fin)
{
fin_off = data_frame->df_offset + data_frame->df_size;
if (has_bytes_after(block, fin_off - block->db_off) ||
hash_find(hdi, key))
{
return INS_FRAME_ERR;
}
hdi->hdi_flags |= HDI_FIN;
hdi->hdi_fin_off = fin_off;
}
return INS_FRAME_OK;
}
static enum ins_frame
hash_di_insert_frame (struct data_in *data_in,
struct stream_frame *new_frame, uint64_t read_offset)
{
struct hash_data_in *const hdi = HDI_PTR(data_in);
const struct data_frame *const data_frame = &new_frame->data_frame;
enum ins_frame ins;
ins = data_in_hash_insert_data_frame(data_in, data_frame, read_offset);
lsquic_packet_in_put(hdi->hdi_conn_pub->mm, new_frame->packet_in);
lsquic_malo_put(new_frame);
return ins;
}
#if __GNUC__
# define ctz __builtin_ctzll
#else
static unsigned
ctz (unsigned long long x)
{
unsigned n = 0;
if (0 == (x & ((1ULL << 32) - 1))) { n += 32; x >>= 32; }
if (0 == (x & ((1ULL << 16) - 1))) { n += 16; x >>= 16; }
if (0 == (x & ((1ULL << 8) - 1))) { n += 8; x >>= 8; }
if (0 == (x & ((1ULL << 4) - 1))) { n += 4; x >>= 4; }
if (0 == (x & ((1ULL << 2) - 1))) { n += 2; x >>= 2; }
if (0 == (x & ((1ULL << 1) - 1))) { n += 1; x >>= 1; }
return n;
}
#endif
static unsigned
n_avail_bytes (const struct data_block *block, unsigned set, unsigned bit)
{
unsigned count;
uint64_t part;
part = ~(block->db_set[ set ] >> bit);
if (part)
{
count = ctz(part);
if (count < 64 - bit)
return count;
}
else
count = 64;
++set;
for ( ; set < N_DB_SETS && ~0ULL == block->db_set[ set ]; ++set)
count += 64;
if (set < N_DB_SETS)
{
part = ~block->db_set[ set ];
if (part)
count += ctz(part);
else
count += 64;
}
return count;
}
/* Data block is readable if there is at least one readable byte at
* `read_offset' or there is FIN at that offset.
*/
static int
setup_data_frame (struct hash_data_in *hdi, const uint64_t read_offset,
struct data_block *block)
{
unsigned set, bit;
uint64_t offset;
offset = read_offset % DB_DATA_SIZE;
set = offset >> 6;
bit = offset & 0x3F;
if (block->db_set[ set ] & (1ULL << bit))
{
hdi->hdi_last_block = block;
hdi->hdi_data_frame.df_data = block->db_data;
hdi->hdi_data_frame.df_offset = block->db_off;
hdi->hdi_data_frame.df_read_off = offset;
hdi->hdi_data_frame.df_size = offset +
n_avail_bytes(block, set, bit);
hdi->hdi_data_frame.df_fin =
(hdi->hdi_flags & HDI_FIN) &&
hdi->hdi_data_frame.df_read_off +
hdi->hdi_data_frame.df_size == hdi->hdi_fin_off;
return 1;
}
else if ((hdi->hdi_flags & HDI_FIN) && read_offset == hdi->hdi_fin_off)
{
hdi->hdi_last_block = block;
hdi->hdi_data_frame.df_data = NULL;
hdi->hdi_data_frame.df_offset = block->db_off;
hdi->hdi_data_frame.df_read_off = offset;
hdi->hdi_data_frame.df_size = offset;
hdi->hdi_data_frame.df_fin = 1;
return 1;
}
else
return 0;
}
static struct data_frame *
hash_di_get_frame (struct data_in *data_in, uint64_t read_offset)
{
struct hash_data_in *const hdi = HDI_PTR(data_in);
struct data_block *block;
uint64_t key;
key = read_offset - (read_offset % DB_DATA_SIZE);
block = hash_find(hdi, key);
if (!block)
{
if ((hdi->hdi_flags & HDI_FIN) && read_offset == hdi->hdi_fin_off)
{
hdi->hdi_last_block = NULL;
hdi->hdi_data_frame.df_data = NULL;
hdi->hdi_data_frame.df_offset = read_offset -
read_offset % DB_DATA_SIZE;
hdi->hdi_data_frame.df_read_off = 0;
hdi->hdi_data_frame.df_size = 0;
hdi->hdi_data_frame.df_fin = 1;
return &hdi->hdi_data_frame;
}
else
return NULL;
}
if (setup_data_frame(hdi, read_offset, block))
return &hdi->hdi_data_frame;
else
return NULL;
}
static void
hash_di_frame_done (struct data_in *data_in, struct data_frame *data_frame)
{
struct hash_data_in *const hdi = HDI_PTR(data_in);
struct data_block *const block = hdi->hdi_last_block;
if (block)
{
if (data_frame->df_read_off == DB_DATA_SIZE ||
!has_bytes_after(block, data_frame->df_read_off))
{
hash_remove(hdi, block);
free(block);
if (0 == hdi->hdi_count && 0 == (hdi->hdi_flags & HDI_FIN))
{
LSQ_DEBUG("hash empty, want to switch");
hdi->hdi_data_in.di_flags |= DI_SWITCH_IMPL;
}
}
}
else
assert(data_frame->df_fin && data_frame->df_size == 0);
}
static int
hash_di_empty (struct data_in *data_in)
{
struct hash_data_in *const hdi = HDI_PTR(data_in);
return hdi->hdi_count == 0;
}
struct data_in *
hash_di_switch_impl (struct data_in *data_in, uint64_t read_offset)
{
struct hash_data_in *const hdi = HDI_PTR(data_in);
struct data_in *new_data_in;
assert(hdi->hdi_count == 0);
new_data_in = data_in_nocopy_new(hdi->hdi_conn_pub, hdi->hdi_stream_id);
data_in->di_if->di_destroy(data_in);
return new_data_in;
}
static const struct data_in_iface di_if_hash = {
.di_destroy = hash_di_destroy,
.di_empty = hash_di_empty,
.di_frame_done = hash_di_frame_done,
.di_get_frame = hash_di_get_frame,
.di_insert_frame = hash_di_insert_frame,
.di_switch_impl = hash_di_switch_impl,
};

View File

@ -0,0 +1,426 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_di_nocopy.c -- The "no-copy" data in stream.
*
* Data from packets is not copied: the packets are referenced by stream
* frames. When all data from stream frame is read, the frame is released
* and packet reference count is decremented, which possibly results in
* packet being released as well.
*
* This approach works well in regular circumstances; there are two scenarios
* when it does not:
*
* A. If we have many out-of-order frames, insertion into the list becomes
* expensive. In the degenerate case, we'd have to traverse the whole
* list to find appropriate position.
*
* B. Having many frames ties up resources, as each frame keeps a reference
* to the packet that contains it. This is a possible attack vector:
* send many one-byte packets; a single hole at the beginning will stop
* the server from being able to read the stream, thus tying up resources.
*
* If we detect that either (A) or (B) is true, we request that the stream
* switch to a more robust incoming stream frame handler by setting
* DI_SWITCH_IMPL flag.
*
* For a small number of elements, (A) and (B) do not matter and the checks
* are not performed. This number is defined by EFF_CHECK_THRESH_LOW. On
* the other side of the spectrum, if the number of frames grows very high,
* we want to switch to a more memory-efficient implementation even if (A)
* and (B) are not true. EFF_CHECK_THRESH_HIGH defines this threshold.
*
* Between the low and high thresholds, we detect efficiency problems as
* follows.
*
* To detect (A), we count how many elements we have to traverse during
* insertion. If we have to traverse at least half the list
* EFF_FAR_TRAVERSE_COUNT in a row, DI_SWITCH_IMPL is issued.
*
* If average stream frame size is smaller than EFF_TINY_FRAME_SZ bytes,
* (B) condition is true. In addition, if there are more than EFF_MAX_HOLES
* in the stream, this is also indicative of (B).
*/
#include <assert.h>
#include <inttypes.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/queue.h>
#include "lsquic.h"
#include "lsquic_types.h"
#include "lsquic_int_types.h"
#include "lsquic_conn_flow.h"
#include "lsquic_packet_in.h"
#include "lsquic_rtt.h"
#include "lsquic_sfcw.h"
#include "lsquic_stream.h"
#include "lsquic_mm.h"
#include "lsquic_malo.h"
#include "lsquic_conn.h"
#include "lsquic_conn_public.h"
#include "lsquic_data_in_if.h"
#define LSQUIC_LOGGER_MODULE LSQLM_DI
#define LSQUIC_LOG_CONN_ID ncdi->ncdi_conn_pub->lconn->cn_cid
#define LSQUIC_LOG_STREAM_ID ncdi->ncdi_stream_id
#include "lsquic_logger.h"
/* If number of elements is at or below this number, we do not bother to check
* efficiency conditions.
*/
#define EFF_CHECK_THRESH_LOW 10
/* If number of elements is higher than this number, efficiency alert
* is issued unconditionally.
*/
#define EFF_CHECK_THRESH_HIGH 1000
/* Maximum number of consecutive far traversals */
#define EFF_FAR_TRAVERSE_COUNT 4
/* Maximum number of holes that is not deemed suspicious */
#define EFF_MAX_HOLES 5
/* What is deemed a tiny frame, in bytes. If it is a power of two, calculation
* is cheaper.
*/
#define EFF_TINY_FRAME_SZ 64
TAILQ_HEAD(stream_frames_tailq, stream_frame);
struct nocopy_data_in
{
struct stream_frames_tailq ncdi_frames_in;
struct data_in ncdi_data_in;
struct lsquic_conn_public *ncdi_conn_pub;
uint64_t ncdi_byteage;
uint32_t ncdi_stream_id;
unsigned ncdi_n_frames;
unsigned ncdi_n_holes;
unsigned ncdi_cons_far;
};
#define NCDI_PTR(data_in) (struct nocopy_data_in *) \
((unsigned char *) (data_in) - offsetof(struct nocopy_data_in, ncdi_data_in))
#define STREAM_FRAME_PTR(data_frame) (struct stream_frame *) \
((unsigned char *) (data_frame) - offsetof(struct stream_frame, data_frame))
static const struct data_in_iface di_if_nocopy;
struct data_in *
data_in_nocopy_new (struct lsquic_conn_public *conn_pub, uint32_t stream_id)
{
struct nocopy_data_in *ncdi;
ncdi = malloc(sizeof(*ncdi));
if (!ncdi)
return NULL;
TAILQ_INIT(&ncdi->ncdi_frames_in);
ncdi->ncdi_data_in.di_if = &di_if_nocopy;
ncdi->ncdi_data_in.di_flags = 0;
ncdi->ncdi_conn_pub = conn_pub;
ncdi->ncdi_stream_id = stream_id;
ncdi->ncdi_byteage = 0;
ncdi->ncdi_n_frames = 0;
ncdi->ncdi_n_holes = 0;
ncdi->ncdi_cons_far = 0;
return &ncdi->ncdi_data_in;
}
static void
nocopy_di_destroy (struct data_in *data_in)
{
struct nocopy_data_in *const ncdi = NCDI_PTR(data_in);
stream_frame_t *frame;
while ((frame = TAILQ_FIRST(&ncdi->ncdi_frames_in)))
{
TAILQ_REMOVE(&ncdi->ncdi_frames_in, frame, next_frame);
lsquic_packet_in_put(ncdi->ncdi_conn_pub->mm, frame->packet_in);
lsquic_malo_put(frame);
}
free(ncdi);
}
#if 1
#define CHECK_ORDER(ncdi)
#else
static int
ordered (const struct nocopy_data_in *ncdi)
{
const stream_frame_t *frame;
uint64_t off = 0;
int ordered = 1;
TAILQ_FOREACH(frame, &ncdi->ncdi_frames_in, next_frame)
{
ordered &= off <= frame->data_frame.df_offset;
off = frame->data_frame.df_offset;
}
return ordered;
}
#define CHECK_ORDER(ncdi) assert(ordered(ncdi))
#endif
/* To reduce the number of conditionals, logical operators have been replaced
* with arithmetic operators. Return value is an integer in range [0, 3].
* Bit 0 is set due to FIN in previous frame. If bit 1 is set, it means that
* it's a dup.
*/
static int
insert_frame (struct nocopy_data_in *ncdi, struct stream_frame *new_frame,
uint64_t read_offset, unsigned *p_n_frames)
{
int ins;
unsigned count;
stream_frame_t *prev_frame, *next_frame;
/* Find position in the list, going backwards. We go backwards because
* that is the most likely scenario.
*/
next_frame = TAILQ_LAST(&ncdi->ncdi_frames_in, stream_frames_tailq);
if (next_frame && new_frame->data_frame.df_offset < next_frame->data_frame.df_offset)
{
count = 1;
prev_frame = TAILQ_PREV(next_frame, stream_frames_tailq, next_frame);
for ( ; prev_frame &&
new_frame->data_frame.df_offset < next_frame->data_frame.df_offset;
next_frame = prev_frame,
prev_frame = TAILQ_PREV(prev_frame, stream_frames_tailq, next_frame))
{
if (new_frame->data_frame.df_offset >= prev_frame->data_frame.df_offset)
break;
++count;
}
}
else
{
count = 0;
prev_frame = NULL;
}
if (!prev_frame && next_frame && new_frame->data_frame.df_offset >=
next_frame->data_frame.df_offset)
{
prev_frame = next_frame;
next_frame = TAILQ_NEXT(next_frame, next_frame);
}
/* Perform checks */
if (prev_frame)
ins =
(((prev_frame->data_frame.df_offset == new_frame->data_frame.df_offset) &
(prev_frame->data_frame.df_size == new_frame->data_frame.df_size) &
(prev_frame->data_frame.df_fin == new_frame->data_frame.df_fin)) << 1) /* Duplicate */
| prev_frame->data_frame.df_fin /* FIN in the middle or dup */
| (prev_frame->data_frame.df_offset + prev_frame->data_frame.df_size
> new_frame->data_frame.df_offset) /* Overlap */
;
else
ins = 0;
if (next_frame)
ins |=
(((next_frame->data_frame.df_offset == new_frame->data_frame.df_offset) &
(next_frame->data_frame.df_size == new_frame->data_frame.df_size) &
(next_frame->data_frame.df_fin == new_frame->data_frame.df_fin)) << 1) /* Duplicate */
| (new_frame->data_frame.df_offset < read_offset) << 1 /* Duplicate */
| new_frame->data_frame.df_fin /* FIN in the middle or dup */
| (new_frame->data_frame.df_offset + new_frame->data_frame.df_size
> next_frame->data_frame.df_offset) /* Overlap */
;
else
ins |=
(new_frame->data_frame.df_offset < read_offset) << 1 /* Duplicate */
;
if (ins)
return ins;
if (prev_frame)
{
TAILQ_INSERT_AFTER(&ncdi->ncdi_frames_in, prev_frame, new_frame, next_frame);
ncdi->ncdi_n_holes += prev_frame->data_frame.df_offset +
prev_frame->data_frame.df_size != new_frame->data_frame.df_offset;
if (next_frame)
{
ncdi->ncdi_n_holes += new_frame->data_frame.df_offset +
new_frame->data_frame.df_size != next_frame->data_frame.df_offset;
--ncdi->ncdi_n_holes;
}
}
else
{
ncdi->ncdi_n_holes += next_frame && new_frame->data_frame.df_offset +
new_frame->data_frame.df_size != next_frame->data_frame.df_offset;
TAILQ_INSERT_HEAD(&ncdi->ncdi_frames_in, new_frame, next_frame);
}
CHECK_ORDER(ncdi);
++ncdi->ncdi_n_frames;
ncdi->ncdi_byteage += new_frame->data_frame.df_size;
*p_n_frames = count;
return 0;
}
static int
check_efficiency (struct nocopy_data_in *ncdi, unsigned count)
{
if (ncdi->ncdi_n_frames <= EFF_CHECK_THRESH_LOW)
{
ncdi->ncdi_cons_far = 0;
return 0;
}
if (ncdi->ncdi_n_frames > EFF_CHECK_THRESH_HIGH)
return 1;
if (count >= ncdi->ncdi_n_frames / 2)
{
++ncdi->ncdi_cons_far;
if (ncdi->ncdi_cons_far > EFF_FAR_TRAVERSE_COUNT)
return 1;
}
else
ncdi->ncdi_cons_far = 0;
if (ncdi->ncdi_n_holes > EFF_MAX_HOLES)
return 1;
if (ncdi->ncdi_byteage / EFF_TINY_FRAME_SZ < ncdi->ncdi_n_frames)
return 1;
return 0;
}
static void
set_eff_alert (struct nocopy_data_in *ncdi)
{
LSQ_DEBUG("low efficiency: n_frames: %u; n_holes: %u; cons_far: %u; "
"byteage: %"PRIu64, ncdi->ncdi_n_frames, ncdi->ncdi_n_holes,
ncdi->ncdi_cons_far, ncdi->ncdi_byteage);
ncdi->ncdi_data_in.di_flags |= DI_SWITCH_IMPL;
}
static enum ins_frame
nocopy_di_insert_frame (struct data_in *data_in,
struct stream_frame *new_frame, uint64_t read_offset)
{
struct nocopy_data_in *const ncdi = NCDI_PTR(data_in);
unsigned count;
int ins;
assert(0 == (new_frame->data_frame.df_fin & ~1));
ins = insert_frame(ncdi, new_frame, read_offset, &count);
switch (ins)
{
case 0:
if (check_efficiency(ncdi, count))
set_eff_alert(ncdi);
return INS_FRAME_OK;
case 2:
case 3:
lsquic_packet_in_put(ncdi->ncdi_conn_pub->mm, new_frame->packet_in);
lsquic_malo_put(new_frame);
return INS_FRAME_DUP;
default:
assert(1 == ins);
lsquic_packet_in_put(ncdi->ncdi_conn_pub->mm, new_frame->packet_in);
lsquic_malo_put(new_frame);
return INS_FRAME_ERR;
}
}
static struct data_frame *
nocopy_di_get_frame (struct data_in *data_in, uint64_t read_offset)
{
struct nocopy_data_in *const ncdi = NCDI_PTR(data_in);
struct stream_frame *frame = TAILQ_FIRST(&ncdi->ncdi_frames_in);
if (frame && frame->data_frame.df_offset +
frame->data_frame.df_read_off == read_offset)
return &frame->data_frame;
else
return NULL;
}
static void
nocopy_di_frame_done (struct data_in *data_in, struct data_frame *data_frame)
{
struct nocopy_data_in *const ncdi = NCDI_PTR(data_in);
struct stream_frame *const frame = STREAM_FRAME_PTR(data_frame), *first;
assert(data_frame->df_read_off == data_frame->df_size);
TAILQ_REMOVE(&ncdi->ncdi_frames_in, frame, next_frame);
first = TAILQ_FIRST(&ncdi->ncdi_frames_in);
ncdi->ncdi_n_holes -= first && frame->data_frame.df_offset +
frame->data_frame.df_size != first->data_frame.df_offset;
--ncdi->ncdi_n_frames;
ncdi->ncdi_byteage -= frame->data_frame.df_size;
lsquic_packet_in_put(ncdi->ncdi_conn_pub->mm, frame->packet_in);
lsquic_malo_put(frame);
}
static int
nocopy_di_empty (struct data_in *data_in)
{
struct nocopy_data_in *const ncdi = NCDI_PTR(data_in);
return TAILQ_EMPTY(&ncdi->ncdi_frames_in);
}
struct data_in *
nocopy_di_switch_impl (struct data_in *data_in, uint64_t read_offset)
{
struct nocopy_data_in *const ncdi = NCDI_PTR(data_in);
struct data_in *new_data_in;
stream_frame_t *frame;
enum ins_frame ins;
new_data_in = data_in_hash_new(ncdi->ncdi_conn_pub, ncdi->ncdi_stream_id,
ncdi->ncdi_byteage);
if (!new_data_in)
goto end;
while ((frame = TAILQ_FIRST(&ncdi->ncdi_frames_in)))
{
TAILQ_REMOVE(&ncdi->ncdi_frames_in, frame, next_frame);
ins = data_in_hash_insert_data_frame(new_data_in, &frame->data_frame,
read_offset);
lsquic_packet_in_put(ncdi->ncdi_conn_pub->mm, frame->packet_in);
lsquic_malo_put(frame);
if (INS_FRAME_ERR == ins)
{
new_data_in->di_if->di_destroy(new_data_in);
new_data_in = NULL;
goto end;
}
}
end:
data_in->di_if->di_destroy(data_in);
return new_data_in;
}
static const struct data_in_iface di_if_nocopy = {
.di_destroy = nocopy_di_destroy,
.di_empty = nocopy_di_empty,
.di_frame_done = nocopy_di_frame_done,
.di_get_frame = nocopy_di_get_frame,
.di_insert_frame = nocopy_di_insert_frame,
.di_switch_impl = nocopy_di_switch_impl,
};

View File

@ -0,0 +1,53 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#include <time.h>
#include "lsquic_eng_hist.h"
#if ENG_HIST_ENABLED
#define LSQUIC_LOGGER_MODULE LSQLM_ENG_HIST
#include "lsquic_logger.h"
static void
log_hist_slice (const struct hist_slice *slice, time_t t)
{
size_t strftime(char *s, size_t max, const char *format,
const struct tm *tm);
if (slice->sl_packets_in == 0 &&
slice->sl_packets_out == 0 &&
slice->sl_del_mini_conns == 0 &&
slice->sl_del_full_conns == 0)
return;
struct tm tm;
char timestr[sizeof("12:00:00")];
localtime_r(&t, &tm);
strftime(timestr, sizeof(timestr), "%T", &tm);
LSQ_DEBUG("%s: pi: %u; po: %u; +mc: %u; -mc: %u; +fc: %u; -fc: %u",
timestr,
slice->sl_packets_in,
slice->sl_packets_out,
slice->sl_new_mini_conns,
slice->sl_del_mini_conns,
slice->sl_new_full_conns,
slice->sl_del_full_conns);
}
void
eng_hist_log (const struct eng_hist *hist)
{
unsigned i, idx;
time_t t0 = time(NULL) - ENG_HIST_NELEMS + 1;
for (i = 0; i < ENG_HIST_NELEMS; ++i)
{
idx = (hist->eh_prev_idx + i + 1) & (ENG_HIST_NELEMS - 1);
if (i >= ENG_HIST_NELEMS - ENG_HIST_N_TO_PRINT)
log_hist_slice(&hist->eh_slices[idx], t0 + i);
}
}
#endif

View File

@ -0,0 +1,96 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_eng_hist.h - Engine history.
*
* Keep track of new and destroyed connections, packets in and packets out.
*/
#ifndef LSQUIC_ENG_HIST
#define LSQUIC_ENG_HIST
#define ENG_HIST_ENABLED 1
#define ENG_HIST_BITS 2
#define ENG_HIST_NELEMS (1 << ENG_HIST_BITS)
#ifndef ENG_HIST_N_TO_PRINT
/* By default, we do not print the whole history every second just
* the latest entry.
*/
# define ENG_HIST_N_TO_PRINT 1
#endif
/* Keeps history per slice of time -- one second */
struct hist_slice
{
unsigned sl_packets_in,
sl_packets_out,
sl_new_full_conns,
sl_new_mini_conns,
sl_del_full_conns,
sl_del_mini_conns;
};
struct eng_hist
{
struct hist_slice eh_slices[ENG_HIST_NELEMS];
unsigned eh_cur_idx,
eh_prev_idx;
};
#if ENG_HIST_ENABLED
/* Initialize engine history */
#define eng_hist_init(eh) do { \
memset(eh, 0, sizeof(*(eh))); \
(eh)->eh_cur_idx = (eh)->eh_prev_idx = \
time(NULL) & (ENG_HIST_NELEMS - 1); \
} while (0)
/* Clear slice at current index */
#define eng_hist_clear_cur(eh) do { \
memset(&(eh)->eh_slices[(eh)->eh_cur_idx], 0, \
sizeof(struct hist_slice)); \
} while (0)
void
eng_hist_log (const struct eng_hist *);
/* Switch to next slice if necessary */
#define eng_hist_tick(eh, now) do { \
if (0 == (now)) \
(eh)->eh_cur_idx = time(NULL) & (ENG_HIST_NELEMS - 1); \
else \
(eh)->eh_cur_idx = ((now) / 1000000) & (ENG_HIST_NELEMS - 1); \
if ((eh)->eh_cur_idx != (eh)->eh_prev_idx) \
{ \
eng_hist_log(eh); \
eng_hist_clear_cur(eh); \
(eh)->eh_prev_idx = (eh)->eh_cur_idx; \
} \
} while (0)
/* Increment element `what'. Slice increment is handled in this macro, too. */
#define eng_hist_inc(eh, now, what) do { \
eng_hist_tick(eh, now); \
++(eh)->eh_slices[(eh)->eh_cur_idx].what; \
} while (0)
#else /* !ENG_HIST_ENABLED */
#define eng_hist_init(eh)
#define eng_hist_clear_cur(eh)
#define eng_hist_tick(eh, now)
#define eng_hist_inc(eh, now, what)
#define eng_hist_log(eh)
#endif /* ENG_HIST_ENABLED */
#endif /* LSQUIC_ENG_HIST */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,75 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_engine_public.h -- Engine's "public interface"
*
*/
#ifndef LSQUIC_ENGINE_PUBLIC_H
#define LSQUIC_ENGINE_PUBLIC_H 1
struct lsquic_conn;
struct lsquic_engine;
struct lsquic_engine_public {
struct lsquic_mm enp_mm;
struct lsquic_engine_settings enp_settings;
const struct lsquic_packout_mem_if
*enp_pmi;
void *enp_pmi_ctx;
struct lsquic_engine *enp_engine;
enum {
ENPUB_PROC = (1 << 0), /* Being processed by one of the user-facing
* functions.
*/
} enp_flags;
unsigned char enp_ver_tags_buf[ sizeof(lsquic_ver_tag_t) * N_LSQVER ];
unsigned enp_ver_tags_len;
};
/* These values are printable ASCII characters for ease of printing the
* whole history in a single line of a log message. If connection was
* processed as result of being put onto the queue, the letter is converted
* to uppercase.
*
* The letters are assigned by first letter of the verb for most obvious
* and important actions, like "read" and "write" and other letters of
* the verb or some other letters for other actions.
*
* Each reason is either expected to produce user read from the stream
* or putting stream data into packet for sending out. This is documented
* in a separate comment column below.
*/
enum rw_reason
{
RW_REASON_EMPTY = '\0', /* No init required */
/* Expected action: */
RW_REASON_USER_WRITE = 'w', /* write */
RW_REASON_USER_WRITEV = 'v', /* write */
RW_REASON_USER_READ = 'r', /* write (WINDOW_UPDATE frame) */
RW_REASON_FLUSH = 'f', /* write */
RW_REASON_STREAM_CLOSE = 'c', /* write */
RW_REASON_RST_IN = 'n', /* read */
RW_REASON_STREAM_IN = 'd', /* read */
RW_REASON_RESET_EXT = 'e', /* write */
RW_REASON_WANTREAD = 'a', /* read */
RW_REASON_SHUTDOWN = 'u', /* write */
RW_REASON_WRITEFILE = 't', /* write */
RW_REASON_SENDFILE = 's', /* write */
};
/* Put connection onto Pending RW Queue if it is not already on it. If
* connection is being destroyed, this is a no-op.
* XXX Is the bit about "being destroyed" still true?
*/
void
lsquic_engine_add_conn_to_pend_rw (struct lsquic_engine_public *enpub,
lsquic_conn_t *conn, enum rw_reason);
/* Put connection onto Advisory Tick Time Queue if it is not already on it.
*/
void
lsquic_engine_add_conn_to_attq (struct lsquic_engine_public *enpub,
lsquic_conn_t *, lsquic_time_t);
#endif

View File

@ -0,0 +1,314 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#include <arpa/inet.h>
#include <errno.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include "lsquic.h"
#include "lsquic_types.h"
#include "lsquic_int_types.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_in.h"
#include "lsquic_packet_out.h"
#include "lsquic_parse.h"
#include "lsquic_frame_common.h"
#include "lsquic_frame_reader.h"
#include "lsquic_ev_log.h"
#define LSQUIC_LOGGER_MODULE LSQLM_EVENT
#include "lsquic_logger.h"
/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
/* |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| */
/* Messages that do not include connection ID go above this point */
#define LSQUIC_LOG_CONN_ID cid
#define LCID(a...) LSQ_LOG2(LSQ_LOG_DEBUG, a) /* LCID: log with CID */
/* Messages that are to include connection ID go below this point */
/* |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| */
/* VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV */
void
lsquic_ev_log_packet_in (lsquic_cid_t cid, const lsquic_packet_in_t *packet_in)
{
LCID("packet in: %"PRIu64, packet_in->pi_packno);
}
void
lsquic_ev_log_ack_frame_in (lsquic_cid_t cid, const struct ack_info *acki)
{
size_t sz;
char *buf;
if ((buf = acki2str(acki, &sz)))
{
LCID("ACK frame in: %.*s", (int) sz, buf);
free(buf);
}
}
void
lsquic_ev_log_stream_frame_in (lsquic_cid_t cid,
const struct stream_frame *frame)
{
LCID("STREAM frame in: stream %u; offset %"PRIu64"; size %"PRIu16
"; fin: %d", frame->stream_id, frame->data_frame.df_offset,
frame->data_frame.df_size, (int) frame->data_frame.df_fin);
}
void
lsquic_ev_log_stop_waiting_frame_in (lsquic_cid_t cid, lsquic_packno_t least)
{
LCID("STOP_WAITING frame in: least unacked packno %"PRIu64, least);
}
void
lsquic_ev_log_window_update_frame_in (lsquic_cid_t cid, uint32_t stream_id,
uint64_t offset)
{
LCID("WINDOW_UPDATE frame in: stream %"PRIu32"; offset %"PRIu64,
stream_id, offset);
}
void
lsquic_ev_log_blocked_frame_in (lsquic_cid_t cid, uint32_t stream_id)
{
LCID("BLOCKED frame in: stream %"PRIu32, stream_id);
}
void
lsquic_ev_log_connection_close_frame_in (lsquic_cid_t cid,
uint32_t error_code, int reason_len, const char *reason)
{
LCID("CONNECTION_CLOSE frame in: error code %"PRIu32", reason: %.*s",
error_code, reason_len, reason);
}
void
lsquic_ev_log_goaway_frame_in (lsquic_cid_t cid, uint32_t error_code,
uint32_t stream_id, int reason_len, const char *reason)
{
LCID("GOAWAY frame in: error code %"PRIu32", stream %"PRIu32
", reason: %.*s", error_code, stream_id, reason_len, reason);
}
void
lsquic_ev_log_rst_stream_frame_in (lsquic_cid_t cid, uint32_t stream_id,
uint64_t offset, uint32_t error_code)
{
LCID("RST_FRAME frame in: error code %"PRIu32", stream %"PRIu32
", offset: %"PRIu64, error_code, stream_id, offset);
}
void
lsquic_ev_log_padding_frame_in (lsquic_cid_t cid, size_t len)
{
LCID("PADDING frame in of %zd bytes", len);
}
void
lsquic_ev_log_ping_frame_in (lsquic_cid_t cid)
{
LCID("PING frame in");
}
void
lsquic_ev_log_packet_created (lsquic_cid_t cid,
const struct lsquic_packet_out *packet_out)
{
LCID("created packet %"PRIu64"; flags: version=%d, nonce=%d, conn_id=%d",
packet_out->po_packno,
!!(packet_out->po_flags & PO_VERSION),
!!(packet_out->po_flags & PO_NONCE),
!!(packet_out->po_flags & PO_CONN_ID));
}
void
lsquic_ev_log_packet_sent (lsquic_cid_t cid,
const struct lsquic_packet_out *packet_out)
{
char frames[lsquic_frame_types_str_sz];
if (lsquic_packet_out_verneg(packet_out))
LCID("sent version negotiation packet, size %hu",
packet_out->po_data_sz);
else if (lsquic_packet_out_pubres(packet_out))
LCID("sent public reset packet, size %hu", packet_out->po_data_sz);
else
LCID("sent packet %"PRIu64", size %hu, frame types: %s",
packet_out->po_packno, packet_out->po_enc_data_sz,
/* Frame types is a list of different frames types contained
* in the packet, no more. Count and order of frames is not
* printed.
*/
lsquic_frame_types_to_str(frames, sizeof(frames),
packet_out->po_frame_types));
}
void
lsquic_ev_log_packet_not_sent (lsquic_cid_t cid,
const struct lsquic_packet_out *packet_out)
{
char frames[lsquic_frame_types_str_sz];
LCID("unsent packet %"PRIu64", size %hu, frame types: %s",
packet_out->po_packno, packet_out->po_enc_data_sz,
/* Frame types is a list of different frames types contained in
* the packet, no more. Count and order of frames is not printed.
*/
lsquic_frame_types_to_str(frames, sizeof(frames),
packet_out->po_frame_types));
}
void
lsquic_ev_log_http_headers_in (lsquic_cid_t cid, int is_server,
const struct uncompressed_headers *uh)
{
const char *cr, *p;
if (uh->uh_flags & UH_PP)
LCID("read push promise; stream %"PRIu32", promised stream %"PRIu32,
uh->uh_stream_id, uh->uh_oth_stream_id);
else
LCID("read %s headers; stream: %"PRIu32", depends on stream: %"PRIu32
", weight: %hu, exclusive: %d, fin: %d",
is_server ? "request" : "response",
uh->uh_stream_id, uh->uh_oth_stream_id, uh->uh_weight,
(int) uh->uh_exclusive, !!(uh->uh_flags & UH_FIN));
for (p = uh->uh_headers; p < uh->uh_headers + uh->uh_size; p = cr + 2)
{
cr = strchr(p, '\r');
if (cr && cr > p)
LCID(" %.*s", (int) (cr - p), p);
else
break;
}
}
void
lsquic_ev_log_generated_stream_frame (lsquic_cid_t cid,
const struct parse_funcs *pf, const unsigned char *buf, size_t bufsz)
{
struct stream_frame frame;
int len;
len = pf->pf_parse_stream_frame(buf, bufsz, &frame);
if (len > 0)
LCID("generated STREAM frame: stream %"PRIu32", offset: %"PRIu64
", size: %"PRIu16", fin: %d", frame.stream_id,
frame.data_frame.df_offset, frame.data_frame.df_size,
frame.data_frame.df_fin);
else
LSQ_LOG2(LSQ_LOG_WARN, "cannot parse STREAM frame");
}
void
lsquic_ev_log_generated_ack_frame (lsquic_cid_t cid, const struct parse_funcs *pf,
const unsigned char *ack_buf, size_t ack_buf_sz)
{
struct ack_info acki;
size_t sz;
char *buf;
int len;
len = pf->pf_parse_ack_frame(ack_buf, ack_buf_sz, &acki);
if (len < 0)
{
LSQ_LOG2(LSQ_LOG_WARN, "cannot parse ACK frame");
return;
}
if ((buf = acki2str(&acki, &sz)))
{
LCID("generated ACK frame: %.*s", (int) sz, buf);
free(buf);
}
}
void
lsquic_ev_log_generated_stop_waiting_frame (lsquic_cid_t cid,
lsquic_packno_t lunack)
{
LCID("generated STOP_WAITING frame; least unacked: %"PRIu64, lunack);
}
void
lsquic_ev_log_generated_http_headers (lsquic_cid_t cid, uint32_t stream_id,
int is_server, const struct http_prio_frame *prio_frame,
const struct lsquic_http_headers *headers)
{
uint32_t dep_stream_id;
int exclusive, i;
unsigned short weight;
if (is_server)
LCID("generated HTTP response HEADERS for stream %"PRIu32, stream_id);
else
{
memcpy(&dep_stream_id, prio_frame->hpf_stream_id, 4);
dep_stream_id = htonl(dep_stream_id);
exclusive = dep_stream_id >> 31;
dep_stream_id &= ~(1 << 31);
weight = prio_frame->hpf_weight + 1;
LCID("generated HTTP request HEADERS for stream %"PRIu32
", dep stream: %"PRIu32", weight: %hu, exclusive: %d", stream_id,
dep_stream_id, weight, exclusive);
}
for (i = 0; i < headers->count; ++i)
LCID(" %.*s: %.*s",
(int) headers->headers[i].name.iov_len,
(char *) headers->headers[i].name.iov_base,
(int) headers->headers[i].value.iov_len,
(char *) headers->headers[i].value.iov_base);
}
void
lsquic_ev_log_generated_http_push_promise (lsquic_cid_t cid,
uint32_t stream_id, uint32_t promised_stream_id,
const struct lsquic_http_headers *headers,
const struct lsquic_http_headers *extra_headers)
{
int i;
LCID("generated HTTP PUSH_PROMISE for stream %"PRIu32"; promised stream %"
PRIu32, stream_id, promised_stream_id);
for (i = 0; i < headers->count; ++i)
LCID(" %.*s: %.*s",
(int) headers->headers[i].name.iov_len,
(char *) headers->headers[i].name.iov_base,
(int) headers->headers[i].value.iov_len,
(char *) headers->headers[i].value.iov_base);
if (extra_headers)
for (i = 0; i < extra_headers->count; ++i)
LCID(" %.*s: %.*s",
(int) extra_headers->headers[i].name.iov_len,
(char *) extra_headers->headers[i].name.iov_base,
(int) extra_headers->headers[i].value.iov_len,
(char *) extra_headers->headers[i].value.iov_base);
}

View File

@ -0,0 +1,206 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_ev_log.h -- Event logger
*/
#ifndef LSQUIC_EV_LOG_H
#define LSQUIC_EV_LOG_H 1
#include "lsquic_int_types.h"
struct ack_info;
struct http_prio_frame;
struct lsquic_http_headers;
struct lsquic_packet_in;
struct lsquic_packet_out;
struct parse_funcs;
struct stream_frame;
struct uncompressed_headers;
/* Log a generic event not tied to any particular connection */
#define EV_LOG_GENERIC_EVENT(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_logger_log1(LSQ_LOG_DEBUG, LSQLM_EVENT, args); \
} while (0)
/* Log a generic event associated with connection `cid' */
#define EV_LOG_CONN_EVENT(cid, args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_logger_log2(LSQ_LOG_DEBUG, LSQLM_EVENT, cid, args); \
} while (0)
void
lsquic_ev_log_packet_in (lsquic_cid_t, const struct lsquic_packet_in *);
#define EV_LOG_PACKET_IN(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_packet_in(args); \
} while (0)
void
lsquic_ev_log_ack_frame_in (lsquic_cid_t, const struct ack_info *);
#define EV_LOG_ACK_FRAME_IN(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_ack_frame_in(args); \
} while (0)
void
lsquic_ev_log_stream_frame_in (lsquic_cid_t, const struct stream_frame *);
#define EV_LOG_STREAM_FRAME_IN(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_stream_frame_in(args); \
} while (0)
void
lsquic_ev_log_window_update_frame_in (lsquic_cid_t, uint32_t stream_id,
uint64_t offset);
#define EV_LOG_WINDOW_UPDATE_FRAME_IN(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_window_update_frame_in(args); \
} while (0)
void
lsquic_ev_log_blocked_frame_in (lsquic_cid_t, uint32_t stream_id);
#define EV_LOG_BLOCKED_FRAME_IN(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_blocked_frame_in(args); \
} while (0)
void
lsquic_ev_log_stop_waiting_frame_in (lsquic_cid_t, lsquic_packno_t);
#define EV_LOG_STOP_WAITING_FRAME_IN(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_stop_waiting_frame_in(args); \
} while (0)
void
lsquic_ev_log_connection_close_frame_in (lsquic_cid_t, uint32_t error_code,
int reason_len, const char *reason);
#define EV_LOG_CONNECTION_CLOSE_FRAME_IN(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_connection_close_frame_in(args); \
} while (0)
void
lsquic_ev_log_goaway_frame_in (lsquic_cid_t, uint32_t error_code,
uint32_t stream_id, int reason_len, const char *reason);
#define EV_LOG_GOAWAY_FRAME_IN(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_goaway_frame_in(args); \
} while (0)
void
lsquic_ev_log_rst_stream_frame_in (lsquic_cid_t, uint32_t stream_id,
uint64_t offset, uint32_t error_code);
#define EV_LOG_RST_STREAM_FRAME_IN(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_rst_stream_frame_in(args); \
} while (0)
void
lsquic_ev_log_padding_frame_in (lsquic_cid_t, size_t len);
#define EV_LOG_PADDING_FRAME_IN(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_padding_frame_in(args); \
} while (0)
void
lsquic_ev_log_ping_frame_in (lsquic_cid_t);
#define EV_LOG_PING_FRAME_IN(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_ping_frame_in(args); \
} while (0)
void
lsquic_ev_log_packet_created (lsquic_cid_t, const struct lsquic_packet_out *);
#define EV_LOG_PACKET_CREATED(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_packet_created(args); \
} while (0)
void
lsquic_ev_log_packet_sent (lsquic_cid_t, const struct lsquic_packet_out *);
#define EV_LOG_PACKET_SENT(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_packet_sent(args); \
} while (0)
void
lsquic_ev_log_packet_not_sent (lsquic_cid_t, const struct lsquic_packet_out *);
#define EV_LOG_PACKET_NOT_SENT(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_packet_not_sent(args); \
} while (0)
void
lsquic_ev_log_http_headers_in (lsquic_cid_t, int is_server,
const struct uncompressed_headers *);
#define EV_LOG_HTTP_HEADERS_IN(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_http_headers_in(args); \
} while (0)
void
lsquic_ev_log_generated_stream_frame (lsquic_cid_t, const struct parse_funcs *pf,
const unsigned char *, size_t len);
#define EV_LOG_GENERATED_STREAM_FRAME(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_generated_stream_frame(args); \
} while (0)
void
lsquic_ev_log_generated_ack_frame (lsquic_cid_t, const struct parse_funcs *,
const unsigned char *, size_t len);
#define EV_LOG_GENERATED_ACK_FRAME(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_generated_ack_frame(args); \
} while (0)
void
lsquic_ev_log_generated_stop_waiting_frame (lsquic_cid_t, lsquic_packno_t);
#define EV_LOG_GENERATED_STOP_WAITING_FRAME(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_generated_stop_waiting_frame(args); \
} while (0)
void
lsquic_ev_log_generated_http_headers (lsquic_cid_t, uint32_t stream_id,
int is_server, const struct http_prio_frame *,
const struct lsquic_http_headers *);
#define EV_LOG_GENERATED_HTTP_HEADERS(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_generated_http_headers(args); \
} while (0)
void
lsquic_ev_log_generated_http_push_promise (lsquic_cid_t, uint32_t stream_id,
uint32_t promised_stream_id,
const struct lsquic_http_headers *headers,
const struct lsquic_http_headers *extra_headers);
#define EV_LOG_GENERATED_HTTP_PUSH_PROMISE(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_generated_http_push_promise(args); \
} while (0)
#endif

View File

@ -0,0 +1,26 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#include <stdint.h>
#include "lsquic_frame_common.h"
const char *
lsquic_http_setting_id2str (enum settings_param id)
{
switch (id)
{
case SETTINGS_HEADER_TABLE_SIZE:
return "SETTINGS_HEADER_TABLE_SIZE";
case SETTINGS_ENABLE_PUSH:
return "SETTINGS_ENABLE_PUSH";
case SETTINGS_MAX_CONCURRENT_STREAMS:
return "SETTINGS_MAX_CONCURRENT_STREAMS";
case SETTINGS_INITIAL_WINDOW_SIZE:
return "SETTINGS_INITIAL_WINDOW_SIZE";
case SETTINGS_MAX_FRAME_SIZE:
return "SETTINGS_MAX_FRAME_SIZE";
case SETTINGS_MAX_HEADER_LIST_SIZE:
return "SETTINGS_MAX_HEADER_LIST_SIZE";
}
return "<unknown>";
}

View File

@ -0,0 +1,81 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_frame_common.h
*/
#ifndef LSQUIC_FRAME_COMMON_H
#define LSQUIC_FRAME_COMMON_H 1
enum http_frame_type
{
HTTP_FRAME_DATA = 0x00,
HTTP_FRAME_HEADERS = 0x01,
HTTP_FRAME_PRIORITY = 0x02,
HTTP_FRAME_RST_STREAM = 0x03,
HTTP_FRAME_SETTINGS = 0x04,
HTTP_FRAME_PUSH_PROMISE = 0x05,
HTTP_FRAME_PING = 0x06,
HTTP_FRAME_GOAWAY = 0x07,
HTTP_FRAME_WINDOW_UPDATE = 0x08,
HTTP_FRAME_CONTINUATION = 0x09,
N_HTTP_FRAME_TYPES
};
enum http_frame_header_flags /* RFC 7540, Section 6.2 */
{
HFHF_END_STREAM = 0x01,
HFHF_END_HEADERS = 0x04,
HFHF_PADDED = 0x08,
HFHF_PRIORITY = 0x20,
};
struct http_frame_header /* RFC 7540, Section 4.1 */
{
unsigned char hfh_length[3];
unsigned char hfh_type; /* enum http_frame_type */
unsigned char hfh_flags;
unsigned char hfh_stream_id[4];
};
#define hfh_get_length(hfh) ( ((hfh)->hfh_length[0] << 16) | \
((hfh)->hfh_length[1] << 8) | \
(hfh)->hfh_length[2] )
enum settings_param /* RFC 7540, Section 6.5.2 */
{
SETTINGS_HEADER_TABLE_SIZE = 0x0001,
SETTINGS_ENABLE_PUSH = 0x0002,
SETTINGS_MAX_CONCURRENT_STREAMS = 0x0003,
SETTINGS_INITIAL_WINDOW_SIZE = 0x0004,
SETTINGS_MAX_FRAME_SIZE = 0x0005,
SETTINGS_MAX_HEADER_LIST_SIZE = 0x0006,
};
/* This also doubles as HEADERS frame payload prefix: */
struct http_prio_frame /* RFC 7540, Section 6.3 */
{
unsigned char hpf_stream_id[4]; /* High bit is the exclusive flag */
unsigned char hpf_weight;
};
struct http_push_promise_frame /* RFC 7540, Section 6.6 */
{
unsigned char hppf_promised_id[4]; /* High bit is reserved */
};
struct lsquic_http2_setting
{
uint16_t id;
uint32_t value;
};
const char *
lsquic_http_setting_id2str (enum settings_param id);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,120 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_frame_reader.h -- Read HTTP frames from stream
*/
#ifndef LSQUIC_FRAME_READER_H
#define LSQUIC_FRAME_READER_H 1
#include <stddef.h>
#include <stdint.h>
struct lsquic_hdec;
struct lsquic_mm;
struct lsquic_stream;
struct lsquic_frame_reader;
enum frame_reader_flags
{
FRF_SERVER = (1 << 0),
FRF_HAVE_PREV = (1 << 1),
};
/* Frame reader may hit some error conditions which are reported using
* callback fc_on_error. These codes are later mapped stream- or
* connection-level errors.
*/
enum frame_reader_error
{
FR_ERR_DUPLICATE_PSEH = 1, /* Duplicate pseudo-header */
FR_ERR_INCOMPL_REQ_PSEH, /* Not all request pseudo-headers are present */
FR_ERR_UNNEC_REQ_PSEH, /* Unnecessary request pseudo-header present in
* the response.
*/
FR_ERR_INCOMPL_RESP_PSEH, /* Not all response pseudo-headers are present */
FR_ERR_UNNEC_RESP_PSEH, /* Unnecessary response pseudo-header present in
* the response.
*/
FR_ERR_UNKNOWN_PSEH, /* Unknown pseudo-header */
FR_ERR_UPPERCASE_HEADER, /* Uppercase letter in header */
FR_ERR_MISPLACED_PSEH,
FR_ERR_MISSING_PSEH,
FR_ERR_DECOMPRESS,
FR_ERR_INVALID_FRAME_SIZE, /* E.g. a SETTINGS frame length is not a multiple
* of 6 (RFC 7540, Section 6.5.1).
*/
FR_ERR_NONZERO_STREAM_ID,
FR_ERR_ZERO_STREAM_ID,
FR_ERR_SELF_DEP_STREAM, /* A stream in priority frame cannot depend on
* itself (RFC 7540, Section 5.3.1).
*/
FR_ERR_HEADERS_TOO_LARGE,
FR_ERR_UNEXPECTED_PUSH,
FR_ERR_NOMEM, /* Cannot allocate any more memory. */
FR_ERR_EXPECTED_CONTIN, /* Expected continuation frame. */
};
/* This struct is used to return decoded HEADERS and PUSH_PROMISE frames.
* Some of the fields are only used for HEADERS frames. They are marked
* with "H" comment below.
*/
struct uncompressed_headers
{
uint32_t uh_stream_id;
uint32_t uh_oth_stream_id; /* For HEADERS frame, the ID of the
* stream that this stream depends
* on. (Zero means unset.) For
* PUSH_PROMISE, the promised stream
* ID.
*/
unsigned uh_size; /* Number of characters in uh_headers, not
* counting the NUL byte.
*/
unsigned /* H */ uh_off;
unsigned short /* H */ uh_weight; /* 1 - 256; 0 means not set */
signed char /* H */ uh_exclusive; /* 0 or 1 when set; -1 means not set */
enum {
/* H */ UH_FIN = (1 << 0),
UH_PP = (1 << 1), /* Push promise */
} uh_flags:8;
char uh_headers[ /* NUL-terminated C string */
#if FRAME_READER_TESTING
FRAME_READER_TESTING
#else
0
#endif
];
};
struct frame_reader_callbacks
{
void (*frc_on_headers) (void *frame_cb_ctx, struct uncompressed_headers *);
void (*frc_on_push_promise) (void *frame_cb_ctx, struct uncompressed_headers *);
void (*frc_on_settings) (void *frame_cb_ctx, uint16_t setting_id,
uint32_t setting_value);
void (*frc_on_priority) (void *frame_cb_ctx, uint32_t stream_id,
int exclusive, uint32_t dep_stream_id,
unsigned weight);
void (*frc_on_error) (void *frame_cb_ctx, uint32_t stream_id,
enum frame_reader_error);
};
typedef ssize_t (*fr_stream_read_f)(struct lsquic_stream *, void *, size_t);
struct lsquic_frame_reader *
lsquic_frame_reader_new (enum frame_reader_flags, unsigned max_headers_sz,
struct lsquic_mm *, struct lsquic_stream *,
fr_stream_read_f, struct lsquic_hdec *,
const struct frame_reader_callbacks *,
void *fr_cb_ctx);
int
lsquic_frame_reader_read (struct lsquic_frame_reader *);
void
lsquic_frame_reader_destroy (struct lsquic_frame_reader *);
#endif

View File

@ -0,0 +1,713 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_frame_writer.c -- write frames to HEADERS stream.
*
* The frame is first written to list of frame_buf's (frabs) and then
* out to the stream. This is done because frame's size is written out
* to the stream and we may not have enough room in the stream to fit
* the whole frame.
*/
#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include "lsquic_arr.h"
#include "lsquic_hpack_enc.h"
#include "lsquic_mm.h"
#include "lsquic.h"
#include "lsquic_frame_writer.h"
#include "lsquic_frame_common.h"
#include "lsquic_ev_log.h"
#define LSQUIC_LOGGER_MODULE LSQLM_FRAME_WRITER
#define LSQUIC_LOG_CONN_ID lsquic_conn_id(lsquic_stream_conn(fw->fw_stream))
#include "lsquic_logger.h"
#ifndef LSQUIC_FRAB_SZ
# define LSQUIC_FRAB_SZ 0x1000
#endif
struct frame_buf
{
TAILQ_ENTRY(frame_buf) frab_next;
unsigned short frab_size,
frab_off;
unsigned char frab_buf[
LSQUIC_FRAB_SZ
- sizeof(TAILQ_ENTRY(frame_buf))
- sizeof(unsigned short) * 2
];
};
#define frab_left_to_read(f) ((f)->frab_size - (f)->frab_off)
#define frab_left_to_write(f) ((unsigned short) sizeof((f)->frab_buf) - (f)->frab_size)
#define frab_write_to(f) ((f)->frab_buf + (f)->frab_size)
/* Make sure that frab_buf is at least five bytes long, otherwise a frame
* won't fit into two adjacent frabs.
*/
typedef char three_byte_frab_buf[(sizeof(((struct frame_buf *)0)->frab_buf) >= 5) - 1];
TAILQ_HEAD(frame_buf_head, frame_buf);
struct lsquic_frame_writer
{
struct lsquic_stream *fw_stream;
fw_write_f fw_write;
fw_wavail_f fw_wavail;
fw_flush_f fw_flush;
struct lsquic_mm *fw_mm;
struct lsquic_henc *fw_henc;
struct frame_buf_head fw_frabs;
unsigned fw_max_frame_sz;
uint32_t fw_max_header_list_sz; /* 0 means unlimited */
enum {
FW_SERVER = (1 << 0),
} fw_flags;
};
/* RFC 7540, Section 4.2 */
#define MIN_MAX_FRAME_SIZE (1 << 14)
#define MAX_MAX_FRAME_SIZE ((1 << 24) - 1)
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define SETTINGS_FRAME_SZ 6
#define ABS_MIN_FRAME_SIZE MAX(SETTINGS_FRAME_SZ, \
sizeof(struct http_prio_frame))
struct lsquic_frame_writer *
lsquic_frame_writer_new (struct lsquic_mm *mm, struct lsquic_stream *stream,
unsigned max_frame_sz, struct lsquic_henc *henc, fw_write_f write,
fw_wavail_f wavail, fw_flush_f flush, int is_server)
{
struct lsquic_frame_writer *fw;
/* When frame writer is instantiated, limit the maximum size to
* MIN_MAX_FRAME_SIZE. The reference implementation has this value
* hardcoded and QUIC does not provide a mechanism to advertise a
* different value.
*/
if (0 == max_frame_sz)
max_frame_sz = MIN_MAX_FRAME_SIZE;
else
LSQ_LOG1(LSQ_LOG_WARN, "max frame size specified to be %u bytes "
"-- this better be test code!", max_frame_sz);
if (!is_server && max_frame_sz < ABS_MIN_FRAME_SIZE)
{
LSQ_LOG1(LSQ_LOG_ERROR, "max frame size must be at least %zd bytes, "
"which is the size of priority information that client always "
"writes", ABS_MIN_FRAME_SIZE);
return NULL;
}
fw = malloc(sizeof(*fw));
if (!fw)
return NULL;
fw->fw_mm = mm;
fw->fw_henc = henc;
fw->fw_stream = stream;
fw->fw_write = write;
fw->fw_wavail = wavail;
fw->fw_flush = flush;
fw->fw_max_frame_sz = max_frame_sz;
fw->fw_max_header_list_sz = 0;
if (is_server)
fw->fw_flags = FW_SERVER;
else
fw->fw_flags = 0;
TAILQ_INIT(&fw->fw_frabs);
return fw;
}
void
lsquic_frame_writer_destroy (struct lsquic_frame_writer *fw)
{
struct frame_buf *frab;
while ((frab = TAILQ_FIRST(&fw->fw_frabs)))
{
TAILQ_REMOVE(&fw->fw_frabs, frab, frab_next);
lsquic_mm_put_4k(fw->fw_mm, frab);
}
free(fw);
}
static struct frame_buf *
fw_get_frab (struct lsquic_frame_writer *fw)
{
struct frame_buf *frab;
frab = lsquic_mm_get_4k(fw->fw_mm);
if (frab)
memset(frab, 0, offsetof(struct frame_buf, frab_buf));
return frab;
}
static void
fw_put_frab (struct lsquic_frame_writer *fw, struct frame_buf *frab)
{
TAILQ_REMOVE(&fw->fw_frabs, frab, frab_next);
lsquic_mm_put_4k(fw->fw_mm, frab);
}
static int
fw_write_to_frab (struct lsquic_frame_writer *fw, const void *buf, size_t bufsz)
{
const unsigned char *p = buf;
const unsigned char *const end = p + bufsz;
struct frame_buf *frab;
unsigned ntowrite;
while (p < end)
{
frab = TAILQ_LAST(&fw->fw_frabs, frame_buf_head);
if (!(frab && (ntowrite = frab_left_to_write(frab)) > 0))
{
frab = fw_get_frab(fw);
if (!frab)
return -1;
TAILQ_INSERT_TAIL(&fw->fw_frabs, frab, frab_next);
ntowrite = frab_left_to_write(frab);
}
if (ntowrite > bufsz)
ntowrite = bufsz;
memcpy(frab_write_to(frab), p, ntowrite);
p += ntowrite;
bufsz -= ntowrite;
frab->frab_size += ntowrite;
}
return 0;
}
int
lsquic_frame_writer_have_leftovers (const struct lsquic_frame_writer *fw)
{
return !TAILQ_EMPTY(&fw->fw_frabs);
}
int
lsquic_frame_writer_flush (struct lsquic_frame_writer *fw)
{
size_t navail = fw->fw_wavail(fw->fw_stream);
struct frame_buf *frab;
while (navail > 0 && (frab = TAILQ_FIRST(&fw->fw_frabs)))
{
size_t ntowrite = frab_left_to_read(frab);
if (navail < ntowrite)
ntowrite = navail;
ssize_t nw = fw->fw_write(fw->fw_stream,
frab->frab_buf + frab->frab_off, ntowrite);
if (nw > 0)
{
navail -= nw;
frab->frab_off += nw;
if (frab->frab_off == frab->frab_size)
{
TAILQ_REMOVE(&fw->fw_frabs, frab, frab_next);
fw_put_frab(fw, frab);
}
}
else
return -1;
}
(void) fw->fw_flush(fw->fw_stream);
return 0;
}
struct header_framer_ctx
{
struct lsquic_frame_writer
*hfc_fw;
struct {
struct frame_buf *frab;
unsigned short off;
} hfc_header_ptr; /* Points to byte *after* current frame header */
unsigned hfc_max_frame_sz; /* Maximum frame size. We always fill it. */
unsigned hfc_cur_sz; /* Number of bytes in the current frame. */
unsigned hfc_n_frames; /* Number of frames written. */
uint32_t hfc_stream_id; /* Stream ID */
enum http_frame_header_flags
hfc_first_flags;
enum http_frame_type
hfc_frame_type;
};
static void
hfc_init (struct header_framer_ctx *hfc, struct lsquic_frame_writer *fw,
unsigned max_frame_sz, enum http_frame_type frame_type,
uint32_t stream_id, enum http_frame_header_flags first_flags)
{
memset(hfc, 0, sizeof(*hfc));
hfc->hfc_fw = fw;
hfc->hfc_frame_type = frame_type;
hfc->hfc_stream_id = stream_id;
hfc->hfc_first_flags = first_flags;
hfc->hfc_max_frame_sz = max_frame_sz;
hfc->hfc_cur_sz = max_frame_sz;
}
static void
hfc_save_ptr (struct header_framer_ctx *hfc)
{
hfc->hfc_header_ptr.frab = TAILQ_LAST(&hfc->hfc_fw->fw_frabs, frame_buf_head);
hfc->hfc_header_ptr.off = hfc->hfc_header_ptr.frab->frab_size;
}
static void
hfc_terminate_frame (struct header_framer_ctx *hfc,
enum http_frame_header_flags flags)
{
union {
struct http_frame_header fh;
unsigned char buf[ sizeof(struct http_frame_header) ];
} u;
uint32_t stream_id;
struct frame_buf *frab;
/* Construct the frame */
u.fh.hfh_length[0] = hfc->hfc_cur_sz >> 16;
u.fh.hfh_length[1] = hfc->hfc_cur_sz >> 8;
u.fh.hfh_length[2] = hfc->hfc_cur_sz;
u.fh.hfh_flags = flags;
if (1 == hfc->hfc_n_frames)
{
u.fh.hfh_type = hfc->hfc_frame_type;
u.fh.hfh_flags |= hfc->hfc_first_flags;
}
else
u.fh.hfh_type = HTTP_FRAME_CONTINUATION;
stream_id = htonl(hfc->hfc_stream_id);
memcpy(u.fh.hfh_stream_id, &stream_id, sizeof(stream_id));
if (hfc->hfc_header_ptr.off >= sizeof(u.fh))
{ /* Write in a single chunk */
assert(0 == memcmp("123456789", hfc->hfc_header_ptr.frab->frab_buf +
hfc->hfc_header_ptr.off - sizeof(u.buf), sizeof(u.buf)));
memcpy(hfc->hfc_header_ptr.frab->frab_buf + hfc->hfc_header_ptr.off -
sizeof(u.buf), u.buf, sizeof(u.buf));
}
else
{ /* Write across frab boundary */
memcpy(hfc->hfc_header_ptr.frab->frab_buf,
u.buf + sizeof(u.buf) - hfc->hfc_header_ptr.off,
hfc->hfc_header_ptr.off);
frab = TAILQ_PREV(hfc->hfc_header_ptr.frab, frame_buf_head, frab_next);
memcpy(frab->frab_buf + frab->frab_size - sizeof(u.buf) +
hfc->hfc_header_ptr.off, u.buf,
sizeof(u.buf) - hfc->hfc_header_ptr.off);
}
}
static int
hfc_write (struct header_framer_ctx *hfc, const void *buf, size_t sz)
{
const unsigned char *p = buf;
unsigned avail;
int s;
while (sz > 0)
{
if (hfc->hfc_max_frame_sz == hfc->hfc_cur_sz)
{
if (hfc->hfc_n_frames > 0)
hfc_terminate_frame(hfc, 0);
s = fw_write_to_frab(hfc->hfc_fw, "123456789",
sizeof(struct http_frame_header));
if (s < 0)
return s;
++hfc->hfc_n_frames;
hfc_save_ptr(hfc);
hfc->hfc_cur_sz = 0;
}
avail = hfc->hfc_max_frame_sz - hfc->hfc_cur_sz;
if (sz < avail)
avail = sz;
if (avail)
{
s = fw_write_to_frab(hfc->hfc_fw, p, avail);
if (s < 0)
return s;
hfc->hfc_cur_sz += avail;
sz -= avail;
p += avail;
}
}
return 0;
}
static unsigned
count_uppercase (const unsigned char *buf, size_t sz)
{
static const unsigned char uppercase[0x100] = {
['A'] = 1, ['B'] = 1, ['C'] = 1, ['D'] = 1, ['E'] = 1, ['F'] = 1,
['G'] = 1, ['H'] = 1, ['I'] = 1, ['J'] = 1, ['K'] = 1, ['L'] = 1,
['M'] = 1, ['N'] = 1, ['O'] = 1, ['P'] = 1, ['Q'] = 1, ['R'] = 1,
['S'] = 1, ['T'] = 1, ['U'] = 1, ['V'] = 1, ['W'] = 1, ['X'] = 1,
['Y'] = 1, ['Z'] = 1,
};
unsigned n_uppercase, i;
n_uppercase = 0;
for (i = 0; i < sz; ++i)
n_uppercase += uppercase[ buf[i] ];
return n_uppercase;
}
static uint32_t
calc_headers_size (const struct lsquic_http_headers *headers)
{
int i;
uint32_t size = 0;
for (i = 0; i < headers->count; ++i)
size += 32 + headers->headers[i].name.iov_len +
headers->headers[i].value.iov_len;
return size;
}
static int
check_headers_size (const struct lsquic_frame_writer *fw,
const struct lsquic_http_headers *headers,
const struct lsquic_http_headers *extra_headers)
{
uint32_t headers_sz;
headers_sz = calc_headers_size(headers);
if (extra_headers)
headers_sz += calc_headers_size(extra_headers);
if (headers_sz > fw->fw_max_header_list_sz)
{
LSQ_INFO("Headers size %u is larger than max allowed (%u)",
headers_sz, fw->fw_max_header_list_sz);
errno = EMSGSIZE;
return -1;
}
return 0;
}
static int
check_headers_case (const struct lsquic_frame_writer *fw,
const struct lsquic_http_headers *headers)
{
unsigned n_uppercase;
int i;
n_uppercase = 0;
for (i = 0; i < headers->count; ++i)
n_uppercase += count_uppercase(headers->headers[i].name.iov_base,
headers->headers[i].name.iov_len);
if (n_uppercase)
{
LSQ_INFO("Uppercase letters in header names");
errno = EINVAL;
return -1;
}
return 0;
}
static int
write_headers (struct lsquic_frame_writer *fw,
const struct lsquic_http_headers *headers,
struct header_framer_ctx *hfc, unsigned char *buf4k)
{
unsigned char *end;
int i, s;
for (i = 0; i < headers->count; ++i)
{
end = lsquic_henc_encode(fw->fw_henc, buf4k, buf4k + 0x1000,
headers->headers[i].name.iov_base, headers->headers[i].name.iov_len,
headers->headers[i].value.iov_base, headers->headers[i].value.iov_len, 0);
if (!(end > buf4k))
{
LSQ_WARN("error encoding header");
errno = EBADMSG;
return -1;
}
s = hfc_write(hfc, buf4k, end - buf4k);
if (s < 0)
return s;
}
return 0;
}
int
lsquic_frame_writer_write_headers (struct lsquic_frame_writer *fw,
uint32_t stream_id,
const struct lsquic_http_headers *headers,
int eos, unsigned weight)
{
struct header_framer_ctx hfc;
int s;
struct http_prio_frame prio_frame;
enum http_frame_header_flags flags;
unsigned char *buf;
/* Internal function: weight must be valid here */
assert(weight >= 1 && weight <= 256);
if (fw->fw_max_header_list_sz && 0 != check_headers_size(fw, headers, NULL))
return -1;
if (0 != check_headers_case(fw, headers))
return -1;
if (eos)
flags = HFHF_END_STREAM;
else
flags = 0;
if (!(fw->fw_flags & FW_SERVER))
flags |= HFHF_PRIORITY;
hfc_init(&hfc, fw, fw->fw_max_frame_sz, HTTP_FRAME_HEADERS, stream_id,
flags);
if (!(fw->fw_flags & FW_SERVER))
{
memset(&prio_frame.hpf_stream_id, 0, sizeof(prio_frame.hpf_stream_id));
prio_frame.hpf_weight = weight - 1;
s = hfc_write(&hfc, &prio_frame, sizeof(struct http_prio_frame));
if (s < 0)
return s;
}
buf = lsquic_mm_get_4k(fw->fw_mm);
if (!buf)
return -1;
s = write_headers(fw, headers, &hfc, buf);
lsquic_mm_put_4k(fw->fw_mm, buf);
if (0 == s)
{
EV_LOG_GENERATED_HTTP_HEADERS(LSQUIC_LOG_CONN_ID, stream_id,
fw->fw_flags & FW_SERVER, &prio_frame, headers);
hfc_terminate_frame(&hfc, HFHF_END_HEADERS);
return lsquic_frame_writer_flush(fw);
}
else
return s;
}
int
lsquic_frame_writer_write_promise (struct lsquic_frame_writer *fw,
uint32_t stream_id, uint32_t promised_stream_id,
const struct iovec *path, const struct iovec *host,
const struct lsquic_http_headers *extra_headers)
{
struct header_framer_ctx hfc;
struct http_push_promise_frame push_frame;
lsquic_http_header_t mpas_headers[4];
struct lsquic_http_headers mpas = { /* method, path, authority, scheme */
.headers = mpas_headers,
.count = 4,
};
unsigned char *buf;
int s;
mpas_headers[0].name. iov_base = ":method";
mpas_headers[0].name. iov_len = 7;
mpas_headers[0].value.iov_base = "GET";
mpas_headers[0].value.iov_len = 3;
mpas_headers[1].name .iov_base = ":path";
mpas_headers[1].name .iov_len = 5;
mpas_headers[1].value = *path;
mpas_headers[2].name .iov_base = ":authority";
mpas_headers[2].name .iov_len = 10;
mpas_headers[2].value = *host;
mpas_headers[3].name. iov_base = ":scheme";
mpas_headers[3].name. iov_len = 7;
mpas_headers[3].value.iov_base = "https";
mpas_headers[3].value.iov_len = 5;
if (fw->fw_max_header_list_sz &&
0 != check_headers_size(fw, &mpas, extra_headers))
return -1;
if (extra_headers && 0 != check_headers_case(fw, extra_headers))
return -1;
hfc_init(&hfc, fw, fw->fw_max_frame_sz, HTTP_FRAME_PUSH_PROMISE,
stream_id, 0);
promised_stream_id = htonl(promised_stream_id);
memcpy(push_frame.hppf_promised_id, &promised_stream_id, 4);
s = hfc_write(&hfc, &push_frame, sizeof(struct http_push_promise_frame));
if (s < 0)
return s;
buf = lsquic_mm_get_4k(fw->fw_mm);
if (!buf)
return -1;
s = write_headers(fw, &mpas, &hfc, buf);
if (s != 0)
{
lsquic_mm_put_4k(fw->fw_mm, buf);
return -1;
}
if (extra_headers)
s = write_headers(fw, extra_headers, &hfc, buf);
lsquic_mm_put_4k(fw->fw_mm, buf);
if (0 == s)
{
EV_LOG_GENERATED_HTTP_PUSH_PROMISE(LSQUIC_LOG_CONN_ID, stream_id,
htonl(promised_stream_id), &mpas, extra_headers);
hfc_terminate_frame(&hfc, HFHF_END_HEADERS);
return lsquic_frame_writer_flush(fw);
}
else
return -1;
}
void
lsquic_frame_writer_max_header_list_size (struct lsquic_frame_writer *fw,
uint32_t max_size)
{
LSQ_DEBUG("set max_header_list_sz to %u", max_size);
fw->fw_max_header_list_sz = max_size;
}
static int
write_settings (struct lsquic_frame_writer *fw,
const struct lsquic_http2_setting *settings, unsigned n_settings)
{
struct http_frame_header fh;
unsigned payload_length;
uint32_t val;
uint16_t id;
int s;
payload_length = n_settings * 6;
memset(&fh, 0, sizeof(fh));
fh.hfh_type = HTTP_FRAME_SETTINGS;
fh.hfh_length[0] = payload_length >> 16;
fh.hfh_length[1] = payload_length >> 8;
fh.hfh_length[2] = payload_length;
s = fw_write_to_frab(fw, &fh, sizeof(fh));
if (s != 0)
return s;
do
{
id = htons(settings->id);
val = htonl(settings->value);
if (0 != (s = fw_write_to_frab(fw, &id, sizeof(id))) ||
0 != (s = fw_write_to_frab(fw, &val, sizeof(val))))
return s;
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "wrote HTTP SETTINGS frame: "
"%s=%"PRIu32, lsquic_http_setting_id2str(settings->id),
settings->value);
++settings;
}
while (--n_settings);
return 0;
}
int
lsquic_frame_writer_write_settings (struct lsquic_frame_writer *fw,
const struct lsquic_http2_setting *settings, unsigned n_settings)
{
unsigned settings_per_frame;
unsigned n;
if (0 == n_settings)
{
errno = EINVAL;
return -1;
}
settings_per_frame = fw->fw_max_frame_sz / SETTINGS_FRAME_SZ;
n = 0;
do {
if (settings_per_frame > n_settings - n)
settings_per_frame = n_settings - n;
if (0 != write_settings(fw, &settings[n], settings_per_frame))
return -1;
n += settings_per_frame;
} while (n < n_settings);
return lsquic_frame_writer_flush(fw);
}
int
lsquic_frame_writer_write_priority (struct lsquic_frame_writer *fw,
uint32_t stream_id, int exclusive, uint32_t stream_dep_id,
unsigned weight)
{
unsigned char buf[ sizeof(struct http_frame_header) +
sizeof(struct http_prio_frame) ];
struct http_frame_header *fh = (void *) &buf[0];
struct http_prio_frame *prio_frame = (void *) &buf[sizeof(*fh)];
int s;
if (stream_dep_id & (1UL << 31))
{
LSQ_WARN("stream ID too high (%u): cannot write PRIORITY frame",
stream_dep_id);
return -1;
}
if (weight < 1 || weight > 256)
return -1;
memset(fh, 0, sizeof(*fh));
fh->hfh_type = HTTP_FRAME_PRIORITY;
fh->hfh_length[2] = sizeof(struct http_prio_frame);
stream_id = htonl(stream_id);
memcpy(fh->hfh_stream_id, &stream_id, 4);
stream_dep_id |= !!exclusive << 31;
stream_id = htonl(stream_dep_id);
memcpy(prio_frame->hpf_stream_id, &stream_id, 4);
prio_frame->hpf_weight = weight - 1;
s = fw_write_to_frab(fw, buf, sizeof(buf));
if (s != 0)
return s;
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "wrote HTTP PRIORITY frame: "
"stream %"PRIu32"; weight: %u; exclusive: %d",
htonl(stream_id), weight, !!exclusive);
return lsquic_frame_writer_flush(fw);
}

View File

@ -0,0 +1,63 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_frame_writer.h -- write frames to HEADERS stream.
*/
#ifndef LSQUIC_FRAME_WRITER_H
#define LSQUIC_FRAME_WRITER_H 1
#include <stddef.h>
#include <stdint.h>
struct iovec;
struct lsquic_henc;
struct lsquic_mm;
struct lsquic_frame_writer;
struct lsquic_stream;
struct lsquic_http_headers;
struct lsquic_http2_setting;
typedef ssize_t (*fw_write_f)(struct lsquic_stream *, const void *, size_t);
typedef size_t (*fw_wavail_f)(const struct lsquic_stream *stream);
typedef int (*fw_flush_f)(struct lsquic_stream *);
struct lsquic_frame_writer *
lsquic_frame_writer_new (struct lsquic_mm *, struct lsquic_stream *,
unsigned max_frame_sz, struct lsquic_henc *,
fw_write_f, fw_wavail_f, fw_flush_f, int is_server);
void
lsquic_frame_writer_destroy (struct lsquic_frame_writer *);
int
lsquic_frame_writer_have_leftovers (const struct lsquic_frame_writer *);
int
lsquic_frame_writer_flush (struct lsquic_frame_writer *);
int
lsquic_frame_writer_write_headers (struct lsquic_frame_writer *,
uint32_t stream_id,
const struct lsquic_http_headers *,
int eos, unsigned weight);
int
lsquic_frame_writer_write_settings (struct lsquic_frame_writer *,
const struct lsquic_http2_setting *, unsigned n_settings);
int
lsquic_frame_writer_write_priority (struct lsquic_frame_writer *,
uint32_t stream_id, int exclusive, uint32_t stream_dep_id,
unsigned priority);
int
lsquic_frame_writer_write_promise (struct lsquic_frame_writer *,
uint32_t stream_id, uint32_t promised_stream_id,
const struct iovec *path, const struct iovec *host,
const struct lsquic_http_headers *headers);
void
lsquic_frame_writer_max_header_list_size (struct lsquic_frame_writer *,
uint32_t max_size);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,19 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef LSQUIC_FULL_CONN_H
#define LSQUIC_FULL_CONN_H
struct lsquic_conn;
struct lsquic_stream_if;
struct lsquic_engine_public;
struct lsquic_conn *
full_conn_client_new (struct lsquic_engine_public *,
const struct lsquic_stream_if *,
void *stream_if_ctx,
unsigned flags /* Only FC_SERVER and FC_HTTP */,
const char *hostname, unsigned short max_packet_size);
void
full_conn_close_internal (lsquic_conn_t *, int is_user);
#endif

View File

@ -0,0 +1,22 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* Global state
*/
#include "lsquic_types.h"
#include "lsquic.h"
#include "lsquic_handshake.h"
int
lsquic_global_init (int flags)
{
return handshake_init(flags);
}
void
lsquic_global_cleanup (void)
{
handshake_cleanup();
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,254 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef LSQUIC_HANDSHAKE_H
#define LSQUIC_HANDSHAKE_H
#include <stdint.h>
#include <openssl/base.h>
#include <openssl/aead.h>
#include <time.h>
#include "lsquic_str.h"
struct lsquic_engine_public;
struct sockaddr;
#include "lsquic_qtags.h"
#define STK_LENGTH 60
#define SNO_LENGTH 56
#define SCID_LENGTH 16
#define DNONC_LENGTH 32
#define aes128_key_len 16
#define aes128_iv_len 4
/* client side, certs and hashs
*/
typedef struct cert_hash_item_st
{
struct lsquic_str* domain; /*with port, such as "xyz.com:8088" as the key */
struct lsquic_str* crts;
struct lsquic_str* hashs;
int count;
} cert_hash_item_t;
enum handshake_error /* TODO: rename this enum */
{
DATA_NOT_ENOUGH = -2,
DATA_FORMAT_ERROR = -1,
HS_ERROR = -1,
DATA_NO_ERROR = 0,
HS_SHLO = 0,
HS_1RTT = 1,
HS_2RTT = 2,
};
enum handshake_state
{
HSK_CHLO_REJ = 0,
HSK_SHLO,
HSK_COMPLETED,
N_HSK_STATES
};
typedef struct tag_value_st
{
uint32_t tag;
const char * value;
int len;
} tag_value_t;
typedef struct hs_ctx_st
{
enum {
HSET_TCID = (1 << 0), /* tcid is set */
HSET_SMHL = (1 << 1), /* smhl is set */
HSET_SCID = (1 << 2),
} set;
enum {
HOPT_NSTP = (1 << 0), /* NSTP option present in COPT */
HOPT_SREJ = (1 << 1), /* SREJ option present in COPT */
} opts;
uint32_t pdmd;
uint32_t aead;
uint32_t kexs;
uint32_t mids;
uint32_t scls;
uint32_t cfcw;
uint32_t sfcw;
uint32_t srbf;
uint32_t icsl;
uint32_t irtt;
uint64_t rcid;
uint32_t tcid;
uint32_t smhl;
uint64_t ctim; /* any usage? */
uint64_t sttl;
unsigned char scid[SCID_LENGTH];
//unsigned char chlo_hash[32]; //SHA256 HASH of CHLO
unsigned char nonc[DNONC_LENGTH]; /* 4 tm, 8 orbit ---> REJ, 20 rand */
unsigned char pubs[32];
uint32_t rrej;
struct lsquic_str ccs;
struct lsquic_str sni; /* 0 rtt */
struct lsquic_str ccrt;
struct lsquic_str stk;
struct lsquic_str sno;
struct lsquic_str prof;
struct lsquic_str csct;
struct lsquic_str crt; /* compressed certs buffer */
} hs_ctx_t;
/* client side need to store 0rtt info per STK */
typedef struct lsquic_session_cache_info_st
{
unsigned char sscid[SCID_LENGTH];
unsigned char spubs[32]; /* server pub key for next time 0rtt */
uint32_t ver; /* one VERSION */
uint32_t aead;
uint32_t kexs;
uint32_t pdmd;
uint64_t orbt;
uint64_t expy;
int scfg_flag; /* 0, no-init, 1, no parse, 2, parsed */
struct lsquic_str sstk;
struct lsquic_str scfg;
struct lsquic_str sni_key; /* This is only used as key */
} lsquic_session_cache_info_t;
#ifndef LSQUIC_KEEP_ENC_SESS_HISTORY
# ifndef NDEBUG
# define LSQUIC_KEEP_ENC_SESS_HISTORY 1
# else
# define LSQUIC_KEEP_ENC_SESS_HISTORY 0
# endif
#endif
#if LSQUIC_KEEP_ENC_SESS_HISTORY
#define ESHIST_BITS 7
#define ESHIST_MASK ((1 << ESHIST_BITS) - 1)
#define ESHIST_STR_SIZE ((1 << ESHIST_BITS) + 1)
typedef unsigned char eshist_idx_t;
enum enc_sess_history_event
{
ESHE_EMPTY = '\0',
ESHE_SET_SNI = 'I',
ESHE_SET_SNO = 'O',
ESHE_SET_STK = 'K',
ESHE_SET_SCID = 'D',
ESHE_SET_PROF = 'P',
};
#endif
typedef struct lsquic_enc_session
{
enum handshake_state hsk_state;
uint8_t have_key; /* 0, no 1, I, 2, D, 3, F */
uint8_t peer_have_final_key;
uint8_t server_start_use_final_key;
lsquic_cid_t cid;
unsigned char priv_key[32];
EVP_AEAD_CTX *enc_ctx_i;
EVP_AEAD_CTX *dec_ctx_i;
/* Have to save the initial key for diversification need */
unsigned char enc_key_i[aes128_key_len];
unsigned char dec_key_i[aes128_key_len];
unsigned char enc_key_nonce_i[aes128_iv_len];
unsigned char dec_key_nonce_i[aes128_iv_len];
EVP_AEAD_CTX *enc_ctx_f;
EVP_AEAD_CTX *dec_ctx_f;
unsigned char enc_key_nonce_f[aes128_iv_len];
unsigned char dec_key_nonce_f[aes128_iv_len];
hs_ctx_t hs_ctx;
lsquic_session_cache_info_t *info;
SSL_CTX * ssl_ctx;
const struct lsquic_engine_public *enpub;
struct lsquic_str * cert_ptr; /* pointer to the leaf cert of the server, not real copy */
struct lsquic_str chlo; /* real copy of CHLO message */
struct lsquic_str sstk;
struct lsquic_str ssno;
#if LSQUIC_KEEP_ENC_SESS_HISTORY
eshist_idx_t es_hist_idx;
unsigned char es_hist_buf[1 << ESHIST_BITS];
#endif
} lsquic_enc_session_t;
#if LSQUIC_KEEP_ENC_SESS_HISTORY
void
lsquic_get_enc_hist (const lsquic_enc_session_t *, char buf[ESHIST_STR_SIZE]);
#endif
int handshake_init(int flags);
void handshake_cleanup();
lsquic_enc_session_t *
new_enc_session_c(const char *domain, lsquic_cid_t cid,
const struct lsquic_engine_public *);
void free_enc_session(lsquic_enc_session_t *enc_session);
void free_info(lsquic_session_cache_info_t *info);
lsquic_cid_t generate_cid(void);
/* save to hash table */
lsquic_session_cache_info_t *retrieve_session_info_entry(const char *key);
void remove_expire_session_info_entry();
void remove_session_info_entry(struct lsquic_str *key);
cert_hash_item_t *make_cert_hash_item(struct lsquic_str *domain, struct lsquic_str **certs, int count);
void c_free_cert_hash_item(cert_hash_item_t *item);
cert_hash_item_t* c_find_certs(struct lsquic_str *domain);
int c_insert_certs(cert_hash_item_t *item);
/* -1 error, 0, OK, response in `buf' */
int gen_chlo(lsquic_enc_session_t *enc_session, enum lsquic_version,
uint8_t *buf, size_t *len);
int handle_chlo_reply(lsquic_enc_session_t *enc_session, const uint8_t *data,
int len);
int is_hs_done(lsquic_enc_session_t *enc_session);
/**
* The belows are global functions
*/
int lsquic_enc(lsquic_enc_session_t *enc_session, enum lsquic_version,
uint8_t path_id, uint64_t pack_num,
const unsigned char *header, size_t header_len,
const unsigned char *data, size_t data_len,
unsigned char *buf_out, size_t max_out_len, size_t *out_len,
int is_hello);
int lsquic_dec(lsquic_enc_session_t *enc_session, enum lsquic_version,
uint8_t path_id, uint64_t pack_num,
unsigned char *buf, size_t *header_len, size_t data_len,
unsigned char *diversification_nonce,
unsigned char *buf_out, size_t max_out_len, size_t *out_len);
int
get_peer_setting (const lsquic_enc_session_t *, uint32_t tag, uint32_t *val);
int
get_peer_option (const lsquic_enc_session_t *enc_session, uint32_t tag);
#ifdef NDEBUG
#define lsquic_enc_session_have_key_gt_one(e) ((e) && (e)->have_key > 1)
#else
int
lsquic_enc_session_have_key_gt_one (const lsquic_enc_session_t *enc_session);
#endif
#endif

228
src/liblsquic/lsquic_hash.c Normal file
View File

@ -0,0 +1,228 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_hash.c
*/
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include "lsquic_malo.h"
#include "lsquic_hash.h"
#include "lsquic_xxhash.h"
struct lsquic_hash_elem
{
TAILQ_ENTRY(lsquic_hash_elem)
qhe_next_bucket,
qhe_next_all;
const void *qhe_key_data;
unsigned qhe_key_len;
void *qhe_value;
unsigned qhe_hash_val;
};
TAILQ_HEAD(hels_head, lsquic_hash_elem);
#define N_BUCKETS(n_bits) (1U << (n_bits))
#define BUCKNO(n_bits, hash) ((hash) & (N_BUCKETS(n_bits) - 1))
struct lsquic_hash
{
struct hels_head *qh_buckets,
qh_all;
struct malo *qh_malo_els;
struct lsquic_hash_elem *qh_iter_next;
unsigned qh_count;
unsigned qh_nbits;
};
struct lsquic_hash *
lsquic_hash_create (void)
{
struct hels_head *buckets;
struct lsquic_hash *hash;
struct malo *malo;
unsigned nbits = 2;
unsigned i;
buckets = malloc(sizeof(buckets[0]) * N_BUCKETS(nbits));
if (!buckets)
return NULL;
hash = malloc(sizeof(*hash));
if (!hash)
{
free(buckets);
return NULL;
}
malo = lsquic_malo_create(sizeof(struct lsquic_hash_elem));
if (!malo)
{
free(hash);
free(buckets);
return NULL;
}
for (i = 0; i < N_BUCKETS(nbits); ++i)
TAILQ_INIT(&buckets[i]);
TAILQ_INIT(&hash->qh_all);
hash->qh_buckets = buckets;
hash->qh_nbits = nbits;
hash->qh_malo_els = malo;
hash->qh_iter_next = NULL;
hash->qh_count = 0;
return hash;
}
void
lsquic_hash_destroy (struct lsquic_hash *hash)
{
lsquic_malo_destroy(hash->qh_malo_els);
free(hash->qh_buckets);
free(hash);
}
static int
lsquic_hash_grow (struct lsquic_hash *hash)
{
struct hels_head *new_buckets, *new[2];
struct lsquic_hash_elem *el;
unsigned n, old_nbits;
int idx;
old_nbits = hash->qh_nbits;
new_buckets = malloc(sizeof(hash->qh_buckets[0])
* N_BUCKETS(old_nbits + 1));
if (!new_buckets)
return -1;
for (n = 0; n < N_BUCKETS(old_nbits); ++n)
{
new[0] = &new_buckets[n];
new[1] = &new_buckets[n + N_BUCKETS(old_nbits)];
TAILQ_INIT(new[0]);
TAILQ_INIT(new[1]);
while ((el = TAILQ_FIRST(&hash->qh_buckets[n])))
{
TAILQ_REMOVE(&hash->qh_buckets[n], el, qhe_next_bucket);
idx = (BUCKNO(old_nbits + 1, el->qhe_hash_val) >> old_nbits) & 1;
TAILQ_INSERT_TAIL(new[idx], el, qhe_next_bucket);
}
}
free(hash->qh_buckets);
hash->qh_nbits = old_nbits + 1;
hash->qh_buckets = new_buckets;
return 0;
}
struct lsquic_hash_elem *
lsquic_hash_insert (struct lsquic_hash *hash, const void *key,
unsigned key_sz, void *data)
{
unsigned buckno, hash_val;
struct lsquic_hash_elem *el;
el = lsquic_malo_get(hash->qh_malo_els);
if (!el)
return NULL;
if (hash->qh_count >= N_BUCKETS(hash->qh_nbits) / 2 &&
0 != lsquic_hash_grow(hash))
{
lsquic_malo_put(el);
return NULL;
}
hash_val = XXH64(key, key_sz, (uintptr_t) hash);
buckno = BUCKNO(hash->qh_nbits, hash_val);
TAILQ_INSERT_TAIL(&hash->qh_all, el, qhe_next_all);
TAILQ_INSERT_TAIL(&hash->qh_buckets[buckno], el, qhe_next_bucket);
el->qhe_key_data = key;
el->qhe_key_len = key_sz;
el->qhe_value = data;
el->qhe_hash_val = hash_val;
++hash->qh_count;
return el;
}
struct lsquic_hash_elem *
lsquic_hash_find (struct lsquic_hash *hash, const void *key, unsigned key_sz)
{
unsigned buckno, hash_val;
struct lsquic_hash_elem *el;
hash_val = XXH64(key, key_sz, (uintptr_t) hash);
buckno = BUCKNO(hash->qh_nbits, hash_val);
TAILQ_FOREACH(el, &hash->qh_buckets[buckno], qhe_next_bucket)
if (hash_val == el->qhe_hash_val &&
key_sz == el->qhe_key_len &&
0 == memcmp(key, el->qhe_key_data, key_sz))
{
return el;
}
return NULL;
}
void *
lsquic_hashelem_getdata (const struct lsquic_hash_elem *el)
{
return el->qhe_value;
}
void
lsquic_hash_erase (struct lsquic_hash *hash, struct lsquic_hash_elem *el)
{
unsigned buckno;
buckno = BUCKNO(hash->qh_nbits, el->qhe_hash_val);
TAILQ_REMOVE(&hash->qh_buckets[buckno], el, qhe_next_bucket);
TAILQ_REMOVE(&hash->qh_all, el, qhe_next_all);
lsquic_malo_put(el);
--hash->qh_count;
}
void
lsquic_hash_reset_iter (struct lsquic_hash *hash)
{
hash->qh_iter_next = TAILQ_FIRST(&hash->qh_all);
}
struct lsquic_hash_elem *
lsquic_hash_first (struct lsquic_hash *hash)
{
lsquic_hash_reset_iter(hash);
return lsquic_hash_next(hash);
}
struct lsquic_hash_elem *
lsquic_hash_next (struct lsquic_hash *hash)
{
struct lsquic_hash_elem *el;
el = hash->qh_iter_next;
if (el)
hash->qh_iter_next = TAILQ_NEXT(el, qhe_next_all);
return el;
}
unsigned
lsquic_hash_count (struct lsquic_hash *hash)
{
return hash->qh_count;
}

View File

@ -0,0 +1,43 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_hash.c -- A generic hash
*/
#ifndef LSQUIC_HASH_H
#define LSQUIC_HASH_H
struct lsquic_hash;
struct lsquic_hash_elem;
struct lsquic_hash *
lsquic_hash_create (void);
void
lsquic_hash_destroy (struct lsquic_hash *);
struct lsquic_hash_elem *
lsquic_hash_insert (struct lsquic_hash *, const void *key, unsigned key_sz,
void *data);
struct lsquic_hash_elem *
lsquic_hash_find (struct lsquic_hash *, const void *key, unsigned key_sz);
void *
lsquic_hashelem_getdata (const struct lsquic_hash_elem *);
void
lsquic_hash_erase (struct lsquic_hash *, struct lsquic_hash_elem *);
void
lsquic_hash_reset_iter (struct lsquic_hash *);
struct lsquic_hash_elem *
lsquic_hash_first (struct lsquic_hash *);
struct lsquic_hash_elem *
lsquic_hash_next (struct lsquic_hash *);
unsigned
lsquic_hash_count (struct lsquic_hash *);
#endif

View File

@ -0,0 +1,388 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* HEADERS stream logic
*/
#include <assert.h>
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include "lsquic_types.h"
#include "lsquic_frame_common.h"
#include "lsquic_frame_reader.h"
#include "lsquic_frame_writer.h"
#include "lsquic_arr.h"
#include "lsquic_hpack_enc.h"
#include "lsquic_hpack_dec.h"
#include "lsquic.h"
#include "lsquic_headers_stream.h"
#define MAX_HEADERS_SIZE (64 * 1024)
#define MAX_HEADER_TABLE_SIZE (512 * 1024)
#define LSQUIC_LOGGER_MODULE LSQLM_HEADERS
#define LSQUIC_LOG_CONN_ID lsquic_conn_id(lsquic_stream_conn(hs->hs_stream))
#include "lsquic_logger.h"
static const struct frame_reader_callbacks frame_callbacks;
struct headers_stream
{
struct lsquic_stream *hs_stream;
struct lsquic_frame_reader *hs_fr;
struct lsquic_frame_writer *hs_fw;
const struct headers_stream_callbacks
*hs_callbacks;
const struct lsquic_engine_settings
*hs_settings;
void *hs_cb_ctx;
struct lsquic_mm *hs_mm;
struct lsquic_henc hs_henc;
struct lsquic_hdec hs_hdec;
enum {
HS_IS_SERVER = (1 << 0),
HS_HENC_INITED = (1 << 1),
} hs_flags;
};
int
lsquic_headers_stream_send_settings (struct headers_stream *hs,
const struct lsquic_http2_setting *settings, unsigned n_settings)
{
if (0 == lsquic_frame_writer_write_settings(hs->hs_fw, settings,
n_settings))
{
lsquic_stream_wantwrite(hs->hs_stream,
lsquic_frame_writer_have_leftovers(hs->hs_fw));
return 0;
}
else
{
LSQ_WARN("Could not write settings to stream: %s",
strerror(errno));
return -1;
}
}
static lsquic_stream_ctx_t *
headers_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream)
{
struct headers_stream *hs = stream_if_ctx;
lsquic_hdec_init(&hs->hs_hdec);
if (0 != lsquic_henc_init(&hs->hs_henc))
{
LSQ_WARN("could not initialize HPACK encoder: %s", strerror(errno));
return NULL;
}
hs->hs_flags |= HS_HENC_INITED;
hs->hs_stream = stream;
LSQ_DEBUG("stream created");
hs->hs_fr = lsquic_frame_reader_new((hs->hs_flags & HS_IS_SERVER) ? FRF_SERVER : 0,
MAX_HEADERS_SIZE, hs->hs_mm,
stream, lsquic_stream_read, &hs->hs_hdec,
&frame_callbacks, hs);
if (!hs->hs_fr)
{
LSQ_WARN("could not create frame reader: %s", strerror(errno));
hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
return NULL;
}
hs->hs_fw = lsquic_frame_writer_new(hs->hs_mm, stream, 0, &hs->hs_henc,
lsquic_stream_write, lsquic_stream_write_avail,
lsquic_stream_flush, (hs->hs_flags & HS_IS_SERVER));
if (!hs->hs_fw)
{
LSQ_WARN("could not create frame writer: %s", strerror(errno));
hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
return NULL;
}
lsquic_stream_wantread(stream, 1);
return (lsquic_stream_ctx_t *) hs;
}
static void
headers_on_read (lsquic_stream_t *stream, struct lsquic_stream_ctx *ctx)
{
struct headers_stream *hs = (struct headers_stream *) ctx;
if (0 != lsquic_frame_reader_read(hs->hs_fr))
{
LSQ_ERROR("frame reader failed");
hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
}
}
static void
headers_on_write (lsquic_stream_t *stream, struct lsquic_stream_ctx *ctx)
{
struct headers_stream *hs = (struct headers_stream *) ctx;
assert(lsquic_frame_writer_have_leftovers(hs->hs_fw));
int s = lsquic_frame_writer_flush(hs->hs_fw);
if (0 == s)
{
lsquic_stream_wantwrite(stream,
lsquic_frame_writer_have_leftovers(hs->hs_fw));
}
else
{
LSQ_WARN("Error writing to stream: %s", strerror(errno));
hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
}
}
static void
headers_on_close (lsquic_stream_t *stream, struct lsquic_stream_ctx *ctx)
{
struct headers_stream *hs = (struct headers_stream *) ctx;
LSQ_DEBUG("stream closed");
}
int
lsquic_headers_stream_send_headers (struct headers_stream *hs,
uint32_t stream_id, const struct lsquic_http_headers *headers, int eos,
unsigned weight)
{
LSQ_DEBUG("received compressed headers to send");
int s;
s = lsquic_frame_writer_write_headers(hs->hs_fw, stream_id, headers, eos,
weight);
if (0 == s)
{
lsquic_stream_wantwrite(hs->hs_stream,
lsquic_frame_writer_have_leftovers(hs->hs_fw));
}
else
LSQ_INFO("Error writing headers: %s", strerror(errno));
return s;
}
int
lsquic_headers_stream_send_priority (struct headers_stream *hs,
uint32_t stream_id, int exclusive, uint32_t dep_stream_id, unsigned weight)
{
LSQ_DEBUG("received priority to send");
int s;
if (stream_id == dep_stream_id)
{
LSQ_INFO("stream cannot depend on itself"); /* RFC 7540, Sec. 5.3.1. */
return -1;
}
s = lsquic_frame_writer_write_priority(hs->hs_fw, stream_id, exclusive,
dep_stream_id, weight);
if (0 == s)
{
lsquic_stream_wantwrite(hs->hs_stream,
lsquic_frame_writer_have_leftovers(hs->hs_fw));
}
else
LSQ_INFO("Error writing priority frame: %s", strerror(errno));
return s;
}
struct headers_stream *
lsquic_headers_stream_new (int is_server, struct lsquic_mm *mm,
const struct lsquic_engine_settings *settings,
const struct headers_stream_callbacks *callbacks,
void *cb_ctx)
{
struct headers_stream *hs = calloc(1, sizeof(*hs));
if (!hs)
return NULL;
hs->hs_callbacks = callbacks;
hs->hs_cb_ctx = cb_ctx;
if (is_server)
hs->hs_flags = HS_IS_SERVER;
else
hs->hs_flags = 0;
hs->hs_settings = settings;
hs->hs_mm = mm;
return hs;
}
void
lsquic_headers_stream_destroy (struct headers_stream *hs)
{
if (hs->hs_fr)
lsquic_frame_reader_destroy(hs->hs_fr);
if (hs->hs_fw)
lsquic_frame_writer_destroy(hs->hs_fw);
if (hs->hs_flags & HS_HENC_INITED)
lsquic_henc_cleanup(&hs->hs_henc);
lsquic_hdec_cleanup(&hs->hs_hdec);
free(hs);
}
static const struct lsquic_stream_if headers_stream_if =
{
.on_new_stream = headers_on_new_stream,
.on_read = headers_on_read,
.on_write = headers_on_write,
.on_close = headers_on_close,
};
const struct lsquic_stream_if *const lsquic_headers_stream_if =
&headers_stream_if;
static void
headers_on_incoming_headers (void *ctx, struct uncompressed_headers *uh)
{
struct headers_stream *hs = ctx;
hs->hs_callbacks->hsc_on_headers(hs->hs_cb_ctx, uh);
}
static void
headers_on_push_promise (void *ctx, struct uncompressed_headers *uh)
{
struct headers_stream *hs = ctx;
hs->hs_callbacks->hsc_on_push_promise(hs->hs_cb_ctx, uh);
}
static void
headers_on_priority (void *ctx, uint32_t stream_id, int exclusive,
uint32_t dep_stream_id, unsigned weight)
{
struct headers_stream *hs = ctx;
hs->hs_callbacks->hsc_on_priority(hs->hs_cb_ctx, stream_id, exclusive,
dep_stream_id, weight);
}
static void
headers_on_error (void *ctx, uint32_t stream_id, enum frame_reader_error err)
{
struct headers_stream *hs = ctx;
switch (err)
{
case FR_ERR_DUPLICATE_PSEH:
case FR_ERR_INCOMPL_REQ_PSEH:
case FR_ERR_UNNEC_REQ_PSEH:
case FR_ERR_INCOMPL_RESP_PSEH:
case FR_ERR_UNNEC_RESP_PSEH:
case FR_ERR_UNKNOWN_PSEH:
case FR_ERR_UPPERCASE_HEADER:
case FR_ERR_MISPLACED_PSEH:
case FR_ERR_MISSING_PSEH:
case FR_ERR_DECOMPRESS:
case FR_ERR_HEADERS_TOO_LARGE:
case FR_ERR_SELF_DEP_STREAM:
LSQ_INFO("error %u is a stream error (stream %u)", err, stream_id);
hs->hs_callbacks->hsc_on_stream_error(hs->hs_cb_ctx, stream_id);
break;
case FR_ERR_INVALID_FRAME_SIZE:
case FR_ERR_NONZERO_STREAM_ID:
case FR_ERR_UNEXPECTED_PUSH:
case FR_ERR_ZERO_STREAM_ID:
case FR_ERR_NOMEM:
case FR_ERR_EXPECTED_CONTIN:
LSQ_INFO("error %u is a connection error (stream %u)", err, stream_id);
hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
break;
}
}
static void
headers_on_settings (void *ctx, uint16_t setting_id, uint32_t setting_value)
{
struct headers_stream *hs = ctx;
switch (setting_id)
{
case SETTINGS_HEADER_TABLE_SIZE:
if (setting_value > MAX_HEADER_TABLE_SIZE)
{
LSQ_INFO("tried to update table size to %u, which is larger than "
"allowed maximum of %u bytes", setting_value,
MAX_HEADER_TABLE_SIZE);
hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
}
else
{
LSQ_INFO("update hpack table size to %u", setting_value);
lsquic_henc_set_max_capacity(&hs->hs_henc, setting_value);
}
break;
case SETTINGS_MAX_HEADER_LIST_SIZE:
LSQ_INFO("set max header list size to %u", setting_value);
lsquic_frame_writer_max_header_list_size(hs->hs_fw, setting_value);
break;
case SETTINGS_ENABLE_PUSH:
LSQ_INFO("got setting enable_push: %u", setting_value);
if (hs->hs_flags & HS_IS_SERVER)
{
if (setting_value <= 1)
hs->hs_callbacks->hsc_on_enable_push(hs->hs_cb_ctx,
setting_value);
else
{
LSQ_INFO("invalid value of enable_push");
hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
}
}
else
{
LSQ_INFO("it is an error to receive enable_push setting in "
"client mode");
hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
}
break;
case SETTINGS_MAX_CONCURRENT_STREAMS:
case SETTINGS_INITIAL_WINDOW_SIZE:
case SETTINGS_MAX_FRAME_SIZE:
/* [draft-ietf-quic-http-00], Section 3 */
LSQ_INFO("Specifying setting 0x%X is a QUIC error", setting_id);
hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
break;
default:
LSQ_INFO("Ignoring unknown setting 0x%X; value 0x%X", setting_id,
setting_value);
break;
}
}
int
lsquic_headers_stream_push_promise (struct headers_stream *hs,
uint32_t stream_id, uint32_t promised_stream_id,
const struct iovec *path, const struct iovec *host,
const struct lsquic_http_headers *headers)
{
int s;
LSQ_DEBUG("promising stream %u in response to stream %u",
promised_stream_id, stream_id);
s = lsquic_frame_writer_write_promise(hs->hs_fw, stream_id,
promised_stream_id, path, host, headers);
if (0 == s)
{
lsquic_stream_wantwrite(hs->hs_stream,
lsquic_frame_writer_have_leftovers(hs->hs_fw));
}
else
LSQ_INFO("Error writing push promise: %s", strerror(errno));
return s;
}
static const struct frame_reader_callbacks frame_callbacks = {
.frc_on_headers = headers_on_incoming_headers,
.frc_on_push_promise = headers_on_push_promise,
.frc_on_error = headers_on_error,
.frc_on_settings = headers_on_settings,
.frc_on_priority = headers_on_priority,
};

View File

@ -0,0 +1,71 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_headers_stream.h -- HEADERS stream interface
*/
#ifndef LSQUIC_HEADERS_STREAM_H
#define LSQUIC_HEADERS_STREAM_H 1
#include <stdint.h>
struct iovec;
struct lsquic_stream_if;
struct lsquic_stream;
struct lsquic_mm;
struct lsquic_http_headers;
struct lsquic_frame_reader;
struct lsquic_frame_writer;
struct uncompressed_headers;
struct lsquic_engine_settings;
struct lsquic_http2_setting;
/* Incoming frames result in new objects or events. Callbacks in this
* struct are used to dispatch them.
*/
struct headers_stream_callbacks
{
void (*hsc_on_headers)
(void *frame_cb_ctx, struct uncompressed_headers *);
void (*hsc_on_enable_push) (void *hs_cb_ctx, int enable_push);
void (*hsc_on_push_promise)
(void *frame_cb_ctx, struct uncompressed_headers *);
void (*hsc_on_priority) (void *hs_cb_ctx, uint32_t stream_id,
int exclusive, uint32_t dep_stream_id, unsigned weight);
void (*hsc_on_stream_error) (void *hs_cb_ctx, uint32_t stream_id);
void (*hsc_on_conn_error) (void *hs_cb_ctx);
};
struct headers_stream *
lsquic_headers_stream_new (int is_server, struct lsquic_mm *,
const struct lsquic_engine_settings *,
const struct headers_stream_callbacks *,
void *hs_cb_ctx);
void
lsquic_headers_stream_destroy (struct headers_stream *);
int
lsquic_headers_stream_send_headers (struct headers_stream *hs,
uint32_t stream_id,
const struct lsquic_http_headers *, int eos,
unsigned weight);
int
lsquic_headers_stream_push_promise (struct headers_stream *hs,
uint32_t stream_id, uint32_t promised_stream_id,
const struct iovec *path, const struct iovec *host,
const struct lsquic_http_headers *);
int
lsquic_headers_stream_send_priority (struct headers_stream *hs,
uint32_t stream_id, int exclusive, uint32_t dep_stream_id, unsigned weight);
int
lsquic_headers_stream_send_settings (struct headers_stream *hs,
const struct lsquic_http2_setting *, unsigned count);
extern const struct lsquic_stream_if *const lsquic_headers_stream_if;
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,55 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef LSQUIC_HPACK_COMMON_H
#define LSQUIC_HPACK_COMMON_H
#define HPACK_STATIC_TABLE_SIZE 61
#define INITIAL_DYNAMIC_TABLE_SIZE 4096
/* RFC 7541, Section 4.1:
*
* " The size of the dynamic table is the sum of the size of its entries.
* "
* " The size of an entry is the sum of its name's length in octets (as
* " defined in Section 5.2), its value's length in octets, and 32.
*/
#define DYNAMIC_ENTRY_OVERHEAD 32
/**
* @typedef hpack_hdr_tbl_t
* @brief A struct for the static table (name - value)
*/
typedef struct hpack_hdr_tbl_s
{
const char *name;
uint16_t name_len;
const char *val;
uint16_t val_len;
} hpack_hdr_tbl_t;
/**
* @typedef hpack_huff_encode_t
* @brief Huffman encode struct
*/
typedef struct hpack_huff_encode_s
{
uint32_t code;
int bits;
} hpack_huff_encode_t;
/**
* @typedef hpack_huff_decode_t
* @brief Huffman decode struct
*/
typedef struct hpack_huff_decode_s
{
uint8_t state;
uint8_t flags;
uint8_t sym;
} hpack_huff_decode_t;
extern const hpack_huff_decode_t lsquic_hpack_huff_decode_tables[256][16];
extern const hpack_huff_encode_t lsquic_hpack_huff_encode_tables[257];
extern const hpack_hdr_tbl_t lsquic_hpack_stx_tab[HPACK_STATIC_TABLE_SIZE];
#endif

View File

@ -0,0 +1,419 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_hdec.c - HPACK decoder
*/
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include "lsquic_arr.h"
#include "lsquic_hpack_common.h"
#include "lsquic_hpack_dec.h"
/* Dynamic table entry: */
struct dec_table_entry
{
uint16_t dte_name_len;
uint16_t dte_val_len;
char dte_buf[0]; /* Contains both name and value */
};
#define DTE_NAME(dte) ((dte)->dte_buf)
#define DTE_VALUE(dte) (&(dte)->dte_buf[(dte)->dte_name_len])
enum
{
HPACK_HUFFMAN_FLAG_ACCEPTED = 0x01,
HPACK_HUFFMAN_FLAG_SYM = 0x02,
HPACK_HUFFMAN_FLAG_FAIL = 0x04,
};
typedef struct hpack_huff_decode_status_s
{
uint8_t state;
uint8_t eos;
} hpack_huff_decode_status_t;
void
lsquic_hdec_init (struct lsquic_hdec *dec)
{
memset(dec, 0, sizeof(*dec));
dec->hpd_max_capacity = INITIAL_DYNAMIC_TABLE_SIZE;
dec->hpd_cur_max_capacity = INITIAL_DYNAMIC_TABLE_SIZE;
lsquic_arr_init(&dec->hpd_dyn_table);
}
void
lsquic_hdec_cleanup (struct lsquic_hdec *dec)
{
uintptr_t val;
while (lsquic_arr_count(&dec->hpd_dyn_table) > 0)
{
val = lsquic_arr_pop(&dec->hpd_dyn_table);
free((struct dec_table_entry *) val);
}
lsquic_arr_cleanup(&dec->hpd_dyn_table);
}
//https://tools.ietf.org/html/draft-ietf-httpbis-header-compression-12#section-5.1
#ifdef NDEBUG
static
#endif
int
lsquic_hdec_dec_int (const unsigned char **src, const unsigned char *src_end,
uint8_t prefix_bits, uint32_t *value)
{
uint32_t B, M;
uint8_t prefix_max = (1 << prefix_bits) - 1;
*value = (*(*src)++ & prefix_max);
if (*value < prefix_max)
return 0;
/* To optimize the loop for the normal case, the overflow is checked
* outside the loop. The decoder is limited to 28-bit integer values,
* which is far above limitations imposed by the APIs (16-bit integers).
*/
M = 0;
do
{
if ((*src) >= src_end)
return -1;
B = *(*src)++;
*value = *value + ((B & 0x7f) << M);
M += 7;
}
while (B & 0x80);
return -(M > sizeof(*value) * 8);
}
static void
hdec_drop_oldest_entry (struct lsquic_hdec *dec)
{
struct dec_table_entry *entry;
entry = (void *) lsquic_arr_shift(&dec->hpd_dyn_table);
dec->hpd_cur_capacity -= DYNAMIC_ENTRY_OVERHEAD + entry->dte_name_len
+ entry->dte_val_len;
free(entry);
}
static void
hdec_remove_overflow_entries (struct lsquic_hdec *dec)
{
while (dec->hpd_cur_capacity > dec->hpd_cur_max_capacity)
hdec_drop_oldest_entry(dec);
}
static void
hdec_update_max_capacity (struct lsquic_hdec *dec, uint32_t new_capacity)
{
dec->hpd_cur_max_capacity = new_capacity;
hdec_remove_overflow_entries(dec);
}
void
lsquic_hdec_set_max_capacity (struct lsquic_hdec *dec, unsigned max_capacity)
{
dec->hpd_max_capacity = max_capacity;
hdec_update_max_capacity(dec, max_capacity);
}
static unsigned char *
hdec_huff_dec4bits (uint8_t src_4bits, unsigned char *dst,
hpack_huff_decode_status_t *status)
{
const hpack_huff_decode_t cur_dec_code =
lsquic_hpack_huff_decode_tables[status->state][src_4bits];
if (cur_dec_code.flags & HPACK_HUFFMAN_FLAG_FAIL) {
return NULL; //failed
}
if (cur_dec_code.flags & HPACK_HUFFMAN_FLAG_SYM)
{
*dst = cur_dec_code.sym;
dst++;
}
status->state = cur_dec_code.state;
status->eos = ((cur_dec_code.flags & HPACK_HUFFMAN_FLAG_ACCEPTED) != 0);
return dst;
}
static int
hdec_huff_decode (const unsigned char *src, int src_len,
unsigned char *dst, int dst_len)
{
const unsigned char *p_src = src;
const unsigned char *src_end = src + src_len;
unsigned char *p_dst = dst;
unsigned char *dst_end = dst + dst_len;
hpack_huff_decode_status_t status = { 0, 1 };
while (p_src != src_end)
{
if (p_dst == dst_end)
return -2;
if ((p_dst = hdec_huff_dec4bits(*p_src >> 4, p_dst, &status))
== NULL)
return -1;
if (p_dst == dst_end)
return -2;
if ((p_dst = hdec_huff_dec4bits(*p_src & 0xf, p_dst, &status))
== NULL)
return -1;
++p_src;
}
if (!status.eos)
return -1;
return p_dst - dst;
}
//reutrn the length in the dst, also update the src
static int
hdec_dec_str (unsigned char *dst, size_t dst_len, const unsigned char **src,
const unsigned char *src_end)
{
if ((*src) == src_end)
return 0;
int is_huffman = (*(*src) & 0x80);
uint32_t len;
if (0 != lsquic_hdec_dec_int(src, src_end, 7, &len))
return -2; //wrong int
int ret = 0;
if ((uint32_t)(src_end - (*src)) < len) {
return -2; //wrong int
}
if (is_huffman)
{
ret = hdec_huff_decode(*src, len, dst, dst_len);
if (ret < 0)
return -3; //Wrong code
(*src) += len;
}
else
{
if (dst_len < (size_t)(src_end - (*src)))
ret = -3; //dst not enough space
else
{
memcpy(dst, (*src), len);
(*src) += len;
ret = len;
}
}
return ret;
}
/* hpd_dyn_table is a dynamic array. New entries are pushed onto it,
* while old entries are shifted from it.
*/
static struct dec_table_entry *
hdec_get_table_entry (struct lsquic_hdec *dec, uint32_t index)
{
uintptr_t val;
index -= HPACK_STATIC_TABLE_SIZE;
if (index == 0 || index > lsquic_arr_count(&dec->hpd_dyn_table))
return NULL;
index = lsquic_arr_count(&dec->hpd_dyn_table) - index;
val = lsquic_arr_get(&dec->hpd_dyn_table, index);
return (struct dec_table_entry *) val;
}
#ifdef NDEBUG
static
#endif
int
lsquic_hdec_push_entry (struct lsquic_hdec *dec, const char *name,
uint16_t name_len, const char *val, uint16_t val_len)
{
struct dec_table_entry *entry;
size_t size;
size = sizeof(*entry) + name_len + val_len;
entry = malloc(size);
if (!entry)
return -1;
if (0 != lsquic_arr_push(&dec->hpd_dyn_table, (uintptr_t) entry))
{
free(entry);
return -1;
}
dec->hpd_cur_capacity += DYNAMIC_ENTRY_OVERHEAD + name_len + val_len;
entry->dte_name_len = name_len;
entry->dte_val_len = val_len;
memcpy(DTE_NAME(entry), name, name_len);
memcpy(DTE_VALUE(entry), val, val_len);
return 0;
}
int
lsquic_hdec_decode (struct lsquic_hdec *dec,
const unsigned char **src, const unsigned char *src_end,
char *dst, char *const dst_end, uint16_t *name_len, uint16_t *val_len)
{
struct dec_table_entry *entry;
uint32_t index, new_capacity;
int indexed_type, len;
if ((*src) == src_end)
return 0;
while ((*(*src) & 0xe0) == 0x20) //001 xxxxx
{
if (0 != lsquic_hdec_dec_int(src, src_end, 5, &new_capacity))
return -1;
if (new_capacity > dec->hpd_max_capacity)
return -1;
hdec_update_max_capacity(dec, new_capacity);
if (*src == src_end)
return 0;
}
/* lsquic_hdec_dec_int() sets `index' and advances `src'. If we do not call
* it, we set `index' and advance `src' ourselves:
*/
if (*(*src) & 0x80) //1 xxxxxxx
{
if (0 != lsquic_hdec_dec_int(src, src_end, 7, &index))
return -1;
indexed_type = 3; //need to parse value
}
else if (*(*src) > 0x40) //01 xxxxxx
{
if (0 != lsquic_hdec_dec_int(src, src_end, 6, &index))
return -1;
indexed_type = 0;
}
else if (*(*src) == 0x40) //custmized //0100 0000
{
indexed_type = 0;
index = 0;
++(*src);
}
//Never indexed
else if (*(*src) == 0x10) //00010000
{
indexed_type = 2;
index = 0;
++(*src);
}
else if ((*(*src) & 0xf0) == 0x10) //0001 xxxx
{
if (0 != lsquic_hdec_dec_int(src, src_end, 4, &index))
return -1;
indexed_type = 2;
}
//without indexed
else if (*(*src) == 0x00) //0000 0000
{
indexed_type = 1;
index = 0;
++(*src);
}
else // 0000 xxxx
{
if (0 != lsquic_hdec_dec_int(src, src_end, 4, &index))
return -1;
indexed_type = 1;
}
char *const name = dst;
if (index > 0)
{
if (index <= HPACK_STATIC_TABLE_SIZE) //static table
{
if (lsquic_hpack_stx_tab[index - 1].name_len > dst_end - dst)
return -1;
*name_len = lsquic_hpack_stx_tab[index - 1].name_len;
memcpy(name, lsquic_hpack_stx_tab[index - 1].name, *name_len);
if (indexed_type == 3)
{
if (lsquic_hpack_stx_tab[index - 1].name_len +
lsquic_hpack_stx_tab[index - 1].val_len > dst_end - dst)
return -1;
*val_len = lsquic_hpack_stx_tab[index - 1].val_len;
memcpy(name + *name_len, lsquic_hpack_stx_tab[index - 1].val, *val_len);
return 1;
}
}
else
{
entry = hdec_get_table_entry(dec, index);
if (entry == NULL)
return -1;
if (entry->dte_name_len > dst_end - dst)
return -1;
*name_len = entry->dte_name_len;
memcpy(name, DTE_NAME(entry), *name_len);
if (indexed_type == 3)
{
if (entry->dte_name_len + entry->dte_val_len > dst_end - dst)
return -1;
*val_len = entry->dte_val_len;
memcpy(name + *name_len, DTE_VALUE(entry), *val_len);
return 1;
}
}
}
else
{
len = hdec_dec_str((unsigned char *)name, dst_end - dst, src, src_end);
if (len < 0)
return len; //error
if (len > UINT16_MAX)
return -2;
*name_len = len;
}
len = hdec_dec_str((unsigned char *)name + *name_len,
dst_end - dst - *name_len, src, src_end);
if (len < 0)
return len; //error
if (len > UINT16_MAX)
return -2;
*val_len = len;
if (indexed_type == 0)
{
if (0 != lsquic_hdec_push_entry(dec, name, *name_len,
name + *name_len, *val_len))
return -1; //error
}
return 1;
}

View File

@ -0,0 +1,52 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_hdec.h - HPACK decoder
*/
#ifndef LSQUIC_HPACK_DEC_H
#define LSQUIC_HPACK_DEC_H
struct lsquic_hdec
{
unsigned hpd_max_capacity; /* Maximum set by caller */
unsigned hpd_cur_max_capacity; /* Adjusted at runtime */
unsigned hpd_cur_capacity;
struct lsquic_arr hpd_dyn_table;
};
void
lsquic_hdec_init (struct lsquic_hdec *);
void
lsquic_hdec_cleanup (struct lsquic_hdec *);
/** @lsquic_hdecode
* @brief HPACK decode one name/value item
* @param[in,out] dec - A pointer to a valid HPACK API struct
* @param[in,out] src - Address of pointer to source buffer
* @param[in] src_end - A pointer to end of source buffer
* @param[out] dst - A pointer to destination buffer
* @param[out] dst_end - A pointer to end of destination buffer
* @param[out] name_len - The item name's length
* @param[out] value_len - The item value's length
* @return 1: OK, 0: end, -1: FAIL
*/
int
lsquic_hdec_decode (struct lsquic_hdec *dec,
const unsigned char **src, const unsigned char *src_end,
char *dst, char *const dst_end, uint16_t *name_len, uint16_t *val_len);
void
lsquic_hdec_set_max_capacity (struct lsquic_hdec *, unsigned);
#ifndef NDEBUG
int
lsquic_hdec_dec_int (const unsigned char **src, const unsigned char *src_end,
uint8_t prefix_bits, uint32_t *value);
int
lsquic_hdec_push_entry (struct lsquic_hdec *dec, const char *name,
uint16_t name_len, const char *val, uint16_t val_len);
#endif
#endif

View File

@ -0,0 +1,855 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_hpack_enc.c - HPACK encoder
*/
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include "lsquic_hpack_common.h"
#include "lsquic_hpack_enc.h"
#include "lsquic_xxhash.h"
struct double_enc_head
{
struct enc_head by_name;
struct enc_head by_nameval;
};
struct enc_table_entry
{
/* An entry always lives on all three lists */
STAILQ_ENTRY(enc_table_entry) ete_next_nameval,
ete_next_name,
ete_next_all;
unsigned ete_id;
unsigned ete_nameval_hash;
unsigned ete_name_hash;
uint16_t ete_name_len;
uint16_t ete_val_len;
char ete_buf[0];
};
#define ETE_NAME(ete) ((ete)->ete_buf)
#define ETE_VALUE(ete) (&(ete)->ete_buf[(ete)->ete_name_len])
#define N_BUCKETS(n_bits) (1U << (n_bits))
#define BUCKNO(n_bits, hash) ((hash) & (N_BUCKETS(n_bits) - 1))
int
lsquic_henc_init (struct lsquic_henc *enc)
{
struct double_enc_head *buckets;
unsigned nbits = 2;
unsigned i;
buckets = malloc(sizeof(buckets[0]) * N_BUCKETS(nbits));
if (!buckets)
return -1;
for (i = 0; i < N_BUCKETS(nbits); ++i)
{
STAILQ_INIT(&buckets[i].by_name);
STAILQ_INIT(&buckets[i].by_nameval);
}
memset(enc, 0, sizeof(*enc));
STAILQ_INIT(&enc->hpe_all_entries);
enc->hpe_max_capacity = INITIAL_DYNAMIC_TABLE_SIZE;
enc->hpe_buckets = buckets;
/* The initial value of the entry ID is completely arbitrary. As long as
* there are fewer than 2^32 dynamic table entries, the math to calculate
* the entry ID works. To prove to ourselves that the wraparound works
* and to have the unit tests cover it, we initialize the next ID so that
* it is just about to wrap around.
*/
enc->hpe_next_id = ~0 - 3;
enc->hpe_nbits = nbits;
enc->hpe_nelem = 0;
return 0;
}
void
lsquic_henc_cleanup (struct lsquic_henc *enc)
{
struct enc_table_entry *entry, *next;
for (entry = STAILQ_FIRST(&enc->hpe_all_entries); entry; entry = next)
{
next = STAILQ_NEXT(entry, ete_next_all);
free(entry);
}
free(enc->hpe_buckets);
}
//not find return 0, otherwise return the index
#ifdef NDEBUG
static
#endif
unsigned
lsquic_henc_get_stx_tab_id (const char *name, uint16_t name_len,
const char *val, uint16_t val_len, int *val_matched)
{
if (name_len < 3)
return 0;
*val_matched = 0;
//check value first
int i = -1;
switch (*val)
{
case 'G':
i = 1;
break;
case 'P':
i = 2;
break;
case '/':
if (val_len == 1)
i = 3;
else if (val_len == 11)
i = 4;
break;
case 'h':
if (val_len == 4)
i = 5;
else if (val_len == 5)
i = 6;
break;
case '2':
if (val_len == 3)
{
switch (*(val + 2))
{
case '0':
i = 7;
break;
case '4':
i = 8;
break;
case '6':
i = 9;
break;
default:
break;
}
}
break;
case '3':
i = 10;
break;
case '4':
if (val_len == 3)
{
switch (*(val + 2))
{
case '0':
i = 11;
break;
case '4':
i = 12;
default:
break;
}
}
break;
case '5':
i = 13;
break;
case 'g':
i = 15;
break;
default:
break;
}
if (i > 0 && lsquic_hpack_stx_tab[i].val_len == val_len
&& lsquic_hpack_stx_tab[i].name_len == name_len
&& memcmp(val, lsquic_hpack_stx_tab[i].val, val_len) == 0
&& memcmp(name, lsquic_hpack_stx_tab[i].name, name_len) == 0)
{
*val_matched = 1;
return i + 1;
}
//macth name only checking
i = -1;
switch (*name)
{
case ':':
switch (*(name + 1))
{
case 'a':
i = 0;
break;
case 'm':
i = 1;
break;
case 'p':
i = 3;
break;
case 's':
if (*(name + 2) == 'c') //:scheme
i = 5;
else
i = 7;
break;
default:
break;
}
break;
case 'a':
switch (name_len)
{
case 3:
i = 20; //age
break;
case 5:
i = 21; //allow
break;
case 6:
i = 18; //accept
break;
case 13:
if (*(name + 1) == 'u')
i = 22; //authorization
else
i = 17; //accept-ranges
break;
case 14:
i = 14; //accept-charset
break;
case 15:
if (*(name + 7) == 'l')
i = 16; //accept-language,
else
i = 15;// accept-encoding
break;
case 27:
i = 19;//access-control-allow-origin
break;
default:
break;
}
break;
case 'c':
switch (name_len)
{
case 6:
i = 31; //cookie
break;
case 12:
i = 30; //content-type
break;
case 13:
if (*(name + 1) == 'a')
i = 23; //cache-control
else
i = 29; //content-range
break;
case 14:
i = 27; //content-length
break;
case 16:
switch (*(name + 9))
{
case 'n':
i = 25 ;//content-encoding
break;
case 'a':
i = 26; //content-language
break;
case 'o':
i = 28; //content-location
default:
break;
}
break;
case 19:
i = 24; //content-disposition
break;
}
break;
case 'd':
i = 32 ;//date
break;
case 'e':
switch (name_len)
{
case 4:
i = 33; //etag
break;
case 6:
i = 34;
break;
case 7:
i = 35;
break;
default:
break;
}
break;
case 'f':
i = 36; //from
break;
case 'h':
i = 37; //host
break;
case 'i':
switch (name_len)
{
case 8:
if (*(name + 3) == 'm')
i = 38; //if-match
else
i = 41; //if-range
break;
case 13:
i = 40; //if-none-match
break;
case 17:
i = 39; //if-modified-since
break;
case 19:
i = 42; //if-unmodified-since
break;
default:
break;
}
break;
case 'l':
switch (name_len)
{
case 4:
i = 44; //link
break;
case 8:
i = 45; //location
break;
case 13:
i = 43; //last-modified
break;
default:
break;
}
break;
case 'm':
i = 46; //max-forwards
break;
case 'p':
if (name_len == 18)
i = 47; //proxy-authenticate
else
i = 48; //proxy-authorization
break;
case 'r':
if (name_len >= 5)
{
switch (*(name + 4))
{
case 'e':
if (name_len == 5)
i = 49; //range
else
i = 51; //refresh
break;
case 'r':
i = 50; //referer
break;
case 'y':
i = 52; //retry-after
break;
default:
break;
}
}
break;
case 's':
switch (name_len)
{
case 6:
i = 53; //server
break;
case 10:
i = 54; //set-cookie
break;
case 25:
i = 55; //strict-transport-security
break;
default:
break;
}
break;
case 't':
i = 56;//transfer-encoding
break;
case 'u':
i = 57; //user-agent
break;
case 'v':
if (name_len == 4)
i = 58;
else
i = 59;
break;
case 'w':
i = 60;
break;
default:
break;
}
if (i >= 0
&& lsquic_hpack_stx_tab[i].name_len == name_len
&& memcmp(name, lsquic_hpack_stx_tab[i].name, name_len) == 0)
return i + 1;
return 0;
}
/* Given a dynamic entry, return its table ID */
static unsigned
henc_calc_table_id (const struct lsquic_henc *enc,
const struct enc_table_entry *entry)
{
return HPACK_STATIC_TABLE_SIZE
+ (enc->hpe_next_id - entry->ete_id)
;
}
static unsigned
henc_find_table_id (struct lsquic_henc *enc, const char *name,
uint16_t name_len, const char *value, uint16_t value_len,
int *val_matched)
{
struct enc_table_entry *entry;
unsigned name_hash, nameval_hash, buckno, static_table_id;
XXH32_state_t hash_state;
/* First, look for a match in the static table: */
static_table_id = lsquic_henc_get_stx_tab_id(name, name_len, value,
value_len, val_matched);
if (static_table_id > 0 && *val_matched)
return static_table_id;
/* Search by name and value: */
XXH32_reset(&hash_state, (uintptr_t) enc);
XXH32_update(&hash_state, &name_len, sizeof(name_len));
XXH32_update(&hash_state, name, name_len);
name_hash = XXH32_digest(&hash_state);
XXH32_update(&hash_state, &value_len, sizeof(value_len));
XXH32_update(&hash_state, value, value_len);
nameval_hash = XXH32_digest(&hash_state);
buckno = BUCKNO(enc->hpe_nbits, nameval_hash);
STAILQ_FOREACH(entry, &enc->hpe_buckets[buckno].by_nameval, ete_next_nameval)
if (nameval_hash == entry->ete_nameval_hash &&
name_len == entry->ete_name_len &&
value_len == entry->ete_val_len &&
0 == memcmp(name, ETE_NAME(entry), name_len) &&
0 == memcmp(value, ETE_VALUE(entry), value_len))
{
*val_matched = 1;
return henc_calc_table_id(enc, entry);
}
/* Name/value match is not found, but if the caller found a matching
* static table entry, no need to continue to search:
*/
if (static_table_id > 0)
return static_table_id;
/* Search by name only: */
buckno = BUCKNO(enc->hpe_nbits, name_hash);
STAILQ_FOREACH(entry, &enc->hpe_buckets[buckno].by_name, ete_next_name)
if (name_hash == entry->ete_name_hash &&
name_len == entry->ete_name_len &&
0 == memcmp(name, ETE_NAME(entry), name_len))
{
*val_matched = 0;
return henc_calc_table_id(enc, entry);
}
return 0;
}
////https://tools.ietf.org/html/draft-ietf-httpbis-header-compression-12#section-5.1
static unsigned char *
henc_enc_int (unsigned char *dst, unsigned char *const end, uint32_t value,
uint8_t prefix_bits)
{
unsigned char *const dst_orig = dst;
/* This function assumes that at least one byte is available */
assert(dst < end);
if (value < (uint32_t)(1 << prefix_bits) - 1)
*dst++ |= value;
else
{
*dst++ |= (1 << prefix_bits) - 1;
value -= (1 << prefix_bits) - 1;
while (value >= 128)
{
if (dst < end)
{
*dst++ = (0x80 | value);
value >>= 7;
}
else
return dst_orig;
}
if (dst < end)
*dst++ = value;
else
return dst_orig;
}
return dst;
}
static int
henc_huffman_enc (const unsigned char *src, const unsigned char *const src_end,
unsigned char *dst, int dst_len)
{
const unsigned char *p_src = src;
unsigned char *p_dst = dst;
unsigned char *dst_end = p_dst + dst_len;
uint64_t bits = 0;
int bits_left = 40;
hpack_huff_encode_t cur_enc_code;
assert(dst_len > 0);
while (p_src != src_end)
{
cur_enc_code = lsquic_hpack_huff_encode_tables[(int) *p_src++];
assert(bits_left >= cur_enc_code.bits); // (possible negative shift, undefined behavior)
bits |= (uint64_t)cur_enc_code.code << (bits_left - cur_enc_code.bits);
bits_left -= cur_enc_code.bits;
while (bits_left <= 32)
{
*p_dst++ = bits >> 32;
bits <<= 8;
bits_left += 8;
if (p_dst == dst_end)
return -1; //dst does not have enough space
}
}
if (bits_left != 40)
{
assert(bits_left < 40 && bits_left > 0);
bits |= ((uint64_t)1 << bits_left) - 1;
*p_dst++ = bits >> 32;
}
return p_dst - dst;
}
#ifdef NDEBUG
static
#endif
int
lsquic_henc_enc_str (unsigned char *const dst, size_t dst_len,
const unsigned char *str, uint16_t str_len)
{
unsigned char size_buf[4];
unsigned char *p;
unsigned size_len;
int rc;
if (dst_len > 1)
/* We guess that the string size fits into a single byte -- meaning
* compressed string of size 126 and smaller -- which is the normal
* case. Thus, we immediately write compressed string to the output
* buffer. If our guess is not correct, we fix it later.
*/
rc = henc_huffman_enc(str, str + str_len, dst + 1, dst_len - 1);
else if (dst_len == 1)
/* Here, the call can only succeed if the string to encode is empty. */
rc = 0;
else
return -1;
/*
* Check if need huffman encoding or not
* Comment: (size_t)rc <= str_len = means if same length, still use Huffman
* ^
*/
if (rc > 0 && (size_t)rc <= str_len)
{
if (rc < 127)
{
*dst = 0x80 | rc;
return 1 + rc;
}
size_buf[0] = 0x80;
str_len = rc;
str = dst + 1;
}
else if (str_len <= dst_len - 1)
{
if (str_len < 127)
{
*dst = str_len;
memcpy(dst + 1, str, str_len);
return 1 + str_len;
}
size_buf[0] = 0x00;
}
else
return -1;
/* The guess of one-byte size was incorrect. Perform necessary
* adjustments.
*/
p = henc_enc_int(size_buf, size_buf + sizeof(size_buf), str_len, 7);
if (p == size_buf)
return -1;
size_len = p - size_buf;
assert(size_len > 1);
/* Check if there is enough room in the output buffer for both
* encoded size and the string.
*/
if (size_len + str_len > dst_len)
return -1;
memmove(dst + size_len, str, str_len);
memcpy(dst, size_buf, size_len);
return size_len + str_len;
}
static void
henc_drop_oldest_entry (struct lsquic_henc *enc)
{
struct enc_table_entry *entry;
unsigned buckno;
entry = STAILQ_FIRST(&enc->hpe_all_entries);
assert(entry);
STAILQ_REMOVE_HEAD(&enc->hpe_all_entries, ete_next_all);
buckno = BUCKNO(enc->hpe_nbits, entry->ete_nameval_hash);
assert(entry == STAILQ_FIRST(&enc->hpe_buckets[buckno].by_nameval));
STAILQ_REMOVE_HEAD(&enc->hpe_buckets[buckno].by_nameval, ete_next_nameval);
buckno = BUCKNO(enc->hpe_nbits, entry->ete_name_hash);
assert(entry == STAILQ_FIRST(&enc->hpe_buckets[buckno].by_name));
STAILQ_REMOVE_HEAD(&enc->hpe_buckets[buckno].by_name, ete_next_name);
enc->hpe_cur_capacity -= DYNAMIC_ENTRY_OVERHEAD + entry->ete_name_len
+ entry->ete_val_len;
--enc->hpe_nelem;
free(entry);
}
static void
henc_remove_overflow_entries (struct lsquic_henc *enc)
{
while (enc->hpe_cur_capacity > enc->hpe_max_capacity)
henc_drop_oldest_entry(enc);
}
static int
henc_grow_tables (struct lsquic_henc *enc)
{
struct double_enc_head *new_buckets, *new[2];
struct enc_table_entry *entry;
unsigned n, old_nbits;
int idx;
old_nbits = enc->hpe_nbits;
new_buckets = malloc(sizeof(enc->hpe_buckets[0])
* N_BUCKETS(old_nbits + 1));
if (!new_buckets)
return -1;
for (n = 0; n < N_BUCKETS(old_nbits); ++n)
{
new[0] = &new_buckets[n];
new[1] = &new_buckets[n + N_BUCKETS(old_nbits)];
STAILQ_INIT(&new[0]->by_name);
STAILQ_INIT(&new[1]->by_name);
STAILQ_INIT(&new[0]->by_nameval);
STAILQ_INIT(&new[1]->by_nameval);
while ((entry = STAILQ_FIRST(&enc->hpe_buckets[n].by_name)))
{
STAILQ_REMOVE_HEAD(&enc->hpe_buckets[n].by_name, ete_next_name);
idx = (BUCKNO(old_nbits + 1, entry->ete_name_hash) >> old_nbits) & 1;
STAILQ_INSERT_TAIL(&new[idx]->by_name, entry, ete_next_name);
}
while ((entry = STAILQ_FIRST(&enc->hpe_buckets[n].by_nameval)))
{
STAILQ_REMOVE_HEAD(&enc->hpe_buckets[n].by_nameval, ete_next_nameval);
idx = (BUCKNO(old_nbits + 1, entry->ete_nameval_hash) >> old_nbits) & 1;
STAILQ_INSERT_TAIL(&new[idx]->by_nameval, entry, ete_next_nameval);
}
}
free(enc->hpe_buckets);
enc->hpe_nbits = old_nbits + 1;
enc->hpe_buckets = new_buckets;
return 0;
}
#ifdef NDEBUG
static
#endif
int
lsquic_henc_push_entry (struct lsquic_henc *enc, const char *name,
uint16_t name_len, const char *value, uint16_t value_len)
{
unsigned name_hash, nameval_hash, buckno;
struct enc_table_entry *entry;
XXH32_state_t hash_state;
size_t size;
if (enc->hpe_nelem >= N_BUCKETS(enc->hpe_nbits) / 2 &&
0 != henc_grow_tables(enc))
return -1;
size = sizeof(*entry) + name_len + value_len;
entry = malloc(size);
if (!entry)
return -1;
XXH32_reset(&hash_state, (uintptr_t) enc);
XXH32_update(&hash_state, &name_len, sizeof(name_len));
XXH32_update(&hash_state, name, name_len);
name_hash = XXH32_digest(&hash_state);
XXH32_update(&hash_state, &value_len, sizeof(value_len));
XXH32_update(&hash_state, value, value_len);
nameval_hash = XXH32_digest(&hash_state);
entry->ete_name_hash = name_hash;
entry->ete_nameval_hash = nameval_hash;
entry->ete_name_len = name_len;
entry->ete_val_len = value_len;
entry->ete_id = enc->hpe_next_id++;
memcpy(ETE_NAME(entry), name, name_len);
memcpy(ETE_VALUE(entry), value, value_len);
STAILQ_INSERT_TAIL(&enc->hpe_all_entries, entry, ete_next_all);
buckno = BUCKNO(enc->hpe_nbits, nameval_hash);
STAILQ_INSERT_TAIL(&enc->hpe_buckets[buckno].by_nameval, entry, ete_next_nameval);
buckno = BUCKNO(enc->hpe_nbits, name_hash);
STAILQ_INSERT_TAIL(&enc->hpe_buckets[buckno].by_name, entry, ete_next_name);
enc->hpe_cur_capacity += DYNAMIC_ENTRY_OVERHEAD + name_len + value_len;
++enc->hpe_nelem;
henc_remove_overflow_entries(enc);
return 0;
}
unsigned char *
lsquic_henc_encode (struct lsquic_henc *enc, unsigned char *dst,
unsigned char *dst_end, const char *name, uint16_t name_len,
const char *value, uint16_t value_len, int indexed_type)
{
//indexed_type: 0, Add, 1,: without, 2: never
static const char indexed_prefix_number[] = {0x40, 0x00, 0x10};
unsigned char *const dst_org = dst;
int val_matched, rc;
unsigned table_id;
assert(indexed_type >= 0 && indexed_type <= 2);
if (dst_end <= dst)
return dst_org;
table_id = henc_find_table_id(enc, name, name_len, value, value_len,
&val_matched);
if (table_id > 0)
{
if (val_matched)
{
*dst = 0x80;
dst = henc_enc_int(dst, dst_end, table_id, 7);
/* No need to check return value: we pass it up as-is because
* the behavior is the same.
*/
return dst;
}
else
{
*dst = indexed_prefix_number[indexed_type];
dst = henc_enc_int(dst, dst_end, table_id, ((indexed_type == 0) ? 6 : 4));
if (dst == dst_org)
return dst_org;
}
}
else
{
*dst++ = indexed_prefix_number[indexed_type];
rc = lsquic_henc_enc_str(dst, dst_end - dst, (const unsigned char *)name, name_len);
if (rc < 0)
return dst_org; //Failed to enc this header, return unchanged ptr.
dst += rc;
}
rc = lsquic_henc_enc_str(dst, dst_end - dst, (const unsigned char *)value, value_len);
if (rc < 0)
return dst_org; //Failed to enc this header, return unchanged ptr.
dst += rc;
if (indexed_type == 0)
{
rc = lsquic_henc_push_entry(enc, name, name_len, value, value_len);
if (rc != 0)
return dst_org; //Failed to enc this header, return unchanged ptr.
}
return dst;
}
void
lsquic_henc_set_max_capacity (struct lsquic_henc *enc, unsigned max_capacity)
{
enc->hpe_max_capacity = max_capacity;
henc_remove_overflow_entries(enc);
}
#ifndef NDEBUG
void
lsquic_henc_iter_reset (struct lsquic_henc *enc)
{
enc->hpe_iter = STAILQ_FIRST(&enc->hpe_all_entries);
}
/* Returns 0 if entry is found */
int
lsquic_henc_iter_next (struct lsquic_henc *enc,
struct enc_dyn_table_entry *retval)
{
const struct enc_table_entry *entry;
entry = enc->hpe_iter;
if (!entry)
return -1;
enc->hpe_iter = STAILQ_NEXT(entry, ete_next_all);
retval->name = ETE_NAME(entry);
retval->value = ETE_VALUE(entry);
retval->name_len = entry->ete_name_len;
retval->value_len = entry->ete_val_len;
retval->entry_id = henc_calc_table_id(enc, entry);
return 0;
}
#endif

View File

@ -0,0 +1,102 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_hpack_enc.h - HPACK encoder
*/
#ifndef LSQUIC_HPACK_ENC_H
#define LSQUIC_HPACK_ENC_H 1
struct enc_table_entry;
#ifndef NDEBUG
struct enc_dyn_table_entry
{
const char *name, /* Not NUL-terminated */
*value; /* Not NUL-terminated */
unsigned name_len,
value_len;
unsigned entry_id;
};
#endif
STAILQ_HEAD(enc_head, enc_table_entry);
struct double_enc_head;
struct lsquic_henc
{
unsigned hpe_cur_capacity;
unsigned hpe_max_capacity;
/* Each new dynamic table entry gets the next number. It is used to
* calculate the entry's position in the decoder table without having
* to maintain an actual array.
*/
unsigned hpe_next_id;
/* Dynamic table entries (struct enc_table_entry) live in two hash
* tables: name/value hash table and name hash table. These tables
* are the same size.
*/
unsigned hpe_nelem;
unsigned hpe_nbits;
struct enc_head hpe_all_entries;
struct double_enc_head
*hpe_buckets;
#ifndef NDEBUG
const struct enc_table_entry
*hpe_iter;
#endif
};
/* Initialization routine allocates memory. -1 is returned if memory
* could not be allocated. 0 is returned on success.
*/
int
lsquic_henc_init (struct lsquic_henc *);
void
lsquic_henc_cleanup (struct lsquic_henc *);
/** @lsquic_hpack_encode
* @brief HPACK encode one name/value item
* @param[in,out] henc - A pointer to a valid HPACK API struct
* @param[out] dst - A pointer to destination buffer
* @param[out] dst_end - A pointer to end of destination buffer
* @param[in] name - A pointer to the item name
* @param[in] name_len - The item name's length
* @param[in] value - A pointer to the item value
* @param[in] value_len - The item value's length
* @param[in] indexed_type - 0, Add, 1,: without, 2: never
* @return The (possibly advanced) dst pointer
*/
unsigned char *
lsquic_henc_encode (struct lsquic_henc *henc, unsigned char *dst,
unsigned char *dst_end, const char *name, uint16_t name_len,
const char *value, uint16_t value_len, int indexed_type);
void
lsquic_henc_set_max_capacity (struct lsquic_henc *, unsigned);
#ifndef NDEBUG
unsigned
lsquic_henc_get_stx_tab_id (const char *name, uint16_t name_len,
const char *val, uint16_t val_len, int *val_matched);
int
lsquic_henc_push_entry (struct lsquic_henc *enc, const char *name,
uint16_t name_len, const char *value, uint16_t value_len);
int
lsquic_henc_enc_str (unsigned char *const dst, size_t dst_len,
const unsigned char *str, uint16_t str_len);
void
lsquic_henc_iter_reset (struct lsquic_henc *enc);
/* Returns 0 if entry is found */
int
lsquic_henc_iter_next (struct lsquic_henc *enc, struct enc_dyn_table_entry *);
#endif
#endif

View File

@ -0,0 +1,22 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef LSQUIC_INT_TYPES_H
#define LSQUIC_INT_TYPES_H 1
/* Types included in this file are only used internally. Types used in
* include/lsquic.h should be listed in include/lsquic_types.h
*/
#include <stdint.h>
typedef uint64_t lsquic_time_t; /* Microseconds since the epoch */
typedef uint64_t lsquic_packno_t;
typedef uint32_t lsquic_ver_tag_t; /* Opaque 4-byte value */
/* The `low' and `high' members are inclusive: if the range only has one
* member, low == high.
*/
struct lsquic_packno_range {
lsquic_packno_t low, high;
};
#endif

View File

@ -0,0 +1,328 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* LSQUIC Logger implementation.
*/
#include <errno.h>
#include <inttypes.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#define LSQUIC_LOGGER_MODULE LSQLM_LOGGER /* Quis custodiet ipsos custodes? */
#include "lsquic_logger.h"
#include "lsquic.h"
static enum lsquic_logger_timestamp_style g_llts = LLTS_NONE;
static int
null_vprintf (void *ctx, const char *fmt, va_list ap)
{
return 0;
}
static int
file_vprintf (void *ctx, const char *fmt, va_list ap)
{
return vfprintf((FILE *) ctx, fmt, ap);
}
static const struct lsquic_logger_if file_logger_if = {
.vprintf = file_vprintf,
};
static const struct lsquic_logger_if null_logger_if = {
.vprintf = null_vprintf,
};
static void *logger_ctx = NULL;
static const struct lsquic_logger_if *logger_if = &null_logger_if;
enum lsq_log_level lsq_log_levels[N_LSQUIC_LOGGER_MODULES] = {
[LSQLM_NOMODULE] = LSQ_LOG_WARN,
[LSQLM_LOGGER] = LSQ_LOG_WARN,
[LSQLM_EVENT] = LSQ_LOG_WARN,
[LSQLM_ENGINE] = LSQ_LOG_WARN,
[LSQLM_CONN] = LSQ_LOG_WARN,
[LSQLM_RECHIST] = LSQ_LOG_WARN,
[LSQLM_STREAM] = LSQ_LOG_WARN,
[LSQLM_PARSE] = LSQ_LOG_WARN,
[LSQLM_CFCW] = LSQ_LOG_WARN,
[LSQLM_SFCW] = LSQ_LOG_WARN,
[LSQLM_SENDCTL] = LSQ_LOG_WARN,
[LSQLM_ALARMSET] = LSQ_LOG_WARN,
[LSQLM_CRYPTO] = LSQ_LOG_WARN,
[LSQLM_HANDSHAKE] = LSQ_LOG_WARN,
[LSQLM_HSK_ADAPTER] = LSQ_LOG_WARN,
[LSQLM_CUBIC] = LSQ_LOG_WARN,
[LSQLM_HEADERS] = LSQ_LOG_WARN,
[LSQLM_FRAME_READER]= LSQ_LOG_WARN,
[LSQLM_FRAME_WRITER]= LSQ_LOG_WARN,
[LSQLM_CONN_HASH] = LSQ_LOG_WARN,
[LSQLM_ENG_HIST] = LSQ_LOG_WARN,
[LSQLM_SPI] = LSQ_LOG_WARN,
[LSQLM_DI] = LSQ_LOG_WARN,
[LSQLM_PACER] = LSQ_LOG_WARN,
};
const char *const lsqlm_to_str[N_LSQUIC_LOGGER_MODULES] = {
[LSQLM_NOMODULE] = "",
[LSQLM_LOGGER] = "logger",
[LSQLM_EVENT] = "event",
[LSQLM_ENGINE] = "engine",
[LSQLM_CONN] = "conn",
[LSQLM_RECHIST] = "rechist",
[LSQLM_STREAM] = "stream",
[LSQLM_PARSE] = "parse",
[LSQLM_CFCW] = "cfcw",
[LSQLM_SFCW] = "sfcw",
[LSQLM_SENDCTL] = "sendctl",
[LSQLM_ALARMSET] = "alarmset",
[LSQLM_CRYPTO] = "crypto",
[LSQLM_HANDSHAKE] = "handshake",
[LSQLM_HSK_ADAPTER] = "hsk-adapter",
[LSQLM_CUBIC] = "cubic",
[LSQLM_HEADERS] = "headers",
[LSQLM_FRAME_READER]= "frame-reader",
[LSQLM_FRAME_WRITER]= "frame-writer",
[LSQLM_CONN_HASH] = "conn-hash",
[LSQLM_ENG_HIST] = "eng-hist",
[LSQLM_SPI] = "spi",
[LSQLM_DI] = "di",
[LSQLM_PACER] = "pacer",
};
const char *const lsq_loglevel2str[N_LSQUIC_LOG_LEVELS] = {
[LSQ_LOG_ALERT] = "ALERT",
[LSQ_LOG_CRIT] = "CRIT",
[LSQ_LOG_DEBUG] = "DEBUG",
[LSQ_LOG_EMERG] = "EMERG",
[LSQ_LOG_ERROR] = "ERROR",
[LSQ_LOG_INFO] = "INFO",
[LSQ_LOG_NOTICE] = "NOTICE",
[LSQ_LOG_WARN] = "WARN",
};
static void
lsquic_printf (const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
logger_if->vprintf(logger_ctx, fmt, ap);
va_end(ap);
}
static void
print_timestamp (void)
{
struct tm tm;
struct timeval tv;
gettimeofday(&tv, NULL);
localtime_r(&tv.tv_sec, &tm);
if (g_llts == LLTS_YYYYMMDD_HHMMSSMS)
lsquic_printf("%04d-%02d-%02d %02d:%02d:%02d.%03d ",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec, (int) (tv.tv_usec / 1000));
else if (g_llts == LLTS_HHMMSSMS)
lsquic_printf("%02d:%02d:%02d.%03d ", tm.tm_hour, tm.tm_min,
tm.tm_sec, (int) (tv.tv_usec / 1000));
else if (g_llts == LLTS_HHMMSSUS)
lsquic_printf("%02d:%02d:%02d.%06d ", tm.tm_hour, tm.tm_min,
tm.tm_sec, (int) tv.tv_usec);
else if (g_llts == LLTS_CHROMELIKE)
lsquic_printf("%02d%02d/%02d%02d%02d.%06d ", tm.tm_mon + 1,
tm.tm_mday,tm.tm_hour, tm.tm_min, tm.tm_sec, (int) tv.tv_usec);
}
void
lsquic_logger_log3 (enum lsq_log_level log_level,
enum lsquic_logger_module module,
uint64_t conn_id, uint32_t stream_id, const char *fmt, ...)
{
const int saved_errno = errno;
if (g_llts != LLTS_NONE)
print_timestamp();
lsquic_printf("[%s] [QUIC:%"PRIu64"-%"PRIu32"] %s: ",
lsq_loglevel2str[log_level], conn_id, stream_id, lsqlm_to_str[module]);
va_list ap;
va_start(ap, fmt);
logger_if->vprintf(logger_ctx, fmt, ap);
va_end(ap);
lsquic_printf("\n");
errno = saved_errno;
}
void
lsquic_logger_log2 (enum lsq_log_level log_level,
enum lsquic_logger_module module,
uint64_t conn_id, const char *fmt, ...)
{
const int saved_errno = errno;
if (g_llts != LLTS_NONE)
print_timestamp();
lsquic_printf("[%s] [QUIC:%"PRIu64"] %s: ",
lsq_loglevel2str[log_level], conn_id, lsqlm_to_str[module]);
va_list ap;
va_start(ap, fmt);
logger_if->vprintf(logger_ctx, fmt, ap);
va_end(ap);
lsquic_printf("\n");
errno = saved_errno;
}
void
lsquic_logger_log1 (enum lsq_log_level log_level,
enum lsquic_logger_module module,
const char *fmt, ...)
{
const int saved_errno = errno;
if (g_llts != LLTS_NONE)
print_timestamp();
lsquic_printf("[%s] %s: ", lsq_loglevel2str[log_level],
lsqlm_to_str[module]);
va_list ap;
va_start(ap, fmt);
logger_if->vprintf(logger_ctx, fmt, ap);
va_end(ap);
lsquic_printf("\n");
errno = saved_errno;
}
void
lsquic_logger_log0 (enum lsq_log_level log_level, const char *fmt, ...)
{
const int saved_errno = errno;
if (g_llts != LLTS_NONE)
print_timestamp();
lsquic_printf("[%s] ", lsq_loglevel2str[log_level]);
va_list ap;
va_start(ap, fmt);
logger_if->vprintf(logger_ctx, fmt, ap);
va_end(ap);
lsquic_printf("\n");
errno = saved_errno;
}
void
lsquic_logger_init (const struct lsquic_logger_if *lif, void *lctx,
unsigned llts)
{
logger_if = lif;
logger_ctx = lctx;
if (llts < N_LLTS)
g_llts = llts;
LSQ_DEBUG("%s called", __func__);
}
enum lsquic_logger_module
lsquic_str_to_logger_module (const char *str)
{
enum lsquic_logger_module i;
for (i = 0; i < sizeof(lsqlm_to_str) / sizeof(lsqlm_to_str[0]); ++i)
if (0 == strcasecmp(lsqlm_to_str[i], str))
return i;
return -1;
}
enum lsq_log_level
lsquic_str_to_log_level (const char *str)
{
if (0 == strcasecmp(str, "emerg"))
return LSQ_LOG_EMERG;
if (0 == strcasecmp(str, "alert"))
return LSQ_LOG_ALERT;
if (0 == strcasecmp(str, "crit"))
return LSQ_LOG_CRIT;
if (0 == strcasecmp(str, "error"))
return LSQ_LOG_ERROR;
if (0 == strcasecmp(str, "warn"))
return LSQ_LOG_WARN;
if (0 == strcasecmp(str, "notice"))
return LSQ_LOG_NOTICE;
if (0 == strcasecmp(str, "info"))
return LSQ_LOG_INFO;
if (0 == strcasecmp(str, "debug"))
return LSQ_LOG_DEBUG;
return -1;
}
void
lsquic_log_to_fstream (FILE *file, unsigned llts)
{
lsquic_logger_init(&file_logger_if, file, llts);
}
int
lsquic_logger_lopt (const char *optarg_orig)
{
char *const optarg = strdup(optarg_orig);
char *mod_str;
int i;
for (i = 0; (mod_str = strtok(i ? NULL : optarg, ",")); ++i) {
char *level_str = strchr(mod_str, '=');
if (!level_str) {
fprintf(stderr, "Invalid module specification `%s'\n", mod_str);
break;
}
*level_str = '\0';
++level_str;
enum lsquic_logger_module mod = lsquic_str_to_logger_module(mod_str);
if (-1 == (int) mod) {
fprintf(stderr, "`%s' is not a valid module name\n", mod_str);
break;
}
enum lsq_log_level level = lsquic_str_to_log_level(level_str);
if (-1 == (int) level) {
fprintf(stderr, "`%s' is not a valid level\n", level_str);
break;
}
lsq_log_levels[mod] = level;
LSQ_INFO("set %s to %s", mod_str, level_str);
}
free(optarg);
return mod_str == NULL ? 0 : -1;
}
int
lsquic_set_log_level (const char *level_str)
{
enum lsq_log_level level;
unsigned i;
level = lsquic_str_to_log_level(level_str);
if ((int) level >= 0)
{
for (i = 0; i < sizeof(lsq_log_levels) / sizeof(lsq_log_levels[0]); ++i)
lsq_log_levels[i] = level;
return 0;
}
else
return -1;
}

View File

@ -0,0 +1,201 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_logger.h -- logging functions and macros.
*
* Usage (this assumes MY_MODULE) is a part of enum lsquic_logger_module):
* #define LSQUIC_LOGGER_MODULE MY_MODULE
* #include "lsquic_logger.h"
* LSQ_INFO("info message");
*
* If you want log messages from your module to contain connection ID, #define
* LSQUIC_LOG_CONN_ID so that it evaluates to connection ID. If, in addition,
* you want stream ID to be logged, #define LSQUIC_LOG_STREAM_ID similarly.
* See existing code for examples.
*
* To add a module:
* 1. Add entry to enum lsquic_logger_module.
* 2. Update lsqlm_to_str.
* 3. Update lsq_log_levels.
*/
#ifndef LSQUIC_LOGGER_H
#define LSQUIC_LOGGER_H
#include <stdint.h>
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifndef LSQUIC_LOWEST_LOG_LEVEL
# define LSQUIC_LOWEST_LOG_LEVEL LSQ_LOG_DEBUG
#endif
/* Same levels as in sys/syslog.h: */
enum lsq_log_level {
LSQ_LOG_EMERG,
LSQ_LOG_ALERT,
LSQ_LOG_CRIT,
LSQ_LOG_ERROR,
LSQ_LOG_WARN,
LSQ_LOG_NOTICE,
LSQ_LOG_INFO,
LSQ_LOG_DEBUG,
N_LSQUIC_LOG_LEVELS
};
enum lsquic_logger_module {
LSQLM_NOMODULE,
LSQLM_LOGGER,
LSQLM_EVENT,
LSQLM_ENGINE,
LSQLM_CONN,
LSQLM_RECHIST,
LSQLM_STREAM,
LSQLM_PARSE,
LSQLM_CFCW,
LSQLM_SFCW,
LSQLM_SENDCTL,
LSQLM_ALARMSET,
LSQLM_CRYPTO,
LSQLM_HANDSHAKE,
LSQLM_HSK_ADAPTER,
LSQLM_CUBIC,
LSQLM_HEADERS,
LSQLM_FRAME_WRITER,
LSQLM_FRAME_READER,
LSQLM_CONN_HASH,
LSQLM_ENG_HIST,
LSQLM_SPI,
LSQLM_DI,
LSQLM_PACER,
N_LSQUIC_LOGGER_MODULES
};
/* Each module has its own log level.
*/
extern enum lsq_log_level lsq_log_levels[N_LSQUIC_LOGGER_MODULES];
extern const char *const lsqlm_to_str[N_LSQUIC_LOGGER_MODULES];
extern const char *const lsq_loglevel2str[N_LSQUIC_LOG_LEVELS];
#define LSQ_LOG_ENABLED_EXT(level, module) ( \
level <= LSQUIC_LOWEST_LOG_LEVEL && level <= lsq_log_levels[module])
#define LSQ_LOG_ENABLED(level) LSQ_LOG_ENABLED_EXT(level, LSQUIC_LOGGER_MODULE)
/* The functions that perform actual logging are void. This is an
* optimization. In majority of cases the calls will succeed; even if
* they fail, there is nothing (at least, nothing simple) to be done to
* handle logging failure.
*/
/* There are four levels of log functions, depending on whether they take
* the following arguments:
* 1. Logger module
* 2. Connection ID
* 3. Stream ID
*
* Each level of logging function supports one additional argument, as seen
* below. LSQ_LOG is set to one of LSQ_LOG0, LSQ_LOG1, LSQ_LOG2, or LSQ_LOG3.
* You can still use LSQ_LOG{0..3} directly.
*/
void
lsquic_logger_log3 (enum lsq_log_level, enum lsquic_logger_module,
uint64_t conn_id, uint32_t stream_id,
const char *format, ...)
#if __GNUC__
__attribute__((format(printf, 5, 6)))
#endif
;
# define LSQ_LOG3(level, ...) do { \
if (LSQ_LOG_ENABLED(level)) \
lsquic_logger_log3(level, LSQUIC_LOGGER_MODULE, \
LSQUIC_LOG_CONN_ID, LSQUIC_LOG_STREAM_ID, __VA_ARGS__); \
} while (0)
void
lsquic_logger_log2 (enum lsq_log_level, enum lsquic_logger_module,
uint64_t conn_id, const char *format, ...)
#if __GNUC__
__attribute__((format(printf, 4, 5)))
#endif
;
# define LSQ_LOG2(level, ...) do { \
if (LSQ_LOG_ENABLED(level)) \
lsquic_logger_log2(level, LSQUIC_LOGGER_MODULE, \
LSQUIC_LOG_CONN_ID, __VA_ARGS__); \
} while (0)
void
lsquic_logger_log1 (enum lsq_log_level, enum lsquic_logger_module,
const char *format, ...)
#if __GNUC__
__attribute__((format(printf, 3, 4)))
#endif
;
# define LSQ_LOG1(level, ...) do { \
if (LSQ_LOG_ENABLED(level)) \
lsquic_logger_log1(level, LSQUIC_LOGGER_MODULE, __VA_ARGS__); \
} while (0)
void
lsquic_logger_log0 (enum lsq_log_level, const char *format, ...)
#if __GNUC__
__attribute__((format(printf, 2, 3)))
#endif
;
# define LSQ_LOG0(level, ...) do { \
if (LSQ_LOG_ENABLED(level)) \
lsquic_logger_log0(level, __VA_ARGS__); \
} while (0)
#if defined(LSQUIC_LOGGER_MODULE)
#if defined(LSQUIC_LOG_CONN_ID)
#if defined(LSQUIC_LOG_STREAM_ID)
# define LSQ_LOG LSQ_LOG3
#else
# define LSQ_LOG LSQ_LOG2
#endif
#else
# define LSQ_LOG LSQ_LOG1
#endif
#else
# define LSQ_LOG LSQ_LOG0
# define LSQUIC_LOGGER_MODULE LSQLM_NOMODULE
#endif
#define LSQ_DEBUG(...) LSQ_LOG(LSQ_LOG_DEBUG, __VA_ARGS__)
#define LSQ_WARN(...) LSQ_LOG(LSQ_LOG_WARN, __VA_ARGS__)
#define LSQ_ALERT(...) LSQ_LOG(LSQ_LOG_ALERT, __VA_ARGS__)
#define LSQ_CRIT(...) LSQ_LOG(LSQ_LOG_CRIT, __VA_ARGS__)
#define LSQ_ERROR(...) LSQ_LOG(LSQ_LOG_ERROR, __VA_ARGS__)
#define LSQ_NOTICE(...) LSQ_LOG(LSQ_LOG_NOTICE, __VA_ARGS__)
#define LSQ_INFO(...) LSQ_LOG(LSQ_LOG_INFO, __VA_ARGS__)
#define LSQ_EMERG(...) LSQ_LOG(LSQ_LOG_EMERG, __VA_ARGS__)
/* Shorthand for printing to file streams using internal lsquic_logger_if
*/
void
lsquic_log_to_fstream (FILE *, unsigned llts);
enum lsquic_logger_module
lsquic_str_to_logger_module (const char *);
enum lsq_log_level
lsquic_str_to_log_level (const char *);
/* Parse and set log levels passed via -l flag. If an error is encountered,
* an error message is printed to stderr and negative value is returned.
*/
int
lsquic_logger_lopt (const char *optarg);
#ifdef __cplusplus
}
#endif
#endif

289
src/liblsquic/lsquic_malo.c Normal file
View File

@ -0,0 +1,289 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_malo.c -- malo allocator implementation.
*
* The malo allocator is a pool of objects of fixed size. It tries to
* allocate and deallocate objects as fast as possible. To do so, it
* does the following:
*
* 1. Allocations occur 4 KB at a time.
* 2. No division or multiplication operations are performed.
*
* (In recent testing, malo was about 2.7 times faster than malloc for
* 64-byte objects.)
*
* Besides speed, two other important characteristics distinguish it
* from other pool allocators:
*
* 1. To free (put) an object, one does not need a pointer to the malo
* object. This makes this allocator easy to use.
* 2. A built-in iterator is provided to iterate over all allocated
* objects (with ability to safely release objects while iterator
* is active). This may be useful in some circumstances.
*
* To gain all these advantages, there are trade-offs:
*
* 1. There are two memory penalties:
* a. Per object overhead. To avoid division and multiplication,
* the object sizes is rounded up to the nearest power or two,
* starting with 64 bytes (minumum) and up to 2 KB (maximum).
* Thus, a 104-byte object will have a 24-byte overhead; a
* 130-byte object will have 126-byte overhead. This is
* something to keep in mind.
* b. Per page overhead. Page links occupy some bytes in the
* page. To keep things fast, at least one slot per page is
* always occupied, independent of object size. Thus, for a
* 1 KB object size, 25% of the page is used for the page
* header.
* 2. 4 KB pages are not freed until the malo allocator is destroyed.
* This is something to keep in mind.
*
* P.S. In Russian, "malo" (мало) means "little" or "few". Thus, the
* malo allocator aims to perform its job in as few CPU cycles as
* possible.
*/
#include <assert.h>
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/queue.h>
#include "fiu-local.h"
#include "lsquic_malo.h"
/* 64 slots in a 4KB page means that the smallest object is 64 bytes.
* The largest object is 2KB.
*/
#define MALO_MIN_NBITS 6
#define MALO_MAX_NBITS 11
/* A "free page" is a page with free slots available.
*/
static unsigned find_free_slot (uint64_t slots);
static unsigned size_in_bits (size_t sz);
struct malo_page {
SLIST_ENTRY(malo_page) next_page;
LIST_ENTRY(malo_page) next_free_page;
struct malo *malo;
uint64_t slots,
full_slot_mask;
unsigned nbits;
unsigned initial_slot;
};
typedef char malo_header_fits_in_one_slot
[0 - (sizeof(struct malo_page) > (1 << MALO_MAX_NBITS))];
struct malo {
struct malo_page page_header;
SLIST_HEAD(, malo_page) all_pages;
LIST_HEAD(, malo_page) free_pages;
struct {
struct malo_page *cur_page;
unsigned next_slot;
} iter;
};
struct malo *
lsquic_malo_create (size_t obj_size)
{
unsigned nbits = size_in_bits(obj_size);
if (nbits < MALO_MIN_NBITS)
nbits = MALO_MIN_NBITS;
else if (nbits > MALO_MAX_NBITS)
{
errno = EOVERFLOW;
return NULL;
}
struct malo *malo;
if (0 != posix_memalign((void **) &malo, 0x1000, 0x1000))
return NULL;
SLIST_INIT(&malo->all_pages);
LIST_INIT(&malo->free_pages);
malo->iter.cur_page = &malo->page_header;
malo->iter.next_slot = 0;
int n_slots = sizeof(*malo) / (1 << nbits)
+ ((sizeof(*malo) % (1 << nbits)) > 0);
struct malo_page *const page = &malo->page_header;
SLIST_INSERT_HEAD(&malo->all_pages, page, next_page);
LIST_INSERT_HEAD(&malo->free_pages, page, next_free_page);
page->malo = malo;
if (nbits == MALO_MIN_NBITS)
page->full_slot_mask = ~0ULL;
else
page->full_slot_mask = (1ULL << (1 << (12 - nbits))) - 1;
page->slots = (1ULL << n_slots) - 1;
page->nbits = nbits;
page->initial_slot = n_slots;
return malo;
}
static struct malo_page *
allocate_page (struct malo *malo)
{
struct malo_page *page;
if (0 != posix_memalign((void **) &page, 0x1000, 0x1000))
return NULL;
SLIST_INSERT_HEAD(&malo->all_pages, page, next_page);
LIST_INSERT_HEAD(&malo->free_pages, page, next_free_page);
page->slots = 1;
page->full_slot_mask = malo->page_header.full_slot_mask;
page->nbits = malo->page_header.nbits;
page->malo = malo;
page->initial_slot = 1;
return page;
}
#define FAIL_NOMEM do { errno = ENOMEM; return NULL; } while (0)
/* Get a new object. */
void *
lsquic_malo_get (struct malo *malo)
{
fiu_do_on("malo/get", FAIL_NOMEM);
struct malo_page *page = LIST_FIRST(&malo->free_pages);
if (!page)
{
page = allocate_page(malo);
if (!page)
return NULL;
}
unsigned slot = find_free_slot(page->slots);
page->slots |= (1ULL << slot);
if (page->full_slot_mask == page->slots)
LIST_REMOVE(page, next_free_page);
return (char *) page + (slot << page->nbits);
}
/* Return obj to the pool */
void
lsquic_malo_put (void *obj)
{
uintptr_t page_addr = (uintptr_t) obj & ~((1 << 12) - 1);
struct malo_page *page = (void *) page_addr;
unsigned slot = ((uintptr_t) obj - page_addr) >> page->nbits;
if (page->full_slot_mask == page->slots)
LIST_INSERT_HEAD(&page->malo->free_pages, page, next_free_page);
page->slots &= ~(1ULL << slot);
}
void
lsquic_malo_destroy (struct malo *malo)
{
struct malo_page *page, *next;
page = SLIST_FIRST(&malo->all_pages);
while (page != &malo->page_header)
{
next = SLIST_NEXT(page, next_page);
free(page);
page = next;
}
free(page);
}
/* The iterator is built-in. Usage:
* void *obj;
* for (obj = lsquic_malo_first(malo); obj; lsquic_malo_next(malo))
* do_stuff(obj);
*/
void *
lsquic_malo_first (struct malo *malo)
{
malo->iter.cur_page = SLIST_FIRST(&malo->all_pages);
malo->iter.next_slot = malo->iter.cur_page->initial_slot;
return lsquic_malo_next(malo);
}
void *
lsquic_malo_next (struct malo *malo)
{
struct malo_page *page;
unsigned max_slot, slot;
page = malo->iter.cur_page;
if (page)
{
max_slot = 1 << (12 - page->nbits); /* Same for all pages */
slot = malo->iter.next_slot;
while (1)
{
for (; slot < max_slot; ++slot)
{
if (page->slots & (1ULL << slot))
{
malo->iter.cur_page = page;
malo->iter.next_slot = slot + 1;
return (char *) page + (slot << page->nbits);
}
}
page = SLIST_NEXT(page, next_page);
if (page)
slot = page->initial_slot;
else
{
malo->iter.cur_page = NULL; /* Stop iterator */
return NULL;
}
}
}
return NULL;
}
static unsigned
size_in_bits (size_t sz)
{
#if __GNUC__
unsigned clz = __builtin_clz(sz - 1);
return 32 - clz;
#else
unsigned clz;
size_t y;
--sz;
clz = 32;
y = sz >> 16; if (y) { clz -= 16; sz = y; }
y = sz >> 8; if (y) { clz -= 8; sz = y; }
y = sz >> 4; if (y) { clz -= 4; sz = y; }
y = sz >> 2; if (y) { clz -= 2; sz = y; }
y = sz >> 1; if (y) return 32 - clz + 1;
return 32 - clz + sz;
#endif
}
static unsigned
find_free_slot (uint64_t slots)
{
#if __GNUC__
return __builtin_ffsll(~slots) - 1;
#else
unsigned n;
slots =~ slots;
n = 0;
if (0 == (slots & ((1ULL << 32) - 1))) { n += 32; slots >>= 32; }
if (0 == (slots & ((1ULL << 16) - 1))) { n += 16; slots >>= 16; }
if (0 == (slots & ((1ULL << 8) - 1))) { n += 8; slots >>= 8; }
if (0 == (slots & ((1ULL << 4) - 1))) { n += 4; slots >>= 4; }
if (0 == (slots & ((1ULL << 2) - 1))) { n += 2; slots >>= 2; }
if (0 == (slots & ((1ULL << 1) - 1))) { n += 1; slots >>= 1; }
return n;
#endif
}

View File

@ -0,0 +1,38 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_malo.h -- Fast allocator for fixed-sized objects.
*/
#ifndef LSQUIC_MALO_H
#define LSQUIC_MALO_H 1
struct malo;
/* Create a malo allocator for objects of size `obj_size'. */
struct malo *
lsquic_malo_create (size_t obj_size);
/* Get a new object. */
void *
lsquic_malo_get (struct malo *);
/* Return obj to the pool */
void
lsquic_malo_put (void *obj);
/* This deallocates all remaining objects. */
void
lsquic_malo_destroy (struct malo *);
/* The iterator is built-in. Usage:
* void *obj;
* for (obj = lsquic_malo_first(obj); obj; lsquic_malo_next(obj))
* do_stuff(obj);
*/
void *
lsquic_malo_first (struct malo *);
void *
lsquic_malo_next (struct malo *);
#endif

293
src/liblsquic/lsquic_mm.c Normal file
View File

@ -0,0 +1,293 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_mm.c -- Memory manager.
*/
#include <assert.h>
#include <errno.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include "fiu-local.h"
#include "lsquic.h"
#include "lsquic_int_types.h"
#include "lsquic_malo.h"
#include "lsquic_conn.h"
#include "lsquic_rtt.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_in.h"
#include "lsquic_packet_out.h"
#include "lsquic_parse.h"
#include "lsquic_mm.h"
#include "lsquic_engine_public.h"
#define FAIL_NOMEM do { errno = ENOMEM; return NULL; } while (0)
struct payload_buf
{
SLIST_ENTRY(payload_buf) next_pb;
};
struct packet_out_buf
{
SLIST_ENTRY(packet_out_buf) next_pob;
};
struct four_k_page
{
SLIST_ENTRY(four_k_page) next_fkp;
};
struct sixteen_k_page
{
SLIST_ENTRY(sixteen_k_page) next_skp;
};
int
lsquic_mm_init (struct lsquic_mm *mm)
{
int i;
mm->acki = malloc(sizeof(*mm->acki));
mm->malo.stream_frame = lsquic_malo_create(sizeof(struct stream_frame));
mm->malo.stream_rec_arr = lsquic_malo_create(sizeof(struct stream_rec_arr));
mm->malo.packet_in = lsquic_malo_create(sizeof(struct lsquic_packet_in));
mm->malo.packet_out = lsquic_malo_create(sizeof(struct lsquic_packet_out));
TAILQ_INIT(&mm->free_packets_in);
for (i = 0; i < MM_N_OUT_BUCKETS; ++i)
SLIST_INIT(&mm->packet_out_bufs[i]);
SLIST_INIT(&mm->payload_bufs);
SLIST_INIT(&mm->four_k_pages);
SLIST_INIT(&mm->sixteen_k_pages);
if (mm->acki && mm->malo.stream_frame && mm->malo.stream_rec_arr &&
mm->malo.packet_in)
{
return 0;
}
else
return -1;
}
void
lsquic_mm_cleanup (struct lsquic_mm *mm)
{
int i;
struct packet_out_buf *pob;
struct payload_buf *pb;
struct four_k_page *fkp;
struct sixteen_k_page *skp;
free(mm->acki);
lsquic_malo_destroy(mm->malo.packet_in);
lsquic_malo_destroy(mm->malo.packet_out);
lsquic_malo_destroy(mm->malo.stream_frame);
lsquic_malo_destroy(mm->malo.stream_rec_arr);
for (i = 0; i < MM_N_OUT_BUCKETS; ++i)
while ((pob = SLIST_FIRST(&mm->packet_out_bufs[i])))
{
SLIST_REMOVE_HEAD(&mm->packet_out_bufs[i], next_pob);
free(pob);
}
while ((pb = SLIST_FIRST(&mm->payload_bufs)))
{
SLIST_REMOVE_HEAD(&mm->payload_bufs, next_pb);
free(pb);
}
while ((fkp = SLIST_FIRST(&mm->four_k_pages)))
{
SLIST_REMOVE_HEAD(&mm->four_k_pages, next_fkp);
free(fkp);
}
while ((skp = SLIST_FIRST(&mm->sixteen_k_pages)))
{
SLIST_REMOVE_HEAD(&mm->sixteen_k_pages, next_skp);
free(skp);
}
}
struct lsquic_packet_in *
lsquic_mm_get_packet_in (struct lsquic_mm *mm)
{
struct lsquic_packet_in *packet_in;
fiu_do_on("mm/packet_in", FAIL_NOMEM);
packet_in = TAILQ_FIRST(&mm->free_packets_in);
if (packet_in)
{
assert(0 == packet_in->pi_refcnt);
TAILQ_REMOVE(&mm->free_packets_in, packet_in, pi_next);
}
else
packet_in = lsquic_malo_get(mm->malo.packet_in);
if (packet_in)
memset(packet_in, 0, sizeof(*packet_in));
return packet_in;
}
/* Based on commonly used MTUs, ordered from small to large: */
enum {
PACKET_OUT_PAYLOAD_0 = 1280 - QUIC_MIN_PACKET_OVERHEAD,
PACKET_OUT_PAYLOAD_1 = QUIC_MAX_IPv6_PACKET_SZ - QUIC_MIN_PACKET_OVERHEAD,
PACKET_OUT_PAYLOAD_2 = QUIC_MAX_IPv4_PACKET_SZ - QUIC_MIN_PACKET_OVERHEAD,
};
static const unsigned packet_out_sizes[] = {
PACKET_OUT_PAYLOAD_0,
PACKET_OUT_PAYLOAD_1,
PACKET_OUT_PAYLOAD_2,
};
static unsigned
packet_out_index (unsigned size)
{
unsigned idx = (size > PACKET_OUT_PAYLOAD_0)
+ (size > PACKET_OUT_PAYLOAD_1);
return idx;
}
void
lsquic_mm_put_packet_out (struct lsquic_mm *mm,
struct lsquic_packet_out *packet_out)
{
struct packet_out_buf *pob;
unsigned idx;
assert(packet_out->po_data);
pob = (struct packet_out_buf *) packet_out->po_data;
idx = packet_out_index(packet_out->po_n_alloc);
SLIST_INSERT_HEAD(&mm->packet_out_bufs[idx], pob, next_pob);
lsquic_malo_put(packet_out);
}
struct lsquic_packet_out *
lsquic_mm_get_packet_out (struct lsquic_mm *mm, struct malo *malo,
unsigned short size)
{
struct lsquic_packet_out *packet_out;
struct packet_out_buf *pob;
unsigned idx;
assert(size <= QUIC_MAX_PAYLOAD_SZ);
fiu_do_on("mm/packet_out", FAIL_NOMEM);
packet_out = lsquic_malo_get(malo ? malo : mm->malo.packet_out);
if (!packet_out)
return NULL;
idx = packet_out_index(size);
pob = SLIST_FIRST(&mm->packet_out_bufs[idx]);
if (pob)
SLIST_REMOVE_HEAD(&mm->packet_out_bufs[idx], next_pob);
else
{
pob = malloc(packet_out_sizes[idx]);
if (!pob)
{
lsquic_malo_put(packet_out);
return NULL;
}
}
memset(packet_out, 0, sizeof(*packet_out));
STAILQ_INIT(&packet_out->po_srec_arrs);
packet_out->po_n_alloc = size;
packet_out->po_data = (unsigned char *) pob;
return packet_out;
}
void *
lsquic_mm_get_1370 (struct lsquic_mm *mm)
{
struct payload_buf *pb = SLIST_FIRST(&mm->payload_bufs);
fiu_do_on("mm/1370", FAIL_NOMEM);
if (pb)
SLIST_REMOVE_HEAD(&mm->payload_bufs, next_pb);
else
pb = malloc(1370);
return pb;
}
void
lsquic_mm_put_1370 (struct lsquic_mm *mm, void *mem)
{
struct payload_buf *pb = mem;
SLIST_INSERT_HEAD(&mm->payload_bufs, pb, next_pb);
}
void *
lsquic_mm_get_4k (struct lsquic_mm *mm)
{
struct four_k_page *fkp = SLIST_FIRST(&mm->four_k_pages);
fiu_do_on("mm/4k", FAIL_NOMEM);
if (fkp)
SLIST_REMOVE_HEAD(&mm->four_k_pages, next_fkp);
else
fkp = malloc(0x1000);
return fkp;
}
void
lsquic_mm_put_4k (struct lsquic_mm *mm, void *mem)
{
struct four_k_page *fkp = mem;
SLIST_INSERT_HEAD(&mm->four_k_pages, fkp, next_fkp);
}
void *
lsquic_mm_get_16k (struct lsquic_mm *mm)
{
struct sixteen_k_page *skp = SLIST_FIRST(&mm->sixteen_k_pages);
fiu_do_on("mm/16k", FAIL_NOMEM);
if (skp)
SLIST_REMOVE_HEAD(&mm->sixteen_k_pages, next_skp);
else
skp = malloc(16 * 1024);
return skp;
}
void
lsquic_mm_put_16k (struct lsquic_mm *mm, void *mem)
{
struct sixteen_k_page *skp = mem;
SLIST_INSERT_HEAD(&mm->sixteen_k_pages, skp, next_skp);
}
void
lsquic_mm_put_packet_in (struct lsquic_mm *mm,
struct lsquic_packet_in *packet_in)
{
assert(0 == packet_in->pi_refcnt);
if (packet_in->pi_flags & PI_OWN_DATA)
lsquic_mm_put_1370(mm, packet_in->pi_data);
TAILQ_INSERT_HEAD(&mm->free_packets_in, packet_in, pi_next);
}

78
src/liblsquic/lsquic_mm.h Normal file
View File

@ -0,0 +1,78 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_mm.h -- Memory manager.
*
* Allocators and in this class are meant to be used for the lifetime of
* QUIC engine.
*/
#ifndef LSQUIC_MM_H
#define LSQUIC_MM_H 1
struct lsquic_engine_public;
struct lsquic_packet_in;
struct lsquic_packet_out;
struct ack_info;
struct malo;
#define MM_N_OUT_BUCKETS 3
struct lsquic_mm {
struct ack_info *acki;
struct {
struct malo *stream_frame; /* For struct stream_frame */
struct malo *stream_rec_arr;/* For struct stream_rec_arr */
struct malo *packet_in; /* For struct lsquic_packet_in */
struct malo *packet_out; /* For struct lsquic_packet_out */
} malo;
TAILQ_HEAD(, lsquic_packet_in) free_packets_in;
SLIST_HEAD(, packet_out_buf) packet_out_bufs[MM_N_OUT_BUCKETS];
SLIST_HEAD(, payload_buf) payload_bufs;
SLIST_HEAD(, four_k_page) four_k_pages;
SLIST_HEAD(, sixteen_k_page) sixteen_k_pages;
};
int
lsquic_mm_init (struct lsquic_mm *);
void
lsquic_mm_cleanup (struct lsquic_mm *);
struct lsquic_packet_in *
lsquic_mm_get_packet_in (struct lsquic_mm *);
void
lsquic_mm_put_packet_in (struct lsquic_mm *, struct lsquic_packet_in *);
#define lsquic_packet_in_put(mm, p) do { \
assert((p)->pi_refcnt != 0); \
if (--(p)->pi_refcnt == 0) \
lsquic_mm_put_packet_in(mm, p); \
} while (0)
struct lsquic_packet_out *
lsquic_mm_get_packet_out (struct lsquic_mm *, struct malo *,
unsigned short size);
void
lsquic_mm_put_packet_out (struct lsquic_mm *, struct lsquic_packet_out *);
void *
lsquic_mm_get_1370 (struct lsquic_mm *);
void
lsquic_mm_put_1370 (struct lsquic_mm *, void *);
void *
lsquic_mm_get_4k (struct lsquic_mm *);
void
lsquic_mm_put_4k (struct lsquic_mm *, void *);
void *
lsquic_mm_get_16k (struct lsquic_mm *);
void
lsquic_mm_put_16k (struct lsquic_mm *, void *);
#endif

View File

@ -0,0 +1,180 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#include <assert.h>
#include <inttypes.h>
#include <stdint.h>
#ifndef NDEBUG
#include <stdlib.h> /* getenv */
#endif
#include <string.h>
#include "lsquic_types.h"
#include "lsquic_int_types.h"
#include "lsquic_pacer.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_out.h"
#include "lsquic_util.h"
#define LSQUIC_LOGGER_MODULE LSQLM_PACER
#define LSQUIC_LOG_CONN_ID pacer->pa_cid
#include "lsquic_logger.h"
#ifndef MAX
# define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
void
pacer_init (struct pacer *pacer, lsquic_cid_t cid, unsigned max_intertick)
{
memset(pacer, 0, sizeof(*pacer));
pacer->pa_burst_tokens = 10;
pacer->pa_cid = cid;
pacer->pa_max_intertick = max_intertick;
#ifndef NDEBUG
const char *val;
if ((val = getenv("LSQUIC_PACER_INTERTICK")))
{
pacer->pa_flags |= PA_CONSTANT_INTERTICK;
pacer->pa_intertick_avg = atoi(val);
}
#endif
}
void
pacer_packet_scheduled (struct pacer *pacer, unsigned n_in_flight,
int in_recovery, tx_time_f tx_time, void *tx_ctx)
{
lsquic_time_t delay, sched_time;
int app_limited, making_up;
if (n_in_flight == 0 && !in_recovery)
{
pacer->pa_burst_tokens = 10;
LSQ_DEBUG("%s: replenish tokens: %u", __func__, pacer->pa_burst_tokens);
}
if (pacer->pa_burst_tokens > 0)
{
--pacer->pa_burst_tokens;
pacer->pa_flags &= ~PA_LAST_SCHED_DELAYED;
pacer->pa_next_sched = 0;
pacer->pa_last_delayed = 0;
LSQ_DEBUG("%s: tokens: %u", __func__, pacer->pa_burst_tokens);
return;
}
sched_time = pacer->pa_now;
delay = tx_time(tx_ctx);
if (pacer->pa_flags & PA_LAST_SCHED_DELAYED)
{
pacer->pa_next_sched += delay;
app_limited = pacer->pa_last_delayed != 0
&& pacer->pa_last_delayed + delay <= sched_time;
making_up = pacer->pa_next_sched <= sched_time;
LSQ_DEBUG("making up: %d; app limited; %d", making_up, app_limited);
if (making_up && !app_limited)
pacer->pa_last_delayed = sched_time;
else
{
pacer->pa_flags &= ~PA_LAST_SCHED_DELAYED;
pacer->pa_last_delayed = 0;
}
}
else
pacer->pa_next_sched = MAX(pacer->pa_next_sched + delay,
sched_time + delay);
LSQ_DEBUG("next_sched is set to %"PRIu64" usec from now",
pacer->pa_next_sched - lsquic_time_now());
}
void
pacer_loss_event (struct pacer *pacer)
{
pacer->pa_burst_tokens = 0;
LSQ_DEBUG("%s: tokens: %u", __func__, pacer->pa_burst_tokens);
}
static unsigned
clock_granularity (const struct pacer *pacer)
{
#ifndef NDEBUG
if (pacer->pa_flags & PA_CONSTANT_INTERTICK)
return pacer->pa_max_intertick;
#endif
return pacer->pa_intertick_var;
}
int
pacer_can_schedule (struct pacer *pacer, unsigned n_in_flight)
{
int can;
if (pacer->pa_burst_tokens > 0 || n_in_flight == 0)
can = 1;
else if (pacer->pa_next_sched > pacer->pa_now + clock_granularity(pacer))
{
pacer->pa_flags |= PA_LAST_SCHED_DELAYED;
can = 0;
}
else
can = 1;
LSQ_DEBUG("%s: %d", __func__, can);
return can;
}
#define ALPHA_SHIFT 3
#define BETA_SHIFT 2
static void
update_avg_intertick (struct pacer *pacer, unsigned intertick)
{
unsigned diff;
#ifndef NDEBUG
if (pacer->pa_flags & PA_CONSTANT_INTERTICK)
return;
#endif
if (pacer->pa_intertick_avg)
{
if (intertick > pacer->pa_intertick_avg)
diff = intertick - pacer->pa_intertick_avg;
else
diff = pacer->pa_intertick_avg - intertick;
pacer->pa_intertick_var -= pacer->pa_intertick_var >> BETA_SHIFT;
pacer->pa_intertick_var += diff >> BETA_SHIFT;
pacer->pa_intertick_avg -= pacer->pa_intertick_avg >> ALPHA_SHIFT;
pacer->pa_intertick_avg += intertick >> ALPHA_SHIFT;
}
else
{
pacer->pa_intertick_avg = intertick;
pacer->pa_intertick_var = intertick >> 1;
}
}
void
pacer_tick (struct pacer *pacer, lsquic_time_t now)
{
unsigned intertick;
assert(now > pacer->pa_now);
if (pacer->pa_now)
{
assert(now - pacer->pa_now < (1ULL << sizeof(unsigned) * 8));
intertick = now - pacer->pa_now;
LSQ_DEBUG("intertick estimate: %u; real value: %u; error: %d",
clock_granularity(pacer), intertick,
(int) clock_granularity(pacer) - (int) intertick);
update_avg_intertick(pacer, intertick);
}
pacer->pa_now = now;
}

View File

@ -0,0 +1,54 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef LSQUIC_PACER_H
#define LSQUIC_PACER_H 1
struct pacer
{
lsquic_cid_t pa_cid; /* Used for logging */
lsquic_time_t pa_next_sched;
lsquic_time_t pa_last_delayed;
lsquic_time_t pa_now;
/* All tick times are in microseconds */
unsigned pa_max_intertick; /* Maximum intertick time */
/* We keep an average of intertick times, which is our best estimate
* for the time when the connection ticks next. This estimate is used
* to see whether a packet can be scheduled or not.
*/
unsigned pa_intertick_avg; /* Smoothed average */
unsigned pa_intertick_var; /* Variance */
unsigned short pa_packet_size;
unsigned char pa_burst_tokens;
enum {
PA_LAST_SCHED_DELAYED = (1 << 0),
#ifndef NDEBUG
PA_CONSTANT_INTERTICK = (1 << 1), /* Use fake intertick time for testing */
#endif
} pa_flags:8;
};
typedef lsquic_time_t (*tx_time_f)(void *ctx);
void
pacer_init (struct pacer *, lsquic_cid_t, unsigned max_intertick);
void
pacer_tick (struct pacer *, lsquic_time_t);
int
pacer_can_schedule (struct pacer *, unsigned n_in_flight);
void
pacer_packet_scheduled (struct pacer *pacer, unsigned n_in_flight,
int in_recovery, tx_time_f tx_time, void *tx_ctx);
void
pacer_loss_event (struct pacer *);
#define pacer_next_sched(pacer) (+(pacer)->pa_next_sched)
#endif

View File

@ -0,0 +1,123 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_packet_common.c -- some common packet-related routines
*/
#include <stdio.h>
#include <stdlib.h>
#include "lsquic_logger.h"
#include "lsquic_packet_common.h"
static const char * const frame_type_2_str[N_QUIC_FRAMES] = {
[QUIC_FRAME_INVALID] = "QUIC_FRAME_INVALID",
[QUIC_FRAME_STREAM] = "QUIC_FRAME_STREAM",
[QUIC_FRAME_ACK] = "QUIC_FRAME_ACK",
[QUIC_FRAME_PADDING] = "QUIC_FRAME_PADDING",
[QUIC_FRAME_RST_STREAM] = "QUIC_FRAME_RST_STREAM",
[QUIC_FRAME_CONNECTION_CLOSE] = "QUIC_FRAME_CONNECTION_CLOSE",
[QUIC_FRAME_GOAWAY] = "QUIC_FRAME_GOAWAY",
[QUIC_FRAME_WINDOW_UPDATE] = "QUIC_FRAME_WINDOW_UPDATE",
[QUIC_FRAME_BLOCKED] = "QUIC_FRAME_BLOCKED",
[QUIC_FRAME_STOP_WAITING] = "QUIC_FRAME_STOP_WAITING",
[QUIC_FRAME_PING] = "QUIC_FRAME_PING",
};
#define SLEN(x) (sizeof(#x) - sizeof("QUIC_FRAME_"))
const size_t lsquic_frame_types_str_sz =
/* We don't need to include INVALID frame in this list because it is
* never a part of any frame list bitmask (e.g. po_frame_types).
*/
SLEN(QUIC_FRAME_STREAM) + 1 +
SLEN(QUIC_FRAME_ACK) + 1 +
SLEN(QUIC_FRAME_PADDING) + 1 +
SLEN(QUIC_FRAME_RST_STREAM) + 1 +
SLEN(QUIC_FRAME_CONNECTION_CLOSE) + 1 +
SLEN(QUIC_FRAME_GOAWAY) + 1 +
SLEN(QUIC_FRAME_WINDOW_UPDATE) + 1 +
SLEN(QUIC_FRAME_BLOCKED) + 1 +
SLEN(QUIC_FRAME_STOP_WAITING) + 1 +
SLEN(QUIC_FRAME_PING) + 1;
const char *
lsquic_frame_types_to_str (char *buf, size_t bufsz, short frame_types)
{
char *p;
int i, w;
size_t sz;
if (bufsz > 0)
buf[0] = '\0';
p = buf;
for (i = 0; i < N_QUIC_FRAMES; ++i)
{
if (frame_types & (1 << i))
{
sz = bufsz - (p - buf);
w = snprintf(p, sz, "%.*s%s", p > buf, " ",
frame_type_2_str[i] + sizeof("QUIC_FRAME_") - 1);
if (w > (int) sz)
{
LSQ_WARN("not enough room for all frame types");
break;
}
p += w;
}
frame_types &= ~(1 << i);
}
return buf;
}
enum lsquic_packno_bits
calc_packno_bits (lsquic_packno_t packno, lsquic_packno_t least_unacked,
uint64_t n_in_flight)
{
uint64_t delta;
unsigned bits;
delta = packno - least_unacked;
if (n_in_flight > delta)
delta = n_in_flight;
delta *= 4;
bits = (delta > (1ULL << 8))
+ (delta > (1ULL << 16))
+ (delta > (1ULL << 32));
return bits;
}
lsquic_packno_t
restore_packno (lsquic_packno_t cur_packno,
enum lsquic_packno_bits cur_packno_bits,
lsquic_packno_t max_packno)
{
lsquic_packno_t candidates[3], epoch_delta;
int64_t diffs[3];
unsigned min, len;
len = packno_bits2len(cur_packno_bits);
epoch_delta = 1ULL << (len << 3);
candidates[1] = (max_packno & ~(epoch_delta - 1)) + cur_packno;
candidates[0] = candidates[1] - epoch_delta;
candidates[2] = candidates[1] + epoch_delta;
diffs[0] = llabs((int64_t) candidates[0] - (int64_t) max_packno);
diffs[1] = llabs((int64_t) candidates[1] - (int64_t) max_packno);
diffs[2] = llabs((int64_t) candidates[2] - (int64_t) max_packno);
min = diffs[1] < diffs[0];
if (diffs[2] < diffs[min])
min = 2;
return candidates[min];
}

View File

@ -0,0 +1,118 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef LSQUIC_PACKET_COMMON_H
#define LSQUIC_PACKET_COMMON_H 1
#include <stdint.h>
#include "lsquic_int_types.h"
enum PACKET_PUBLIC_FLAGS
{
PACKET_PUBLIC_FLAGS_VERSION = 1,
PACKET_PUBLIC_FLAGS_RST = 2,
PACKET_PUBLIC_FLAGS_NONCE = 4,
PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID = 8,
PACKET_PUBLIC_FLAGS_MULTIPATH = 1 << 6,
PACKET_PUBLIC_FLAGS_TWO_OR_MORE_BYTES = 1 << 7,
};
enum QUIC_FRAME_TYPE
{
QUIC_FRAME_INVALID,
/*Special*/
QUIC_FRAME_STREAM,
QUIC_FRAME_ACK,
/*Regular*/
QUIC_FRAME_PADDING,
QUIC_FRAME_RST_STREAM,
QUIC_FRAME_CONNECTION_CLOSE,
QUIC_FRAME_GOAWAY,
QUIC_FRAME_WINDOW_UPDATE,
QUIC_FRAME_BLOCKED,
QUIC_FRAME_STOP_WAITING,
QUIC_FRAME_PING,
N_QUIC_FRAMES
};
extern const size_t lsquic_frame_types_str_sz;
const char *
lsquic_frame_types_to_str (char *buf, size_t bufsz, short frame_types);
#define QFRAME_REGEN_MASK ((1 << QUIC_FRAME_ACK) \
| (1 << QUIC_FRAME_STOP_WAITING))
#define QFRAME_REGENERATE(frame_type) ((1 << (frame_type)) & QFRAME_REGEN_MASK)
#define QFRAME_ACKABLE_MASK ( \
(1 << QUIC_FRAME_STREAM) \
| (1 << QUIC_FRAME_RST_STREAM) \
| (1 << QUIC_FRAME_GOAWAY) \
| (1 << QUIC_FRAME_WINDOW_UPDATE) \
| (1 << QUIC_FRAME_PING) \
| (1 << QUIC_FRAME_BLOCKED) \
)
#define QFRAME_ACKABLE(frame_type) ((1 << (frame_type)) & QFRAME_ACKABLE_MASK)
#define QFRAME_RETRANSMITTABLE_MASK ( \
(1 << QUIC_FRAME_STREAM) \
| (1 << QUIC_FRAME_RST_STREAM) \
| (1 << QUIC_FRAME_GOAWAY) \
| (1 << QUIC_FRAME_WINDOW_UPDATE) \
| (1 << QUIC_FRAME_BLOCKED) \
| (1 << QUIC_FRAME_CONNECTION_CLOSE) \
)
#define QFRAME_RETRANSMITTABLE(frame_type) \
((1 << (frame_type)) & QFRAME_RETRANSMITTABLE_MASK)
#define QUIC_MAX_PUBHDR_SZ (1 /* Type */ + 8 /* CID */ + 4 /* Version */ \
+ 32 /* Nonce */ + 6 /* Packet Number */ )
#define QUIC_MIN_PUBHDR_SZ (1 /* Type */ + 1 /* Packet number */)
/* 12 bytes of FNV hash or encryption IV */
#define QUIC_PACKET_HASH_SZ 12
/* [draft-hamilton-quic-transport-protocol-01], Section 7 */
#define QUIC_MAX_IPv4_PACKET_SZ 1370
#define QUIC_MAX_IPv6_PACKET_SZ 1350
#define QUIC_MAX_PACKET_SZ (QUIC_MAX_IPv4_PACKET_SZ > \
QUIC_MAX_IPv6_PACKET_SZ ? QUIC_MAX_IPv4_PACKET_SZ : QUIC_MAX_IPv6_PACKET_SZ)
#define QUIC_MIN_PACKET_OVERHEAD (QUIC_PACKET_HASH_SZ + QUIC_MIN_PUBHDR_SZ)
#define QUIC_MAX_PAYLOAD_SZ (QUIC_MAX_PACKET_SZ - QUIC_MIN_PACKET_OVERHEAD)
#define QUIC_WUF_SZ 13 /* Type (1) + Stream ID (4) + Offset (8) */
#define QUIC_BLOCKED_FRAME_SZ 5 /* Type (1) + Stream ID (4) */
#define QUIC_RST_STREAM_SZ 17 /* Type (1) + Stream ID (4) + Offset (8) +
Error code (4) */
#define QUIC_GOAWAY_FRAME_SZ 11 /* Type (1) + Error code (4) + Stream ID (4) +
Reason phrase length (2) */
/* Bitmask to be used as bits 4 and 5 (0x30) in common header's flag field: */
enum lsquic_packno_bits
{
PACKNO_LEN_1 = 0,
PACKNO_LEN_2 = 1,
PACKNO_LEN_4 = 2,
PACKNO_LEN_6 = 3,
};
enum lsquic_packno_bits
calc_packno_bits (lsquic_packno_t packno, lsquic_packno_t least_unacked,
uint64_t n_in_flight);
#define packno_bits2len(b) (((b) << 1) + !(b))
lsquic_packno_t
restore_packno (lsquic_packno_t cur_packno,
enum lsquic_packno_bits cur_packno_bits,
lsquic_packno_t max_packno);
#endif

View File

@ -0,0 +1,35 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#include <assert.h>
#include <stdint.h>
#include <string.h>
#include "lsquic_int_types.h"
#include "lsquic_types.h"
#include "lsquic_packet_in.h"
int
packet_in_ver_first (const lsquic_packet_in_t *packet_in, struct ver_iter *vi,
lsquic_ver_tag_t *ver_tag)
{
vi->packet_in = packet_in;
vi->off = packet_in->pi_quic_ver;
return packet_in_ver_next(vi, ver_tag);
}
int
packet_in_ver_next (struct ver_iter *vi, lsquic_ver_tag_t *ver_tag)
{
if (vi->off + 4 <= vi->packet_in->pi_header_sz)
{
memcpy(ver_tag, vi->packet_in->pi_data + vi->off, 4);
vi->off += 4;
return 1;
}
else
{
assert(vi->packet_in->pi_header_sz == vi->off);
return 0;
}
}

View File

@ -0,0 +1,101 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_packet_in.h
*/
#ifndef LSQUIC_PACKET_IN_H
#define LSQUIC_PACKET_IN_H 1
#include <sys/queue.h>
struct lsquic_packet_in;
struct data_frame
{
const unsigned char *df_data; /* Pointer to data */
uint64_t df_offset; /* Stream offset */
uint16_t df_read_off; /* Read offset */
uint16_t df_size; /* Size of df_data */
signed char df_fin; /* FIN? */
};
typedef struct stream_frame
{
/* Stream frames are stored in a list inside stream. */
TAILQ_ENTRY(stream_frame) next_frame;
/* `data' points somewhere into the packet payload. The packet object
* is reference-counted. When the frame is freed, the packet is released
* via lsquic_packet_put(). If data_length is zero, the frame does not
* keep a reference to the incoming packet and this pointer is not set.
*/
struct lsquic_packet_in *packet_in;
struct data_frame data_frame;
uint32_t stream_id; /* Parsed from packet */
} stream_frame_t;
typedef struct lsquic_packet_in
{
TAILQ_ENTRY(lsquic_packet_in) pi_next;
lsquic_time_t pi_received; /* Time received */
lsquic_cid_t pi_conn_id;
lsquic_packno_t pi_packno;
unsigned short pi_header_sz; /* Points to payload */
unsigned short pi_data_sz; /* Data plus header */
/* A packet may be referred to by one or more frames and packets_in
* list.
*/
unsigned short pi_refcnt;
short pi_frame_types;
unsigned short pi_hsk_stream; /* Offset to handshake stream
* frame, only valid if
* PI_HSK_STREAM is set.
*/
unsigned char pi_quic_ver; /* Offset to QUIC version */
unsigned char pi_nonce; /* Offset to nonce */
enum {
PI_DECRYPTED = (1 << 0),
PI_OWN_DATA = (1 << 1), /* We own pi_data */
PI_CONN_ID = (1 << 2), /* pi_conn_id is set */
} pi_flags:8;
/* If PI_OWN_DATA flag is not set, `pi_data' points to user-supplied
* packet data, which is NOT TO BE MODIFIED.
*/
unsigned char *pi_data;
} lsquic_packet_in_t;
#define lsquic_packet_in_public_flags(p) ((p)->pi_data[0])
#define lsquic_packet_in_is_prst(p) \
(lsquic_packet_in_public_flags(p) & PACKET_PUBLIC_FLAGS_RST)
#define lsquic_packet_in_packno_bits(p) \
((lsquic_packet_in_public_flags(p) >> 4) & 3)
#define lsquic_packet_in_upref(p) (++(p)->pi_refcnt)
#define lsquic_packet_in_get(p) (lsquic_packet_in_upref(p), (p))
#define lsquic_packet_in_nonce(p) \
((p)->pi_nonce ? (p)->pi_data + (p)->pi_nonce : NULL)
/* The version iterator is used on a version negotiation packet only.
* The iterator functions return 1 when next version is returned and
* 0 when there are no more versions.
*/
struct ver_iter
{
const struct lsquic_packet_in *packet_in;
unsigned off;
};
int
packet_in_ver_first (const lsquic_packet_in_t *packet_in, struct ver_iter *,
lsquic_ver_tag_t *ver_tag);
int
packet_in_ver_next (struct ver_iter *, lsquic_ver_tag_t *ver_tag);
#endif

View File

@ -0,0 +1,303 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_packet_out.c
*/
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include "lsquic.h"
#include "lsquic_int_types.h"
#include "lsquic_malo.h"
#include "lsquic_mm.h"
#include "lsquic_engine_public.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_in.h"
#include "lsquic_packet_out.h"
#include "lsquic_parse.h"
#include "lsquic_sfcw.h"
#include "lsquic_stream.h"
#include "lsquic_logger.h"
typedef char _stream_rec_arr_is_at_most_64bytes[
(sizeof(struct stream_rec_arr) <= 64) - 1];
struct stream_rec *
posi_first (struct packet_out_srec_iter *posi,
lsquic_packet_out_t *packet_out)
{
posi->packet_out = packet_out;
posi->past_srec = 0;
return posi_next(posi);
}
struct stream_rec *
posi_next (struct packet_out_srec_iter *posi)
{
if (posi->past_srec)
{
while (posi->cur_srec_arr)
{
for (; posi->srec_idx < sizeof(posi->cur_srec_arr->srecs) / sizeof(posi->cur_srec_arr->srecs[0]);
++posi->srec_idx)
{
if (posi->cur_srec_arr->srecs[ posi->srec_idx ].sr_frame_types)
return &posi->cur_srec_arr->srecs[ posi->srec_idx++ ];
}
posi->cur_srec_arr = STAILQ_NEXT(posi->cur_srec_arr, next_stream_rec_arr);
posi->srec_idx = 0;
}
return NULL;
}
else
{
++posi->past_srec;
posi->cur_srec_arr = STAILQ_FIRST(&posi->packet_out->po_srec_arrs);
posi->srec_idx = 0;
if (posi->packet_out->po_srec.sr_frame_types)
return &posi->packet_out->po_srec;
return posi_next(posi);
}
}
/* Assumption: there can only be one STREAM and only one RST_STREAM frame
* for a particular stream per packet. The latter is true because a stream
* will only send out one of them. The former is true due the way packets
* are filled: stream will write out STREAM frame as large as it can.
*
* Assumption: frames are added to the packet_out in order of their placement
* in packet_out->po_data. There is an assertion in this function that guards
* for this.
*/
int
lsquic_packet_out_add_stream (lsquic_packet_out_t *packet_out,
struct lsquic_mm *mm,
struct lsquic_stream *new_stream,
enum QUIC_FRAME_TYPE frame_type,
unsigned short off)
{
struct packet_out_srec_iter posi;
struct stream_rec_arr *srec_arr;
struct stream_rec *srec;
for (srec = posi_first(&posi, packet_out); srec; srec = posi_next(&posi))
if (srec->sr_stream == new_stream)
{
switch (frame_type)
{
case QUIC_FRAME_STREAM:
assert(!(srec->sr_frame_types & (1 << QUIC_FRAME_STREAM)));
srec->sr_frame_types |= (1 << QUIC_FRAME_STREAM);
srec->sr_off = off;
break;
default:
assert(QUIC_FRAME_RST_STREAM == frame_type);
assert(!(srec->sr_frame_types & (1 << QUIC_FRAME_RST_STREAM)));
srec->sr_frame_types |= (1 << QUIC_FRAME_RST_STREAM);
break;
}
return 0; /* Update existing record */
}
else if (srec->sr_frame_types & (1 << QUIC_FRAME_STREAM) & (1 << frame_type))
assert(srec->sr_off < off); /* Check that STREAM frames are added in order */
++new_stream->n_unacked;
if (!srec_taken(&packet_out->po_srec))
{
packet_out->po_srec.sr_frame_types = (1 << frame_type);
packet_out->po_srec.sr_stream = new_stream;
packet_out->po_srec.sr_off = off;
return 0; /* Insert in first slot */
}
STAILQ_FOREACH(srec_arr, &packet_out->po_srec_arrs, next_stream_rec_arr)
{
unsigned i;
for (i = 0; i < sizeof(srec_arr->srecs) / sizeof(srec_arr->srecs[0]); ++i)
if (!srec_taken(&srec_arr->srecs[i]))
{
srec_arr->srecs[i].sr_frame_types = (1 << frame_type);
srec_arr->srecs[i].sr_stream = new_stream;
srec_arr->srecs[i].sr_off = off;
return 0; /* Insert in existing srec */
}
}
srec_arr = lsquic_malo_get(mm->malo.stream_rec_arr);
if (!srec_arr)
return -1;
memset(srec_arr, 0, sizeof(*srec_arr));
srec_arr->srecs[0].sr_frame_types = (1 << frame_type);
srec_arr->srecs[0].sr_stream = new_stream;
srec_arr->srecs[0].sr_off = off;
STAILQ_INSERT_TAIL(&packet_out->po_srec_arrs, srec_arr, next_stream_rec_arr);
return 0; /* Insert in new srec */
}
lsquic_packet_out_t *
lsquic_packet_out_new (struct lsquic_mm *mm, struct malo *malo, int use_cid,
unsigned short max_size, enum lsquic_packno_bits bits,
const lsquic_ver_tag_t *ver_tag, const unsigned char *nonce)
{
lsquic_packet_out_t *packet_out;
enum packet_out_flags flags;
unsigned short header_size;
flags = bits << POBIT_SHIFT;
if (ver_tag)
flags |= PO_VERSION;
if (nonce)
flags |= PO_NONCE;
if (use_cid)
flags |= PO_CONN_ID;
header_size = lsquic_po_header_length(flags);
if (header_size + QUIC_PACKET_HASH_SZ >= max_size)
{
errno = EINVAL;
return NULL;
}
packet_out = lsquic_mm_get_packet_out(mm, malo, max_size - header_size
- QUIC_PACKET_HASH_SZ);
if (!packet_out)
return NULL;
packet_out->po_flags = PO_WRITEABLE | flags;
if (ver_tag)
packet_out->po_ver_tag = *ver_tag;
if (nonce)
{
/* Nonces are allocated for a very small number of packets. This
* memory is too expensive to carry in every packet.
*/
packet_out->po_nonce = malloc(32);
if (!packet_out->po_nonce)
{
lsquic_mm_put_packet_out(mm, packet_out);
return NULL;
}
memcpy(packet_out->po_nonce, nonce, 32);
}
return packet_out;
}
void
lsquic_packet_out_destroy (lsquic_packet_out_t *packet_out,
struct lsquic_engine_public *enpub)
{
struct stream_rec_arr *srec_arr;
while ((srec_arr = STAILQ_FIRST(&packet_out->po_srec_arrs)))
{
STAILQ_REMOVE_HEAD(&packet_out->po_srec_arrs, next_stream_rec_arr);
lsquic_malo_put(srec_arr);
}
if (packet_out->po_flags & PO_ENCRYPTED)
enpub->enp_pmi->pmi_release(enpub->enp_pmi_ctx,
packet_out->po_enc_data);
if (packet_out->po_nonce)
free(packet_out->po_nonce);
lsquic_mm_put_packet_out(&enpub->enp_mm, packet_out);
}
/* If `stream_id' is zero, stream frames from all reset streams are elided.
* Otherwise, elision is limited to the specified stream.
*/
void
lsquic_packet_out_elide_reset_stream_frames (lsquic_packet_out_t *packet_out,
const struct parse_funcs *pf,
uint32_t stream_id)
{
struct packet_out_srec_iter posi;
struct stream_rec *srec;
struct stream_frame frame;
unsigned short adj = 0;
int n_stream_frames = 0, n_elided = 0;
int victim;
for (srec = posi_first(&posi, packet_out); srec; srec = posi_next(&posi))
{
if (srec->sr_frame_types & (1 << QUIC_FRAME_STREAM))
{
++n_stream_frames;
/* Offsets of all STREAM frames should be adjusted */
srec->sr_off -= adj;
if (stream_id)
{
victim = srec->sr_stream->id == stream_id;
if (victim)
{
assert(lsquic_stream_is_reset(srec->sr_stream));
}
}
else
victim = lsquic_stream_is_reset(srec->sr_stream);
if (victim)
{
++n_elided;
const int len =
pf->pf_parse_stream_frame(packet_out->po_data + srec->sr_off,
packet_out->po_data_sz - srec->sr_off, &frame);
if (len < 0)
{ /* This is pretty severe: we should be able to parse our own
* frames. Should this abort the connection?
*/
LSQ_ERROR("can't parse our own stream frame");
return;
}
assert(frame.stream_id == srec->sr_stream->id);
/* Move the data and adjust sizes */
adj += len;
memmove(packet_out->po_data + srec->sr_off,
packet_out->po_data + srec->sr_off + len,
packet_out->po_data_sz - srec->sr_off - len);
packet_out->po_data_sz -= len;
/* See what we can do with the stream */
srec->sr_frame_types &= ~(1 << QUIC_FRAME_STREAM);
if (!srec_taken(srec))
lsquic_stream_acked(srec->sr_stream);
}
}
}
assert(n_stream_frames);
if (n_elided == n_stream_frames)
packet_out->po_frame_types &= ~(1 << QUIC_FRAME_STREAM);
}
void
lsquic_packet_out_chop_regen (lsquic_packet_out_t *packet_out)
{
struct packet_out_srec_iter posi;
struct stream_rec *srec;
unsigned delta;
delta = packet_out->po_regen_sz;
packet_out->po_data_sz -= delta;
memmove(packet_out->po_data, packet_out->po_data + delta,
packet_out->po_data_sz);
packet_out->po_regen_sz = 0;
for (srec = posi_first(&posi, packet_out); srec; srec = posi_next(&posi))
if (srec->sr_frame_types & (1 << QUIC_FRAME_STREAM))
srec->sr_off -= delta;
}

View File

@ -0,0 +1,162 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_packet_out.h -- Structure and routines dealing with packet_out
*/
#ifndef LSQUIC_PACKET_OUT_H
#define LSQUIC_PACKET_OUT_H 1
#include <sys/queue.h>
struct malo;
struct lsquic_engine_public;
struct lsquic_mm;
struct lsquic_stream;
struct parse_funcs;
/* Each stream_rec is associated with one packet_out. packet_out can have
* zero or more stream_rec structures. stream_rec keeps a pointer to a stream
* that has STREAM or RST_STREAM frames inside packet_out. `sr_frame_types'
* is a bitmask that records which of these two frames are in the packet.
* If this value is zero, `sr_stream' and `sr_off' values are not valid.
* `sr_off' indicates where inside packet_out->po_data STREAM frame begins.
*
* We need this information for two reasons:
* 1. A stream is not destroyed until all of its STREAM and RST_STREAM
* frames are acknowledged. This is to make sure that we do not exceed
* maximum allowed number of streams.
* 2. When a packet is resubmitted, STREAM frames for a stream that has
* been reset are not to be resubmitted.
*/
struct stream_rec {
struct lsquic_stream *sr_stream;
unsigned short sr_off;
short sr_frame_types;
};
#define srec_taken(srec) ((srec)->sr_frame_types)
struct stream_rec_arr {
STAILQ_ENTRY(stream_rec_arr) next_stream_rec_arr;
struct stream_rec srecs[
( 64 /* Efficient size for malo allocator */
- sizeof(SLIST_ENTRY(stream_rec)) /* next_stream_rec */
) / sizeof(struct stream_rec)
];
};
typedef struct lsquic_packet_out
{
/* `po_next' is used for packets_out, unacked_packets and expired_packets
* lists.
*/
TAILQ_ENTRY(lsquic_packet_out)
po_next;
lsquic_time_t po_sent; /* Time sent */
lsquic_packno_t po_packno;
/* A lot of packets contain data belonging to only one stream. Thus,
* `srec' is used first. If this is not enough, any number of
* stream_rec_arr structures can be allocated to handle more stream
* records.
*/
struct stream_rec po_srec;
STAILQ_HEAD(, stream_rec_arr)
po_srec_arrs;
/* If PO_ENCRYPTED is set, this points to the buffer that holds encrypted
* data.
*/
unsigned char *po_enc_data;
lsquic_ver_tag_t po_ver_tag; /* Set if PO_VERSION is set */
short po_frame_types; /* Bitmask of QUIC_FRAME_* */
unsigned short po_data_sz; /* Number of usable bytes in data */
unsigned short po_enc_data_sz; /* Number of usable bytes in data */
unsigned short po_regen_sz; /* Number of bytes at the beginning
* of data containing bytes that are
* not to be retransmitted, e.g. ACK
* frames.
*/
unsigned short po_n_alloc; /* Total number of bytes allocated in po_data */
enum packet_out_flags {
PO_HELLO = (1 << 1), /* Packet contains SHLO or CHLO data */
PO_ENCRYPTED= (1 << 3), /* po_enc_data has encrypted data */
PO_WRITEABLE= (1 << 4), /* Packet is writeable */
#define POBIT_SHIFT 5
PO_BITS_0 = (1 << 5), /* PO_BITS_0 and PO_BITS_1 encode the */
PO_BITS_1 = (1 << 6), /* packet number length. See macros below. */
PO_NONCE = (1 << 7), /* Use value in `po_nonce' to generate header */
PO_VERSION = (1 << 8), /* Use value in `po_ver_tag' to generate header */
PO_CONN_ID = (1 << 9), /* Include connection ID in public header */
PO_REPACKNO = (1 <<10), /* Regenerate packet number */
PO_NOENCRYPT= (1 <<11), /* Do not encrypt data in po_data */
PO_VERNEG = (1 <<12), /* Version negotiation packet. */
} po_flags:16;
unsigned char *po_nonce; /* Use to generate header if PO_NONCE is set */
unsigned char *po_data;
} lsquic_packet_out_t;
/* The size of lsquic_packet_out_t could be further reduced:
*
* po_ver_tag could be encoded as a few bits representing enum lsquic_version
* in po_flags. The cost is a bit of complexity. This will save us four bytes.
*/
#define lsquic_packet_out_avail(p) ((unsigned short) \
((p)->po_n_alloc - (p)->po_data_sz))
#define lsquic_packet_out_packno_bits(p) (((p)->po_flags >> POBIT_SHIFT) & 0x3)
/* XXX This will need to be made into a method for Q041 */
#define lsquic_po_header_length(po_flags) ( \
1 /* Type */ \
+ (!!((po_flags) & PO_CONN_ID) << 3) /* Connection ID */ \
+ (!!((po_flags) & PO_VERSION) << 2) /* Version */ \
+ (!!((po_flags) & PO_NONCE) << 5) /* Nonce */ \
+ packno_bits2len(((po_flags) >> POBIT_SHIFT) & 0x3) /* Packet number */ \
)
#define lsquic_packet_out_verneg(p) \
(((p)->po_flags & (PO_NOENCRYPT|PO_VERNEG)) == (PO_NOENCRYPT|PO_VERNEG))
#define lsquic_packet_out_pubres(p) \
(((p)->po_flags & (PO_NOENCRYPT|PO_VERNEG)) == PO_NOENCRYPT )
struct packet_out_srec_iter {
lsquic_packet_out_t *packet_out;
struct stream_rec_arr *cur_srec_arr;
unsigned srec_idx;
int past_srec;
};
struct stream_rec *
posi_first (struct packet_out_srec_iter *posi, lsquic_packet_out_t *);
struct stream_rec *
posi_next (struct packet_out_srec_iter *posi);
lsquic_packet_out_t *
lsquic_packet_out_new (struct lsquic_mm *, struct malo *, int use_cid,
unsigned short size, enum lsquic_packno_bits,
const lsquic_ver_tag_t *, const unsigned char *nonce);
void
lsquic_packet_out_destroy (lsquic_packet_out_t *,
struct lsquic_engine_public *);
int
lsquic_packet_out_add_stream (lsquic_packet_out_t *packet_out,
struct lsquic_mm *mm,
struct lsquic_stream *new_stream,
enum QUIC_FRAME_TYPE,
unsigned short off);
void
lsquic_packet_out_elide_reset_stream_frames (lsquic_packet_out_t *,
const struct parse_funcs *, uint32_t);
void
lsquic_packet_out_chop_regen (lsquic_packet_out_t *);
#endif

View File

@ -0,0 +1,138 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_packints.c -- Packet intervals implementation.
*/
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/queue.h>
#include "lsquic_int_types.h"
#include "lsquic_packints.h"
void
lsquic_packints_init (struct packints *pints)
{
TAILQ_INIT(&pints->pk_intervals);
pints->pk_cur = NULL;
}
void
lsquic_packints_cleanup (struct packints *pints)
{
struct packet_interval *pi, *next;
for (pi = TAILQ_FIRST(&pints->pk_intervals); pi; pi = next)
{
next = TAILQ_NEXT(pi, next_pi);
free(pi);
}
}
static int
grow_pi (struct packet_interval *pi, lsquic_packno_t packno)
{
if (pi->range.low - 1 == packno) {
--pi->range.low;
return 1;
}
if (pi->range.high + 1 == packno) {
++pi->range.high;
return 1;
}
return 0;
}
#if LSQUIC_PACKINTS_SANITY_CHECK
void
lsquic_packints_sanity_check (const struct packints *packints)
{
struct packet_interval *pi;
uint64_t prev_high;
prev_high = 0;
TAILQ_FOREACH(pi, &packints->pk_intervals, next_pi)
{
if (prev_high)
{
assert(pi->range.high + 1 < prev_high);
assert(pi->range.high >= pi->range.low);
}
else
prev_high = pi->range.high;
}
}
#endif
enum packints_status
lsquic_packints_add (struct packints *pints, lsquic_packno_t packno)
{
struct packet_interval *pi, *prev;
prev = NULL;
TAILQ_FOREACH(pi, &pints->pk_intervals, next_pi)
{
if (packno <= pi->range.high)
{
if (packno >= pi->range.low)
return PACKINTS_DUP;
} else {
if (packno > pi->range.high)
break;
}
prev = pi;
}
if ((prev && grow_pi(prev, packno)) || (pi && grow_pi(pi, packno)))
{
if (prev && pi && (prev->range.low - 1 == pi->range.high)) {
prev->range.low = pi->range.low;
TAILQ_REMOVE(&pints->pk_intervals, pi, next_pi);
free(pi);
}
}
else
{
struct packet_interval *newpi = malloc(sizeof(*newpi));
if (!newpi)
return PACKINTS_ERR;
newpi->range.low = newpi->range.high = packno;
if (pi)
TAILQ_INSERT_BEFORE(pi, newpi, next_pi);
else
TAILQ_INSERT_TAIL(&pints->pk_intervals, newpi, next_pi);
}
lsquic_packints_sanity_check(pints);
return PACKINTS_OK;
}
const struct lsquic_packno_range *
lsquic_packints_first (struct packints *pints)
{
pints->pk_cur = TAILQ_FIRST(&pints->pk_intervals);
return lsquic_packints_next(pints);
}
const struct lsquic_packno_range *
lsquic_packints_next (struct packints *pints)
{
const struct lsquic_packno_range *range;
if (pints->pk_cur)
{
range = &pints->pk_cur->range;
pints->pk_cur = TAILQ_NEXT(pints->pk_cur, next_pi);
return range;
}
else
return NULL;
}

View File

@ -0,0 +1,47 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_packints.h -- Ordered (high to low) list of packet intervals.
*/
#ifndef LSQUIC_PACKINTS_H
#define LSQUIC_PACKINTS_H 1
#define LSQUIC_PACKINTS_SANITY_CHECK 0
#include <sys/queue.h>
struct packet_interval {
TAILQ_ENTRY(packet_interval) next_pi;
struct lsquic_packno_range range;
};
struct packints {
TAILQ_HEAD(, packet_interval) pk_intervals;
struct packet_interval *pk_cur;
};
void
lsquic_packints_init (struct packints *);
void
lsquic_packints_cleanup (struct packints *);
enum packints_status { PACKINTS_OK, PACKINTS_DUP, PACKINTS_ERR, };
enum packints_status
lsquic_packints_add (struct packints *, lsquic_packno_t);
const struct lsquic_packno_range *
lsquic_packints_first (struct packints *);
const struct lsquic_packno_range *
lsquic_packints_next (struct packints *);
#if LSQUIC_PACKINTS_SANITY_CHECK
void
lsquic_packints_sanity_check (const struct packints *);
#else
# define lsquic_packints_sanity_check(pints)
#endif
#endif

View File

@ -0,0 +1,211 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef LSQUIC_PARSE_H
#define LSQUIC_PARSE_H 1
#include <stdint.h>
#include "lsquic_packet_common.h"
struct lsquic_packet_in;
struct stream_frame;
#define LSQUIC_PARSE_ACK_TIMESTAMPS 0
typedef struct ack_info
{
unsigned n_timestamps; /* 0 to 255 */
unsigned n_ranges; /* This is at least 1 */
/* Largest acked is ack_info.ranges[0].high */
lsquic_time_t lack_delta;
struct {
lsquic_packno_t high, low;
} ranges[256];
#if LSQUIC_PARSE_ACK_TIMESTAMPS
struct {
/* Currently we just read these timestamps in (assuming it is
* compiled in, of course), but do not do anything with them.
* When we do, the representation of these fields should be
* switched to whatever is most appropriate/efficient.
*/
unsigned char packet_delta;
uint64_t delta_usec;
} timestamps[255];
#endif
} ack_info_t;
#define largest_acked(acki) (+(acki)->ranges[0].high)
#define smallest_acked(acki) (+(acki)->ranges[(acki)->n_ranges - 1].low)
/* gaf_: generate ACK frame */
struct lsquic_packno_range;
typedef const struct lsquic_packno_range *
(*gaf_rechist_first_f) (void *rechist);
typedef const struct lsquic_packno_range *
(*gaf_rechist_next_f) (void *rechist);
typedef lsquic_time_t
(*gaf_rechist_largest_recv_f) (void *rechist);
/* gsf_: generate stream frame */
typedef int (*gsf_fin_f) (void *stream);
typedef size_t (*gsf_size_f) (void *stream);
typedef size_t (*gsf_read_f) (void *stream, void *buf, size_t len, int *fin);
struct packin_parse_state {
const unsigned char *pps_p; /* Pointer to packet number */
unsigned pps_nbytes; /* Number of bytes in packet number */
};
/* This structure contains functions that parse and generate packets and
* frames in version-specific manner. To begin with, there is difference
* between GQUIC's little-endian (Q038 and lower) and big-endian formats
* (Q039 and higher).
*/
struct parse_funcs
{
int
(*pf_gen_ver_nego_pkt) (unsigned char *buf, size_t bufsz, uint64_t conn_id,
unsigned version_bitmask);
/* Return buf length */
int
(*pf_gen_reg_pkt_header) (unsigned char *buf, size_t bufsz,
const lsquic_cid_t *, const lsquic_ver_tag_t *,
const unsigned char *nonce, lsquic_packno_t,
enum lsquic_packno_bits);
void
(*pf_parse_packet_in_finish) (struct lsquic_packet_in *packet_in,
struct packin_parse_state *);
enum QUIC_FRAME_TYPE
(*pf_parse_frame_type) (unsigned char);
/* Return used buffer length */
int
(*pf_gen_stream_frame) (unsigned char *buf, size_t bufsz,
uint32_t stream_id, uint64_t offset,
gsf_fin_f, gsf_size_f, gsf_read_f, void *stream);
unsigned
(*pf_parse_stream_frame_header_sz) (unsigned char type);
int
(*pf_parse_stream_frame) (const unsigned char *buf, size_t rem_packet_sz,
struct stream_frame *);
int
(*pf_parse_ack_frame) (const unsigned char *buf, size_t buf_len,
ack_info_t *ack_info);
lsquic_packno_t
(*pf_parse_ack_high) (const unsigned char *buf, size_t buf_len);
int
(*pf_gen_ack_frame) (unsigned char *outbuf, size_t outbuf_sz,
gaf_rechist_first_f, gaf_rechist_next_f,
gaf_rechist_largest_recv_f, void *rechist, lsquic_time_t now,
int *has_missing);
int
(*pf_gen_stop_waiting_frame) (unsigned char *buf, size_t buf_len,
lsquic_packno_t cur_packno, enum lsquic_packno_bits,
lsquic_packno_t least_unacked_packno);
int
(*pf_parse_stop_waiting_frame) (const unsigned char *buf, size_t buf_len,
lsquic_packno_t cur_packno, enum lsquic_packno_bits,
lsquic_packno_t *least_unacked);
int
(*pf_skip_stop_waiting_frame) (size_t buf_len, enum lsquic_packno_bits);
int
(*pf_gen_window_update_frame) (unsigned char *buf, int buf_len,
uint32_t stream_id, uint64_t offset);
int
(*pf_parse_window_update_frame) (const unsigned char *buf, size_t buf_len,
uint32_t *stream_id, uint64_t *offset);
int
(*pf_gen_blocked_frame) (unsigned char *buf, size_t buf_len,
uint32_t stream_id);
int
(*pf_parse_blocked_frame) (const unsigned char *buf, size_t buf_len,
uint32_t *stream_id);
int
(*pf_gen_rst_frame) (unsigned char *buf, size_t buf_len, uint32_t stream_id,
uint64_t offset, uint32_t error_code);
int
(*pf_parse_rst_frame) (const unsigned char *buf, size_t buf_len,
uint32_t *stream_id, uint64_t *offset, uint32_t *error_code);
int
(*pf_gen_connect_close_frame) (unsigned char *buf, int buf_len,
uint32_t error_code, const char *reason, int reason_len);
int
(*pf_parse_connect_close_frame) (const unsigned char *buf, size_t buf_len,
uint32_t *error_code, uint16_t *reason_length,
uint8_t *reason_offset);
int
(*pf_gen_goaway_frame) (unsigned char *buf, size_t buf_len,
uint32_t error_code, uint32_t last_good_stream_id,
const char *reason, size_t reason_len);
int
(*pf_parse_goaway_frame) (const unsigned char *buf, size_t buf_len,
uint32_t *error_code, uint32_t *last_good_stream_id,
uint16_t *reason_length, const char **reason);
int
(*pf_gen_ping_frame) (unsigned char *buf, int buf_len);
#ifndef NDEBUG
/* These float reading and writing functions assume `mem' has at least
* 2 bytes.
*/
void
(*pf_write_float_time16) (lsquic_time_t time_us, void *mem);
uint64_t
(*pf_read_float_time16) (const void *mem);
#endif
size_t
(*pf_calc_stream_frame_header_sz) (uint32_t stream_id, uint64_t offset);
};
extern const struct parse_funcs lsquic_parse_funcs_gquic_le;
/* Q039 and later are big-endian: */
extern const struct parse_funcs lsquic_parse_funcs_gquic_Q039;
extern const struct parse_funcs lsquic_parse_funcs_gquic_Q040;
#define select_pf_by_ver(ver) ( \
((1 << (ver)) & ((1 << LSQVER_035) | \
(1 << LSQVER_037) | (1 << LSQVER_038))) \
? &lsquic_parse_funcs_gquic_le : \
((1 << (ver)) & (1 << LSQVER_039)) \
? &lsquic_parse_funcs_gquic_Q039 \
: &lsquic_parse_funcs_gquic_Q040)
/* This function is QUIC-version independent */
int
parse_packet_in_begin (struct lsquic_packet_in *, size_t length,
int is_server, struct packin_parse_state *);
enum QUIC_FRAME_TYPE
parse_frame_type_gquic_Q035_thru_Q039 (unsigned char first_byte);
enum QUIC_FRAME_TYPE
parse_frame_type_gquic_Q040 (unsigned char first_byte);
unsigned
parse_stream_frame_header_sz_gquic (unsigned char type);
size_t
calc_stream_frame_header_sz_gquic (uint32_t stream_id, uint64_t offset);
/* This maps two bits as follows:
* 00 -> 1
* 01 -> 2
* 10 -> 4
* 11 -> 6
*
* Assumes that only two low bits are set.
*/
#define twobit_to_1246(bits) ((bits) * 2 + !(bits))
/* This maps two bits as follows:
* 00 -> 1
* 01 -> 2
* 10 -> 4
* 11 -> 8
*
* Assumes that only two low bits are set.
*/
#define twobit_to_1248(bits) (1 << (bits))
char *
acki2str (const struct ack_info *acki, size_t *sz);
#endif

View File

@ -0,0 +1,574 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#include <assert.h>
#include <inttypes.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include <sys/types.h>
#include "lsquic_types.h"
#include "lsquic_alarmset.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_in.h"
#include "lsquic_parse.h"
#include "lsquic_rechist.h"
#include "lsquic_sfcw.h"
#include "lsquic_stream.h"
#include "lsquic_mm.h"
#include "lsquic_malo.h"
#include "lsquic_version.h"
#include "lsquic.h"
#include "lsquic_parse_gquic_be.h"
#define LSQUIC_LOGGER_MODULE LSQLM_PARSE
#include "lsquic_logger.h"
int
gquic_ietf_gen_rst_frame (unsigned char *buf, size_t buf_len,
uint32_t stream_id, uint64_t offset, uint32_t error_code)
{
unsigned char *p = buf;
if (buf_len < QUIC_RST_STREAM_SZ)
return -1;
*p = 0x01;
++p;
#if __BYTE_ORDER == __LITTLE_ENDIAN
stream_id = bswap_32(stream_id);
#endif
memcpy(p, &stream_id, 4);
p += 4;
#if __BYTE_ORDER == __LITTLE_ENDIAN
error_code = bswap_32(error_code);
#endif
memcpy(p, &error_code, 4);
p += 4;
#if __BYTE_ORDER == __LITTLE_ENDIAN
offset = bswap_64(offset);
#endif
memcpy(p, &offset, 8);
p += 8;
return p - buf;
}
int
gquic_ietf_parse_rst_frame (const unsigned char *buf, size_t buf_len,
uint32_t *stream_id, uint64_t *offset, uint32_t *error_code)
{
if (buf_len < QUIC_RST_STREAM_SZ)
return -1;
READ_UINT(*stream_id, 32, buf + 1, 4);
READ_UINT(*error_code, 32, buf + 1 + 4, 4);
READ_UINT(*offset, 64, buf + 1 + 4 + 4, 8);
return QUIC_RST_STREAM_SZ;
}
unsigned
gquic_ietf_parse_stream_frame_header_sz (unsigned char type)
{
const unsigned data_len = (type & 1) << 1;
const unsigned stream_id_len = ((type >> 3) & 3) + 1;
const unsigned offset_len = (((type >> 1) & 3) << 1) +
((3 == ((type >> 1) & 3)) << 1);
return 1 + data_len + offset_len + stream_id_len;
}
int
gquic_ietf_parse_stream_frame (const unsigned char *buf, size_t rem_packet_sz,
stream_frame_t *stream_frame)
{
/* 11FSSOOD */
const unsigned char *p = buf;
const unsigned char *const pend = p + rem_packet_sz;
CHECK_SPACE(1, p, pend);
const char type = *p++;
const unsigned data_len = (type & 1) << 1;
const unsigned stream_id_len = ((type >> 3) & 3) + 1;
const unsigned offset_len = (((type >> 1) & 3) << 1) +
((3 == ((type >> 1) & 3)) << 1);
const unsigned need = data_len + offset_len + stream_id_len;
CHECK_SPACE(need, p, pend);
memset(stream_frame, 0, sizeof(*stream_frame));
stream_frame->data_frame.df_fin = !!(type & 0x20);
if (data_len)
{
READ_UINT(stream_frame->data_frame.df_size, 16, p, data_len);
p += data_len;
}
READ_UINT(stream_frame->stream_id, 32, p, stream_id_len);
p += stream_id_len;
READ_UINT(stream_frame->data_frame.df_offset, 64, p, offset_len);
p += offset_len;
if (data_len)
{
CHECK_SPACE(stream_frame->data_frame.df_size, p, pend);
stream_frame->data_frame.df_data = p;
p += stream_frame->data_frame.df_size;
}
else
{
stream_frame->data_frame.df_size = pend - p;
stream_frame->data_frame.df_data = p;
p = pend;
}
/* From the spec: "A stream frame must always have either non-zero
* data length or the FIN bit set.'
*/
if (!(stream_frame->data_frame.df_size ||
stream_frame->data_frame.df_fin))
return -1;
assert(p <= pend);
return p - (unsigned char *) buf;
}
static size_t
gquic_ietf_calc_stream_frame_header_sz (uint32_t stream_id, uint64_t offset)
{
return
/* Type */
1
/* SS: Stream ID length: 1, 2, 3, or 4 bytes */
+ (stream_id > 0x0000FF)
+ (stream_id > 0x00FFFF)
+ (stream_id > 0xFFFFFF)
+ 1
/* OO: Offset length: 0, 2, 4, or 8 bytes */
+ ((offset >= (1ULL << 32)) << 2)
+ ((offset >= (1ULL << 16)) << 1)
+ ((offset > 1) << 1)
;
}
int
gquic_ietf_gen_stream_frame (unsigned char *buf, size_t buf_len, uint32_t stream_id,
uint64_t offset, gsf_fin_f gsf_fin, gsf_size_f gsf_size,
gsf_read_f gsf_read, void *stream)
{
/* 11FSSOOD */
unsigned slen, olen, dlen;
unsigned char *p = buf + 1;
int fin;
/* SS: Stream ID length: 1, 2, 3, or 4 bytes */
slen = (stream_id > 0x0000FF)
+ (stream_id > 0x00FFFF)
+ (stream_id > 0xFFFFFF)
+ 1;
/* OO: Offset length: 0, 2, 4, or 8 bytes */
olen = ((offset >= (1ULL << 32)) << 2)
+ ((offset >= (1ULL << 16)) << 1)
+ ((offset > 1) << 1)
;
fin = gsf_fin(stream);
if (!fin)
{
unsigned size, n_avail;
uint16_t nr;
size = gsf_size(stream);
n_avail = buf_len - (p + slen + olen - buf);
/* If we cannot fill remaining buffer, we need to include data
* length.
*/
dlen = (size < n_avail) << 1;
n_avail -= dlen;
CHECK_SPACE(1 + olen + slen + dlen +
+ 1 /* We need to write at least 1 byte */, buf, buf + buf_len);
p += dlen; /* Save room for data length */
#if __BYTE_ORDER == __LITTLE_ENDIAN
stream_id = bswap_32(stream_id);
#endif
memcpy(p, (unsigned char *) &stream_id + 4 - slen, slen);
p += slen;
#if __BYTE_ORDER == __LITTLE_ENDIAN
offset = bswap_64(offset);
#endif
memcpy(p, (unsigned char *) &offset + 8 - olen, olen);
p += olen;
/* Read as much as we can */
nr = gsf_read(stream, p, n_avail, &fin);
assert(nr != 0);
if (dlen)
{
uint16_t nr_copy = nr;
#if __BYTE_ORDER == __LITTLE_ENDIAN
nr_copy = bswap_16(nr_copy);
#endif
memcpy(p - slen - olen - 2, &nr_copy, 2);
}
p += nr;
}
else
{
dlen = 2;
CHECK_SPACE(1 + slen + olen + 2, buf, buf + buf_len);
memset(p, 0, 2);
p += 2;
#if __BYTE_ORDER == __LITTLE_ENDIAN
stream_id = bswap_32(stream_id);
#endif
memcpy(p, (unsigned char *) &stream_id + 4 - slen, slen);
p += slen;
#if __BYTE_ORDER == __LITTLE_ENDIAN
offset = bswap_64(offset);
#endif
memcpy(p, (unsigned char *) &offset + 8 - olen, olen);
p += olen;
}
/* Convert slen to bit representation: 0 - 3: */
slen -= 1;
assert(slen <= 3);
/* Convert olen to bit representation: 0 - 3: */
olen >>= 1;
olen -= olen == 4;
assert(olen <= 3);
buf[0] = 0xC0
| (fin << 5)
| (slen << 3)
| (olen << 1)
| !!dlen
;
return p - buf;
}
/* This is a special function: it is used to extract the largest observed
* packet number from ACK frame that we ourselves generated. This allows
* us to skip some checks.
*/
lsquic_packno_t
gquic_ietf_parse_ack_high (const unsigned char *buf, size_t buf_len)
{
unsigned char type;
unsigned largest_obs_len;
unsigned n_blocks_len;
lsquic_packno_t packno;
type = buf[0];
largest_obs_len = twobit_to_1248((type >> 2) & 3);
n_blocks_len = !!(type & 0x10);
assert(parse_frame_type_gquic_Q040(type) == QUIC_FRAME_ACK);
assert(buf_len >= 1 + n_blocks_len + 1 + largest_obs_len);
READ_UINT(packno, 64, buf + 1 + n_blocks_len + 1, largest_obs_len);
return packno;
}
int
gquic_ietf_parse_ack_frame (const unsigned char *buf, size_t buf_len,
ack_info_t *ack)
{
/* 101NLLMM */
lsquic_packno_t tmp_packno;
const unsigned char type = buf[0];
const unsigned char *p = buf + 1;
const unsigned char *const pend = buf + buf_len;
uint8_t n_blocks, n_ts;
assert((type & 0xE0) == 0xA0); /* We're passed correct frame type */
const int ack_block_len = twobit_to_1248(type & 3); /* MM */
const int largest_obs_len = twobit_to_1248((type >> 2) & 3); /* LL */
if (type & 0x10) { /* N */
CHECK_SPACE(2, p , pend);
n_blocks = *p++;
}
else
{
CHECK_SPACE(1, p , pend);
n_blocks = 0;
}
n_ts = *p++;
const unsigned timestamps_size =
(n_ts > 0) * (1 + 4) + /* Delta LA, First Timestamp */
(n_ts > 1) * (n_ts - 1) * (1 + 2); /* Delta LA, Time Since Previous */
CHECK_SPACE(
largest_obs_len + /* Largest Acknowledged */
2 + /* ACK delay */
ack_block_len + /* First ACK block length */
n_blocks * (1 + ack_block_len) + /* ACK blocks */
timestamps_size
,p , pend);
READ_UINT(ack->ranges[0].high, 64, p, largest_obs_len);
p += largest_obs_len;
ack->lack_delta = gquic_be_read_float_time16(p);
p += 2;
READ_UINT(tmp_packno, 64, p, ack_block_len);
ack->ranges[0].low = ack->ranges[0].high - tmp_packno + 1;
p += ack_block_len;
if (n_blocks)
{
unsigned i, n, gap;
for (i = 0, n = 1, gap = 0; i < n_blocks; ++i)
{
uint64_t length;
gap += *p;
READ_UINT(length, 64, p + 1, ack_block_len);
p += 1 + ack_block_len;
if (length)
{
ack->ranges[n].high = ack->ranges[n - 1].low - gap - 1;
ack->ranges[n].low = ack->ranges[n].high - length + 1;
++n;
gap = 0;
}
}
ack->n_ranges = n;
}
else
ack->n_ranges = 1;
ack->n_timestamps = n_ts;
if (n_ts)
{
#if LSQUIC_PARSE_ACK_TIMESTAMPS
/* TODO */
#else
/* Just skip them for now */
p += timestamps_size;
#endif
}
assert(p <= pend);
return p - (unsigned char *) buf;
}
/* This function makes an assumption that there is at least one range */
int
gquic_ietf_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
gaf_rechist_first_f rechist_first, gaf_rechist_next_f rechist_next,
gaf_rechist_largest_recv_f rechist_largest_recv,
void *rechist, lsquic_time_t now, int *has_missing)
{
lsquic_packno_t tmp_packno;
const struct lsquic_packno_range *const first = rechist_first(rechist);
if (!first)
{
errno = EINVAL;
return -1;
}
/* Copy values from the first range, because the memory the pointer
* points to may change:
*/
const lsquic_packno_t first_low = first->low, first_high = first->high;
unsigned char *p = outbuf;
unsigned char *const type = p;
unsigned char *const end = p + outbuf_sz;
unsigned char *n_ranges_p;
#define AVAIL() (end - p)
#define CHECKOUT(sz) do { \
if ((intptr_t) (sz) > AVAIL()) { \
errno = ENOBUFS; \
return -1; \
} \
} while (0)
CHECKOUT(1);
++p;
/* 101NLLMM */
*type = 0xA0;
unsigned largest_acked_len, ack_block_len, bits;
/* Calculate largest ACKed len and set `LL' bits: */
const lsquic_packno_t maxno = first_high;
bits = (maxno >= (1ULL << 8))
+ (maxno >= (1ULL << 16))
+ (maxno >= (1ULL << 32));
largest_acked_len = twobit_to_1248(bits);
*type |= bits << 2;
/* Calculate largest ACK block length and set `MM' bits: */
unsigned n_ranges = 0;
lsquic_packno_t maxdiff = 0;
const struct lsquic_packno_range *range;
for (range = rechist_first(rechist); range; range = rechist_next(rechist))
{
++n_ranges;
const lsquic_packno_t diff = range->high - range->low + 1;
if (diff > maxdiff)
maxdiff = diff;
}
bits = (maxdiff >= (1ULL << 8))
+ (maxdiff >= (1ULL << 16))
+ (maxdiff >= (1ULL << 32));
ack_block_len = twobit_to_1248(bits);
*type |= bits;
if (n_ranges > 1)
{
CHECKOUT(2);
*type |= 0x10; /* N */
n_ranges_p = p++; /* Set Num Blocks later */
}
else
{
CHECKOUT(1);
n_ranges_p = NULL;
}
*p++ = 0; /* Do not provide any timestamps. TODO perhaps? */
CHECKOUT(largest_acked_len);
tmp_packno = maxno;
#if __BYTE_ORDER == __LITTLE_ENDIAN
tmp_packno = bswap_64(maxno);
#endif
memcpy(p, (unsigned char *) &tmp_packno + 8 - largest_acked_len,
largest_acked_len);
p += largest_acked_len;
CHECKOUT(2);
lsquic_time_t diff = now - rechist_largest_recv(rechist);
gquic_be_write_float_time16(diff, p);
LSQ_DEBUG("%s: diff: %"PRIu64"; encoded: 0x%04X", __func__, diff,
*(uint16_t*)p);
p += 2;
*has_missing = n_ranges > 1;
if (n_ranges > 1)
{
/* We need to write out at least one range */
CHECKOUT(2 * (1 + ack_block_len));
lsquic_packno_t diff = maxno - first_low + 1;
#if __BYTE_ORDER == __LITTLE_ENDIAN
diff = bswap_64(diff);
#endif
memcpy(p, (unsigned char *) &diff + 8 - ack_block_len,
ack_block_len);
p += ack_block_len;
/* Write out ack blocks until one of the following occurs:
* 1. We run out of intervals.
* 2. We run out of room.
* 3. We run out of highest possible number of ACK blocks (0xFF).
*/
range = rechist_first(rechist);
lsquic_packno_t gap = 0;
n_ranges = 0;
do {
if (0 == gap)
{
const lsquic_packno_t prev_low = range->low;
range = rechist_next(rechist);
if (!range)
break;
gap = prev_low - range->high - 1;
}
if (gap >= 0x100)
{
*p = 0xFF;
gap -= 0xFF;
memset(p + 1, 0, ack_block_len);
}
else
{
*p = gap;
gap = 0;
diff = range->high - range->low + 1;
#if __BYTE_ORDER == __LITTLE_ENDIAN
diff = bswap_64(diff);
#endif
memcpy(p + 1, (unsigned char *) &diff + 8 - ack_block_len,
ack_block_len);
}
p += ack_block_len + 1;
++n_ranges;
} while (n_ranges < 0xFF &&
AVAIL() >= (intptr_t) ack_block_len + 1 + 1 /* timestamp byte */);
*n_ranges_p = n_ranges;
}
else
{
CHECKOUT(ack_block_len);
lsquic_packno_t diff = maxno - first_low + 1;
#if __BYTE_ORDER == __LITTLE_ENDIAN
diff = bswap_64(diff);
#endif
memcpy(p, (unsigned char *) &diff + 8 - ack_block_len, ack_block_len);
p += ack_block_len;
}
return p - (unsigned char *) outbuf;
#undef CHECKOUT
}
const struct parse_funcs lsquic_parse_funcs_gquic_Q040 =
{
.pf_gen_ver_nego_pkt = gquic_be_gen_ver_nego_pkt,
.pf_gen_reg_pkt_header = gquic_be_gen_reg_pkt_header,
.pf_parse_packet_in_finish = gquic_be_parse_packet_in_finish,
.pf_gen_stream_frame = gquic_ietf_gen_stream_frame,
.pf_calc_stream_frame_header_sz = gquic_ietf_calc_stream_frame_header_sz,
.pf_parse_stream_frame_header_sz = gquic_ietf_parse_stream_frame_header_sz,
.pf_parse_stream_frame = gquic_ietf_parse_stream_frame,
.pf_parse_ack_frame = gquic_ietf_parse_ack_frame,
.pf_parse_ack_high = gquic_ietf_parse_ack_high,
.pf_gen_ack_frame = gquic_ietf_gen_ack_frame,
.pf_gen_stop_waiting_frame = gquic_be_gen_stop_waiting_frame,
.pf_parse_stop_waiting_frame = gquic_be_parse_stop_waiting_frame,
.pf_skip_stop_waiting_frame = gquic_be_skip_stop_waiting_frame,
.pf_gen_window_update_frame = gquic_be_gen_window_update_frame,
.pf_parse_window_update_frame = gquic_be_parse_window_update_frame,
.pf_gen_blocked_frame = gquic_be_gen_blocked_frame,
.pf_parse_blocked_frame = gquic_be_parse_blocked_frame,
.pf_gen_rst_frame = gquic_ietf_gen_rst_frame,
.pf_parse_rst_frame = gquic_ietf_parse_rst_frame,
.pf_gen_connect_close_frame = gquic_be_gen_connect_close_frame,
.pf_parse_connect_close_frame = gquic_be_parse_connect_close_frame,
.pf_gen_goaway_frame = gquic_be_gen_goaway_frame,
.pf_parse_goaway_frame = gquic_be_parse_goaway_frame,
.pf_gen_ping_frame = gquic_be_gen_ping_frame,
#ifndef NDEBUG
.pf_write_float_time16 = gquic_be_write_float_time16,
.pf_read_float_time16 = gquic_be_read_float_time16,
#endif
.pf_parse_frame_type = parse_frame_type_gquic_Q040,
};

View File

@ -0,0 +1,947 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_parse_gquic_be.c -- Parsing functions specific to big-endian
* (Q039 and higher) GQUIC.
*/
#include <assert.h>
#include <inttypes.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include <sys/types.h>
#include "lsquic_types.h"
#include "lsquic_alarmset.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_in.h"
#include "lsquic_parse.h"
#include "lsquic_rechist.h"
#include "lsquic_sfcw.h"
#include "lsquic_stream.h"
#include "lsquic_mm.h"
#include "lsquic_malo.h"
#include "lsquic_version.h"
#include "lsquic.h"
#include "lsquic_parse_gquic_be.h" /* Include to catch mismatches */
#define LSQUIC_LOGGER_MODULE LSQLM_PARSE
#include "lsquic_logger.h"
/* read 16 bits(2 bytes) time, unit: us */
uint64_t
gquic_be_read_float_time16 (const void *mem)
{
uint16_t val;
READ_UINT(val, 16, mem, 2);
uint64_t temp = val;
uint16_t exp = (temp >> 11) & 0x1F;
if (0 == exp)
return temp;
else
{
--exp;
temp &= 0x7FF;
temp |= 0x800;
return temp << exp;
}
}
void
gquic_be_write_float_time16 (lsquic_time_t time_us, void *mem)
{
uint16_t ret = 0;
uint16_t high, i;
if (time_us < ((uint64_t)1 << 11))
ret = time_us;
else if(time_us > 0x3FFC0000000)
ret = 0xFFFF;
else
{
high = 0;
for (i = 16; i > 0; i /= 2)
{
if (time_us >= (uint64_t)1 << (11 + i))
{
high |= i;
time_us >>= i;
}
}
ret = time_us + (high << 11);
}
#if __BYTE_ORDER == __LITTLE_ENDIAN
ret = bswap_16(ret);
#endif
memcpy(mem, (void *)&ret, 2);
}
/* Parse out packet number */
void
gquic_be_parse_packet_in_finish (lsquic_packet_in_t *packet_in,
struct packin_parse_state *state)
{
lsquic_packno_t packno;
if (state->pps_nbytes)
{
READ_UINT(packno, 64, state->pps_p, state->pps_nbytes);
packet_in->pi_packno = packno;
}
}
int
gquic_be_gen_ver_nego_pkt (unsigned char *buf, size_t bufsz, uint64_t conn_id,
unsigned version_bitmask)
{
int sz;
unsigned char *p = buf;
unsigned char *const pend = p + bufsz;
CHECK_SPACE(1, p, pend);
*p = PACKET_PUBLIC_FLAGS_VERSION | PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID;
++p;
CHECK_SPACE(8, p, pend);
memcpy(p, &conn_id, 8);
p += 8;
sz = gen_ver_tags(p, pend - p, version_bitmask);
if (sz < 0)
return -1;
return p + sz - buf;
}
int
gquic_be_gen_reg_pkt_header (unsigned char *buf, size_t bufsz, const lsquic_cid_t *conn_id,
const lsquic_ver_tag_t *ver, const unsigned char *nonce,
lsquic_packno_t packno, enum lsquic_packno_bits bits)
{
unsigned packnum_len, header_len;
unsigned char *p;
packnum_len = packno_bits2len(bits);
header_len = 1 + (!!conn_id << 3) + (!!ver << 2) + ((!!nonce) << 5)
+ packnum_len;
if (header_len > bufsz)
{
errno = ENOBUFS;
return -1;
}
p = buf;
*p = (!!conn_id << 3)
| (bits << 4)
| ((!!nonce) << 2)
| !!ver;
++p;
if (conn_id)
{
memcpy(p, conn_id , sizeof(*conn_id));
p += sizeof(*conn_id);
}
if (ver)
{
memcpy(p, ver, 4);
p += 4;
}
if (nonce)
{
memcpy(p, nonce , 32);
p += 32;
}
#if __BYTE_ORDER == __LITTLE_ENDIAN
packno = bswap_64(packno);
#endif
memcpy(p, (unsigned char *) &packno + 8 - packnum_len, packnum_len);
p += packnum_len;
assert(p - buf == (intptr_t) header_len);
return header_len;
}
int
gquic_be_gen_stream_frame (unsigned char *buf, size_t buf_len, uint32_t stream_id,
uint64_t offset, gsf_fin_f gsf_fin, gsf_size_f gsf_size,
gsf_read_f gsf_read, void *stream)
{
/* 1fdoooss */
unsigned slen, olen, dlen;
unsigned char *p = buf + 1;
int fin;
/* ss: Stream ID length: 1, 2, 3, or 4 bytes */
slen = (stream_id > 0x0000FF)
+ (stream_id > 0x00FFFF)
+ (stream_id > 0xFFFFFF)
+ 1;
/* ooo: Offset length: 0, 2, 3, 4, 5, 6, 7, or 8 bytes */
olen = (offset >= (1ULL << 56))
+ (offset >= (1ULL << 48))
+ (offset >= (1ULL << 40))
+ (offset >= (1ULL << 32))
+ (offset >= (1ULL << 24))
+ (offset >= (1ULL << 16))
+ ((offset > 0) << 1);
fin = gsf_fin(stream);
if (!fin)
{
unsigned size, n_avail;
uint16_t nr;
size = gsf_size(stream);
n_avail = buf_len - (p + slen + olen - buf);
/* If we cannot fill remaining buffer, we need to include data
* length.
*/
dlen = (size < n_avail) << 1;
n_avail -= dlen;
CHECK_SPACE(1 + olen + slen + dlen +
+ 1 /* We need to write at least 1 byte */, buf, buf + buf_len);
#if __BYTE_ORDER == __LITTLE_ENDIAN
stream_id = bswap_32(stream_id);
#endif
memcpy(p, (unsigned char *) &stream_id + 4 - slen, slen);
p += slen;
#if __BYTE_ORDER == __LITTLE_ENDIAN
offset = bswap_64(offset);
#endif
memcpy(p, (unsigned char *) &offset + 8 - olen, olen);
p += olen;
/* Read as much as we can */
nr = gsf_read(stream, p + dlen, n_avail, &fin);
assert(nr != 0);
if (dlen)
{
uint16_t nr_copy = nr;
#if __BYTE_ORDER == __LITTLE_ENDIAN
nr_copy = bswap_16(nr_copy);
#endif
memcpy(p, &nr_copy, 2);
}
p += dlen + nr;
}
else
{
dlen = 2;
CHECK_SPACE(1 + slen + olen + 2, buf, buf + buf_len);
#if __BYTE_ORDER == __LITTLE_ENDIAN
stream_id = bswap_32(stream_id);
#endif
memcpy(p, (unsigned char *) &stream_id + 4 - slen, slen);
p += slen;
#if __BYTE_ORDER == __LITTLE_ENDIAN
offset = bswap_64(offset);
#endif
memcpy(p, (unsigned char *) &offset + 8 - olen, olen);
p += olen;
memset(p, 0, 2);
p += 2;
}
/* Convert slen to bit representation: 0 - 3: */
slen -= 1;
assert(slen <= 3);
/* Convert olen to bit representation: 0 - 7: */
olen += !olen;
olen -= 1;
assert(olen <= 7);
buf[0] = 0x80
| (fin << 6)
| (dlen << 4)
| (olen << 2)
| slen
;
return p - buf;
}
/* return parsed (used) buffer length */
int
gquic_be_parse_stream_frame (const unsigned char *buf, size_t rem_packet_sz,
stream_frame_t *stream_frame)
{
/* 1fdoooss */
const unsigned char *p = buf;
const unsigned char *const pend = p + rem_packet_sz;
CHECK_SPACE(1, p, pend);
const char type = *p++;
const unsigned data_len = (type >> 4) & 2;
const unsigned offset_len = ((type >> 2) & 7) + 1 - !((type >> 2) & 7);
const unsigned stream_id_len = 1 + (type & 3);
const unsigned need = data_len + offset_len + stream_id_len;
CHECK_SPACE(need, p, pend);
memset(stream_frame, 0, sizeof(*stream_frame));
stream_frame->data_frame.df_fin = (type >> 6) & 1;
memcpy((unsigned char *) &stream_frame->stream_id + 4 - stream_id_len, p,
stream_id_len);
#if __BYTE_ORDER == __LITTLE_ENDIAN
stream_frame->stream_id = bswap_32(stream_frame->stream_id);
#endif
p += stream_id_len;
memcpy((unsigned char *) &stream_frame->data_frame.df_offset
+ 8 - offset_len, p, offset_len);
#if __BYTE_ORDER == __LITTLE_ENDIAN
stream_frame->data_frame.df_offset =
bswap_64(stream_frame->data_frame.df_offset);
#endif
p += offset_len;
if (data_len)
{
memcpy(&stream_frame->data_frame.df_size, p, data_len);
#if __BYTE_ORDER == __LITTLE_ENDIAN
stream_frame->data_frame.df_size =
bswap_16(stream_frame->data_frame.df_size);
#endif
p += data_len;
CHECK_SPACE(stream_frame->data_frame.df_size, p, pend);
stream_frame->data_frame.df_data = p;
p += stream_frame->data_frame.df_size;
}
else
{
stream_frame->data_frame.df_size = pend - p;
stream_frame->data_frame.df_data = p;
p = pend;
}
/* From the spec: "A stream frame must always have either non-zero
* data length or the FIN bit set.'
*/
if (!(stream_frame->data_frame.df_size ||
stream_frame->data_frame.df_fin))
return -1;
assert(p <= pend);
return p - (unsigned char *) buf;
}
/* This is a special function: it is used to extract the largest observed
* packet number from ACK frame that we ourselves generated. This allows
* us to skip some checks.
*/
lsquic_packno_t
gquic_be_parse_ack_high (const unsigned char *buf, size_t buf_len)
{
unsigned char type;
unsigned largest_obs_len;
lsquic_packno_t packno;
type = buf[0];
largest_obs_len = twobit_to_1246((type >> 2) & 3);
assert(parse_frame_type_gquic_Q035_thru_Q039(type) == QUIC_FRAME_ACK);
assert(buf_len >= 1 + largest_obs_len);
READ_UINT(packno, 64, buf + 1, largest_obs_len);
return packno;
}
/* Return parsed (used) buffer length.
* If parsing failed, negative value is returned.
*/
int
gquic_be_parse_ack_frame (const unsigned char *buf, size_t buf_len, ack_info_t *ack)
{
/* 01nullmm */
lsquic_packno_t tmp_packno;
const unsigned char type = buf[0];
const unsigned char *p = buf + 1;
const unsigned char *const pend = buf + buf_len;
assert((type & 0xC0) == 0x40); /* We're passed correct frame type */
const int ack_block_len = twobit_to_1246(type & 3); /* mm */
const int largest_obs_len = twobit_to_1246((type >> 2) & 3); /* ll */
CHECK_SPACE(largest_obs_len, p , pend);
READ_UINT(ack->ranges[0].high, 64, p, largest_obs_len);
p += largest_obs_len;
CHECK_SPACE(2, p , pend);
ack->lack_delta = gquic_be_read_float_time16(p);
p += 2;
unsigned n_blocks;
if (type & 0x20)
{
CHECK_SPACE(1, p , pend);
n_blocks = *p;
++p;
}
else
n_blocks = 0;
CHECK_SPACE(ack_block_len, p , pend);
READ_UINT(tmp_packno, 64, p, ack_block_len);
ack->ranges[0].low = ack->ranges[0].high - tmp_packno + 1;
p += ack_block_len;
if (n_blocks)
{
CHECK_SPACE((ack_block_len + 1) * n_blocks, p , pend);
unsigned i, n, gap;
for (i = 0, n = 1, gap = 0; i < n_blocks; ++i)
{
uint64_t length;
gap += *p;
READ_UINT(length, 64, p + 1, ack_block_len);
p += 1 + ack_block_len;
if (length)
{
ack->ranges[n].high = ack->ranges[n - 1].low - gap - 1;
ack->ranges[n].low = ack->ranges[n].high - length + 1;
++n;
gap = 0;
}
}
ack->n_ranges = n;
}
else
ack->n_ranges = 1;
CHECK_SPACE(1, p , pend);
ack->n_timestamps = *p;
++p;
if (ack->n_timestamps)
{
#if LSQUIC_PARSE_ACK_TIMESTAMPS
CHECK_SPACE(5, p , pend);
ack->timestamps[0].packet_delta = *p++;
memcpy(&ack->timestamps[0].delta_usec, p, 4);
p += 4;
unsigned i;
for (i = 1; i < ack->n_timestamps; ++i)
{
CHECK_SPACE(3, p , pend);
ack->timestamps[i].packet_delta = *p++;
uint64_t delta_time = read_float_time16(p);
p += 2;
ack->timestamps[i].delta_usec =
ack->timestamps[i - 1].delta_usec + delta_time;
}
#else
unsigned timestamps_size = 5 + 3 * (ack->n_timestamps - 1);
CHECK_SPACE(timestamps_size, p, pend);
p += timestamps_size;
#endif
}
assert(p <= pend);
return p - (unsigned char *) buf;
}
int
gquic_be_gen_stop_waiting_frame(unsigned char *buf, size_t buf_len,
lsquic_packno_t cur_packno, enum lsquic_packno_bits bits,
lsquic_packno_t least_unacked_packno)
{
lsquic_packno_t delta;
unsigned packnum_len = packno_bits2len(bits);
if (buf_len >= 1 + packnum_len)
{
*buf = 0x06;
delta = cur_packno - least_unacked_packno;
#if __BYTE_ORDER == __LITTLE_ENDIAN
delta = bswap_64(delta);
#endif
memcpy(buf + 1, (unsigned char *) &delta + 8 - packnum_len,
packnum_len);
return 1 + packnum_len;
}
else
return -1;
}
int
gquic_be_parse_stop_waiting_frame (const unsigned char *buf, size_t buf_len,
lsquic_packno_t cur_packno, enum lsquic_packno_bits bits,
lsquic_packno_t *least_unacked)
{
lsquic_packno_t delta;
unsigned packnum_len = packno_bits2len(bits);
if (buf_len >= 1 + packnum_len)
{
READ_UINT(delta, 64, buf + 1, packnum_len);
*least_unacked = cur_packno - delta;
return 1 + packnum_len;
}
else
return -1;
}
int
gquic_be_skip_stop_waiting_frame (size_t buf_len, enum lsquic_packno_bits bits)
{
unsigned packnum_len = packno_bits2len(bits);
if (buf_len >= 1 + packnum_len)
return 1 + packnum_len;
else
return -1;
}
int
gquic_be_gen_window_update_frame (unsigned char *buf, int buf_len, uint32_t stream_id,
uint64_t offset)
{
if (buf_len < QUIC_WUF_SZ)
return -1;
*buf = 0x04;
#if __BYTE_ORDER == __LITTLE_ENDIAN
stream_id = bswap_32(stream_id);
#endif
memcpy(buf + 1, (unsigned char *) &stream_id, 4);
#if __BYTE_ORDER == __LITTLE_ENDIAN
offset = bswap_64(offset);
#endif
memcpy(buf + 1 + 4, (unsigned char *) &offset, 8);
return QUIC_WUF_SZ;
}
int
gquic_be_parse_window_update_frame (const unsigned char *buf, size_t buf_len,
uint32_t *stream_id, uint64_t *offset)
{
if (buf_len < QUIC_WUF_SZ)
return -1;
READ_UINT(*stream_id, 32, buf + 1, 4);
READ_UINT(*offset, 64, buf + 1 + 4, 8);
return QUIC_WUF_SZ;
}
int
gquic_be_gen_blocked_frame (unsigned char *buf, size_t buf_len, uint32_t stream_id)
{
if (buf_len < QUIC_BLOCKED_FRAME_SZ)
return -1;
*buf = 0x05;
#if __BYTE_ORDER == __LITTLE_ENDIAN
stream_id = bswap_32(stream_id);
#endif
memcpy(buf + 1, &stream_id, 4);
return QUIC_BLOCKED_FRAME_SZ;
}
int
gquic_be_parse_blocked_frame (const unsigned char *buf, size_t buf_len,
uint32_t *stream_id)
{
if (buf_len < QUIC_BLOCKED_FRAME_SZ)
return -1;
READ_UINT(*stream_id, 32, buf + 1, 4);
return QUIC_BLOCKED_FRAME_SZ;
}
int
gquic_be_gen_rst_frame (unsigned char *buf, size_t buf_len, uint32_t stream_id,
uint64_t offset, uint32_t error_code)
{
unsigned char *p = buf;
if (buf_len < QUIC_RST_STREAM_SZ)
return -1;
*p = 0x01;
++p;
#if __BYTE_ORDER == __LITTLE_ENDIAN
stream_id = bswap_32(stream_id);
#endif
memcpy(p, &stream_id, 4);
p += 4;
#if __BYTE_ORDER == __LITTLE_ENDIAN
offset = bswap_64(offset);
#endif
memcpy(p, &offset, 8);
p += 8;
#if __BYTE_ORDER == __LITTLE_ENDIAN
error_code = bswap_32(error_code);
#endif
memcpy(p, &error_code, 4);
p += 4;
return p - buf;
}
int
gquic_be_parse_rst_frame (const unsigned char *buf, size_t buf_len, uint32_t *stream_id,
uint64_t *offset, uint32_t *error_code)
{
if (buf_len < QUIC_RST_STREAM_SZ)
return -1;
READ_UINT(*stream_id, 32, buf + 1, 4);
READ_UINT(*offset, 64, buf + 1 + 4, 8);
READ_UINT(*error_code, 32, buf + 1 + 4 + 8, 4);
return QUIC_RST_STREAM_SZ;
}
int
gquic_be_gen_ping_frame (unsigned char *buf, int buf_len)
{
if (buf_len > 0)
{
buf[0] = 0x07;
return 1;
}
else
return -1;
}
int
gquic_be_gen_connect_close_frame (unsigned char *buf, int buf_len, uint32_t error_code,
const char *reason, int reason_len)
{
unsigned char *p = buf;
if (buf_len < 7)
return -1;
*p = 0x02;
++p;
#if __BYTE_ORDER == __LITTLE_ENDIAN
error_code = bswap_32(error_code);
#endif
memcpy(p, &error_code, 4);
p += 4;
#if __BYTE_ORDER == __LITTLE_ENDIAN
const uint16_t copy = bswap_16(reason_len);
memcpy(p, &copy, 2);
#else
memcpy(p, &reason_len, 2);
#endif
p += 2;
memcpy(p, reason, reason_len);
p += reason_len;
if (buf_len < p - buf)
return -2;
return p - buf;
}
int
gquic_be_parse_connect_close_frame (const unsigned char *buf, size_t buf_len,
uint32_t *error_code, uint16_t *reason_len, uint8_t *reason_offset)
{
if (buf_len < 7)
return -1;
READ_UINT(*error_code, 32, buf + 1, 4);
READ_UINT(*reason_len, 16, buf + 1 + 4, 2);
*reason_offset = 7;
if (buf_len < 7u + *reason_len)
return -2;
return 7 + *reason_len;
}
int
gquic_be_gen_goaway_frame(unsigned char *buf, size_t buf_len, uint32_t error_code,
uint32_t last_good_stream_id, const char *reason,
size_t reason_len)
{
unsigned char *p = buf;
if (buf_len < QUIC_GOAWAY_FRAME_SZ + reason_len)
return -1;
*p = 0x03;
++p;
#if __BYTE_ORDER == __LITTLE_ENDIAN
error_code = bswap_32(error_code);
#endif
memcpy(p, &error_code, 4);
p += 4;
#if __BYTE_ORDER == __LITTLE_ENDIAN
last_good_stream_id = bswap_32(last_good_stream_id);
#endif
memcpy(p, &last_good_stream_id, 4);
p += 4;
#if __BYTE_ORDER == __LITTLE_ENDIAN
uint16_t copy = bswap_16(reason_len);
memcpy(p, &copy, 2);
#else
memcpy(p, &reason_len, 2);
#endif
p += 2;
if (reason_len)
{
memcpy(p, reason, reason_len);
p += reason_len;
}
return p - buf;
}
/* the reason is buf + *reason_offset, length is *reason_length */
int
gquic_be_parse_goaway_frame (const unsigned char *buf, size_t buf_len,
uint32_t *error_code, uint32_t *last_good_stream_id,
uint16_t *reason_length, const char **reason)
{
if (buf_len < QUIC_GOAWAY_FRAME_SZ)
return -1;
READ_UINT(*error_code, 32, buf + 1, 4);
READ_UINT(*last_good_stream_id, 32, buf + 1 + 4, 4);
READ_UINT(*reason_length, 16, buf + 1 + 4 + 4, 2);
if (*reason_length)
{
if ((int)buf_len < QUIC_GOAWAY_FRAME_SZ + *reason_length)
return -2;
*reason = (const char *) buf + QUIC_GOAWAY_FRAME_SZ;
}
else
*reason = NULL;
return QUIC_GOAWAY_FRAME_SZ + *reason_length;
}
/* Returns number of bytes written or -1 on failure */
/* This function makes an assumption that there is at least one range */
int
gquic_be_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
gaf_rechist_first_f rechist_first, gaf_rechist_next_f rechist_next,
gaf_rechist_largest_recv_f rechist_largest_recv,
void *rechist, lsquic_time_t now, int *has_missing)
{
lsquic_packno_t tmp_packno;
const struct lsquic_packno_range *const first = rechist_first(rechist);
if (!first)
{
errno = EINVAL;
return -1;
}
/* Copy values from the first range, because the memory the pointer
* points to may change:
*/
const lsquic_packno_t first_low = first->low, first_high = first->high;
unsigned char *p = outbuf;
unsigned char *const type = p;
unsigned char *const end = p + outbuf_sz;
#define AVAIL() (end - p)
#define CHECKOUT(sz) do { \
if ((intptr_t) (sz) > AVAIL()) { \
errno = ENOBUFS; \
return -1; \
} \
} while (0)
CHECKOUT(1);
++p;
/* 01nullmm */
*type = 0x40;
unsigned largest_acked_len, ack_block_len, bits;
/* Calculate largest ACKed len and set `ll' bits: */
const lsquic_packno_t maxno = first_high;
bits = (maxno >= (1ULL << 8))
+ (maxno >= (1ULL << 16))
+ (maxno >= (1ULL << 32));
largest_acked_len = (1 << bits) - ((maxno >= (1ULL << 32)) << 1);
*type |= bits << 2;
/* Calculate largest ACK block length and set `mm' bits: */
unsigned n_ranges = 0;
lsquic_packno_t maxdiff = 0;
const struct lsquic_packno_range *range;
for (range = rechist_first(rechist); range; range = rechist_next(rechist))
{
++n_ranges;
const lsquic_packno_t diff = range->high - range->low + 1;
if (diff > maxdiff)
maxdiff = diff;
}
bits = (maxdiff >= (1ULL << 8))
+ (maxdiff >= (1ULL << 16))
+ (maxdiff >= (1ULL << 32));
ack_block_len = (1 << bits) - ((maxdiff >= (1ULL << 32)) << 1);
*type |= bits;
CHECKOUT(largest_acked_len);
tmp_packno = maxno;
#if __BYTE_ORDER == __LITTLE_ENDIAN
tmp_packno = bswap_64(maxno);
#endif
memcpy(p, (unsigned char *) &tmp_packno + 8 - largest_acked_len,
largest_acked_len);
p += largest_acked_len;
CHECKOUT(2);
lsquic_time_t diff = now - rechist_largest_recv(rechist);
gquic_be_write_float_time16(diff, p);
LSQ_DEBUG("%s: diff: %"PRIu64"; encoded: 0x%04X", __func__, diff,
*(uint16_t*)p);
p += 2;
if (n_ranges > 1)
{
*has_missing = 1;
*type |= 0x20;
/* We need to write out at least one range */
CHECKOUT(2 * (1 + ack_block_len));
unsigned char *const n_ranges_p = p; /* Set this later */
lsquic_packno_t diff = maxno - first_low + 1;
#if __BYTE_ORDER == __LITTLE_ENDIAN
diff = bswap_64(diff);
#endif
memcpy(p + 1, (unsigned char *) &diff + 8 - ack_block_len,
ack_block_len);
p += ack_block_len + 1;
/* Write out ack blocks until one of the following occurs:
* 1. We run out of intervals.
* 2. We run out of room.
* 3. We run out of highest possible number of ACK blocks (0xFF).
*/
range = rechist_first(rechist);
lsquic_packno_t gap = 0;
n_ranges = 0;
do {
if (0 == gap)
{
const lsquic_packno_t prev_low = range->low;
range = rechist_next(rechist);
if (!range)
break;
gap = prev_low - range->high - 1;
}
if (gap >= 0x100)
{
*p = 0xFF;
gap -= 0xFF;
memset(p + 1, 0, ack_block_len);
}
else
{
*p = gap;
gap = 0;
diff = range->high - range->low + 1;
#if __BYTE_ORDER == __LITTLE_ENDIAN
diff = bswap_64(diff);
#endif
memcpy(p + 1, (unsigned char *) &diff + 8 - ack_block_len,
ack_block_len);
}
p += ack_block_len + 1;
++n_ranges;
} while (n_ranges < 0xFF &&
AVAIL() >= (intptr_t) ack_block_len + 1 + 1 /* timestamp byte */);
*n_ranges_p = n_ranges;
}
else
{
*has_missing = 0;
CHECKOUT(ack_block_len);
lsquic_packno_t diff = maxno - first_low + 1;
#if __BYTE_ORDER == __LITTLE_ENDIAN
diff = bswap_64(diff);
#endif
memcpy(p, (unsigned char *) &diff + 8 - ack_block_len, ack_block_len);
p += ack_block_len;
}
/* We do not generate timestamp list because the reference implementation
* does not use them. When that changes, we will start sending timestamps
* over.
*/
CHECKOUT(1);
*p = 0;
++p;
return p - (unsigned char *) outbuf;
#undef CHECKOUT
}
const struct parse_funcs lsquic_parse_funcs_gquic_Q039 =
{
.pf_gen_ver_nego_pkt = gquic_be_gen_ver_nego_pkt,
.pf_gen_reg_pkt_header = gquic_be_gen_reg_pkt_header,
.pf_parse_packet_in_finish = gquic_be_parse_packet_in_finish,
.pf_gen_stream_frame = gquic_be_gen_stream_frame,
.pf_calc_stream_frame_header_sz = calc_stream_frame_header_sz_gquic,
.pf_parse_stream_frame_header_sz = parse_stream_frame_header_sz_gquic,
.pf_parse_stream_frame = gquic_be_parse_stream_frame,
.pf_parse_ack_frame = gquic_be_parse_ack_frame,
.pf_parse_ack_high = gquic_be_parse_ack_high,
.pf_gen_ack_frame = gquic_be_gen_ack_frame,
.pf_gen_stop_waiting_frame = gquic_be_gen_stop_waiting_frame,
.pf_parse_stop_waiting_frame = gquic_be_parse_stop_waiting_frame,
.pf_skip_stop_waiting_frame = gquic_be_skip_stop_waiting_frame,
.pf_gen_window_update_frame = gquic_be_gen_window_update_frame,
.pf_parse_window_update_frame = gquic_be_parse_window_update_frame,
.pf_gen_blocked_frame = gquic_be_gen_blocked_frame,
.pf_parse_blocked_frame = gquic_be_parse_blocked_frame,
.pf_gen_rst_frame = gquic_be_gen_rst_frame,
.pf_parse_rst_frame = gquic_be_parse_rst_frame,
.pf_gen_connect_close_frame = gquic_be_gen_connect_close_frame,
.pf_parse_connect_close_frame = gquic_be_parse_connect_close_frame,
.pf_gen_goaway_frame = gquic_be_gen_goaway_frame,
.pf_parse_goaway_frame = gquic_be_parse_goaway_frame,
.pf_gen_ping_frame = gquic_be_gen_ping_frame,
#ifndef NDEBUG
.pf_write_float_time16 = gquic_be_write_float_time16,
.pf_read_float_time16 = gquic_be_read_float_time16,
#endif
.pf_parse_frame_type = parse_frame_type_gquic_Q035_thru_Q039,
};

View File

@ -0,0 +1,135 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef LSQUIC_PARSE_GQUIC_BE_H
#define LSQUIC_PARSE_GQUIC_BE_H
/* Header file to make it easy to reference gQUIC parsing functions. This
* is only meant to be used internally. The alternative would be to place
* all gQUIC-big-endian functions -- from all versions -- in a single file,
* and that would be a mess.
*/
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__)
#include <sys/endian.h>
#define bswap_16 bswap16
#define bswap_32 bswap32
#define bswap_64 bswap64
#else
#include <byteswap.h>
#endif
#define CHECK_SPACE(need, pstart, pend) \
do { if ((intptr_t) (need) > ((pend) - (pstart))) { return -1; } } while (0)
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define READ_UINT(varname, varwidth, src, nbytes) do { \
varname = 0; \
memcpy((unsigned char *) &varname + varwidth / 8 - (nbytes), (src), \
(nbytes)); \
varname = bswap_##varwidth(varname); \
} while (0)
#else
#define READ_UINT(varname, varwidth, src, nbytes) do { \
varname = 0; \
memcpy((unsigned char *) &varname + varwidth / 8 - (nbytes), (src), \
(nbytes)); \
} while (0)
#endif
uint64_t
gquic_be_read_float_time16 (const void *mem);
void
gquic_be_write_float_time16 (lsquic_time_t time_us, void *mem);
void
gquic_be_parse_packet_in_finish (lsquic_packet_in_t *packet_in,
struct packin_parse_state *state);
int
gquic_be_gen_ver_nego_pkt (unsigned char *buf, size_t bufsz, uint64_t conn_id,
unsigned version_bitmask);
int
gquic_be_gen_reg_pkt_header (unsigned char *buf, size_t bufsz, const lsquic_cid_t *conn_id,
const lsquic_ver_tag_t *ver, const unsigned char *nonce,
lsquic_packno_t packno, enum lsquic_packno_bits bits);
int
gquic_be_gen_stream_frame (unsigned char *buf, size_t buf_len, uint32_t stream_id,
uint64_t offset, gsf_fin_f gsf_fin, gsf_size_f gsf_size,
gsf_read_f gsf_read, void *stream);
int
gquic_be_parse_stream_frame (const unsigned char *buf, size_t rem_packet_sz,
stream_frame_t *stream_frame);
lsquic_packno_t
gquic_be_parse_ack_high (const unsigned char *buf, size_t buf_len);
int
gquic_be_parse_ack_frame (const unsigned char *buf, size_t buf_len, ack_info_t *ack);
int
gquic_be_gen_stop_waiting_frame(unsigned char *buf, size_t buf_len,
lsquic_packno_t cur_packno, enum lsquic_packno_bits bits,
lsquic_packno_t least_unacked_packno);
int
gquic_be_parse_stop_waiting_frame (const unsigned char *buf, size_t buf_len,
lsquic_packno_t cur_packno, enum lsquic_packno_bits bits,
lsquic_packno_t *least_unacked);
int
gquic_be_skip_stop_waiting_frame (size_t buf_len, enum lsquic_packno_bits bits);
int
gquic_be_gen_window_update_frame (unsigned char *buf, int buf_len, uint32_t stream_id,
uint64_t offset);
int
gquic_be_parse_window_update_frame (const unsigned char *buf, size_t buf_len,
uint32_t *stream_id, uint64_t *offset);
int
gquic_be_gen_blocked_frame (unsigned char *buf, size_t buf_len, uint32_t stream_id);
int
gquic_be_parse_blocked_frame (const unsigned char *buf, size_t buf_len,
uint32_t *stream_id);
int
gquic_be_gen_rst_frame (unsigned char *buf, size_t buf_len, uint32_t stream_id,
uint64_t offset, uint32_t error_code);
int
gquic_be_parse_rst_frame (const unsigned char *buf, size_t buf_len, uint32_t *stream_id,
uint64_t *offset, uint32_t *error_code);
int
gquic_be_gen_ping_frame (unsigned char *buf, int buf_len);
int
gquic_be_gen_connect_close_frame (unsigned char *buf, int buf_len, uint32_t error_code,
const char *reason, int reason_len);
int
gquic_be_parse_connect_close_frame (const unsigned char *buf, size_t buf_len,
uint32_t *error_code, uint16_t *reason_len, uint8_t *reason_offset);
int
gquic_be_gen_goaway_frame(unsigned char *buf, size_t buf_len, uint32_t error_code,
uint32_t last_good_stream_id, const char *reason,
size_t reason_len);
int
gquic_be_parse_goaway_frame (const unsigned char *buf, size_t buf_len,
uint32_t *error_code, uint32_t *last_good_stream_id,
uint16_t *reason_length, const char **reason);
int
gquic_be_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
gaf_rechist_first_f rechist_first, gaf_rechist_next_f rechist_next,
gaf_rechist_largest_recv_f rechist_largest_recv,
void *rechist, lsquic_time_t now, int *has_missing);
#endif

View File

@ -0,0 +1,731 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_parse_gquic_common.c -- Parsing functions common to GQUIC
*/
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include "lsquic_types.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_in.h"
#include "lsquic_parse.h"
#include "lsquic.h"
#define LSQUIC_LOGGER_MODULE LSQLM_PARSE
#include "lsquic_logger.h"
#define CHECK_SPACE(need, pstart, pend) \
do { if ((intptr_t) (need) > ((pend) - (pstart))) { return -1; } } while (0)
/* This partially parses `packet_in' and returns 0 if in case it succeeded and
* -1 on failure.
*
* After this function returns 0, connection ID, nonce, and version fields can
* be examined. To finsh parsing the packet, call version-specific
* pf_parse_packet_in_finish() routine.
*/
int
parse_packet_in_begin (lsquic_packet_in_t *packet_in, size_t length,
int is_server, struct packin_parse_state *state)
{
int nbytes;
enum PACKET_PUBLIC_FLAGS public_flags;
const unsigned char *p = packet_in->pi_data;
const unsigned char *const pend = packet_in->pi_data + length;
CHECK_SPACE(1, p, pend);
public_flags = *p++;
if (public_flags & PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID)
{
CHECK_SPACE(8, p, pend);
memcpy(&packet_in->pi_conn_id, p, 8);
packet_in->pi_flags |= PI_CONN_ID;
p += 8;
}
if (public_flags & PACKET_PUBLIC_FLAGS_VERSION)
{
/* It seems that version negotiation packets sent by Google may have
* NONCE bit set. Ignore it:
*/
public_flags &= ~PACKET_PUBLIC_FLAGS_NONCE;
if (is_server)
{
CHECK_SPACE(4, p, pend);
packet_in->pi_quic_ver = p - packet_in->pi_data;
p += 4;
}
else
{ /* OK, we have a version negotiation packet. We need to verify
* that it has correct structure. See Section 4.3 of
* [draft-ietf-quic-transport-00].
*/
if ((public_flags & ~(PACKET_PUBLIC_FLAGS_VERSION|
PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID))
|| ((pend - p) & 3))
return -1;
CHECK_SPACE(4, p, pend);
packet_in->pi_quic_ver = p - packet_in->pi_data;
p = pend;
}
}
else
{
/* From [draft-hamilton-quic-transport-protocol-01]:
* 0x40 = MULTIPATH. This bit is reserved for multipath use.
* 0x80 is currently unused, and must be set to 0.
*
* The reference implementation checks that two high bits are not set
* if version flag is not set or if the version is the same. For our
* purposes, all GQUIC version we support so far have these bits set
* to zero.
*/
if (public_flags & (0x80|0x40))
return -1;
packet_in->pi_quic_ver = 0;
}
if (!is_server && (public_flags & PACKET_PUBLIC_FLAGS_NONCE) ==
PACKET_PUBLIC_FLAGS_NONCE)
{
CHECK_SPACE(32, p, pend);
packet_in->pi_nonce = p - packet_in->pi_data;
p += 32;
}
else
packet_in->pi_nonce = 0;
state->pps_p = p;
packet_in->pi_packno = 0;
if (0 == (public_flags & (PACKET_PUBLIC_FLAGS_VERSION|PACKET_PUBLIC_FLAGS_RST))
|| ((public_flags & PACKET_PUBLIC_FLAGS_VERSION) && is_server))
{
nbytes = twobit_to_1246((public_flags >> 4) & 3);
CHECK_SPACE(nbytes, p, pend);
p += nbytes;
state->pps_nbytes = nbytes;
}
else
state->pps_nbytes = 0;
packet_in->pi_header_sz = p - packet_in->pi_data;
packet_in->pi_frame_types = 0;
memset(&packet_in->pi_next, 0, sizeof(packet_in->pi_next));
packet_in->pi_data_sz = length;
packet_in->pi_refcnt = 0;
packet_in->pi_received = 0;
return 0;
}
static const enum QUIC_FRAME_TYPE byte2frame_type_Q035_thru_Q039[0x100] =
{
[0x00] = QUIC_FRAME_PADDING,
[0x01] = QUIC_FRAME_RST_STREAM,
[0x02] = QUIC_FRAME_CONNECTION_CLOSE,
[0x03] = QUIC_FRAME_GOAWAY,
[0x04] = QUIC_FRAME_WINDOW_UPDATE,
[0x05] = QUIC_FRAME_BLOCKED,
[0x06] = QUIC_FRAME_STOP_WAITING,
[0x07] = QUIC_FRAME_PING,
[0x08] = QUIC_FRAME_INVALID,
[0x09] = QUIC_FRAME_INVALID,
[0x0A] = QUIC_FRAME_INVALID,
[0x0B] = QUIC_FRAME_INVALID,
[0x0C] = QUIC_FRAME_INVALID,
[0x0D] = QUIC_FRAME_INVALID,
[0x0E] = QUIC_FRAME_INVALID,
[0x0F] = QUIC_FRAME_INVALID,
[0x10] = QUIC_FRAME_INVALID,
[0x11] = QUIC_FRAME_INVALID,
[0x12] = QUIC_FRAME_INVALID,
[0x13] = QUIC_FRAME_INVALID,
[0x14] = QUIC_FRAME_INVALID,
[0x15] = QUIC_FRAME_INVALID,
[0x16] = QUIC_FRAME_INVALID,
[0x17] = QUIC_FRAME_INVALID,
[0x18] = QUIC_FRAME_INVALID,
[0x19] = QUIC_FRAME_INVALID,
[0x1A] = QUIC_FRAME_INVALID,
[0x1B] = QUIC_FRAME_INVALID,
[0x1C] = QUIC_FRAME_INVALID,
[0x1D] = QUIC_FRAME_INVALID,
[0x1E] = QUIC_FRAME_INVALID,
[0x1F] = QUIC_FRAME_INVALID,
[0x20] = QUIC_FRAME_INVALID,
[0x21] = QUIC_FRAME_INVALID,
[0x22] = QUIC_FRAME_INVALID,
[0x23] = QUIC_FRAME_INVALID,
[0x24] = QUIC_FRAME_INVALID,
[0x25] = QUIC_FRAME_INVALID,
[0x26] = QUIC_FRAME_INVALID,
[0x27] = QUIC_FRAME_INVALID,
[0x28] = QUIC_FRAME_INVALID,
[0x29] = QUIC_FRAME_INVALID,
[0x2A] = QUIC_FRAME_INVALID,
[0x2B] = QUIC_FRAME_INVALID,
[0x2C] = QUIC_FRAME_INVALID,
[0x2D] = QUIC_FRAME_INVALID,
[0x2E] = QUIC_FRAME_INVALID,
[0x2F] = QUIC_FRAME_INVALID,
[0x30] = QUIC_FRAME_INVALID,
[0x31] = QUIC_FRAME_INVALID,
[0x32] = QUIC_FRAME_INVALID,
[0x33] = QUIC_FRAME_INVALID,
[0x34] = QUIC_FRAME_INVALID,
[0x35] = QUIC_FRAME_INVALID,
[0x36] = QUIC_FRAME_INVALID,
[0x37] = QUIC_FRAME_INVALID,
[0x38] = QUIC_FRAME_INVALID,
[0x39] = QUIC_FRAME_INVALID,
[0x3A] = QUIC_FRAME_INVALID,
[0x3B] = QUIC_FRAME_INVALID,
[0x3C] = QUIC_FRAME_INVALID,
[0x3D] = QUIC_FRAME_INVALID,
[0x3E] = QUIC_FRAME_INVALID,
[0x3F] = QUIC_FRAME_INVALID,
[0x40] = QUIC_FRAME_ACK,
[0x41] = QUIC_FRAME_ACK,
[0x42] = QUIC_FRAME_ACK,
[0x43] = QUIC_FRAME_ACK,
[0x44] = QUIC_FRAME_ACK,
[0x45] = QUIC_FRAME_ACK,
[0x46] = QUIC_FRAME_ACK,
[0x47] = QUIC_FRAME_ACK,
[0x48] = QUIC_FRAME_ACK,
[0x49] = QUIC_FRAME_ACK,
[0x4A] = QUIC_FRAME_ACK,
[0x4B] = QUIC_FRAME_ACK,
[0x4C] = QUIC_FRAME_ACK,
[0x4D] = QUIC_FRAME_ACK,
[0x4E] = QUIC_FRAME_ACK,
[0x4F] = QUIC_FRAME_ACK,
[0x50] = QUIC_FRAME_ACK,
[0x51] = QUIC_FRAME_ACK,
[0x52] = QUIC_FRAME_ACK,
[0x53] = QUIC_FRAME_ACK,
[0x54] = QUIC_FRAME_ACK,
[0x55] = QUIC_FRAME_ACK,
[0x56] = QUIC_FRAME_ACK,
[0x57] = QUIC_FRAME_ACK,
[0x58] = QUIC_FRAME_ACK,
[0x59] = QUIC_FRAME_ACK,
[0x5A] = QUIC_FRAME_ACK,
[0x5B] = QUIC_FRAME_ACK,
[0x5C] = QUIC_FRAME_ACK,
[0x5D] = QUIC_FRAME_ACK,
[0x5E] = QUIC_FRAME_ACK,
[0x5F] = QUIC_FRAME_ACK,
[0x60] = QUIC_FRAME_ACK,
[0x61] = QUIC_FRAME_ACK,
[0x62] = QUIC_FRAME_ACK,
[0x63] = QUIC_FRAME_ACK,
[0x64] = QUIC_FRAME_ACK,
[0x65] = QUIC_FRAME_ACK,
[0x66] = QUIC_FRAME_ACK,
[0x67] = QUIC_FRAME_ACK,
[0x68] = QUIC_FRAME_ACK,
[0x69] = QUIC_FRAME_ACK,
[0x6A] = QUIC_FRAME_ACK,
[0x6B] = QUIC_FRAME_ACK,
[0x6C] = QUIC_FRAME_ACK,
[0x6D] = QUIC_FRAME_ACK,
[0x6E] = QUIC_FRAME_ACK,
[0x6F] = QUIC_FRAME_ACK,
[0x70] = QUIC_FRAME_ACK,
[0x71] = QUIC_FRAME_ACK,
[0x72] = QUIC_FRAME_ACK,
[0x73] = QUIC_FRAME_ACK,
[0x74] = QUIC_FRAME_ACK,
[0x75] = QUIC_FRAME_ACK,
[0x76] = QUIC_FRAME_ACK,
[0x77] = QUIC_FRAME_ACK,
[0x78] = QUIC_FRAME_ACK,
[0x79] = QUIC_FRAME_ACK,
[0x7A] = QUIC_FRAME_ACK,
[0x7B] = QUIC_FRAME_ACK,
[0x7C] = QUIC_FRAME_ACK,
[0x7D] = QUIC_FRAME_ACK,
[0x7E] = QUIC_FRAME_ACK,
[0x7F] = QUIC_FRAME_ACK,
[0x80] = QUIC_FRAME_STREAM,
[0x81] = QUIC_FRAME_STREAM,
[0x82] = QUIC_FRAME_STREAM,
[0x83] = QUIC_FRAME_STREAM,
[0x84] = QUIC_FRAME_STREAM,
[0x85] = QUIC_FRAME_STREAM,
[0x86] = QUIC_FRAME_STREAM,
[0x87] = QUIC_FRAME_STREAM,
[0x88] = QUIC_FRAME_STREAM,
[0x89] = QUIC_FRAME_STREAM,
[0x8A] = QUIC_FRAME_STREAM,
[0x8B] = QUIC_FRAME_STREAM,
[0x8C] = QUIC_FRAME_STREAM,
[0x8D] = QUIC_FRAME_STREAM,
[0x8E] = QUIC_FRAME_STREAM,
[0x8F] = QUIC_FRAME_STREAM,
[0x90] = QUIC_FRAME_STREAM,
[0x91] = QUIC_FRAME_STREAM,
[0x92] = QUIC_FRAME_STREAM,
[0x93] = QUIC_FRAME_STREAM,
[0x94] = QUIC_FRAME_STREAM,
[0x95] = QUIC_FRAME_STREAM,
[0x96] = QUIC_FRAME_STREAM,
[0x97] = QUIC_FRAME_STREAM,
[0x98] = QUIC_FRAME_STREAM,
[0x99] = QUIC_FRAME_STREAM,
[0x9A] = QUIC_FRAME_STREAM,
[0x9B] = QUIC_FRAME_STREAM,
[0x9C] = QUIC_FRAME_STREAM,
[0x9D] = QUIC_FRAME_STREAM,
[0x9E] = QUIC_FRAME_STREAM,
[0x9F] = QUIC_FRAME_STREAM,
[0xA0] = QUIC_FRAME_STREAM,
[0xA1] = QUIC_FRAME_STREAM,
[0xA2] = QUIC_FRAME_STREAM,
[0xA3] = QUIC_FRAME_STREAM,
[0xA4] = QUIC_FRAME_STREAM,
[0xA5] = QUIC_FRAME_STREAM,
[0xA6] = QUIC_FRAME_STREAM,
[0xA7] = QUIC_FRAME_STREAM,
[0xA8] = QUIC_FRAME_STREAM,
[0xA9] = QUIC_FRAME_STREAM,
[0xAA] = QUIC_FRAME_STREAM,
[0xAB] = QUIC_FRAME_STREAM,
[0xAC] = QUIC_FRAME_STREAM,
[0xAD] = QUIC_FRAME_STREAM,
[0xAE] = QUIC_FRAME_STREAM,
[0xAF] = QUIC_FRAME_STREAM,
[0xB0] = QUIC_FRAME_STREAM,
[0xB1] = QUIC_FRAME_STREAM,
[0xB2] = QUIC_FRAME_STREAM,
[0xB3] = QUIC_FRAME_STREAM,
[0xB4] = QUIC_FRAME_STREAM,
[0xB5] = QUIC_FRAME_STREAM,
[0xB6] = QUIC_FRAME_STREAM,
[0xB7] = QUIC_FRAME_STREAM,
[0xB8] = QUIC_FRAME_STREAM,
[0xB9] = QUIC_FRAME_STREAM,
[0xBA] = QUIC_FRAME_STREAM,
[0xBB] = QUIC_FRAME_STREAM,
[0xBC] = QUIC_FRAME_STREAM,
[0xBD] = QUIC_FRAME_STREAM,
[0xBE] = QUIC_FRAME_STREAM,
[0xBF] = QUIC_FRAME_STREAM,
[0xC0] = QUIC_FRAME_STREAM,
[0xC1] = QUIC_FRAME_STREAM,
[0xC2] = QUIC_FRAME_STREAM,
[0xC3] = QUIC_FRAME_STREAM,
[0xC4] = QUIC_FRAME_STREAM,
[0xC5] = QUIC_FRAME_STREAM,
[0xC6] = QUIC_FRAME_STREAM,
[0xC7] = QUIC_FRAME_STREAM,
[0xC8] = QUIC_FRAME_STREAM,
[0xC9] = QUIC_FRAME_STREAM,
[0xCA] = QUIC_FRAME_STREAM,
[0xCB] = QUIC_FRAME_STREAM,
[0xCC] = QUIC_FRAME_STREAM,
[0xCD] = QUIC_FRAME_STREAM,
[0xCE] = QUIC_FRAME_STREAM,
[0xCF] = QUIC_FRAME_STREAM,
[0xD0] = QUIC_FRAME_STREAM,
[0xD1] = QUIC_FRAME_STREAM,
[0xD2] = QUIC_FRAME_STREAM,
[0xD3] = QUIC_FRAME_STREAM,
[0xD4] = QUIC_FRAME_STREAM,
[0xD5] = QUIC_FRAME_STREAM,
[0xD6] = QUIC_FRAME_STREAM,
[0xD7] = QUIC_FRAME_STREAM,
[0xD8] = QUIC_FRAME_STREAM,
[0xD9] = QUIC_FRAME_STREAM,
[0xDA] = QUIC_FRAME_STREAM,
[0xDB] = QUIC_FRAME_STREAM,
[0xDC] = QUIC_FRAME_STREAM,
[0xDD] = QUIC_FRAME_STREAM,
[0xDE] = QUIC_FRAME_STREAM,
[0xDF] = QUIC_FRAME_STREAM,
[0xE0] = QUIC_FRAME_STREAM,
[0xE1] = QUIC_FRAME_STREAM,
[0xE2] = QUIC_FRAME_STREAM,
[0xE3] = QUIC_FRAME_STREAM,
[0xE4] = QUIC_FRAME_STREAM,
[0xE5] = QUIC_FRAME_STREAM,
[0xE6] = QUIC_FRAME_STREAM,
[0xE7] = QUIC_FRAME_STREAM,
[0xE8] = QUIC_FRAME_STREAM,
[0xE9] = QUIC_FRAME_STREAM,
[0xEA] = QUIC_FRAME_STREAM,
[0xEB] = QUIC_FRAME_STREAM,
[0xEC] = QUIC_FRAME_STREAM,
[0xED] = QUIC_FRAME_STREAM,
[0xEE] = QUIC_FRAME_STREAM,
[0xEF] = QUIC_FRAME_STREAM,
[0xF0] = QUIC_FRAME_STREAM,
[0xF1] = QUIC_FRAME_STREAM,
[0xF2] = QUIC_FRAME_STREAM,
[0xF3] = QUIC_FRAME_STREAM,
[0xF4] = QUIC_FRAME_STREAM,
[0xF5] = QUIC_FRAME_STREAM,
[0xF6] = QUIC_FRAME_STREAM,
[0xF7] = QUIC_FRAME_STREAM,
[0xF8] = QUIC_FRAME_STREAM,
[0xF9] = QUIC_FRAME_STREAM,
[0xFA] = QUIC_FRAME_STREAM,
[0xFB] = QUIC_FRAME_STREAM,
[0xFC] = QUIC_FRAME_STREAM,
[0xFD] = QUIC_FRAME_STREAM,
[0xFE] = QUIC_FRAME_STREAM,
[0xFF] = QUIC_FRAME_STREAM,
};
static const enum QUIC_FRAME_TYPE byte2frame_type_Q040[0x100] =
{
[0x00] = QUIC_FRAME_PADDING,
[0x01] = QUIC_FRAME_RST_STREAM,
[0x02] = QUIC_FRAME_CONNECTION_CLOSE,
[0x03] = QUIC_FRAME_GOAWAY,
[0x04] = QUIC_FRAME_WINDOW_UPDATE,
[0x05] = QUIC_FRAME_BLOCKED,
[0x06] = QUIC_FRAME_STOP_WAITING,
[0x07] = QUIC_FRAME_PING,
[0x08] = QUIC_FRAME_INVALID,
[0x09] = QUIC_FRAME_INVALID,
[0x0A] = QUIC_FRAME_INVALID,
[0x0B] = QUIC_FRAME_INVALID,
[0x0C] = QUIC_FRAME_INVALID,
[0x0D] = QUIC_FRAME_INVALID,
[0x0E] = QUIC_FRAME_INVALID,
[0x0F] = QUIC_FRAME_INVALID,
[0x10] = QUIC_FRAME_INVALID,
[0x11] = QUIC_FRAME_INVALID,
[0x12] = QUIC_FRAME_INVALID,
[0x13] = QUIC_FRAME_INVALID,
[0x14] = QUIC_FRAME_INVALID,
[0x15] = QUIC_FRAME_INVALID,
[0x16] = QUIC_FRAME_INVALID,
[0x17] = QUIC_FRAME_INVALID,
[0x18] = QUIC_FRAME_INVALID,
[0x19] = QUIC_FRAME_INVALID,
[0x1A] = QUIC_FRAME_INVALID,
[0x1B] = QUIC_FRAME_INVALID,
[0x1C] = QUIC_FRAME_INVALID,
[0x1D] = QUIC_FRAME_INVALID,
[0x1E] = QUIC_FRAME_INVALID,
[0x1F] = QUIC_FRAME_INVALID,
[0x20] = QUIC_FRAME_INVALID,
[0x21] = QUIC_FRAME_INVALID,
[0x22] = QUIC_FRAME_INVALID,
[0x23] = QUIC_FRAME_INVALID,
[0x24] = QUIC_FRAME_INVALID,
[0x25] = QUIC_FRAME_INVALID,
[0x26] = QUIC_FRAME_INVALID,
[0x27] = QUIC_FRAME_INVALID,
[0x28] = QUIC_FRAME_INVALID,
[0x29] = QUIC_FRAME_INVALID,
[0x2A] = QUIC_FRAME_INVALID,
[0x2B] = QUIC_FRAME_INVALID,
[0x2C] = QUIC_FRAME_INVALID,
[0x2D] = QUIC_FRAME_INVALID,
[0x2E] = QUIC_FRAME_INVALID,
[0x2F] = QUIC_FRAME_INVALID,
[0x30] = QUIC_FRAME_INVALID,
[0x31] = QUIC_FRAME_INVALID,
[0x32] = QUIC_FRAME_INVALID,
[0x33] = QUIC_FRAME_INVALID,
[0x34] = QUIC_FRAME_INVALID,
[0x35] = QUIC_FRAME_INVALID,
[0x36] = QUIC_FRAME_INVALID,
[0x37] = QUIC_FRAME_INVALID,
[0x38] = QUIC_FRAME_INVALID,
[0x39] = QUIC_FRAME_INVALID,
[0x3A] = QUIC_FRAME_INVALID,
[0x3B] = QUIC_FRAME_INVALID,
[0x3C] = QUIC_FRAME_INVALID,
[0x3D] = QUIC_FRAME_INVALID,
[0x3E] = QUIC_FRAME_INVALID,
[0x3F] = QUIC_FRAME_INVALID,
[0x40] = QUIC_FRAME_INVALID,
[0x41] = QUIC_FRAME_INVALID,
[0x42] = QUIC_FRAME_INVALID,
[0x43] = QUIC_FRAME_INVALID,
[0x44] = QUIC_FRAME_INVALID,
[0x45] = QUIC_FRAME_INVALID,
[0x46] = QUIC_FRAME_INVALID,
[0x47] = QUIC_FRAME_INVALID,
[0x48] = QUIC_FRAME_INVALID,
[0x49] = QUIC_FRAME_INVALID,
[0x4A] = QUIC_FRAME_INVALID,
[0x4B] = QUIC_FRAME_INVALID,
[0x4C] = QUIC_FRAME_INVALID,
[0x4D] = QUIC_FRAME_INVALID,
[0x4E] = QUIC_FRAME_INVALID,
[0x4F] = QUIC_FRAME_INVALID,
[0x50] = QUIC_FRAME_INVALID,
[0x51] = QUIC_FRAME_INVALID,
[0x52] = QUIC_FRAME_INVALID,
[0x53] = QUIC_FRAME_INVALID,
[0x54] = QUIC_FRAME_INVALID,
[0x55] = QUIC_FRAME_INVALID,
[0x56] = QUIC_FRAME_INVALID,
[0x57] = QUIC_FRAME_INVALID,
[0x58] = QUIC_FRAME_INVALID,
[0x59] = QUIC_FRAME_INVALID,
[0x5A] = QUIC_FRAME_INVALID,
[0x5B] = QUIC_FRAME_INVALID,
[0x5C] = QUIC_FRAME_INVALID,
[0x5D] = QUIC_FRAME_INVALID,
[0x5E] = QUIC_FRAME_INVALID,
[0x5F] = QUIC_FRAME_INVALID,
[0x60] = QUIC_FRAME_INVALID,
[0x61] = QUIC_FRAME_INVALID,
[0x62] = QUIC_FRAME_INVALID,
[0x63] = QUIC_FRAME_INVALID,
[0x64] = QUIC_FRAME_INVALID,
[0x65] = QUIC_FRAME_INVALID,
[0x66] = QUIC_FRAME_INVALID,
[0x67] = QUIC_FRAME_INVALID,
[0x68] = QUIC_FRAME_INVALID,
[0x69] = QUIC_FRAME_INVALID,
[0x6A] = QUIC_FRAME_INVALID,
[0x6B] = QUIC_FRAME_INVALID,
[0x6C] = QUIC_FRAME_INVALID,
[0x6D] = QUIC_FRAME_INVALID,
[0x6E] = QUIC_FRAME_INVALID,
[0x6F] = QUIC_FRAME_INVALID,
[0x70] = QUIC_FRAME_INVALID,
[0x71] = QUIC_FRAME_INVALID,
[0x72] = QUIC_FRAME_INVALID,
[0x73] = QUIC_FRAME_INVALID,
[0x74] = QUIC_FRAME_INVALID,
[0x75] = QUIC_FRAME_INVALID,
[0x76] = QUIC_FRAME_INVALID,
[0x77] = QUIC_FRAME_INVALID,
[0x78] = QUIC_FRAME_INVALID,
[0x79] = QUIC_FRAME_INVALID,
[0x7A] = QUIC_FRAME_INVALID,
[0x7B] = QUIC_FRAME_INVALID,
[0x7C] = QUIC_FRAME_INVALID,
[0x7D] = QUIC_FRAME_INVALID,
[0x7E] = QUIC_FRAME_INVALID,
[0x7F] = QUIC_FRAME_INVALID,
[0x80] = QUIC_FRAME_INVALID,
[0x81] = QUIC_FRAME_INVALID,
[0x82] = QUIC_FRAME_INVALID,
[0x83] = QUIC_FRAME_INVALID,
[0x84] = QUIC_FRAME_INVALID,
[0x85] = QUIC_FRAME_INVALID,
[0x86] = QUIC_FRAME_INVALID,
[0x87] = QUIC_FRAME_INVALID,
[0x88] = QUIC_FRAME_INVALID,
[0x89] = QUIC_FRAME_INVALID,
[0x8A] = QUIC_FRAME_INVALID,
[0x8B] = QUIC_FRAME_INVALID,
[0x8C] = QUIC_FRAME_INVALID,
[0x8D] = QUIC_FRAME_INVALID,
[0x8E] = QUIC_FRAME_INVALID,
[0x8F] = QUIC_FRAME_INVALID,
[0x90] = QUIC_FRAME_INVALID,
[0x91] = QUIC_FRAME_INVALID,
[0x92] = QUIC_FRAME_INVALID,
[0x93] = QUIC_FRAME_INVALID,
[0x94] = QUIC_FRAME_INVALID,
[0x95] = QUIC_FRAME_INVALID,
[0x96] = QUIC_FRAME_INVALID,
[0x97] = QUIC_FRAME_INVALID,
[0x98] = QUIC_FRAME_INVALID,
[0x99] = QUIC_FRAME_INVALID,
[0x9A] = QUIC_FRAME_INVALID,
[0x9B] = QUIC_FRAME_INVALID,
[0x9C] = QUIC_FRAME_INVALID,
[0x9D] = QUIC_FRAME_INVALID,
[0x9E] = QUIC_FRAME_INVALID,
[0x9F] = QUIC_FRAME_INVALID,
[0xA0] = QUIC_FRAME_ACK,
[0xA1] = QUIC_FRAME_ACK,
[0xA2] = QUIC_FRAME_ACK,
[0xA3] = QUIC_FRAME_ACK,
[0xA4] = QUIC_FRAME_ACK,
[0xA5] = QUIC_FRAME_ACK,
[0xA6] = QUIC_FRAME_ACK,
[0xA7] = QUIC_FRAME_ACK,
[0xA8] = QUIC_FRAME_ACK,
[0xA9] = QUIC_FRAME_ACK,
[0xAA] = QUIC_FRAME_ACK,
[0xAB] = QUIC_FRAME_ACK,
[0xAC] = QUIC_FRAME_ACK,
[0xAD] = QUIC_FRAME_ACK,
[0xAE] = QUIC_FRAME_ACK,
[0xAF] = QUIC_FRAME_ACK,
[0xB0] = QUIC_FRAME_ACK,
[0xB1] = QUIC_FRAME_ACK,
[0xB2] = QUIC_FRAME_ACK,
[0xB3] = QUIC_FRAME_ACK,
[0xB4] = QUIC_FRAME_ACK,
[0xB5] = QUIC_FRAME_ACK,
[0xB6] = QUIC_FRAME_ACK,
[0xB7] = QUIC_FRAME_ACK,
[0xB8] = QUIC_FRAME_ACK,
[0xB9] = QUIC_FRAME_ACK,
[0xBA] = QUIC_FRAME_ACK,
[0xBB] = QUIC_FRAME_ACK,
[0xBC] = QUIC_FRAME_ACK,
[0xBD] = QUIC_FRAME_ACK,
[0xBE] = QUIC_FRAME_ACK,
[0xBF] = QUIC_FRAME_ACK,
[0xC0] = QUIC_FRAME_STREAM,
[0xC1] = QUIC_FRAME_STREAM,
[0xC2] = QUIC_FRAME_STREAM,
[0xC3] = QUIC_FRAME_STREAM,
[0xC4] = QUIC_FRAME_STREAM,
[0xC5] = QUIC_FRAME_STREAM,
[0xC6] = QUIC_FRAME_STREAM,
[0xC7] = QUIC_FRAME_STREAM,
[0xC8] = QUIC_FRAME_STREAM,
[0xC9] = QUIC_FRAME_STREAM,
[0xCA] = QUIC_FRAME_STREAM,
[0xCB] = QUIC_FRAME_STREAM,
[0xCC] = QUIC_FRAME_STREAM,
[0xCD] = QUIC_FRAME_STREAM,
[0xCE] = QUIC_FRAME_STREAM,
[0xCF] = QUIC_FRAME_STREAM,
[0xD0] = QUIC_FRAME_STREAM,
[0xD1] = QUIC_FRAME_STREAM,
[0xD2] = QUIC_FRAME_STREAM,
[0xD3] = QUIC_FRAME_STREAM,
[0xD4] = QUIC_FRAME_STREAM,
[0xD5] = QUIC_FRAME_STREAM,
[0xD6] = QUIC_FRAME_STREAM,
[0xD7] = QUIC_FRAME_STREAM,
[0xD8] = QUIC_FRAME_STREAM,
[0xD9] = QUIC_FRAME_STREAM,
[0xDA] = QUIC_FRAME_STREAM,
[0xDB] = QUIC_FRAME_STREAM,
[0xDC] = QUIC_FRAME_STREAM,
[0xDD] = QUIC_FRAME_STREAM,
[0xDE] = QUIC_FRAME_STREAM,
[0xDF] = QUIC_FRAME_STREAM,
[0xE0] = QUIC_FRAME_STREAM,
[0xE1] = QUIC_FRAME_STREAM,
[0xE2] = QUIC_FRAME_STREAM,
[0xE3] = QUIC_FRAME_STREAM,
[0xE4] = QUIC_FRAME_STREAM,
[0xE5] = QUIC_FRAME_STREAM,
[0xE6] = QUIC_FRAME_STREAM,
[0xE7] = QUIC_FRAME_STREAM,
[0xE8] = QUIC_FRAME_STREAM,
[0xE9] = QUIC_FRAME_STREAM,
[0xEA] = QUIC_FRAME_STREAM,
[0xEB] = QUIC_FRAME_STREAM,
[0xEC] = QUIC_FRAME_STREAM,
[0xED] = QUIC_FRAME_STREAM,
[0xEE] = QUIC_FRAME_STREAM,
[0xEF] = QUIC_FRAME_STREAM,
[0xF0] = QUIC_FRAME_STREAM,
[0xF1] = QUIC_FRAME_STREAM,
[0xF2] = QUIC_FRAME_STREAM,
[0xF3] = QUIC_FRAME_STREAM,
[0xF4] = QUIC_FRAME_STREAM,
[0xF5] = QUIC_FRAME_STREAM,
[0xF6] = QUIC_FRAME_STREAM,
[0xF7] = QUIC_FRAME_STREAM,
[0xF8] = QUIC_FRAME_STREAM,
[0xF9] = QUIC_FRAME_STREAM,
[0xFA] = QUIC_FRAME_STREAM,
[0xFB] = QUIC_FRAME_STREAM,
[0xFC] = QUIC_FRAME_STREAM,
[0xFD] = QUIC_FRAME_STREAM,
[0xFE] = QUIC_FRAME_STREAM,
[0xFF] = QUIC_FRAME_STREAM,
};
enum QUIC_FRAME_TYPE
parse_frame_type_gquic_Q035_thru_Q039 (unsigned char b)
{
return byte2frame_type_Q035_thru_Q039[b];
}
enum QUIC_FRAME_TYPE
parse_frame_type_gquic_Q040 (unsigned char b)
{
return byte2frame_type_Q040[b];
}
unsigned
parse_stream_frame_header_sz_gquic (unsigned char type)
{
const unsigned data_len = (type >> 4) & 2;
const unsigned offset_len = ((type >> 2) & 7) + 1 - !((type >> 2) & 7);
const unsigned stream_id_len = 1 + (type & 3);
return 1 + data_len + offset_len + stream_id_len;
}
size_t
calc_stream_frame_header_sz_gquic (uint32_t stream_id, uint64_t offset)
{
return
/* Type */
1
/* Stream ID length */
+ ((stream_id) > 0x0000FF)
+ ((stream_id) > 0x00FFFF)
+ ((stream_id) > 0xFFFFFF)
+ 1
/* Offset length */
+ ((offset) >= (1ULL << 56))
+ ((offset) >= (1ULL << 48))
+ ((offset) >= (1ULL << 40))
+ ((offset) >= (1ULL << 32))
+ ((offset) >= (1ULL << 24))
+ ((offset) >= (1ULL << 16))
+ (((offset) > 0) << 1)
/* Add data length (2) yourself, if necessary */
;
}
char *
acki2str (const struct ack_info *acki, size_t *sz)
{
size_t off, bufsz, nw;
unsigned n;
char *buf;
bufsz = acki->n_ranges * (3 /* [-] */ + 20 /* ~0ULL */ * 2);
buf = malloc(bufsz);
if (!buf)
{
LSQ_WARN("%s: malloc(%zd) failure: %s", __func__, bufsz,
strerror(errno));
return NULL;
}
off = 0;
for (n = 0; n < acki->n_ranges; ++n)
{
nw = snprintf(buf + off, bufsz - off, "[%"PRIu64"-%"PRIu64"]",
acki->ranges[n].high, acki->ranges[n].low);
if (nw > bufsz - off)
break;
off += nw;
}
*sz = off;
return buf;
}

View File

@ -0,0 +1,878 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_parse_gquic_le.c -- Parsing functions specific to little-endian
* (Q038 and lower) GQUIC.
*/
#include <assert.h>
#include <inttypes.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include <sys/types.h>
#include "lsquic_types.h"
#include "lsquic_alarmset.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_in.h"
#include "lsquic_parse.h"
#include "lsquic_rechist.h"
#include "lsquic_sfcw.h"
#include "lsquic_stream.h"
#include "lsquic_mm.h"
#include "lsquic_malo.h"
#include "lsquic_version.h"
#include "lsquic.h"
#define LSQUIC_LOGGER_MODULE LSQLM_PARSE
#include "lsquic_logger.h"
#define CHECK_SPACE(need, pstart, pend) \
do { if ((intptr_t) (need) > ((pend) - (pstart))) { return -1; } } while (0)
/* get the len bytes as a uint64_t */
static uint64_t get_vary_len_int64(const void *mem, uint8_t len)
{
assert(len <= 8);
uint64_t v = 0;
memcpy(&v, mem, len);
return v;
}
static void write_vary_len_to_buf(uint64_t v, unsigned char *mem, uint8_t len)
{
assert(len <= 8);
memcpy(mem, &v, len);
}
/* read 16 bits(2 bytes) time, unit: us */
static uint64_t
gquic_le_read_float_time16 (const void *mem)
{
uint64_t temp = get_vary_len_int64(mem, 2);
uint16_t exp = (temp >> 11) & 0x1F;
if (0 == exp)
return temp;
else
{
--exp;
temp &= 0x7FF;
temp |= 0x800;
return temp << exp;
}
}
static void
gquic_le_write_float_time16 (lsquic_time_t time_us, void *mem)
{
uint16_t ret = 0;
uint16_t high, i;
if (time_us < ((uint64_t)1 << 11))
ret = time_us;
else if(time_us > 0x3FFC0000000)
ret = 0xFFFF;
else
{
high = 0;
for (i = 16; i > 0; i /= 2)
{
if (time_us >= (uint64_t)1 << (11 + i))
{
high |= i;
time_us >>= i;
}
}
ret = time_us + (high << 11);
}
memcpy(mem, (void *)&ret, 2);
}
static int packet_length_arr[] = {1, 2, 4, 6};
/* make sure the flag is lower 2 bits set for this checking */
static int
flag_to_pkt_num_len(unsigned char flag)
{
return packet_length_arr[flag & 0x03];
}
/* Parse out packet number */
static void
gquic_le_parse_packet_in_finish (lsquic_packet_in_t *packet_in,
struct packin_parse_state *state)
{
if (state->pps_nbytes)
memcpy((void *)&packet_in->pi_packno, state->pps_p, state->pps_nbytes);
}
static int
gquic_le_gen_ver_nego_pkt (unsigned char *buf, size_t bufsz, uint64_t conn_id,
unsigned version_bitmask)
{
int sz;
unsigned char *p = buf;
unsigned char *const pend = p + bufsz;
CHECK_SPACE(1, p, pend);
*p = PACKET_PUBLIC_FLAGS_VERSION | PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID;
++p;
CHECK_SPACE(8, p, pend);
memcpy(p, &conn_id, 8);
p += 8;
sz = gen_ver_tags(p, pend - p, version_bitmask);
if (sz < 0)
return -1;
return p + sz - buf;
}
static int
gquic_le_gen_reg_pkt_header (unsigned char *buf, size_t bufsz, const lsquic_cid_t *conn_id,
const lsquic_ver_tag_t *ver, const unsigned char *nonce,
lsquic_packno_t packno, enum lsquic_packno_bits bits)
{
unsigned packnum_len, header_len;
unsigned char *p;
packnum_len = packno_bits2len(bits);
header_len = 1 + (!!conn_id << 3) + (!!ver << 2) + ((!!nonce) << 5)
+ packnum_len;
if (header_len > bufsz)
{
errno = ENOBUFS;
return -1;
}
p = buf;
*p = (!!conn_id << 3)
| (bits << 4)
| ((!!nonce) << 2)
| !!ver;
++p;
if (conn_id)
{
memcpy(p, conn_id , sizeof(*conn_id));
p += sizeof(*conn_id);
}
if (ver)
{
memcpy(p, ver, 4);
p += 4;
}
if (nonce)
{
memcpy(p, nonce , 32);
p += 32;
}
/* ENDIAN */
memcpy(p, &packno, packnum_len);
p += packnum_len;
assert(p - buf == (intptr_t) header_len);
return header_len;
}
static int
gquic_le_gen_stream_frame (unsigned char *buf, size_t buf_len, uint32_t stream_id,
uint64_t offset, gsf_fin_f gsf_fin, gsf_size_f gsf_size,
gsf_read_f gsf_read, void *stream)
{
/* 1fdoooss */
unsigned slen, olen, dlen;
unsigned char *p = buf + 1;
int fin;
/* ss: Stream ID length: 1, 2, 3, or 4 bytes */
slen = (stream_id > 0x0000FF)
+ (stream_id > 0x00FFFF)
+ (stream_id > 0xFFFFFF)
+ 1;
/* ooo: Offset length: 0, 2, 3, 4, 5, 6, 7, or 8 bytes */
olen = (offset >= (1ULL << 56))
+ (offset >= (1ULL << 48))
+ (offset >= (1ULL << 40))
+ (offset >= (1ULL << 32))
+ (offset >= (1ULL << 24))
+ (offset >= (1ULL << 16))
+ ((offset > 0) << 1);
fin = gsf_fin(stream);
if (!fin)
{
unsigned size, n_avail;
uint16_t nr;
size = gsf_size(stream);
n_avail = buf_len - (p + slen + olen - buf);
/* If we cannot fill remaining buffer, we need to include data
* length.
*/
dlen = (size < n_avail) << 1;
n_avail -= dlen;
CHECK_SPACE(1 + olen + slen + dlen +
+ 1 /* We need to write at least 1 byte */, buf, buf + buf_len);
memcpy(p, &stream_id, slen);
p += slen;
memcpy(p, &offset, olen);
p += olen;
/* Read as much as we can */
nr = gsf_read(stream, p + dlen, n_avail, &fin);
assert(nr != 0);
if (dlen)
memcpy(p, &nr, 2);
p += dlen + nr;
}
else
{
dlen = 2;
CHECK_SPACE(1 + slen + olen + 2, buf, buf + buf_len);
memcpy(p, &stream_id, slen);
p += slen;
memcpy(p, &offset, olen);
p += olen;
memset(p, 0, 2);
p += 2;
}
/* Convert slen to bit representation: 0 - 3: */
slen -= 1;
assert(slen <= 3);
/* Convert olen to bit representation: 0 - 7: */
olen += !olen;
olen -= 1;
assert(olen <= 7);
buf[0] = 0x80
| (fin << 6)
| (dlen << 4)
| (olen << 2)
| slen
;
return p - buf;
}
/* return parsed (used) buffer length */
static int
gquic_le_parse_stream_frame (const unsigned char *buf, size_t rem_packet_sz,
stream_frame_t *stream_frame)
{
/* 1fdoooss */
const unsigned char *p = buf;
const unsigned char *const pend = p + rem_packet_sz;
CHECK_SPACE(1, p, pend);
const char type = *p++;
const unsigned data_len = (type >> 4) & 2;
const unsigned offset_len = ((type >> 2) & 7) + 1 - !((type >> 2) & 7);
const unsigned stream_id_len = 1 + (type & 3);
const unsigned need = data_len + offset_len + stream_id_len;
CHECK_SPACE(need, p, pend);
memset(stream_frame, 0, sizeof(*stream_frame));
stream_frame->data_frame.df_fin = (type >> 6) & 1;
memcpy(&stream_frame->stream_id, p, stream_id_len);
p += stream_id_len;
memcpy(&stream_frame->data_frame.df_offset, p, offset_len);
p += offset_len;
if (data_len)
{
memcpy(&stream_frame->data_frame.df_size, p, data_len);
p += data_len;
CHECK_SPACE(stream_frame->data_frame.df_size, p, pend);
stream_frame->data_frame.df_data = p;
p += stream_frame->data_frame.df_size;
}
else
{
stream_frame->data_frame.df_size = pend - p;
stream_frame->data_frame.df_data = p;
p = pend;
}
/* From the spec: "A stream frame must always have either non-zero
* data length or the FIN bit set.'
*/
if (!(stream_frame->data_frame.df_size ||
stream_frame->data_frame.df_fin))
return -1;
assert(p <= pend);
return p - (unsigned char *) buf;
}
/* This is a special function: it is used to extract the largest observed
* packet number from ACK frame that we ourselves generated. This allows
* us to skip some checks.
*/
static lsquic_packno_t
gquic_le_parse_ack_high (const unsigned char *buf, size_t buf_len)
{
unsigned char type;
unsigned largest_obs_len;
type = buf[0];
largest_obs_len = flag_to_pkt_num_len(type >> 2);
assert(parse_frame_type_gquic_Q035_thru_Q039(type) == QUIC_FRAME_ACK);
assert(buf_len >= 1 + largest_obs_len);
return get_vary_len_int64(buf + 1, largest_obs_len);
}
/* Return parsed (used) buffer length.
* If parsing failed, negative value is returned.
*/
static int
gquic_le_parse_ack_frame (const unsigned char *buf, size_t buf_len, ack_info_t *ack)
{
/* 01nullmm */
const unsigned char type = buf[0];
const unsigned char *p = buf + 1;
const unsigned char *const pend = buf + buf_len;
assert((type & 0xC0) == 0x40); /* We're passed correct frame type */
const int ack_block_len = flag_to_pkt_num_len(type); /* mm */
const int largest_obs_len = flag_to_pkt_num_len(type >> 2); /* ll */
CHECK_SPACE(largest_obs_len, p , pend);
ack->ranges[0].high = get_vary_len_int64(p, largest_obs_len);
p += largest_obs_len;
CHECK_SPACE(2, p , pend);
ack->lack_delta = gquic_le_read_float_time16(p);
p += 2;
unsigned n_blocks;
if (type & 0x20)
{
CHECK_SPACE(1, p , pend);
n_blocks = *p;
++p;
}
else
n_blocks = 0;
CHECK_SPACE(ack_block_len, p , pend);
ack->ranges[0].low = ack->ranges[0].high
- get_vary_len_int64(p, ack_block_len) + 1;
p += ack_block_len;
if (n_blocks)
{
CHECK_SPACE((ack_block_len + 1) * n_blocks, p , pend);
unsigned i, n, gap;
for (i = 0, n = 1, gap = 0; i < n_blocks; ++i)
{
gap += *p;
const uint64_t length = get_vary_len_int64(p + 1, ack_block_len);
p += 1 + ack_block_len;
if (length)
{
ack->ranges[n].high = ack->ranges[n - 1].low - gap - 1;
ack->ranges[n].low = ack->ranges[n].high - length + 1;
++n;
gap = 0;
}
}
ack->n_ranges = n;
}
else
ack->n_ranges = 1;
CHECK_SPACE(1, p , pend);
ack->n_timestamps = *p;
++p;
if (ack->n_timestamps)
{
#if LSQUIC_PARSE_ACK_TIMESTAMPS
CHECK_SPACE(5, p , pend);
ack->timestamps[0].packet_delta = *p++;
memcpy(&ack->timestamps[0].delta_usec, p, 4);
p += 4;
unsigned i;
for (i = 1; i < ack->n_timestamps; ++i)
{
CHECK_SPACE(3, p , pend);
ack->timestamps[i].packet_delta = *p++;
uint64_t delta_time = read_float_time16(p);
p += 2;
ack->timestamps[i].delta_usec =
ack->timestamps[i - 1].delta_usec + delta_time;
}
#else
unsigned timestamps_size = 5 + 3 * (ack->n_timestamps - 1);
CHECK_SPACE(timestamps_size, p, pend);
p += timestamps_size;
#endif
}
assert(p <= pend);
return p - (unsigned char *) buf;
}
static int
gquic_le_gen_stop_waiting_frame(unsigned char *buf, size_t buf_len,
lsquic_packno_t cur_packno, enum lsquic_packno_bits bits,
lsquic_packno_t least_unacked_packno)
{
lsquic_packno_t delta;
unsigned packnum_len = packno_bits2len(bits);
if (buf_len >= 1 + packnum_len)
{
*buf = 0x06;
delta = cur_packno - least_unacked_packno;
write_vary_len_to_buf(delta, buf + 1, packnum_len);
return 1 + packnum_len;
}
else
return -1;
}
static int
gquic_le_parse_stop_waiting_frame (const unsigned char *buf, size_t buf_len,
lsquic_packno_t cur_packno, enum lsquic_packno_bits bits,
lsquic_packno_t *least_unacked)
{
lsquic_packno_t delta;
unsigned packnum_len = packno_bits2len(bits);
if (buf_len >= 1 + packnum_len)
{
delta = 0;
memcpy(&delta, buf + 1, packnum_len);
*least_unacked = cur_packno - delta;
return 1 + packnum_len;
}
else
return -1;
}
static int
gquic_le_skip_stop_waiting_frame (size_t buf_len, enum lsquic_packno_bits bits)
{
unsigned packnum_len = packno_bits2len(bits);
if (buf_len >= 1 + packnum_len)
return 1 + packnum_len;
else
return -1;
}
static int
gquic_le_gen_window_update_frame (unsigned char *buf, int buf_len, uint32_t stream_id,
uint64_t offset)
{
unsigned char *p = buf;
if (buf_len < QUIC_WUF_SZ)
return -1;
*p = 0x04;
++p;
write_vary_len_to_buf(stream_id, p, 4);
p += 4;
write_vary_len_to_buf(offset, p, 8);
p += 8;
return p - buf;
}
static int
gquic_le_parse_window_update_frame (const unsigned char *buf, size_t buf_len,
uint32_t *stream_id, uint64_t *offset)
{
if (buf_len < QUIC_WUF_SZ)
return -1;
*stream_id = get_vary_len_int64(buf + 1, 4);
*offset = get_vary_len_int64(buf + 1 + 4, 8);
return QUIC_WUF_SZ;
}
static int
gquic_le_gen_blocked_frame (unsigned char *buf, size_t buf_len, uint32_t stream_id)
{
unsigned char *p = buf;
if (buf_len < QUIC_BLOCKED_FRAME_SZ)
return -1;
*p = 0x05;
++p;
write_vary_len_to_buf(stream_id, p, 4);
p += 4;
return p - buf;
}
static int
gquic_le_parse_blocked_frame (const unsigned char *buf, size_t buf_len,
uint32_t *stream_id)
{
if (buf_len < QUIC_BLOCKED_FRAME_SZ)
return -1;
*stream_id = get_vary_len_int64(buf + 1, 4);
return QUIC_BLOCKED_FRAME_SZ;
}
static int
gquic_le_gen_rst_frame (unsigned char *buf, size_t buf_len, uint32_t stream_id,
uint64_t offset, uint32_t error_code)
{
unsigned char *p = buf;
if (buf_len < QUIC_RST_STREAM_SZ)
return -1;
*p = 0x01;
++p;
write_vary_len_to_buf(stream_id, p, 4);
p += 4;
write_vary_len_to_buf(offset, p, 8);
p += 8;
write_vary_len_to_buf(error_code, p, 4);
p += 4;
return p - buf;
}
static int
gquic_le_parse_rst_frame (const unsigned char *buf, size_t buf_len, uint32_t *stream_id,
uint64_t *offset, uint32_t *error_code)
{
if (buf_len < 17)
return -1;
*stream_id = get_vary_len_int64(buf + 1, 4);
*offset = get_vary_len_int64(buf + 1 + 4, 8);
*error_code = get_vary_len_int64(buf + 1 + 4 + 8, 4);
return 17;
}
static int
gquic_le_gen_ping_frame (unsigned char *buf, int buf_len)
{
if (buf_len > 0)
{
buf[0] = 0x07;
return 1;
}
else
return -1;
}
static int
gquic_le_gen_connect_close_frame (unsigned char *buf, int buf_len, uint32_t error_code,
const char *reason, int reason_len)
{
unsigned char *p = buf;
if (buf_len < 7)
return -1;
*p = 0x02;
++p;
write_vary_len_to_buf(error_code, p, 4);
p += 4;
write_vary_len_to_buf(reason_len, p, 2);
p += 2;
memcpy(p, reason, reason_len);
p += reason_len;
if (buf_len < p - buf)
return -2;
return p - buf;
}
static int
gquic_le_parse_connect_close_frame (const unsigned char *buf, size_t buf_len,
uint32_t *error_code, uint16_t *reason_len, uint8_t *reason_offset)
{
if (buf_len < 7)
return -1;
*error_code = get_vary_len_int64(buf + 1, 4);
*reason_len = get_vary_len_int64(buf + 1 + 4, 2);
*reason_offset = 7;
if (buf_len < 7u + *reason_len)
return -2;
return 7 + *reason_len;
}
static int
gquic_le_gen_goaway_frame(unsigned char *buf, size_t buf_len, uint32_t error_code,
uint32_t last_good_stream_id, const char *reason,
size_t reason_len)
{
unsigned char *p = buf;
if (buf_len < QUIC_GOAWAY_FRAME_SZ + reason_len)
return -1;
*p = 0x03;
++p;
write_vary_len_to_buf(error_code, p, 4);
p += 4;
write_vary_len_to_buf(last_good_stream_id, p, 4);
p += 4;
write_vary_len_to_buf(reason_len, p, 2);
p += 2;
if (reason_len)
{
memcpy(p, reason, reason_len);
p += reason_len;
}
return p - buf;
}
/* the reason is buf + *reason_offset, length is *reason_length */
static int
gquic_le_parse_goaway_frame (const unsigned char *buf, size_t buf_len,
uint32_t *error_code, uint32_t *last_good_stream_id,
uint16_t *reason_length, const char **reason)
{
if (buf_len < QUIC_GOAWAY_FRAME_SZ)
return -1;
*error_code = get_vary_len_int64(buf + 1, 4);
*last_good_stream_id = get_vary_len_int64(buf + 1 + 4, 4);
*reason_length = get_vary_len_int64(buf + 1 + 4 + 4, 2);
if (*reason_length)
{
if ((int)buf_len < QUIC_GOAWAY_FRAME_SZ + *reason_length)
return -2;
*reason = (const char *) buf + QUIC_GOAWAY_FRAME_SZ;
}
else
*reason = NULL;
return QUIC_GOAWAY_FRAME_SZ + *reason_length;
}
/* Returns number of bytes written or -1 on failure */
/* This function makes an assumption that there is at least one range */
static int
gquic_le_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
gaf_rechist_first_f rechist_first, gaf_rechist_next_f rechist_next,
gaf_rechist_largest_recv_f rechist_largest_recv,
void *rechist, lsquic_time_t now, int *has_missing)
{
const struct lsquic_packno_range *const first = rechist_first(rechist);
if (!first)
{
errno = EINVAL;
return -1;
}
/* Copy values from the first range, because the memory the pointer
* points to may change:
*/
const lsquic_packno_t first_low = first->low, first_high = first->high;
unsigned char *p = outbuf;
unsigned char *const type = p;
unsigned char *const end = p + outbuf_sz;
#define AVAIL() (end - p)
#define CHECKOUT(sz) do { \
if ((intptr_t) (sz) > AVAIL()) { \
errno = ENOBUFS; \
return -1; \
} \
} while (0)
CHECKOUT(1);
++p;
/* 01nullmm */
*type = 0x40;
unsigned largest_acked_len, ack_block_len, bits;
/* Calculate largest ACKed len and set `ll' bits: */
const lsquic_packno_t maxno = first_high;
bits = (maxno >= (1ULL << 8))
+ (maxno >= (1ULL << 16))
+ (maxno >= (1ULL << 32));
largest_acked_len = (1 << bits) - ((maxno >= (1ULL << 32)) << 1);
*type |= bits << 2;
/* Calculate largest ACK block length and set `mm' bits: */
unsigned n_ranges = 0;
lsquic_packno_t maxdiff = 0;
const struct lsquic_packno_range *range;
for (range = rechist_first(rechist); range; range = rechist_next(rechist))
{
++n_ranges;
const lsquic_packno_t diff = range->high - range->low + 1;
if (diff > maxdiff)
maxdiff = diff;
}
bits = (maxdiff >= (1ULL << 8))
+ (maxdiff >= (1ULL << 16))
+ (maxdiff >= (1ULL << 32));
ack_block_len = (1 << bits) - ((maxdiff >= (1ULL << 32)) << 1);
*type |= bits;
CHECKOUT(largest_acked_len);
memcpy(p, &maxno, largest_acked_len);
p += largest_acked_len;
CHECKOUT(2);
lsquic_time_t diff = now - rechist_largest_recv(rechist);
gquic_le_write_float_time16(diff, p);
LSQ_DEBUG("%s: diff: %"PRIu64"; encoded: 0x%04X", __func__, diff,
*(uint16_t*)p);
p += 2;
if (n_ranges > 1)
{
*has_missing = 1;
*type |= 0x20;
/* We need to write out at least one range */
CHECKOUT(2 * (1 + ack_block_len));
unsigned char *const n_ranges_p = p; /* Set this later */
lsquic_packno_t diff = maxno - first_low + 1;
memcpy(p + 1, &diff, ack_block_len);
p += ack_block_len + 1;
/* Write out ack blocks until one of the following occurs:
* 1. We run out of intervals.
* 2. We run out of room.
* 3. We run out of highest possible number of ACK blocks (0xFF).
*/
range = rechist_first(rechist);
lsquic_packno_t gap = 0;
n_ranges = 0;
do {
if (0 == gap)
{
const lsquic_packno_t prev_low = range->low;
range = rechist_next(rechist);
if (!range)
break;
gap = prev_low - range->high - 1;
}
if (gap >= 0x100)
{
*p = 0xFF;
gap -= 0xFF;
memset(p + 1, 0, ack_block_len);
}
else
{
*p = gap;
gap = 0;
diff = range->high - range->low + 1;
memcpy(p + 1, &diff, ack_block_len);
}
p += ack_block_len + 1;
++n_ranges;
} while (n_ranges < 0xFF &&
AVAIL() >= (intptr_t) ack_block_len + 1 + 1 /* timestamp byte */);
*n_ranges_p = n_ranges;
}
else
{
*has_missing = 0;
CHECKOUT(ack_block_len);
const lsquic_packno_t diff = maxno - first_low + 1;
memcpy(p, &diff, ack_block_len);
p += ack_block_len;
}
/* We do not generate timestamp list because the reference implementation
* does not use them. When that changes, we will start sending timestamps
* over.
*/
CHECKOUT(1);
*p = 0;
++p;
return p - (unsigned char *) outbuf;
#undef CHECKOUT
}
const struct parse_funcs lsquic_parse_funcs_gquic_le =
{
.pf_gen_ver_nego_pkt = gquic_le_gen_ver_nego_pkt,
.pf_gen_reg_pkt_header = gquic_le_gen_reg_pkt_header,
.pf_parse_packet_in_finish = gquic_le_parse_packet_in_finish,
.pf_gen_stream_frame = gquic_le_gen_stream_frame,
.pf_calc_stream_frame_header_sz = calc_stream_frame_header_sz_gquic,
.pf_parse_stream_frame_header_sz = parse_stream_frame_header_sz_gquic,
.pf_parse_stream_frame = gquic_le_parse_stream_frame,
.pf_parse_ack_frame = gquic_le_parse_ack_frame,
.pf_parse_ack_high = gquic_le_parse_ack_high,
.pf_gen_ack_frame = gquic_le_gen_ack_frame,
.pf_gen_stop_waiting_frame = gquic_le_gen_stop_waiting_frame,
.pf_parse_stop_waiting_frame = gquic_le_parse_stop_waiting_frame,
.pf_skip_stop_waiting_frame = gquic_le_skip_stop_waiting_frame,
.pf_gen_window_update_frame = gquic_le_gen_window_update_frame,
.pf_parse_window_update_frame = gquic_le_parse_window_update_frame,
.pf_gen_blocked_frame = gquic_le_gen_blocked_frame,
.pf_parse_blocked_frame = gquic_le_parse_blocked_frame,
.pf_gen_rst_frame = gquic_le_gen_rst_frame,
.pf_parse_rst_frame = gquic_le_parse_rst_frame,
.pf_gen_connect_close_frame = gquic_le_gen_connect_close_frame,
.pf_parse_connect_close_frame = gquic_le_parse_connect_close_frame,
.pf_gen_goaway_frame = gquic_le_gen_goaway_frame,
.pf_parse_goaway_frame = gquic_le_parse_goaway_frame,
.pf_gen_ping_frame = gquic_le_gen_ping_frame,
#ifndef NDEBUG
.pf_write_float_time16 = gquic_le_write_float_time16,
.pf_read_float_time16 = gquic_le_read_float_time16,
#endif
.pf_parse_frame_type = parse_frame_type_gquic_Q035_thru_Q039,
};

View File

@ -0,0 +1,61 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef LSQUIC_QTAGS_H
#define LSQUIC_QTAGS_H 1
#define TAG(a, b, c, d) ((uint32_t)(((unsigned) d << 24) + \
((unsigned) c << 16) + ((unsigned) b << 8) + (unsigned) a))
#define QTAG_AEAD TAG('A', 'E', 'A', 'D')
#define QTAG_AESG TAG('A', 'E', 'S', 'G')
#define QTAG_C255 TAG('C', '2', '5', '5')
#define QTAG_CCRT TAG('C', 'C', 'R', 'T')
#define QTAG_CCS TAG('C', 'C', 'S', 0 )
#define QTAG_CFCW TAG('C', 'F', 'C', 'W')
#define QTAG_CHLO TAG('C', 'H', 'L', 'O')
#define QTAG_COPT TAG('C', 'O', 'P', 'T')
#define QTAG_CSCT TAG('C', 'S', 'C', 'T')
#define QTAG_CTIM TAG('C', 'T', 'I', 'M')
#define QTAG_EXPY TAG('E', 'X', 'P', 'Y')
#define QTAG_ICSL TAG('I', 'C', 'S', 'L')
#define QTAG_IRTT TAG('I', 'R', 'T', 'T')
#define QTAG_KEXS TAG('K', 'E', 'X', 'S')
#define QTAG_MIDS TAG('M', 'I', 'D', 'S')
#define QTAG_NONC TAG('N', 'O', 'N', 'C')
#define QTAG_ORBT TAG('O', 'B', 'I', 'T')
#define QTAG_PAD TAG('P', 'A', 'D', 0 )
#define QTAG_PDMD TAG('P', 'D', 'M', 'D')
#define QTAG_PROF TAG('P', 'R', 'O', 'F')
#define QTAG_PUBS TAG('P', 'U', 'B', 'S')
#define QTAG_RCID TAG('R', 'C', 'I', 'D')
#define QTAG_REJ TAG('R', 'E', 'J', 0 )
#define QTAG_RREJ TAG('R', 'R', 'E', 'J')
#define QTAG_SCFG TAG('S', 'C', 'F', 'G')
#define QTAG_SCID TAG('S', 'C', 'I', 'D')
#define QTAG_SCLS TAG('S', 'C', 'L', 'S')
#define QTAG_SFCW TAG('S', 'F', 'C', 'W')
#define QTAG_SHLO TAG('S', 'H', 'L', 'O')
#define QTAG_SNI TAG('S', 'N', 'I', 0 )
#define QTAG_SRBF TAG('S', 'R', 'B', 'F')
#define QTAG_SREJ TAG('S', 'R', 'E', 'J')
#define QTAG_STTL TAG('S', 'T', 'T', 'L')
#define QTAG_TCID TAG('T', 'C', 'I', 'D')
#define QTAG_UAID TAG('U', 'A', 'I', 'D')
#define QTAG_VER TAG('V', 'E', 'R', 0 )
#define QTAG_X509 TAG('X', '5', '0', '9')
#define QTAG_XLCT TAG('X', 'L', 'C', 'T')
#define QTAG_STK TAG('S', 'T', 'K', '\0')
#define QTAG_SNO TAG('S', 'N', 'O', '\0')
#define QTAG_CRT TAG('C', 'R', 'T', '\xFF')
/* SMHL: Support SETTINGS_MAX_HEADER_LIST_SIZE. Based on comments in
* Chrome code, this setting frame will be supported by default in
* Q037.
*/
#define QTAG_SMHL TAG('S', 'M', 'H', 'L')
/* Supported in Q037 and later. If this option is specified in the
* handshake, do not send or process STOP_WINDOW frames.
*/
#define QTAG_NSTP TAG('N', 'S', 'T', 'P')
#endif

View File

@ -0,0 +1,151 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_rechist.c -- History of received packets.
*/
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include "lsquic_int_types.h"
#include "lsquic_types.h"
#include "lsquic_rechist.h"
#define LSQUIC_LOGGER_MODULE LSQLM_RECHIST
#define LSQUIC_LOG_CONN_ID rechist->rh_cid
#include "lsquic_logger.h"
void
lsquic_rechist_init (struct lsquic_rechist *rechist, lsquic_cid_t cid)
{
memset(rechist, 0, sizeof(*rechist));
rechist->rh_cid = cid;
rechist->rh_cutoff = 1;
lsquic_packints_init(&rechist->rh_pints);
LSQ_DEBUG("instantiated received packet history");
}
void
lsquic_rechist_cleanup (lsquic_rechist_t *rechist)
{
lsquic_packints_cleanup(&rechist->rh_pints);
memset(rechist, 0, sizeof(*rechist));
}
enum received_st
lsquic_rechist_received (lsquic_rechist_t *rechist, lsquic_packno_t packno,
lsquic_time_t now)
{
const struct lsquic_packno_range *first_range;
LSQ_DEBUG("received %"PRIu64, packno);
if (packno < rechist->rh_cutoff)
{
if (packno)
return REC_ST_DUP;
else
return REC_ST_ERR;
}
first_range = lsquic_packints_first(&rechist->rh_pints);
if (!first_range || packno > first_range->high)
rechist->rh_largest_acked_received = now;
switch (lsquic_packints_add(&rechist->rh_pints, packno))
{
case PACKINTS_OK:
++rechist->rh_n_packets;
return REC_ST_OK;
case PACKINTS_DUP:
return REC_ST_DUP;
default:
assert(0);
case PACKINTS_ERR:
return REC_ST_ERR;
}
}
void
lsquic_rechist_stop_wait (lsquic_rechist_t *rechist, lsquic_packno_t cutoff)
{
LSQ_INFO("stop wait: %"PRIu64, cutoff);
if (rechist->rh_flags & RH_CUTOFF_SET)
{
assert(cutoff >= rechist->rh_cutoff); /* Check performed in full_conn */
if (cutoff == rechist->rh_cutoff)
return;
}
rechist->rh_cutoff = cutoff;
rechist->rh_flags |= RH_CUTOFF_SET;
struct packet_interval *pi, *next;
for (pi = TAILQ_FIRST(&rechist->rh_pints.pk_intervals); pi; pi = next)
{
next = TAILQ_NEXT(pi, next_pi);
if (pi->range.low < cutoff)
{
if (pi->range.high < cutoff)
{
rechist->rh_n_packets -= pi->range.high - pi->range.low + 1;
TAILQ_REMOVE(&rechist->rh_pints.pk_intervals, pi, next_pi);
free(pi);
}
else
{
rechist->rh_n_packets -= cutoff - pi->range.low;
pi->range.low = cutoff;
}
}
}
lsquic_packints_sanity_check(&rechist->rh_pints);
}
lsquic_packno_t
lsquic_rechist_largest_packno (const lsquic_rechist_t *rechist)
{
const struct packet_interval *pi =
TAILQ_FIRST(&rechist->rh_pints.pk_intervals);
if (pi)
return pi->range.high;
else
return 0; /* Don't call this function if history is empty */
}
lsquic_packno_t
lsquic_rechist_cutoff (const lsquic_rechist_t *rechist)
{
if (rechist->rh_flags & RH_CUTOFF_SET)
return rechist->rh_cutoff;
else
return 0;
}
lsquic_time_t
lsquic_rechist_largest_recv (const lsquic_rechist_t *rechist)
{
return rechist->rh_largest_acked_received;
}
const struct lsquic_packno_range *
lsquic_rechist_first (lsquic_rechist_t *rechist)
{
return lsquic_packints_first(&rechist->rh_pints);
}
const struct lsquic_packno_range *
lsquic_rechist_next (lsquic_rechist_t *rechist)
{
return lsquic_packints_next(&rechist->rh_pints);
}

View File

@ -0,0 +1,69 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_rechist.h -- History of received packets.
*
* The purpose of received packet history is to generate ACK frames.
*/
#ifndef LSQUIC_RECHIST_H
#define LSQUIC_RECHIST_H 1
#include "lsquic_packints.h"
struct lsquic_rechist {
struct packints rh_pints;
lsquic_packno_t rh_cutoff;
lsquic_time_t rh_largest_acked_received;
lsquic_cid_t rh_cid; /* Used for logging */
/* Chromium limits the number of tracked packets (see
* kMaxTrackedPackets). We could do this, too.
*/
unsigned rh_n_packets;
enum {
RH_CUTOFF_SET = (1 << 0),
} rh_flags;
};
typedef struct lsquic_rechist lsquic_rechist_t;
void
lsquic_rechist_init (struct lsquic_rechist *, lsquic_cid_t);
void
lsquic_rechist_cleanup (struct lsquic_rechist *);
enum received_st {
REC_ST_OK,
REC_ST_DUP,
REC_ST_ERR,
};
enum received_st
lsquic_rechist_received (lsquic_rechist_t *, lsquic_packno_t,
lsquic_time_t now);
void
lsquic_rechist_stop_wait (lsquic_rechist_t *, lsquic_packno_t);
/* Returns number of bytes written on success, -1 on failure */
int
lsquic_rechist_make_ackframe (lsquic_rechist_t *,
void *outbuf, size_t outbuf_sz, int *has_missing,
lsquic_time_t now);
const struct lsquic_packno_range *
lsquic_rechist_first (lsquic_rechist_t *);
const struct lsquic_packno_range *
lsquic_rechist_next (lsquic_rechist_t *);
lsquic_packno_t
lsquic_rechist_largest_packno (const lsquic_rechist_t *);
lsquic_packno_t
lsquic_rechist_cutoff (const lsquic_rechist_t *);
lsquic_time_t
lsquic_rechist_largest_recv (const lsquic_rechist_t *);
#endif

View File

@ -0,0 +1,39 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_rtt.c -- RTT calculation
*/
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include "lsquic_int_types.h"
#include "lsquic_rtt.h"
/* See RFC 2988 */
#define ALPHA_SHIFT 3 /* Alpha is 1/8 */
#define BETA_SHIFT 2 /* Beta is 1/4 */
void
lsquic_rtt_stats_update (struct lsquic_rtt_stats *stats,
lsquic_time_t send_delta, lsquic_time_t lack_delta)
{
if (send_delta > lack_delta)
send_delta -= lack_delta;
if (stats->srtt) {
stats->rttvar -= stats->rttvar >> BETA_SHIFT;
// FIXED: subtracting unsigned (the (int) cast gets repromoted to uint64_t
// made abs() irrelevant and allowed overflow. instead cast the difference
// to a signed int64 and use labs() to get abs val.
stats->rttvar += (llabs((int64_t) (send_delta - stats->srtt)))
>> BETA_SHIFT;
stats->srtt -= stats->srtt >> ALPHA_SHIFT;
stats->srtt += send_delta >> ALPHA_SHIFT;
} else {
/* First measurement */
stats->srtt = send_delta;
stats->rttvar = send_delta >> 1;
}
}

View File

@ -0,0 +1,26 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_rtt.h -- RTT calculation
*/
#ifndef LSQUIC_RTT_H
#define LSQUIC_RTT_H 1
/* This struct is initialized by setting it to zero */
struct lsquic_rtt_stats {
lsquic_time_t srtt;
lsquic_time_t rttvar;
};
void
lsquic_rtt_stats_update (struct lsquic_rtt_stats *, lsquic_time_t send_delta,
lsquic_time_t lack_delta);
#define lsquic_rtt_stats_get_srtt(stats) ((stats)->srtt)
#define lsquic_rtt_stats_get_rttvar(stats) ((stats)->rttvar)
#endif

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More