test_h3_framing.c revision fb3e20e0
1/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc. See LICENSE. */ 2/* 3 * test_h3_framing.c -- test generation of H3 frames 4 */ 5 6#include <assert.h> 7#include <errno.h> 8#include <stdio.h> 9#include <stdlib.h> 10#include <string.h> 11#include <sys/queue.h> 12#include <sys/types.h> 13#include <fcntl.h> 14#include <limits.h> 15#ifndef WIN32 16#include <unistd.h> 17#else 18#include <getopt.h> 19#endif 20 21#include "lsquic.h" 22 23#include "lsquic_packet_common.h" 24#include "lsquic_packet_ietf.h" 25#include "lsquic_alarmset.h" 26#include "lsquic_packet_in.h" 27#include "lsquic_conn_flow.h" 28#include "lsquic_rtt.h" 29#include "lsquic_sfcw.h" 30#include "lsquic_varint.h" 31#include "lsquic_hq.h" 32#include "lsquic_hash.h" 33#include "lsquic_stream.h" 34#include "lsquic_types.h" 35#include "lsquic_malo.h" 36#include "lsquic_mm.h" 37#include "lsquic_conn_public.h" 38#include "lsquic_logger.h" 39#include "lsquic_parse.h" 40#include "lsquic_conn.h" 41#include "lsquic_engine_public.h" 42#include "lsquic_cubic.h" 43#include "lsquic_pacer.h" 44#include "lsquic_senhist.h" 45#include "lsquic_bw_sampler.h" 46#include "lsquic_minmax.h" 47#include "lsquic_bbr.h" 48#include "lsquic_send_ctl.h" 49#include "lsquic_ver_neg.h" 50#include "lsquic_packet_out.h" 51#include "lsquic_enc_sess.h" 52#include "lsqpack.h" 53#include "lsxpack_header.h" 54#include "lsquic_frab_list.h" 55#include "lsquic_qenc_hdl.h" 56#include "lsquic_http1x_if.h" 57#include "lsquic_qdec_hdl.h" 58#include "lsquic_varint.h" 59#include "lsquic_hq.h" 60#include "lsquic_data_in_if.h" 61 62static const struct parse_funcs *g_pf = select_pf_by_ver(LSQVER_ID27); 63 64struct test_ctl_settings 65{ 66 int tcs_schedule_stream_packets_immediately; 67 int tcs_have_delayed_packets; 68 unsigned tcs_can_send; 69 enum buf_packet_type 70 tcs_bp_type; 71 enum packno_bits 72 tcs_guess_packno_bits, 73 tcs_calc_packno_bits; 74}; 75 76 77static struct test_ctl_settings g_ctl_settings; 78 79 80static void 81init_buf (void *buf, size_t sz); 82 83 84/* Set values to default */ 85static void 86init_test_ctl_settings (struct test_ctl_settings *settings) 87{ 88 settings->tcs_schedule_stream_packets_immediately = 1; 89 settings->tcs_have_delayed_packets = 0; 90 settings->tcs_can_send = UINT_MAX; 91 settings->tcs_bp_type = BPT_HIGHEST_PRIO; 92 settings->tcs_guess_packno_bits = PACKNO_BITS_1; 93 settings->tcs_calc_packno_bits = PACKNO_BITS_1; 94} 95 96 97enum packno_bits 98lsquic_send_ctl_calc_packno_bits (struct lsquic_send_ctl *ctl) 99{ 100 return g_ctl_settings.tcs_calc_packno_bits; 101} 102 103 104int 105lsquic_send_ctl_schedule_stream_packets_immediately (struct lsquic_send_ctl *ctl) 106{ 107 return g_ctl_settings.tcs_schedule_stream_packets_immediately; 108} 109 110 111int 112lsquic_send_ctl_have_delayed_packets (const struct lsquic_send_ctl *ctl) 113{ 114 return g_ctl_settings.tcs_have_delayed_packets; 115} 116 117 118int 119lsquic_send_ctl_can_send (struct lsquic_send_ctl *ctl) 120{ 121 return ctl->sc_n_scheduled < g_ctl_settings.tcs_can_send; 122} 123 124 125enum packno_bits 126lsquic_send_ctl_guess_packno_bits (struct lsquic_send_ctl *ctl) 127{ 128 return g_ctl_settings.tcs_guess_packno_bits; 129} 130 131 132enum buf_packet_type 133lsquic_send_ctl_determine_bpt (struct lsquic_send_ctl *ctl, 134 const struct lsquic_stream *stream) 135{ 136 return g_ctl_settings.tcs_bp_type; 137} 138 139 140/* This function is only here to avoid crash in the test: */ 141void 142lsquic_engine_add_conn_to_tickable (struct lsquic_engine_public *enpub, 143 lsquic_conn_t *conn) 144{ 145} 146 147 148static unsigned n_closed; 149static enum stream_ctor_flags stream_ctor_flags = 150 SCF_CALL_ON_NEW|SCF_DI_AUTOSWITCH; 151 152struct test_ctx { 153 lsquic_stream_t *stream; 154}; 155 156 157static lsquic_stream_ctx_t * 158on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream) 159{ 160 struct test_ctx *test_ctx = stream_if_ctx; 161 test_ctx->stream = stream; 162 return NULL; 163} 164 165 166static void 167on_close (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h) 168{ 169 ++n_closed; 170} 171 172 173const struct lsquic_stream_if stream_if = { 174 .on_new_stream = on_new_stream, 175 .on_close = on_close, 176}; 177 178 179static size_t 180read_from_scheduled_packets (lsquic_send_ctl_t *send_ctl, lsquic_stream_id_t stream_id, 181 unsigned char *const begin, size_t bufsz, uint64_t first_offset, int *p_fin, 182 int fullcheck) 183{ 184 const struct parse_funcs *const pf_local = send_ctl->sc_conn_pub->lconn->cn_pf; 185 unsigned char *p = begin; 186 unsigned char *const end = p + bufsz; 187 const struct stream_rec *srec; 188 struct packet_out_srec_iter posi; 189 struct lsquic_packet_out *packet_out; 190 struct stream_frame frame; 191 enum quic_frame_type expected_type; 192 int len, fin = 0; 193 194 expected_type = QUIC_FRAME_STREAM; 195 196 TAILQ_FOREACH(packet_out, &send_ctl->sc_scheduled_packets, po_next) 197 for (srec = lsquic_posi_first(&posi, packet_out); srec; 198 srec = lsquic_posi_next(&posi)) 199 { 200 if (fullcheck) 201 { 202 assert(srec->sr_frame_type == expected_type); 203 if (0 && packet_out->po_packno != 1) 204 { 205 /* First packet may contain two stream frames, do not 206 * check it. 207 */ 208 assert(!lsquic_posi_next(&posi)); 209 if (TAILQ_NEXT(packet_out, po_next)) 210 { 211 assert(packet_out->po_data_sz == packet_out->po_n_alloc); 212 assert(srec->sr_len == packet_out->po_data_sz); 213 } 214 } 215 } 216 if (srec->sr_frame_type == expected_type && 217 srec->sr_stream->id == stream_id) 218 { 219 assert(!fin); 220 if (QUIC_FRAME_STREAM == expected_type) 221 len = pf_local->pf_parse_stream_frame(packet_out->po_data + srec->sr_off, 222 packet_out->po_data_sz - srec->sr_off, &frame); 223 else 224 len = pf_local->pf_parse_crypto_frame(packet_out->po_data + srec->sr_off, 225 packet_out->po_data_sz - srec->sr_off, &frame); 226 assert(len > 0); 227 if (QUIC_FRAME_STREAM == expected_type) 228 assert(frame.stream_id == srec->sr_stream->id); 229 else 230 assert(frame.stream_id == ~0ULL); 231 /* Otherwise not enough to copy to: */ 232 assert(end - p >= frame.data_frame.df_size); 233 /* Checks offset ordering: */ 234 assert(frame.data_frame.df_offset == 235 first_offset + (uintptr_t) (p - begin)); 236 if (frame.data_frame.df_fin) 237 { 238 assert(!fin); 239 fin = 1; 240 } 241 memcpy(p, packet_out->po_data + srec->sr_off + len - 242 frame.data_frame.df_size, frame.data_frame.df_size); 243 p += frame.data_frame.df_size; 244 } 245 } 246 247 if (p_fin) 248 *p_fin = fin; 249 return p + bufsz - end; 250} 251 252 253static struct test_ctx test_ctx; 254 255 256struct test_objs { 257 struct lsquic_engine_public eng_pub; 258 struct lsquic_conn lconn; 259 struct lsquic_conn_public conn_pub; 260 struct lsquic_send_ctl send_ctl; 261 struct lsquic_alarmset alset; 262 void *stream_if_ctx; 263 struct ver_neg ver_neg; 264 const struct lsquic_stream_if * 265 stream_if; 266 unsigned initial_stream_window; 267 enum stream_ctor_flags ctor_flags; 268 struct qpack_enc_hdl qeh; 269 struct qpack_dec_hdl qdh; 270 struct lsquic_hset_if hsi_if; 271}; 272 273 274static int 275unit_test_doesnt_write_ack (struct lsquic_conn *lconn) 276{ 277 return 0; 278} 279 280 281static struct network_path network_path; 282 283static struct network_path * 284get_network_path (struct lsquic_conn *lconn, const struct sockaddr *sa) 285{ 286 return &network_path; 287} 288 289 290static const struct conn_iface our_conn_if = 291{ 292 .ci_can_write_ack = unit_test_doesnt_write_ack, 293 .ci_get_path = get_network_path, 294}; 295 296 297static void 298init_test_objs (struct test_objs *tobjs, unsigned initial_conn_window, 299 unsigned initial_stream_window, unsigned short packet_sz) 300{ 301 int s; 302 memset(tobjs, 0, sizeof(*tobjs)); 303 LSCONN_INITIALIZE(&tobjs->lconn); 304 tobjs->lconn.cn_pf = g_pf; 305 tobjs->lconn.cn_version = LSQVER_ID27; 306 tobjs->lconn.cn_esf_c = &lsquic_enc_session_common_ietf_v1; 307 network_path.np_pack_size = packet_sz; 308 tobjs->lconn.cn_if = &our_conn_if; 309 lsquic_mm_init(&tobjs->eng_pub.enp_mm); 310 TAILQ_INIT(&tobjs->conn_pub.sending_streams); 311 TAILQ_INIT(&tobjs->conn_pub.read_streams); 312 TAILQ_INIT(&tobjs->conn_pub.write_streams); 313 TAILQ_INIT(&tobjs->conn_pub.service_streams); 314 lsquic_cfcw_init(&tobjs->conn_pub.cfcw, &tobjs->conn_pub, 315 initial_conn_window); 316 lsquic_conn_cap_init(&tobjs->conn_pub.conn_cap, initial_conn_window); 317 lsquic_alarmset_init(&tobjs->alset, 0); 318 tobjs->conn_pub.mm = &tobjs->eng_pub.enp_mm; 319 tobjs->conn_pub.lconn = &tobjs->lconn; 320 tobjs->conn_pub.enpub = &tobjs->eng_pub; 321 tobjs->conn_pub.send_ctl = &tobjs->send_ctl; 322 tobjs->conn_pub.packet_out_malo = 323 lsquic_malo_create(sizeof(struct lsquic_packet_out)); 324 tobjs->conn_pub.path = &network_path; 325 tobjs->initial_stream_window = initial_stream_window; 326 tobjs->eng_pub.enp_settings.es_cc_algo = 1; /* Cubic */ 327 tobjs->eng_pub.enp_hsi_if = &tobjs->hsi_if; 328 lsquic_send_ctl_init(&tobjs->send_ctl, &tobjs->alset, &tobjs->eng_pub, 329 &tobjs->ver_neg, &tobjs->conn_pub, 0); 330 tobjs->send_ctl.sc_cong_u.cubic.cu_cwnd = ~0ull; 331 tobjs->stream_if = &stream_if; 332 tobjs->stream_if_ctx = &test_ctx; 333 tobjs->ctor_flags = stream_ctor_flags; 334 if ((1 << tobjs->lconn.cn_version) & LSQUIC_IETF_VERSIONS) 335 { 336 lsquic_qeh_init(&tobjs->qeh, &tobjs->lconn); 337 s = lsquic_qeh_settings(&tobjs->qeh, 0, 0, 0, 0); 338 assert(0 == s); 339 tobjs->conn_pub.u.ietf.qeh = &tobjs->qeh; 340 lsquic_qdh_init(&tobjs->qdh, &tobjs->lconn, 0, &tobjs->eng_pub, 0, 0); 341 tobjs->conn_pub.u.ietf.qdh = &tobjs->qdh; 342 } 343} 344 345 346static void 347deinit_test_objs (struct test_objs *tobjs) 348{ 349 assert(!lsquic_malo_first(tobjs->eng_pub.enp_mm.malo.stream_frame)); 350 lsquic_send_ctl_cleanup(&tobjs->send_ctl); 351 lsquic_malo_destroy(tobjs->conn_pub.packet_out_malo); 352 lsquic_mm_cleanup(&tobjs->eng_pub.enp_mm); 353 if ((1 << tobjs->lconn.cn_version) & LSQUIC_IETF_VERSIONS) 354 lsquic_qeh_cleanup(&tobjs->qeh); 355} 356 357 358static struct lsquic_stream * 359new_stream (struct test_objs *tobjs, unsigned stream_id, uint64_t send_off) 360{ 361 return lsquic_stream_new(stream_id, &tobjs->conn_pub, tobjs->stream_if, 362 tobjs->stream_if_ctx, tobjs->initial_stream_window, send_off, 363 tobjs->ctor_flags); 364} 365 366 367struct packetization_test_stream_ctx 368{ 369 const unsigned char *buf; 370 unsigned len, off, write_size; 371 int flush_after_each_write; 372}; 373 374 375static lsquic_stream_ctx_t * 376packetization_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream) 377{ 378 lsquic_stream_wantwrite(stream, 1); 379 return stream_if_ctx; 380} 381 382 383static void 384packetization_on_close (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h) 385{ 386} 387 388 389#define RANDOM_WRITE_SIZE ~0U 390 391static unsigned 392calc_n_to_write (unsigned write_size) 393{ 394 if (write_size == RANDOM_WRITE_SIZE) 395 return rand() % 1000 + 1; 396 else 397 return write_size; 398} 399 400 401static void 402packetization_write_as_much_as_you_can (lsquic_stream_t *stream, 403 lsquic_stream_ctx_t *ctx) 404{ 405 struct packetization_test_stream_ctx *const pack_ctx = (void *) ctx; 406 unsigned n_to_write, n_sched; 407 ssize_t n_written; 408 size_t avail; 409 int s; 410 411 while (pack_ctx->off < pack_ctx->len) 412 { 413 n_to_write = calc_n_to_write(pack_ctx->write_size); 414 n_sched = lsquic_send_ctl_n_scheduled(stream->conn_pub->send_ctl); 415 if (n_to_write > pack_ctx->len - pack_ctx->off) 416 n_to_write = pack_ctx->len - pack_ctx->off; 417 n_written = lsquic_stream_write(stream, pack_ctx->buf + pack_ctx->off, 418 n_to_write); 419 if (n_written == 0) 420 { 421 if (n_to_write && SSHS_BEGIN == stream->sm_send_headers_state 422 && lsquic_send_ctl_can_send(stream->conn_pub->send_ctl)) 423 { 424 avail = lsquic_stream_write_avail(stream); 425 assert(avail == 0 426 || lsquic_send_ctl_n_scheduled( 427 stream->conn_pub->send_ctl) > n_sched); 428 } 429 break; 430 } 431 pack_ctx->off += n_written; 432 if (pack_ctx->flush_after_each_write) 433 { 434 s = lsquic_stream_flush(stream); 435 assert(s == 0); 436 } 437 } 438 439 s = lsquic_stream_flush(stream); 440 assert(s == 0); 441 lsquic_stream_wantwrite(stream, 0); 442} 443 444 445static void 446packetization_perform_one_write (lsquic_stream_t *stream, 447 lsquic_stream_ctx_t *ctx) 448{ 449 struct packetization_test_stream_ctx *const pack_ctx = (void *) ctx; 450 unsigned n_to_write, n_sched; 451 ssize_t n_written; 452 size_t avail; 453 int s; 454 455 n_to_write = calc_n_to_write(pack_ctx->write_size); 456 if (n_to_write > pack_ctx->len - pack_ctx->off) 457 n_to_write = pack_ctx->len - pack_ctx->off; 458 n_sched = lsquic_send_ctl_n_scheduled(stream->conn_pub->send_ctl); 459 n_written = lsquic_stream_write(stream, pack_ctx->buf + pack_ctx->off, 460 n_to_write); 461 assert(n_written >= 0); 462 if (n_written == 0 && SSHS_BEGIN == stream->sm_send_headers_state 463 && n_to_write 464 && lsquic_send_ctl_can_send(stream->conn_pub->send_ctl)) 465 { 466 avail = lsquic_stream_write_avail(stream); 467 assert(avail == 0 468 || lsquic_send_ctl_n_scheduled( 469 stream->conn_pub->send_ctl) > n_sched); 470 } 471 pack_ctx->off += n_written; 472 if (pack_ctx->flush_after_each_write) 473 { 474 s = lsquic_stream_flush(stream); 475 assert(s == 0); 476 } 477 if (n_written == 0) 478 lsquic_stream_wantwrite(stream, 0); 479} 480 481 482static const struct lsquic_stream_if packetization_inside_once_stream_if = { 483 .on_new_stream = packetization_on_new_stream, 484 .on_close = packetization_on_close, 485 .on_write = packetization_write_as_much_as_you_can, 486}; 487 488 489static const struct lsquic_stream_if packetization_inside_many_stream_if = { 490 .on_new_stream = packetization_on_new_stream, 491 .on_close = packetization_on_close, 492 .on_write = packetization_perform_one_write, 493}; 494 495 496#define XHDR(name_, value_) .buf = name_ value_, .name_offset = 0, .name_len = sizeof(name_) - 1, .val_offset = sizeof(name_) - 1, .val_len = sizeof(value_) - 1, 497 498static void 499test_hq_framing (int sched_immed, int dispatch_once, unsigned wsize, 500 int flush_after_each_write, size_t conn_limit, 501 unsigned n_packets, unsigned short packet_sz) 502{ 503 struct test_objs tobjs; 504 struct lsquic_stream *stream; 505 size_t nw; 506 int fin, s; 507 unsigned char *buf_in, *buf_out; 508 const size_t buf_in_sz = 0x40000, buf_out_sz = 0x500000; 509 510 /* We'll write headers first after which stream will switch to using 511 * data-framing writer. This is simply so that we don't have to 512 * expose more stream things only for testing. 513 */ 514 struct lsxpack_header header = { XHDR(":method", "GET") }; 515 struct lsquic_http_headers headers = { 1, &header, }; 516 517 buf_in = malloc(buf_in_sz); 518 buf_out = malloc(buf_out_sz); 519 assert(buf_in && buf_out); 520 521 struct packetization_test_stream_ctx packet_stream_ctx = 522 { 523 .buf = buf_in, 524 .off = 0, 525 .len = buf_in_sz, 526 .write_size = wsize, 527 .flush_after_each_write = flush_after_each_write, 528 }; 529 530 init_buf(buf_in, buf_in_sz); 531 532 init_test_ctl_settings(&g_ctl_settings); 533 g_ctl_settings.tcs_schedule_stream_packets_immediately = sched_immed; 534 535 stream_ctor_flags |= SCF_IETF; 536 init_test_objs(&tobjs, conn_limit ? conn_limit : buf_out_sz, buf_out_sz, packet_sz); 537 tobjs.stream_if_ctx = &packet_stream_ctx; 538 tobjs.ctor_flags |= SCF_HTTP|SCF_IETF; 539 if (sched_immed) 540 { 541 g_ctl_settings.tcs_can_send = n_packets; 542 if (dispatch_once) 543 { 544 tobjs.stream_if = &packetization_inside_once_stream_if; 545 tobjs.ctor_flags |= SCF_DISP_RW_ONCE; 546 } 547 else 548 tobjs.stream_if = &packetization_inside_many_stream_if; 549 } 550 else 551 { 552 lsquic_send_ctl_set_max_bpq_count(n_packets); 553 g_ctl_settings.tcs_can_send = INT_MAX; 554 /* Need this for on_new_stream() callback not to mess with 555 * the context, otherwise this is not used. 556 */ 557 tobjs.stream_if = &packetization_inside_many_stream_if; 558 } 559 560 stream = new_stream(&tobjs, 0, buf_out_sz); 561 562 s = lsquic_stream_send_headers(stream, &headers, 0); 563 assert(0 == s); 564 565 if (sched_immed) 566 { 567 lsquic_stream_dispatch_write_events(stream); 568 lsquic_stream_flush(stream); 569 } 570 else 571 { 572 packetization_write_as_much_as_you_can(stream, 573 (void *) &packet_stream_ctx); 574 g_ctl_settings.tcs_schedule_stream_packets_immediately = 1; 575 lsquic_send_ctl_schedule_buffered(&tobjs.send_ctl, BPT_HIGHEST_PRIO); 576 g_ctl_settings.tcs_schedule_stream_packets_immediately = 0; 577 } 578 lsquic_send_ctl_set_max_bpq_count(10); 579 580 /* Verify written data: */ 581 nw = read_from_scheduled_packets(&tobjs.send_ctl, 0, buf_out, buf_out_sz, 582 0, &fin, 1); 583 if (!conn_limit) 584 assert(nw > buf_in_sz); 585 { /* Remove framing and verify contents */ 586 const unsigned char *src; 587 unsigned char *dst; 588 uint64_t sz; 589 unsigned frame_type; 590 int s; 591 592 src = buf_out; 593 dst = buf_out; 594 while (src < buf_out + nw) 595 { 596 frame_type = *src++; 597 s = vint_read(src, buf_out + buf_out_sz, &sz); 598 assert(s > 0); 599 /* In some rare circumstances it is possible to produce zero-length 600 * DATA frames: 601 * 602 * assert(sz > 0); 603 */ 604 assert(sz < (1 << 14)); 605 src += s; 606 if (src == buf_out + s + 1) 607 { 608 /* Ignore headers */ 609 assert(frame_type == HQFT_HEADERS); 610 src += sz; 611 } 612 else 613 { 614 assert(frame_type == HQFT_DATA); 615 if (src + sz > buf_out + nw) /* Chopped DATA frame (last) */ 616 sz = buf_out + nw - src; 617 memmove(dst, src, sz); 618 dst += sz; 619 src += sz; 620 } 621 } 622 if (!conn_limit) 623 assert(buf_in_sz == (uintptr_t) dst - (uintptr_t) buf_out); 624 assert(0 == memcmp(buf_in, buf_out, (uintptr_t) dst - (uintptr_t) buf_out)); 625 } 626 627 lsquic_stream_destroy(stream); 628 deinit_test_objs(&tobjs); 629 free(buf_in); 630 free(buf_out); 631 632 stream_ctor_flags &= ~SCF_IETF; 633} 634 635 636static void 637main_test_hq_framing (void) 638{ 639 const unsigned wsizes[] = { 1, 2, 3, 7, 10, 50, 100, 201, 211, 1000, 2003, 20000, }; 640 const size_t conn_limits[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 641 14, 15, 16, 17, 18, 19, 20, 21, 30, 31, 32, 33, 63, 64, 128, 200, 255, 642 256, 512, 1024, 2045, 2048, 2049, 3000, 4091, 4096, 4097, 5000, 8192, 643 16 * 1024 - 1, 16 * 1024, 32 * 1024, 33 * 1024, }; 644 const unsigned n_packets[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, UINT_MAX, }; 645 const unsigned short packet_sz[] = { 1252, 1370, 0x1000, 0xFF00, }; 646 unsigned i, j, k, l; 647 int sched_immed, dispatch_once, flush_after_each_write; 648 649 for (sched_immed = 0; sched_immed <= 1; ++sched_immed) 650 for (dispatch_once = 0; dispatch_once <= 1; ++dispatch_once) 651 for (i = 0; i < sizeof(wsizes) / sizeof(wsizes[i]); ++i) 652 for (j = 0; j < sizeof(conn_limits) / sizeof(conn_limits[j]); ++j) 653 for (flush_after_each_write = 0; flush_after_each_write < 2; ++flush_after_each_write) 654 for (k = 0; k < sizeof(n_packets) / sizeof(n_packets[0]); ++k) 655 for (l = 0; l < sizeof(packet_sz) / sizeof(packet_sz[0]); ++l) 656 test_hq_framing(sched_immed, dispatch_once, wsizes[i], flush_after_each_write, conn_limits[j], n_packets[k], packet_sz[l]); 657} 658 659 660/* Instead of the not-very-random testing done in main_test_hq_framing(), 661 * the fuzz-guided testing initializes parameters based on the fuzz input 662 * file. This allows afl-fuzz explore the code paths. 663 */ 664void 665fuzz_guided_testing (const char *input) 666{ 667 /* Range */ /* Bytes from file */ 668 unsigned short packet_sz; /* [200, 0x3FFF] */ /* 2 */ 669 unsigned wsize; /* [1, 20000] */ /* 2 */ 670 unsigned n_packets; /* [1, 255] and UINT_MAX */ /* 1 */ 671 size_t conn_limit; /* [1, 33K] */ /* 2 */ 672 int sched_immed; /* 0 or 1 */ /* 1 */ 673 int dispatch_once; /* 0 or 1 */ /* 0 (same as above) */ 674 int flush_after_each_write; /* 0 or 1 */ /* 0 (same as above) */ 675 /* TOTAL: 8 bytes */ 676 677 FILE *f; 678 size_t nread; 679 uint16_t tmp; 680 unsigned char buf[9]; 681 682 f = fopen(input, "rb"); 683 if (!f) 684 { 685 assert(0); 686 return; 687 } 688 689 nread = fread(buf, 1, sizeof(buf), f); 690 if (nread != 8) 691 goto cleanup; 692 693 memcpy(&tmp, &buf[0], 2); 694 if (tmp < 200) 695 tmp = 200; 696 else if (tmp > IQUIC_MAX_OUT_PACKET_SZ) 697 tmp = IQUIC_MAX_OUT_PACKET_SZ; 698 packet_sz = tmp; 699 700 memcpy(&tmp, &buf[2], 2); 701 if (tmp < 1) 702 tmp = 1; 703 else if (tmp > 20000) 704 tmp = 20000; 705 wsize = tmp; 706 707 if (buf[4]) 708 n_packets = buf[4]; 709 else 710 n_packets = UINT_MAX; 711 712 memcpy(&tmp, &buf[5], 2); 713 if (tmp < 1) 714 tmp = 1; 715 else if (tmp > 33 * 1024) 716 tmp = 33 * 1024; 717 conn_limit = tmp; 718 719 sched_immed = !!(buf[7] & 1); 720 dispatch_once = !!(buf[7] & 2); 721 flush_after_each_write = !!(buf[7] & 4); 722 723 test_hq_framing(sched_immed, dispatch_once, wsize, 724 flush_after_each_write, conn_limit, n_packets, packet_sz); 725 726 cleanup: 727 (void) fclose(f); 728} 729 730 731static void 732test_frame_header_split (unsigned n_packets, unsigned extra_sz, 733 int add_one_more) 734{ 735 struct test_objs tobjs; 736 struct lsquic_stream *stream; 737 size_t nw; 738 int fin, s; 739 unsigned char *buf_in, *buf_out; 740 const unsigned wsize = 70; 741 const size_t buf_in_sz = wsize, buf_out_sz = 0x500000; 742 743 struct lsxpack_header header = { XHDR(":method", "GET") }; 744 struct lsquic_http_headers headers = { 1, &header, }; 745 746 buf_in = malloc(buf_in_sz); 747 buf_out = malloc(buf_out_sz); 748 assert(buf_in && buf_out); 749 750 struct packetization_test_stream_ctx packet_stream_ctx = 751 { 752 .buf = buf_in, 753 .off = 0, 754 .len = buf_in_sz, 755 .write_size = wsize, 756 .flush_after_each_write = 0, 757 }; 758 759 init_buf(buf_in, buf_in_sz); 760 761 init_test_ctl_settings(&g_ctl_settings); 762 g_ctl_settings.tcs_schedule_stream_packets_immediately = 1; 763 764 stream_ctor_flags |= SCF_IETF; 765 init_test_objs(&tobjs, 0x1000, buf_out_sz, 1252); 766 tobjs.stream_if_ctx = &packet_stream_ctx; 767 tobjs.ctor_flags |= SCF_HTTP|SCF_IETF; 768 769 g_ctl_settings.tcs_can_send = n_packets; 770 tobjs.stream_if = &packetization_inside_once_stream_if; 771 tobjs.ctor_flags |= SCF_DISP_RW_ONCE; 772 773 struct lsquic_packet_out *const packet_out 774 = lsquic_send_ctl_new_packet_out(&tobjs.send_ctl, 0, PNS_APP, &network_path); 775 assert(packet_out); 776 const size_t pad_size = packet_out->po_n_alloc 777 - 2 /* STREAM header */ 778 - 5 /* 3-byte HEADERS frame */ 779 - extra_sz; 780 packet_out->po_data_sz = pad_size; 781 lsquic_send_ctl_scheduled_one(&tobjs.send_ctl, packet_out); 782 783 stream = new_stream(&tobjs, 0, buf_out_sz); 784 785 s = lsquic_stream_send_headers(stream, &headers, 0); 786 assert(0 == s); 787 788 const ssize_t w = lsquic_stream_write(stream, buf_in, buf_in_sz); 789 assert(w >= 0 && (size_t) w == buf_in_sz); 790 lsquic_stream_flush(stream); 791 792 if (add_one_more) 793 { 794 ++g_ctl_settings.tcs_can_send; 795 lsquic_stream_flush(stream); 796 } 797 798 /* Verify written data: */ 799 nw = read_from_scheduled_packets(&tobjs.send_ctl, 0, buf_out, buf_out_sz, 800 0, &fin, 1); 801 { /* Remove framing and verify contents */ 802 const unsigned char *src; 803 unsigned char *dst; 804 uint64_t sz; 805 unsigned frame_type; 806 int s; 807 808 src = buf_out; 809 dst = buf_out; 810 while (src < buf_out + nw) 811 { 812 frame_type = *src++; 813 s = vint_read(src, buf_out + buf_out_sz, &sz); 814 assert(s > 0); 815 assert(sz > 0); 816 assert(sz < (1 << 14)); 817 src += s; 818 if (src == buf_out + s + 1) 819 { 820 /* Ignore headers */ 821 assert(frame_type == HQFT_HEADERS); 822 src += sz; 823 } 824 else 825 { 826 assert(frame_type == HQFT_DATA); 827 if (src + sz > buf_out + nw) /* Chopped DATA frame (last) */ 828 sz = buf_out + nw - src; 829 memmove(dst, src, sz); 830 dst += sz; 831 src += sz; 832 } 833 } 834 assert(0 == memcmp(buf_in, buf_out, (uintptr_t) dst - (uintptr_t) buf_out)); 835 } 836 837 lsquic_stream_destroy(stream); 838 deinit_test_objs(&tobjs); 839 free(buf_in); 840 free(buf_out); 841 842 stream_ctor_flags &= ~SCF_IETF; 843} 844 845 846static void 847test_zero_size_frame (void) 848{ 849 struct test_objs tobjs; 850 struct lsquic_stream *stream; 851 ssize_t w; 852 size_t nw; 853 int fin, s; 854 unsigned char *buf_in, *buf_out; 855 const unsigned wsize = 7000; 856 const size_t buf_in_sz = wsize, buf_out_sz = 0x500000; 857 858 struct lsxpack_header header = { XHDR(":method", "GET") }; 859 struct lsquic_http_headers headers = { 1, &header, }; 860 861 buf_in = malloc(buf_in_sz); 862 buf_out = malloc(buf_out_sz); 863 assert(buf_in && buf_out); 864 865 struct packetization_test_stream_ctx packet_stream_ctx = 866 { 867 .buf = buf_in, 868 .off = 0, 869 .len = buf_in_sz, 870 .write_size = wsize, 871 .flush_after_each_write = 0, 872 }; 873 874 init_buf(buf_in, buf_in_sz); 875 876 init_test_ctl_settings(&g_ctl_settings); 877 g_ctl_settings.tcs_schedule_stream_packets_immediately = 1; 878 879 stream_ctor_flags |= SCF_IETF; 880 init_test_objs(&tobjs, 0x1000, buf_out_sz, 1252); 881 tobjs.stream_if_ctx = &packet_stream_ctx; 882 tobjs.ctor_flags |= SCF_HTTP|SCF_IETF; 883 884 g_ctl_settings.tcs_can_send = 1; 885 tobjs.stream_if = &packetization_inside_once_stream_if; 886 tobjs.ctor_flags |= SCF_DISP_RW_ONCE; 887 888 struct lsquic_packet_out *const packet_out 889 = lsquic_send_ctl_new_packet_out(&tobjs.send_ctl, 0, PNS_APP, &network_path); 890 assert(packet_out); 891 const size_t pad_size = packet_out->po_n_alloc 892 - 2 /* STREAM header */ 893 - 5 /* 3-byte HEADERS frame */ 894 - 3; 895 packet_out->po_data_sz = pad_size; 896 lsquic_send_ctl_scheduled_one(&tobjs.send_ctl, packet_out); 897 898 stream = new_stream(&tobjs, 0, buf_out_sz); 899 900 s = lsquic_stream_send_headers(stream, &headers, 0); 901 assert(0 == s); 902 903 w = lsquic_stream_write(stream, buf_in, buf_in_sz); 904 assert(w >= 0); 905 lsquic_stream_flush(stream); 906 907 g_ctl_settings.tcs_can_send++; 908 w = lsquic_stream_write(stream, buf_in, buf_in_sz); 909 assert(w >= 0); 910 lsquic_stream_flush(stream); 911 912 /* Verify written data: */ 913 nw = read_from_scheduled_packets(&tobjs.send_ctl, 0, buf_out, buf_out_sz, 914 0, &fin, 1); 915 { /* Remove framing and verify contents */ 916 const unsigned char *src; 917 unsigned char *dst; 918 uint64_t sz; 919 unsigned frame_type; 920 int s; 921 922 src = buf_out; 923 dst = buf_out; 924 while (src < buf_out + nw) 925 { 926 frame_type = *src++; 927 s = vint_read(src, buf_out + buf_out_sz, &sz); 928 assert(s > 0); 929 assert(sz < (1 << 14)); 930 src += s; 931 if (src == buf_out + s + 1) 932 { 933 /* Ignore headers */ 934 assert(frame_type == HQFT_HEADERS); 935 src += sz; 936 } 937 else 938 { 939 assert(frame_type == HQFT_DATA); 940 if (src + sz > buf_out + nw) /* Chopped DATA frame (last) */ 941 sz = buf_out + nw - src; 942 memmove(dst, src, sz); 943 dst += sz; 944 src += sz; 945 } 946 } 947 assert(0 == memcmp(buf_in, buf_out, (uintptr_t) dst - (uintptr_t) buf_out)); 948 } 949 950 lsquic_stream_destroy(stream); 951 deinit_test_objs(&tobjs); 952 free(buf_in); 953 free(buf_out); 954 955 stream_ctor_flags &= ~SCF_IETF; 956} 957 958 959/* Create a new stream frame. Each stream frame has a real packet_in to 960 * back it up, just like in real code. The contents of the packet do 961 * not matter. 962 */ 963static stream_frame_t * 964new_frame_in_ext (struct test_objs *tobjs, size_t off, size_t sz, int fin, 965 const void *data) 966{ 967 lsquic_packet_in_t *packet_in; 968 stream_frame_t *frame; 969 970 assert(sz <= 1370); 971 972 packet_in = lsquic_mm_get_packet_in(&tobjs->eng_pub.enp_mm); 973 if (data) 974 packet_in->pi_data = (void *) data; 975 else 976 { 977 packet_in->pi_data = lsquic_mm_get_packet_in_buf(&tobjs->eng_pub.enp_mm, 1370); 978 packet_in->pi_flags |= PI_OWN_DATA; 979 memset(packet_in->pi_data, 'A', sz); 980 } 981 /* This is not how stream frame looks in the packet: we have no 982 * header. In our test case it does not matter, as we only care 983 * about stream frame. 984 */ 985 packet_in->pi_data_sz = sz; 986 packet_in->pi_refcnt = 1; 987 988 frame = lsquic_malo_get(tobjs->eng_pub.enp_mm.malo.stream_frame); 989 memset(frame, 0, sizeof(*frame)); 990 frame->packet_in = packet_in; 991 frame->data_frame.df_offset = off; 992 frame->data_frame.df_size = sz; 993 frame->data_frame.df_data = &packet_in->pi_data[0]; 994 frame->data_frame.df_fin = fin; 995 996 return frame; 997} 998 999 1000/* Receiving DATA frame with zero payload should result in lsquic_stream_read() 1001 * returning -1. 1002 */ 1003static void 1004test_reading_zero_size_data_frame (void) 1005{ 1006 struct test_objs tobjs; 1007 struct lsquic_stream *stream; 1008 struct stream_frame *frame; 1009 ssize_t nr; 1010 int s; 1011 unsigned char buf[2]; 1012 1013 init_test_ctl_settings(&g_ctl_settings); 1014 1015 stream_ctor_flags |= SCF_IETF; 1016 init_test_objs(&tobjs, 0x1000, 0x2000, 1252); 1017 tobjs.ctor_flags |= SCF_HTTP|SCF_IETF; 1018 1019 stream = new_stream(&tobjs, 0, 0x1000); 1020 1021 /* Fake out reading of HEADERS frame: */ 1022 stream->stream_flags |= STREAM_HAVE_UH; 1023 stream->sm_hq_filter.hqfi_hist_buf = 1 /* CODE_HEADER */; 1024 stream->sm_hq_filter.hqfi_hist_idx++; 1025 1026 /* One-byte DATA frame */ 1027 frame = new_frame_in_ext(&tobjs, 0, 3, 0, (uint8_t[3]){ 0, 1, 'a', }); 1028 s = lsquic_stream_frame_in(stream, frame); 1029 assert(s == 0); /* Self-check */ 1030 1031 /* Zero-length DATA frame */ 1032 frame = new_frame_in_ext(&tobjs, 3, 2, 0, (uint8_t[2]){ 0, 0, }); 1033 s = lsquic_stream_frame_in(stream, frame); 1034 assert(s == 0); /* Self-check */ 1035 1036 assert(stream->read_offset == 2); /* Self-check */ 1037 1038 /* Read 'a' */ 1039 nr = lsquic_stream_read(stream, buf, 1); 1040 assert(nr == 1); 1041 assert(buf[0] == 'a'); 1042 1043 /* Check that read returns -1 */ 1044 nr = lsquic_stream_read(stream, buf, sizeof(buf)); 1045 assert(nr == -1); 1046 1047 /* DATA frame was consumed: */ 1048 assert(stream->read_offset == 5); 1049 1050 lsquic_stream_destroy(stream); 1051 deinit_test_objs(&tobjs); 1052 1053 stream_ctor_flags &= ~SCF_IETF; 1054} 1055 1056 1057/* Receiving DATA frame with zero payload should result in lsquic_stream_read() 1058 * returning -1. 1059 */ 1060static void 1061test_reading_zero_size_data_frame_scenario2 (void) 1062{ 1063 struct test_objs tobjs; 1064 struct lsquic_stream *stream; 1065 struct stream_frame *frame; 1066 ssize_t nr; 1067 int s; 1068 unsigned char buf[2]; 1069 1070 init_test_ctl_settings(&g_ctl_settings); 1071 1072 stream_ctor_flags |= SCF_IETF; 1073 init_test_objs(&tobjs, 0x1000, 0x2000, 1252); 1074 tobjs.ctor_flags |= SCF_HTTP|SCF_IETF; 1075 1076 stream = new_stream(&tobjs, 0, 0x1000); 1077 1078 /* Fake out reading of HEADERS frame: */ 1079 stream->stream_flags |= STREAM_HAVE_UH; 1080 stream->sm_hq_filter.hqfi_hist_buf = 1 /* CODE_HEADER */; 1081 stream->sm_hq_filter.hqfi_hist_idx++; 1082 1083 /* Zero-length DATA frame */ 1084 frame = new_frame_in_ext(&tobjs, 0, 5, 0, (uint8_t[5]){ 0, 1, 'a', 0, 0, }); 1085 s = lsquic_stream_frame_in(stream, frame); 1086 assert(s == 0); /* Self-check */ 1087 1088 assert(stream->read_offset == 2); /* Self-check */ 1089 1090 /* Read 'a' */ 1091 nr = lsquic_stream_read(stream, buf, 1); 1092 assert(nr == 1); 1093 assert(buf[0] == 'a'); 1094 1095 /* Check that read returns -1 */ 1096 nr = lsquic_stream_read(stream, buf, sizeof(buf)); 1097 assert(nr == -1); 1098 1099 /* DATA frame was consumed: */ 1100 assert(stream->read_offset == 5); 1101 1102 lsquic_stream_destroy(stream); 1103 deinit_test_objs(&tobjs); 1104 1105 stream_ctor_flags &= ~SCF_IETF; 1106} 1107 1108 1109/* Receiving DATA frame with zero payload should result in lsquic_stream_read() 1110 * returning -1. 1111 */ 1112static void 1113test_reading_zero_size_data_frame_scenario3 (void) 1114{ 1115 struct test_objs tobjs; 1116 struct lsquic_stream *stream; 1117 struct stream_frame *frame; 1118 ssize_t nr; 1119 int s; 1120 unsigned char buf[2]; 1121 1122 init_test_ctl_settings(&g_ctl_settings); 1123 1124 stream_ctor_flags |= SCF_IETF; 1125 init_test_objs(&tobjs, 0x1000, 0x2000, 1252); 1126 tobjs.ctor_flags |= SCF_HTTP|SCF_IETF; 1127 1128 stream = new_stream(&tobjs, 0, 0x1000); 1129 1130 /* Fake out reading of HEADERS frame: */ 1131 stream->stream_flags |= STREAM_HAVE_UH; 1132 stream->sm_hq_filter.hqfi_hist_buf = 1 /* CODE_HEADER */; 1133 stream->sm_hq_filter.hqfi_hist_idx++; 1134 1135 /* Zero-length DATA frame */ 1136 frame = new_frame_in_ext(&tobjs, 0, 4, 0, (uint8_t[4]){ 0, 1, 'a', 0, }); 1137 s = lsquic_stream_frame_in(stream, frame); 1138 assert(s == 0); /* Self-check */ 1139 1140 assert(stream->read_offset == 2); /* Self-check */ 1141 1142 /* Read 'a' */ 1143 nr = lsquic_stream_read(stream, buf, 1); 1144 assert(nr == 1); 1145 assert(buf[0] == 'a'); 1146 1147 /* Check that read returns -1 */ 1148 nr = lsquic_stream_read(stream, buf, sizeof(buf)); 1149 assert(nr == -1); 1150 1151 /* Zero-length DATA frame */ 1152 frame = new_frame_in_ext(&tobjs, 4, 1, 0, (uint8_t[1]){ 0, }); 1153 s = lsquic_stream_frame_in(stream, frame); 1154 assert(s == 0); /* Self-check */ 1155 1156 /* Check that read returns -1 */ 1157 nr = lsquic_stream_read(stream, buf, sizeof(buf)); 1158 assert(nr == -1); 1159 1160 /* DATA frame was consumed: */ 1161 assert(stream->read_offset == 5); 1162 1163 lsquic_stream_destroy(stream); 1164 deinit_test_objs(&tobjs); 1165 1166 stream_ctor_flags &= ~SCF_IETF; 1167} 1168 1169 1170int 1171main (int argc, char **argv) 1172{ 1173 const char *fuzz_input = NULL; 1174 int opt, add_one_more; 1175 unsigned n_packets, extra_sz; 1176 1177 lsquic_global_init(LSQUIC_GLOBAL_SERVER); 1178 1179 while (-1 != (opt = getopt(argc, argv, "f:l:"))) 1180 { 1181 switch (opt) 1182 { 1183 case 'f': 1184 fuzz_input = optarg; 1185 break; 1186 case 'l': 1187 lsquic_log_to_fstream(stderr, 0); 1188 lsquic_logger_lopt(optarg); 1189 break; 1190 default: 1191 exit(1); 1192 } 1193 } 1194 1195 init_test_ctl_settings(&g_ctl_settings); 1196 1197 if (fuzz_input) 1198 fuzz_guided_testing(fuzz_input); 1199 else 1200 { 1201 main_test_hq_framing(); 1202 for (n_packets = 1; n_packets <= 2; ++n_packets) 1203 for (extra_sz = 0; extra_sz <= 2; ++extra_sz) 1204 for (add_one_more = 0; add_one_more <= 1; ++add_one_more) 1205 test_frame_header_split(n_packets, extra_sz, add_one_more); 1206 test_zero_size_frame(); 1207 test_reading_zero_size_data_frame(); 1208 test_reading_zero_size_data_frame_scenario2(); 1209 test_reading_zero_size_data_frame_scenario3(); 1210 } 1211 1212 return 0; 1213} 1214 1215static const char on_being_idle[] = 1216"ON BEING IDLE." 1217"" 1218"Now, this is a subject on which I flatter myself I really am _au fait_." 1219"The gentleman who, when I was young, bathed me at wisdom's font for nine" 1220"guineas a term--no extras--used to say he never knew a boy who could" 1221"do less work in more time; and I remember my poor grandmother once" 1222"incidentally observing, in the course of an instruction upon the use" 1223"of the Prayer-book, that it was highly improbable that I should ever do" 1224"much that I ought not to do, but that she felt convinced beyond a doubt" 1225"that I should leave undone pretty well everything that I ought to do." 1226"" 1227"I am afraid I have somewhat belied half the dear old lady's prophecy." 1228"Heaven help me! I have done a good many things that I ought not to have" 1229"done, in spite of my laziness. But I have fully confirmed the accuracy" 1230"of her judgment so far as neglecting much that I ought not to have" 1231"neglected is concerned. Idling always has been my strong point. I take" 1232"no credit to myself in the matter--it is a gift. Few possess it. There" 1233"are plenty of lazy people and plenty of slow-coaches, but a genuine" 1234"idler is a rarity. He is not a man who slouches about with his hands in" 1235"his pockets. On the contrary, his most startling characteristic is that" 1236"he is always intensely busy." 1237"" 1238"It is impossible to enjoy idling thoroughly unless one has plenty of" 1239"work to do. There is no fun in doing nothing when you have nothing to" 1240"do. Wasting time is merely an occupation then, and a most exhausting" 1241"one. Idleness, like kisses, to be sweet must be stolen." 1242"" 1243"Many years ago, when I was a young man, I was taken very ill--I never" 1244"could see myself that much was the matter with me, except that I had" 1245"a beastly cold. But I suppose it was something very serious, for the" 1246"doctor said that I ought to have come to him a month before, and that" 1247"if it (whatever it was) had gone on for another week he would not have" 1248"answered for the consequences. It is an extraordinary thing, but I" 1249"never knew a doctor called into any case yet but what it transpired" 1250"that another day's delay would have rendered cure hopeless. Our medical" 1251"guide, philosopher, and friend is like the hero in a melodrama--he" 1252"always comes upon the scene just, and only just, in the nick of time. It" 1253"is Providence, that is what it is." 1254"" 1255"Well, as I was saying, I was very ill and was ordered to Buxton for a" 1256"month, with strict injunctions to do nothing whatever all the while" 1257"that I was there. \"Rest is what you require,\" said the doctor, \"perfect" 1258"rest.\"" 1259"" 1260"It seemed a delightful prospect. \"This man evidently understands my" 1261"complaint,\" said I, and I pictured to myself a glorious time--a four" 1262"weeks' _dolce far niente_ with a dash of illness in it. Not too much" 1263"illness, but just illness enough--just sufficient to give it the flavor" 1264"of suffering and make it poetical. I should get up late, sip chocolate," 1265"and have my breakfast in slippers and a dressing-gown. I should lie out" 1266"in the garden in a hammock and read sentimental novels with a melancholy" 1267"ending, until the books should fall from my listless hand, and I should" 1268"recline there, dreamily gazing into the deep blue of the firmament," 1269"watching the fleecy clouds floating like white-sailed ships across" 1270"its depths, and listening to the joyous song of the birds and the low" 1271"rustling of the trees. Or, on becoming too weak to go out of doors," 1272"I should sit propped up with pillows at the open window of the" 1273"ground-floor front, and look wasted and interesting, so that all the" 1274"pretty girls would sigh as they passed by." 1275"" 1276"And twice a day I should go down in a Bath chair to the Colonnade to" 1277"drink the waters. Oh, those waters! I knew nothing about them then," 1278"and was rather taken with the idea. \"Drinking the waters\" sounded" 1279"fashionable and Queen Anne-fied, and I thought I should like them. But," 1280"ugh! after the first three or four mornings! Sam Weller's description of" 1281"them as \"having a taste of warm flat-irons\" conveys only a faint idea of" 1282"their hideous nauseousness. If anything could make a sick man get well" 1283"quickly, it would be the knowledge that he must drink a glassful of them" 1284"every day until he was recovered. I drank them neat for six consecutive" 1285"days, and they nearly killed me; but after then I adopted the plan of" 1286"taking a stiff glass of brandy-and-water immediately on the top of them," 1287"and found much relief thereby. I have been informed since, by various" 1288"eminent medical gentlemen, that the alcohol must have entirely" 1289"counteracted the effects of the chalybeate properties contained in the" 1290"water. I am glad I was lucky enough to hit upon the right thing." 1291; 1292 1293static void 1294init_buf (void *buf, size_t sz) 1295{ 1296 unsigned char *p = buf; 1297 unsigned char *const end = (unsigned char*)buf + sz; 1298 size_t n; 1299 1300 while (p < end) 1301 { 1302 n = end - p; 1303 if (sizeof(on_being_idle) - 1 < n) 1304 n = sizeof(on_being_idle) - 1; 1305 memcpy(p, on_being_idle, n); 1306 p +=n; 1307 } 1308 1309 assert(p == end); 1310} 1311