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

1148 lines
39 KiB
C

/*
* Author: Christian Huitema
* Copyright (c) 2019, Private Octopus, Inc.
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Packet logging.
*/
#include <stdarg.h>
#include "picoquic_binlog.h"
#include "bytestream.h"
#include "tls_api.h"
#include "picotls.h"
#include "picoquic_unified_log.h"
#include "picoquic_binlog.h"
#define VARINT_LEN(bytes) ((size_t)1 << (((bytes)[0] & 0xC0) >> 6))
static const uint8_t* picoquic_log_fixed_skip(const uint8_t* bytes, const uint8_t* bytes_max, size_t size)
{
return bytes == NULL ? NULL : ((bytes += size) <= bytes_max ? bytes : NULL);
}
static const uint8_t* picoquic_log_varint_skip(const uint8_t* bytes, const uint8_t* bytes_max)
{
return bytes == NULL ? NULL : (bytes < bytes_max ? picoquic_log_fixed_skip(bytes, bytes_max, VARINT_LEN(bytes)) : NULL);
}
static const uint8_t* picoquic_log_varint(const uint8_t* bytes, const uint8_t* bytes_max, uint64_t* n64)
{
size_t len = (bytes == NULL) ? 0 : picoquic_varint_decode(bytes, bytes_max - bytes, n64);
return len == 0 ? NULL : bytes + len;
}
static const uint8_t* picoquic_log_length(const uint8_t* bytes, const uint8_t* bytes_max, size_t* nsz)
{
uint64_t n64 = 0;
size_t len = (bytes == NULL) ? 0 : picoquic_varint_decode(bytes, bytes_max - bytes, &n64);
*nsz = (size_t)n64;
return len == 0 || *nsz != n64 ? NULL : bytes + len;
}
static void picoquic_binlog_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max)
{
if (bytes != NULL && bytes_max != NULL) {
size_t len = bytes_max - bytes;
uint8_t varlen[8];
size_t l_varlen = picoquic_varint_encode(varlen, 8, len);
fwrite(varlen, 1, l_varlen, f);
fwrite(bytes, 1, len, f);
}
}
static const uint8_t* picoquic_log_stream_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max)
{
const uint8_t* bytes_begin = bytes;
uint8_t ftype = bytes[0];
size_t length = 0;
uint8_t log_buffer[256];
int has_length = 0;
size_t extra_bytes = 8;
bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1); /* type */
bytes = picoquic_log_varint_skip(bytes, bytes_max); /* stream */
if ((ftype & 4) != 0) {
bytes = picoquic_log_varint_skip(bytes, bytes_max); /* offset */
}
if ((ftype & 2) != 0) {
bytes = picoquic_log_length(bytes, bytes_max, &length); /* length */
has_length = 1;
} else {
length = bytes_max - bytes;
}
if (length < extra_bytes) {
/* Add up to 8 bytes of content that can be documented in the qlog */
extra_bytes = length;
}
if (has_length) {
picoquic_binlog_frame(f, bytes_begin, bytes + extra_bytes);
}
else {
uint8_t* log_next = log_buffer;
size_t l_head = bytes - bytes_begin;
memcpy(log_buffer, bytes_begin, l_head);
log_next += l_head;
if ((log_next = picoquic_frames_varint_encode(log_next, log_buffer + 256, length)) != NULL) {
memcpy(log_next, bytes, extra_bytes);
log_next += extra_bytes;
picoquic_binlog_frame(f, log_buffer, log_next);
}
else {
picoquic_binlog_frame(f, log_buffer, log_buffer + l_head);
}
}
bytes = picoquic_log_fixed_skip(bytes, bytes_max, length);
return bytes;
}
static const uint8_t* picoquic_log_ack_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max)
{
const uint8_t* bytes_begin = bytes;
uint8_t ftype = bytes[0];
uint64_t nb_blocks;
bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1);
bytes = picoquic_log_varint_skip(bytes, bytes_max);
bytes = picoquic_log_varint_skip(bytes, bytes_max);
bytes = picoquic_log_varint(bytes, bytes_max, &nb_blocks);
for (uint64_t i = 0; bytes != NULL && i <= nb_blocks; i++) {
if (i != 0) {
bytes = picoquic_log_varint_skip(bytes, bytes_max);
}
bytes = picoquic_log_varint_skip(bytes, bytes_max);
}
if (ftype == picoquic_frame_type_ack_ecn) {
bytes = picoquic_log_varint_skip(bytes, bytes_max);
bytes = picoquic_log_varint_skip(bytes, bytes_max);
bytes = picoquic_log_varint_skip(bytes, bytes_max);
}
picoquic_binlog_frame(f, bytes_begin, bytes);
return bytes;
}
static const uint8_t* picoquic_log_reset_stream_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max)
{
const uint8_t * bytes_begin = bytes;
bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1);
bytes = picoquic_log_varint_skip(bytes, bytes_max);
bytes = picoquic_log_varint_skip(bytes, bytes_max);
bytes = picoquic_log_varint_skip(bytes, bytes_max);
picoquic_binlog_frame(f, bytes_begin, bytes);
return bytes;
}
static const uint8_t* picoquic_log_stop_sending_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max)
{
const uint8_t* bytes_begin = bytes;
bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1);
bytes = picoquic_log_varint_skip(bytes, bytes_max);
bytes = picoquic_log_varint_skip(bytes, bytes_max);
picoquic_binlog_frame(f, bytes_begin, bytes);
return bytes;
}
static const uint8_t* picoquic_log_close_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max)
{
const uint8_t* bytes_begin = bytes;
size_t length = 0;
bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1);
bytes = picoquic_log_varint_skip(bytes, bytes_max);
bytes = picoquic_log_varint_skip(bytes, bytes_max);
bytes = picoquic_log_length(bytes, bytes_max, &length);
bytes = picoquic_log_fixed_skip(bytes, bytes_max, length);
picoquic_binlog_frame(f, bytes_begin, bytes);
return bytes;
}
static const uint8_t* picoquic_log_app_close_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max)
{
const uint8_t* bytes_begin = bytes;
size_t length = 0;
bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1);
bytes = picoquic_log_varint_skip(bytes, bytes_max);
bytes = picoquic_log_length(bytes, bytes_max, &length);
bytes = picoquic_log_fixed_skip(bytes, bytes_max, length);
picoquic_binlog_frame(f, bytes_begin, bytes);
return bytes;
}
static const uint8_t* picoquic_log_max_data_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max)
{
const uint8_t* bytes_begin = bytes;
bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1);
bytes = picoquic_log_varint_skip(bytes, bytes_max);
picoquic_binlog_frame(f, bytes_begin, bytes);
return bytes;
}
static const uint8_t* picoquic_log_max_stream_data_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max)
{
const uint8_t* bytes_begin = bytes;
bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1);
bytes = picoquic_log_varint_skip(bytes, bytes_max);
bytes = picoquic_log_varint_skip(bytes, bytes_max);
picoquic_binlog_frame(f, bytes_begin, bytes);
return bytes;
}
static const uint8_t* picoquic_log_max_stream_id_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max)
{
const uint8_t* bytes_begin = bytes;
bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1);
bytes = picoquic_log_varint_skip(bytes, bytes_max);
picoquic_binlog_frame(f, bytes_begin, bytes);
return bytes;
}
static const uint8_t* picoquic_log_blocked_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max)
{
const uint8_t* bytes_begin = bytes;
bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1);
bytes = picoquic_log_varint_skip(bytes, bytes_max);
picoquic_binlog_frame(f, bytes_begin, bytes);
return bytes;
}
static const uint8_t* picoquic_log_stream_blocked_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max)
{
const uint8_t* bytes_begin = bytes;
bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1);
bytes = picoquic_log_varint_skip(bytes, bytes_max);
bytes = picoquic_log_varint_skip(bytes, bytes_max);
picoquic_binlog_frame(f, bytes_begin, bytes);
return bytes;
}
static const uint8_t* picoquic_log_streams_blocked_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max)
{
const uint8_t* bytes_begin = bytes;
bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1);
bytes = picoquic_log_varint_skip(bytes, bytes_max);
picoquic_binlog_frame(f, bytes_begin, bytes);
return bytes;
}
static const uint8_t* picoquic_log_new_connection_id_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max)
{
const uint8_t* bytes_begin = bytes;
bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1);
bytes = picoquic_log_varint_skip(bytes, bytes_max);
bytes = picoquic_log_varint_skip(bytes, bytes_max);
if (bytes != NULL) {
bytes = picoquic_log_fixed_skip(bytes, bytes_max, ((size_t)1) + bytes[0]);
}
bytes = picoquic_log_fixed_skip(bytes, bytes_max, PICOQUIC_RESET_SECRET_SIZE);
picoquic_binlog_frame(f, bytes_begin, bytes);
return bytes;
}
static const uint8_t* picoquic_log_retire_connection_id_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max)
{
const uint8_t* bytes_begin = bytes;
bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1);
bytes = picoquic_log_varint_skip(bytes, bytes_max);
picoquic_binlog_frame(f, bytes_begin, bytes);
return bytes;
}
static const uint8_t* picoquic_log_new_token_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max)
{
const uint8_t* bytes_begin = bytes;
size_t length = 0;
bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1);
bytes = picoquic_log_length(bytes, bytes_max, &length);
bytes = picoquic_log_fixed_skip(bytes, bytes_max, length);
picoquic_binlog_frame(f, bytes_begin, bytes);
return bytes;
}
static const uint8_t* picoquic_log_path_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max)
{
const uint8_t* bytes_begin = bytes;
bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1 + 8);
picoquic_binlog_frame(f, bytes_begin, bytes);
return bytes;
}
static const uint8_t* picoquic_log_crypto_hs_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max)
{
const uint8_t* bytes_begin = bytes;
size_t length = 0;
bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1);
bytes = picoquic_log_varint_skip(bytes, bytes_max);
bytes = picoquic_log_length(bytes, bytes_max, &length);
picoquic_binlog_frame(f, bytes_begin, bytes);
bytes = picoquic_log_fixed_skip(bytes, bytes_max, length);
return bytes;
}
static const uint8_t* picoquic_log_handshake_done_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max)
{
const uint8_t* bytes_begin = bytes;
bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1);
picoquic_binlog_frame(f, bytes_begin, bytes);
return bytes;
}
static const uint8_t* picoquic_log_datagram_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max)
{
const uint8_t* bytes_begin = bytes;
uint8_t ftype = bytes[0];
size_t length = 0;
bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1);
if (ftype & 1) {
bytes = picoquic_log_length(bytes, bytes_max, &length);
} else {
length = bytes_max - bytes;
}
picoquic_binlog_frame(f, bytes_begin, bytes);
bytes = picoquic_log_fixed_skip(bytes, bytes_max, length);
return bytes;
}
static const uint8_t* picoquic_log_time_stamp_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max)
{
const uint8_t* bytes_begin = bytes;
bytes = picoquic_log_varint_skip(bytes, bytes_max); /* frame type as varint */
bytes = picoquic_log_varint_skip(bytes, bytes_max); /* time stamp as varint */
picoquic_binlog_frame(f, bytes_begin, bytes);
return bytes;
}
static const uint8_t* picoquic_log_ack_frequency_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max)
{
const uint8_t* bytes_begin = bytes;
bytes = picoquic_log_varint_skip(bytes, bytes_max); /* frame type as varint */
bytes = picoquic_log_varint_skip(bytes, bytes_max); /* Seq num */
bytes = picoquic_log_varint_skip(bytes, bytes_max); /* Packet tolerance */
bytes = picoquic_log_varint_skip(bytes, bytes_max); /* Max ACK delay */
picoquic_binlog_frame(f, bytes_begin, bytes);
return bytes;
}
static const uint8_t* picoquic_log_erroring_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max)
{
size_t frame_size = bytes_max - bytes;
size_t copied = (frame_size > 8) ? 8 : frame_size;
picoquic_binlog_frame(f, bytes, bytes + copied);
return NULL;
}
static const uint8_t* picoquic_log_padding(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max)
{
picoquic_binlog_frame(f, bytes, bytes + 1);
uint8_t ftype = bytes[0];
while (bytes < bytes_max && bytes[0] == ftype) {
bytes++;
}
return bytes;
}
void picoquic_binlog_frames(FILE * f, const uint8_t* bytes, size_t length)
{
const uint8_t* bytes_max = bytes + length;
while (bytes != NULL && bytes < bytes_max) {
uint64_t ftype= 0;
size_t ftype_ll = picoquic_varint_decode(bytes, length, &ftype);
if (ftype_ll == 0) {
/* Error, incorrect frame type encoding */
bytes = NULL;
break;
}
else if (ftype < 64 && ftype_ll != 1) {
/* Error, incorrect frame type encoding */
bytes = NULL;
break;
}
if (PICOQUIC_IN_RANGE(ftype, picoquic_frame_type_stream_range_min, picoquic_frame_type_stream_range_max)) {
bytes = picoquic_log_stream_frame(f, bytes, bytes_max);
continue;
}
switch (ftype) {
case picoquic_frame_type_ack:
case picoquic_frame_type_ack_ecn:
bytes = picoquic_log_ack_frame(f, bytes, bytes_max);
break;
case picoquic_frame_type_retire_connection_id:
bytes = picoquic_log_retire_connection_id_frame(f, bytes, bytes_max);
break;
case picoquic_frame_type_padding:
case picoquic_frame_type_ping:
bytes = picoquic_log_padding(f, bytes, bytes_max);
break;
case picoquic_frame_type_reset_stream:
bytes = picoquic_log_reset_stream_frame(f, bytes, bytes_max);
break;
case picoquic_frame_type_connection_close:
bytes = picoquic_log_close_frame(f, bytes, bytes_max);
break;
case picoquic_frame_type_application_close:
bytes = picoquic_log_app_close_frame(f, bytes, bytes_max);
break;
case picoquic_frame_type_max_data:
bytes = picoquic_log_max_data_frame(f, bytes, bytes_max);
break;
case picoquic_frame_type_max_stream_data:
bytes = picoquic_log_max_stream_data_frame(f, bytes, bytes_max);
break;
case picoquic_frame_type_max_streams_bidir:
case picoquic_frame_type_max_streams_unidir:
bytes = picoquic_log_max_stream_id_frame(f, bytes, bytes_max);
break;
case picoquic_frame_type_data_blocked:
bytes = picoquic_log_blocked_frame(f, bytes, bytes_max);
break;
case picoquic_frame_type_stream_data_blocked:
bytes = picoquic_log_stream_blocked_frame(f, bytes, bytes_max);
break;
case picoquic_frame_type_streams_blocked_bidir:
case picoquic_frame_type_streams_blocked_unidir:
bytes = picoquic_log_streams_blocked_frame(f, bytes, bytes_max);
break;
case picoquic_frame_type_new_connection_id:
bytes = picoquic_log_new_connection_id_frame(f, bytes, bytes_max);
break;
case picoquic_frame_type_stop_sending:
bytes = picoquic_log_stop_sending_frame(f, bytes, bytes_max);
break;
case picoquic_frame_type_path_challenge:
case picoquic_frame_type_path_response:
bytes = picoquic_log_path_frame(f, bytes, bytes_max);
break;
case picoquic_frame_type_crypto_hs:
bytes = picoquic_log_crypto_hs_frame(f, bytes, bytes_max);
break;
case picoquic_frame_type_new_token:
bytes = picoquic_log_new_token_frame(f, bytes, bytes_max);
break;
case picoquic_frame_type_handshake_done:
bytes = picoquic_log_handshake_done_frame(f, bytes, bytes_max);
break;
case picoquic_frame_type_datagram:
case picoquic_frame_type_datagram_l:
bytes = picoquic_log_datagram_frame(f, bytes, bytes_max);
break;
case picoquic_frame_type_ack_frequency:
bytes = picoquic_log_ack_frequency_frame(f, bytes, bytes_max);
break;
case picoquic_frame_type_time_stamp:
bytes = picoquic_log_time_stamp_frame(f, bytes, bytes_max);
break;
default:
bytes = picoquic_log_erroring_frame(f, bytes, bytes_max);
break;
}
}
}
void binlog_pdu(FILE* f, const picoquic_connection_id_t* cid, int receiving, uint64_t current_time,
const struct sockaddr* addr_peer, const struct sockaddr* addr_local, size_t packet_length)
{
bytestream_buf stream_msg;
bytestream* msg = bytestream_buf_init(&stream_msg, BYTESTREAM_MAX_BUFFER_SIZE);
/* Common chunk header */
bytewrite_cid(msg, cid);
bytewrite_vint(msg, current_time);
bytewrite_vint(msg, ((uint64_t)picoquic_log_event_pdu_sent) + receiving);
/* PDU information */
bytewrite_addr(msg, addr_peer);
bytewrite_vint(msg, packet_length);
bytewrite_addr(msg, addr_local);
uint8_t head[4] = { 0 };
picoformat_32(head, (uint32_t)bytestream_length(msg));
(void)fwrite(head, sizeof(head), 1, f);
(void)fwrite(bytestream_data(msg), bytestream_length(msg), 1, f);
}
static void binlog_pdu_ex(picoquic_cnx_t* cnx, int receiving, uint64_t current_time,
const struct sockaddr* addr_peer, const struct sockaddr* addr_local, size_t packet_length)
{
if (cnx != NULL && cnx->f_binlog != NULL && picoquic_cnx_is_still_logging(cnx)) {
binlog_pdu(cnx->f_binlog, &cnx->initial_cnxid, receiving, current_time, addr_peer, addr_local, packet_length);
}
}
void binlog_packet(FILE* f, const picoquic_connection_id_t* cid, int receiving, uint64_t current_time,
const picoquic_packet_header* ph, const uint8_t* bytes, size_t bytes_max)
{
long fpos0 = ftell(f);
uint8_t head[4] = { 0 };
(void)fwrite(head, 4, 1, f);
bytestream_buf stream_msg;
bytestream* msg = bytestream_buf_init(&stream_msg, BYTESTREAM_MAX_BUFFER_SIZE);
/* Common chunk header */
bytewrite_cid(msg, cid);
bytewrite_vint(msg, current_time);
bytewrite_vint(msg, ((uint64_t)picoquic_log_event_packet_sent) + receiving);
/* packet information */
bytewrite_vint(msg, bytes_max);
/* packet header */
bytewrite_int8(msg, (uint8_t)(64*ph->quic_bit_is_zero + 2 * ph->spin + ph->key_phase));
bytewrite_vint(msg, ph->payload_length);
bytewrite_vint(msg, ph->ptype);
bytewrite_vint(msg, ph->pn64);
bytewrite_cid(msg, &ph->dest_cnx_id);
bytewrite_cid(msg, &ph->srce_cnx_id);
if (ph->ptype != picoquic_packet_1rtt_protected &&
ph->ptype != picoquic_packet_version_negotiation) {
bytewrite_int32(msg, ph->vn);
}
if (ph->ptype == picoquic_packet_initial) {
bytewrite_vint(msg, ph->token_length);
bytewrite_buffer(msg, ph->token_bytes, ph->token_length);
}
(void)fwrite(bytestream_data(msg), bytestream_length(msg), 1, f);
/* frame information */
if (ph->ptype == picoquic_packet_version_negotiation || ph->ptype == picoquic_packet_retry) {
picoquic_binlog_frame(f, bytes + ph->offset, bytes + bytes_max);
}
else if (ph->ptype != picoquic_packet_error) {
picoquic_binlog_frames(f, bytes + ph->offset, ph->payload_length);
}
/* re-write chunk size field */
long fpos1 = ftell(f);
picoformat_32(head, (uint32_t)(fpos1 - fpos0 - 4));
(void)fseek(f, fpos0, SEEK_SET);
(void)fwrite(head, 4, 1, f);
(void)fseek(f, 0, SEEK_END);
}
static void binlog_packet_ex(picoquic_cnx_t* cnx, int receiving, uint64_t current_time,
picoquic_packet_header* ph, const uint8_t* bytes, size_t bytes_max)
{
if (cnx != NULL && cnx->f_binlog != NULL && picoquic_cnx_is_still_logging(cnx)) {
binlog_packet(cnx->f_binlog, &cnx->initial_cnxid, receiving, current_time, ph, bytes, bytes_max);
}
}
void binlog_dropped_packet(picoquic_cnx_t* cnx,
picoquic_packet_header* ph, size_t packet_size, int err,
uint8_t * raw_data, uint64_t current_time)
{
FILE* f = cnx->f_binlog;
size_t raw_size = packet_size;
bytestream_buf stream_msg;
bytestream* msg = bytestream_buf_init(&stream_msg, BYTESTREAM_MAX_BUFFER_SIZE);
if (err == PICOQUIC_ERROR_AEAD_CHECK) {
/* Do not log on decryption error, because the buffer was randomized by decryption */
raw_size = 0;
} else if (raw_size > 32) {
raw_size = 32;
}
bytewrite_int32(msg, 0);
bytewrite_cid(msg, &cnx->initial_cnxid);
bytewrite_vint(msg, current_time);
bytewrite_vint(msg, picoquic_log_event_packet_dropped);
bytewrite_vint(msg, ph->ptype);
bytewrite_vint(msg, packet_size);
bytewrite_vint(msg, err);
bytewrite_vint(msg, raw_size);
(void)bytewrite_buffer(msg, raw_data, raw_size);
/* write the frame length at the reserved spot, and save to log file*/
picoformat_32(msg->data, (uint32_t)(msg->ptr - 4));
(void)fwrite(bytestream_data(msg), bytestream_length(msg), 1, f);
}
void binlog_buffered_packet(picoquic_cnx_t* cnx,
picoquic_packet_type_enum ptype, uint64_t current_time)
{
FILE* f = cnx->f_binlog;
bytestream_buf stream_msg;
bytestream* msg = bytestream_buf_init(&stream_msg, BYTESTREAM_MAX_BUFFER_SIZE);
bytewrite_int32(msg, 0);
bytewrite_cid(msg, &cnx->initial_cnxid);
bytewrite_vint(msg, current_time);
bytewrite_vint(msg, picoquic_log_event_packet_buffered);
bytewrite_vint(msg, ptype);
(void)bytewrite_cstr(msg, "keys_unavailable");
/* write the frame length at the reserved spot, and save to log file*/
picoformat_32(msg->data, (uint32_t)(msg->ptr - 4));
(void)fwrite(bytestream_data(msg), bytestream_length(msg), 1, f);
}
void binlog_outgoing_packet(picoquic_cnx_t* cnx,
uint8_t * bytes, uint64_t sequence_number, size_t pn_length, size_t length,
uint8_t* send_buffer, size_t send_length, uint64_t current_time)
{
FILE* f = cnx->f_binlog;
picoquic_cnx_t* pcnx = cnx;
picoquic_packet_header ph;
size_t checksum_length = 16;
struct sockaddr_in default_addr;
const picoquic_connection_id_t * cnxid = (cnx != NULL) ? &cnx->initial_cnxid : &picoquic_null_connection_id;
memset(&default_addr, 0, sizeof(struct sockaddr_in));
default_addr.sin_family = AF_INET;
picoquic_parse_packet_header((cnx == NULL) ? NULL : cnx->quic, send_buffer, send_length,
((cnx == NULL || cnx->path[0] == NULL) ? (struct sockaddr *)&default_addr :
(struct sockaddr *)&cnx->path[0]->local_addr), &ph, &pcnx, 0);
if (cnx != NULL) {
picoquic_epoch_enum epoch = (ph.ptype == picoquic_packet_1rtt_protected) ? picoquic_epoch_1rtt :
((ph.ptype == picoquic_packet_0rtt_protected) ? picoquic_epoch_0rtt :
((ph.ptype == picoquic_packet_handshake) ? picoquic_epoch_handshake : picoquic_epoch_initial));
if (cnx->crypto_context[epoch].aead_encrypt != NULL) {
checksum_length = picoquic_get_checksum_length(cnx, epoch);
}
}
ph.pn64 = sequence_number;
ph.pn = (uint32_t)ph.pn64;
if (ph.ptype != picoquic_packet_retry) {
if (ph.pn_offset != 0) {
ph.offset = ph.pn_offset + pn_length;
ph.payload_length -= pn_length;
}
}
if (ph.ptype != picoquic_packet_version_negotiation) {
if (ph.payload_length > checksum_length) {
ph.payload_length -= (uint16_t)checksum_length;
}
else {
ph.payload_length = 0;
}
}
binlog_packet(f, cnxid, 0, current_time, &ph, bytes, length);
}
void binlog_packet_lost(picoquic_cnx_t* cnx,
picoquic_packet_type_enum ptype, uint64_t sequence_number, char const * trigger,
picoquic_connection_id_t * dcid, size_t packet_size,
uint64_t current_time)
{
FILE* f = cnx->f_binlog;
bytestream_buf stream_msg;
bytestream* msg = bytestream_buf_init(&stream_msg, BYTESTREAM_MAX_BUFFER_SIZE);
bytewrite_int32(msg, 0);
bytewrite_cid(msg, &cnx->initial_cnxid);
bytewrite_vint(msg, current_time);
bytewrite_vint(msg, picoquic_log_event_packet_lost);
bytewrite_vint(msg, ptype);
bytewrite_vint(msg, sequence_number);
bytewrite_cstr(msg, trigger);
if (dcid != NULL) {
bytewrite_cid(msg, dcid);
}
else {
bytewrite_int8(msg, 0);
}
bytewrite_vint(msg, packet_size);
/* write the frame length at the reserved spot, and save to log file*/
picoformat_32(msg->data, (uint32_t)(msg->ptr - 4));
(void)fwrite(bytestream_data(msg), bytestream_length(msg), 1, f);
}
void binlog_negotiated_alpn(picoquic_cnx_t* cnx, int is_local,
uint8_t const * sni, size_t sni_len, uint8_t const* alpn, size_t alpn_len,
const ptls_iovec_t* alpn_list, size_t alpn_count)
{
FILE* f = cnx->f_binlog;
bytestream_buf stream_msg;
bytestream* msg = bytestream_buf_init(&stream_msg, BYTESTREAM_MAX_BUFFER_SIZE);
bytewrite_cid(msg, &cnx->initial_cnxid);
bytewrite_vint(msg, picoquic_get_quic_time(cnx->quic));
bytewrite_vint(msg, picoquic_log_event_alpn_update);
bytewrite_vint(msg, is_local);
bytewrite_vint(msg, sni_len);
if (sni_len > 0) {
bytewrite_buffer(msg, sni, sni_len);
}
bytewrite_vint(msg, alpn_count);
if (alpn_count > 0) {
for (size_t i = 0; i < alpn_count; i++) {
bytewrite_vint(msg, alpn_list[i].len);
bytewrite_buffer(msg, alpn_list[i].base, alpn_list[i].len);
}
}
bytewrite_vint(msg, alpn_len);
if (alpn_len > 0) {
bytewrite_buffer(msg, alpn, alpn_len);
}
bytestream_buf stream_head;
bytestream* head = bytestream_buf_init(&stream_head, 4);
bytewrite_int32(head, (uint32_t)bytestream_length(msg));
(void)fwrite(bytestream_data(head), bytestream_length(head), 1, f);
(void)fwrite(bytestream_data(msg), bytestream_length(msg), 1, f);
}
void binlog_transport_extension(picoquic_cnx_t* cnx, int is_local,
size_t param_length, uint8_t* params)
{
FILE* f = cnx->f_binlog;
bytestream_buf stream_msg;
bytestream* msg = bytestream_buf_init(&stream_msg, BYTESTREAM_MAX_BUFFER_SIZE);
bytewrite_cid(msg, &cnx->initial_cnxid);
bytewrite_vint(msg, picoquic_get_quic_time(cnx->quic));
bytewrite_vint(msg, picoquic_log_event_param_update);
bytewrite_vint(msg, is_local);
bytewrite_vint(msg, param_length);
if (param_length > 0) {
bytewrite_buffer(msg, params, param_length);
}
bytestream_buf stream_head;
bytestream* head = bytestream_buf_init(&stream_head, 4);
bytewrite_int32(head, (uint32_t)bytestream_length(msg));
(void)fwrite(bytestream_data(head), bytestream_length(head), 1, f);
(void)fwrite(bytestream_data(msg), bytestream_length(msg), 1, f);
}
void binlog_picotls_ticket(FILE* f, picoquic_connection_id_t cnx_id,
uint8_t* ticket, uint16_t ticket_length)
{
bytestream_buf stream_msg;
bytestream * msg = bytestream_buf_init(&stream_msg, BYTESTREAM_MAX_BUFFER_SIZE);
bytewrite_cid(msg, &cnx_id);
bytewrite_vint(msg, 0);
bytewrite_vint(msg, picoquic_log_event_tls_key_update);
bytewrite_vint(msg, ticket_length);
bytewrite_buffer(msg, ticket, ticket_length);
bytestream_buf stream_head;
bytestream * head = bytestream_buf_init(&stream_head, 8);
bytewrite_int32(head, (uint32_t)bytestream_length(msg));
(void)fwrite(bytestream_data(head), bytestream_length(head), 1, f);
(void)fwrite(bytestream_data(msg), bytestream_length(msg), 1, f);
}
static void binlog_picotls_ticket_ex(picoquic_cnx_t* cnx,
uint8_t* ticket, uint16_t ticket_length)
{
if (cnx != NULL && cnx->f_binlog != NULL && picoquic_cnx_is_still_logging(cnx)) {
binlog_picotls_ticket(cnx->f_binlog, cnx->initial_cnxid, ticket, ticket_length);
}
}
FILE* create_binlog(char const* binlog_file, uint64_t creation_time);
void binlog_new_connection(picoquic_cnx_t * cnx)
{
char const* bin_dir = (cnx->quic->binlog_dir == NULL) ? cnx->quic->qlog_dir : cnx->quic->binlog_dir;
if (bin_dir == NULL) {
return;
}
if (cnx->quic->current_number_of_open_logs >= cnx->quic->max_simultaneous_logs) {
return;
}
int ret = 0;
cnx->f_binlog = picoquic_file_close(cnx->f_binlog);
char cid_name[2 * PICOQUIC_CONNECTION_ID_MAX_SIZE + 1];
if (picoquic_print_connection_id_hexa(cid_name, sizeof(cid_name), &cnx->initial_cnxid) != 0) {
ret = -1;
}
char log_filename[512];
if (ret == 0) {
if (picoquic_sprintf(log_filename, sizeof(log_filename), NULL, "%s%s%s.%s.log",
bin_dir, PICOQUIC_FILE_SEPARATOR, cid_name,
(cnx->client_mode)?"client":"server") != 0) {
ret = -1;
}
else {
picoquic_string_free(cnx->binlog_file_name);
cnx->binlog_file_name = picoquic_string_duplicate(log_filename);
}
}
if (ret == 0) {
cnx->f_binlog = create_binlog(log_filename, picoquic_get_quic_time(cnx->quic));
if (cnx->f_binlog == NULL) {
cnx->binlog_file_name = picoquic_string_free(cnx->binlog_file_name);
ret = -1;
}
else {
cnx->quic->current_number_of_open_logs++;
}
}
if (ret == 0) {
bytestream_buf stream_msg;
bytestream * msg = bytestream_buf_init(&stream_msg, BYTESTREAM_MAX_BUFFER_SIZE);
bytewrite_cid(msg, &cnx->initial_cnxid);
bytewrite_vint(msg, cnx->start_time);
bytewrite_vint(msg, picoquic_log_event_new_connection);
bytewrite_int8(msg, cnx->client_mode != 0);
bytewrite_int32(msg, cnx->proposed_version);
bytewrite_cid(msg, &cnx->path[0]->remote_cnxid);
/* Algorithms used */
bytewrite_cstr(msg, cnx->congestion_alg->congestion_algorithm_id);
bytewrite_vint(msg, cnx->spin_policy);
bytestream_buf stream_head;
bytestream * head = bytestream_buf_init(&stream_head, 8);
bytewrite_int32(head, (uint32_t)bytestream_length(msg));
(void)fwrite(bytestream_data(head), bytestream_length(head), 1, cnx->f_binlog);
(void)fwrite(bytestream_data(msg), bytestream_length(msg), 1, cnx->f_binlog);
}
}
void binlog_close_connection(picoquic_cnx_t * cnx)
{
FILE * f = cnx->f_binlog;
if (f == NULL) {
return;
}
bytestream_buf stream_msg;
bytestream * msg = bytestream_buf_init(&stream_msg, BYTESTREAM_MAX_BUFFER_SIZE);
bytewrite_cid(msg, &cnx->initial_cnxid);
bytewrite_vint(msg, picoquic_get_quic_time(cnx->quic));
bytewrite_vint(msg, picoquic_log_event_connection_close);
bytestream_buf stream_head;
bytestream * head = bytestream_buf_init(&stream_head, 8);
bytewrite_int32(head, (uint32_t)bytestream_length(msg));
(void)fwrite(bytestream_data(head), bytestream_length(head), 1, f);
(void)fwrite(bytestream_data(msg), bytestream_length(msg), 1, f);
fflush(f);
cnx->f_binlog = picoquic_file_close(cnx->f_binlog);
if (cnx->quic->qlog_dir != NULL && cnx->quic->autoqlog_fn != NULL) {
(void)cnx->quic->autoqlog_fn(cnx);
}
cnx->binlog_file_name = picoquic_string_free(cnx->binlog_file_name);
if (cnx->quic->current_number_of_open_logs > 0) {
cnx->quic->current_number_of_open_logs--;
}
}
FILE* create_binlog(char const* binlog_file, uint64_t creation_time)
{
FILE* f_binlog = picoquic_file_open(binlog_file, "wb");
if (f_binlog == NULL) {
DBG_PRINTF("Cannot open file %s for write.\n", binlog_file);
}
else {
/* Write a header text with version identifier and current date */
bytestream_buf stream;
bytestream* ps = bytestream_buf_init(&stream, 16);
bytewrite_int32(ps, FOURCC('q', 'l', 'o', 'g'));
bytewrite_int32(ps, 0x01);
bytewrite_int64(ps, creation_time);
if (fwrite(bytestream_data(ps), bytestream_length(ps), 1, f_binlog) <= 0) {
DBG_PRINTF("Cannot write header for file %s.\n", binlog_file);
f_binlog = picoquic_file_close(f_binlog);
}
}
return f_binlog;
}
/*
* Log the state of the congestion management, retransmission, etc.
* Call either just after processing a received packet, or just after
* sending a packet.
*/
void binlog_cc_dump(picoquic_cnx_t* cnx, uint64_t current_time)
{
if (cnx->f_binlog == NULL) {
return;
}
bytestream_buf stream_msg;
bytestream* ps_msg = bytestream_buf_init(&stream_msg, BYTESTREAM_MAX_BUFFER_SIZE);
picoquic_packet_context_t* pkt_ctx = &cnx->pkt_ctx[picoquic_packet_context_application];
picoquic_path_t* path = cnx->path[0];
bytewrite_cid(ps_msg, &cnx->initial_cnxid);
bytewrite_vint(ps_msg, current_time);
bytewrite_vint(ps_msg, picoquic_log_event_cc_update);
bytewrite_vint(ps_msg, cnx->pkt_ctx[picoquic_packet_context_application].send_sequence);
if (pkt_ctx->highest_acknowledged != (uint64_t)(int64_t)-1) {
bytewrite_vint(ps_msg, 1);
bytewrite_vint(ps_msg, pkt_ctx->highest_acknowledged);
bytewrite_vint(ps_msg, pkt_ctx->highest_acknowledged_time - cnx->start_time);
bytewrite_vint(ps_msg, pkt_ctx->latest_time_acknowledged - cnx->start_time);
}
else {
bytewrite_vint(ps_msg, 0);
}
bytewrite_vint(ps_msg, path->cwin);
bytewrite_vint(ps_msg, path->one_way_delay_sample);
bytewrite_vint(ps_msg, path->rtt_sample);
bytewrite_vint(ps_msg, path->smoothed_rtt);
bytewrite_vint(ps_msg, path->rtt_min);
bytewrite_vint(ps_msg, path->bandwidth_estimate);
bytewrite_vint(ps_msg, path->receive_rate_estimate);
bytewrite_vint(ps_msg, path->send_mtu);
bytewrite_vint(ps_msg, path->pacing_packet_time_microsec);
bytewrite_vint(ps_msg, cnx->nb_retransmission_total);
bytewrite_vint(ps_msg, cnx->nb_spurious);
bytewrite_vint(ps_msg, cnx->cwin_blocked);
bytewrite_vint(ps_msg, cnx->flow_blocked);
bytewrite_vint(ps_msg, cnx->stream_blocked);
if (cnx->congestion_alg == NULL) {
bytewrite_vint(ps_msg, 0);
bytewrite_vint(ps_msg, 0);
}
else {
uint64_t cc_state;
uint64_t cc_param;
cnx->congestion_alg->alg_observe(cnx->path[0], &cc_state, &cc_param);
bytewrite_vint(ps_msg, cc_state);
bytewrite_vint(ps_msg, cc_param);
}
bytewrite_vint(ps_msg, path->max_bandwidth_estimate);
bytewrite_vint(ps_msg, path->bytes_in_transit);
bytestream_buf stream_head;
bytestream* ps_head = bytestream_buf_init(&stream_head, BYTESTREAM_MAX_BUFFER_SIZE);
bytewrite_int32(ps_head, (uint32_t)bytestream_length(ps_msg));
(void)fwrite(bytestream_data(ps_head), bytestream_length(ps_head), 1, cnx->f_binlog);
(void)fwrite(bytestream_data(ps_msg), bytestream_length(ps_msg), 1, cnx->f_binlog);
}
/*
* Write an information message frame, for free form debugging.
*/
void picoquic_binlog_message_v(picoquic_cnx_t* cnx, const char* fmt, va_list vargs)
{
if (cnx->f_binlog == NULL) {
return;
}
bytestream_buf stream_msg;
bytestream* ps_msg = bytestream_buf_init(&stream_msg, BYTESTREAM_MAX_BUFFER_SIZE);
size_t message_len;
char* message_text;
int written = -1;
bytewrite_cid(ps_msg, &cnx->initial_cnxid);
bytewrite_vint(ps_msg, picoquic_get_quic_time(cnx->quic));
bytewrite_vint(ps_msg, picoquic_log_event_info_message);
message_text = (char*)(ps_msg->data + ps_msg->ptr);
#ifdef _WINDOWS
written = vsnprintf_s(message_text,
ps_msg->size - ps_msg->ptr, _TRUNCATE, fmt, vargs);
message_len = (written < 0) ? ps_msg->size - ps_msg->ptr - 1 : written;
#else
written = vsnprintf(message_text, ps_msg->size - ps_msg->ptr, fmt, vargs);
if (written < 0 || written >= ps_msg->size - ps_msg->ptr){
message_len = ps_msg->size - ps_msg->ptr - 1;
} else {
message_len = written;
}
#endif
ps_msg->ptr += message_len;
bytestream_buf stream_head;
bytestream* ps_head = bytestream_buf_init(&stream_head, BYTESTREAM_MAX_BUFFER_SIZE);
bytewrite_int32(ps_head, (uint32_t)bytestream_length(ps_msg));
(void)fwrite(bytestream_data(ps_head), bytestream_length(ps_head), 1, cnx->f_binlog);
(void)fwrite(bytestream_data(ps_msg), bytestream_length(ps_msg), 1, cnx->f_binlog);
}
/* Log an event that cannot be attached to a specific connection */
void binlog_ignore_quic_app_message(picoquic_quic_t* quic, const picoquic_connection_id_t* cid, const char* fmt, va_list vargs)
{
#ifdef _WINDOWS
UNREFERENCED_PARAMETER(quic);
UNREFERENCED_PARAMETER(cid);
UNREFERENCED_PARAMETER(fmt);
#endif
}
/* Log arrival or departure of an UDP datagram for an unknown connection */
void binlog_ignore_quic_pdu(picoquic_quic_t* quic, int receiving, uint64_t current_time, uint64_t cid64,
const struct sockaddr* addr_peer, const struct sockaddr* addr_local, size_t packet_length)
{
#ifdef _WINDOWS
UNREFERENCED_PARAMETER(quic);
UNREFERENCED_PARAMETER(receiving);
UNREFERENCED_PARAMETER(current_time);
UNREFERENCED_PARAMETER(addr_peer);
UNREFERENCED_PARAMETER(addr_local);
UNREFERENCED_PARAMETER(packet_length);
#endif
}
/* Log an event relating to a specific connection */
static void binlog_app_message(picoquic_cnx_t* cnx, const char* fmt, va_list vargs)
{
if (cnx->f_binlog != NULL) {
picoquic_binlog_message_v(cnx, fmt, vargs);
}
}
struct st_picoquic_unified_logging_t binlog_functions = {
/* Per context log function */
binlog_ignore_quic_app_message,
binlog_ignore_quic_pdu,
/* Per connection functions */
binlog_app_message,
binlog_pdu_ex,
binlog_packet_ex,
binlog_dropped_packet,
binlog_buffered_packet,
binlog_outgoing_packet,
binlog_packet_lost,
binlog_negotiated_alpn,
binlog_transport_extension,
binlog_picotls_ticket_ex,
binlog_new_connection,
binlog_close_connection,
binlog_cc_dump
};
int picoquic_set_binlog(picoquic_quic_t* quic, char const* binlog_dir)
{
quic->binlog_dir = picoquic_string_free(quic->binlog_dir);
quic->binlog_dir = picoquic_string_duplicate(binlog_dir);
quic->bin_log_fns = &binlog_functions;
return 0;
}
void picoquic_enable_binlog(picoquic_quic_t* quic)
{
quic->bin_log_fns = &binlog_functions;
}