lsquic_http1x_if.c revision 2d296031
1/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */ 2#include <assert.h> 3#include <ctype.h> 4#include <stddef.h> 5#include <stdlib.h> 6#include <string.h> 7 8#include "lsquic.h" 9#include "lsquic_headers.h" 10#include "lsquic_http1x_if.h" 11#include "lshpack.h" 12 13#define LSQUIC_LOGGER_MODULE LSQLM_HTTP1X 14#define LSQUIC_LOG_CONN_ID hwc->hwc_cid 15#include "lsquic_logger.h" 16 17enum pseudo_header 18{ 19 PSEH_METHOD, 20 PSEH_SCHEME, 21 PSEH_AUTHORITY, 22 PSEH_PATH, 23 PSEH_STATUS, 24 N_PSEH 25}; 26 27#define BIT(x) (1 << (x)) 28 29#define ALL_REQUEST_PSEH (BIT(PSEH_METHOD)|BIT(PSEH_SCHEME)|BIT(PSEH_AUTHORITY)|BIT(PSEH_PATH)) 30#define REQUIRED_REQUEST_PSEH (BIT(PSEH_METHOD)|BIT(PSEH_SCHEME)|BIT(PSEH_PATH)) 31 32#define ALL_SERVER_PSEH BIT(PSEH_STATUS) 33#define REQUIRED_SERVER_PSEH ALL_SERVER_PSEH 34 35#define PSEH_LEN(h) (sizeof(#h) - 5) 36 37struct header_writer_ctx 38{ 39 lsquic_cid_t hwc_cid; 40 char *buf; 41 char *cookie_val; 42 unsigned cookie_sz, cookie_nalloc; 43 unsigned max_headers_sz, 44 headers_sz, 45 w_off; 46 enum { 47 HWC_SERVER = 1 << 0, 48 HWC_EXPECT_COLON = 1 << 1, 49 HWC_SEEN_HOST = 1 << 2, 50 HWC_PUSH_PROMISE = 1 << 3, 51 } hwc_flags; 52 enum pseudo_header pseh_mask; 53 char *pseh_bufs[N_PSEH]; 54 struct http1x_headers hwc_h1h; 55}; 56 57 58#define HWC_PSEH_LEN(hwc, ph) ((int) strlen((hwc)->pseh_bufs[ph])) 59 60#define HWC_PSEH_VAL(hwc, ph) ((hwc)->pseh_bufs[ph]) 61 62static void * 63h1h_create_header_set (void *ctx, int is_push_promise) 64{ 65 const struct http1x_ctor_ctx *hcc = ctx; 66 struct header_writer_ctx *hwc; 67 68 hwc = calloc(1, sizeof(*hwc)); 69 if (!hwc) 70 return NULL; 71 72 hwc->hwc_flags = HWC_EXPECT_COLON; 73 if (hcc->is_server) 74 hwc->hwc_flags |= HWC_SERVER; 75 if (is_push_promise) 76 hwc->hwc_flags |= HWC_PUSH_PROMISE; 77 hwc->max_headers_sz = hcc->max_headers_sz; 78 hwc->hwc_cid = hcc->cid; 79 return &hwc->hwc_h1h; 80} 81 82 83static int 84hwc_uh_write (struct header_writer_ctx *hwc, const void *buf, size_t sz) 85{ 86 char *h1h_buf; 87 88 if (hwc->w_off + sz > hwc->headers_sz) 89 { 90 if (hwc->headers_sz * 2 >= hwc->w_off + sz) 91 hwc->headers_sz *= 2; 92 else 93 hwc->headers_sz = hwc->w_off + sz; 94 h1h_buf = realloc(hwc->hwc_h1h.h1h_buf, hwc->headers_sz); 95 if (!h1h_buf) 96 return -1; 97 hwc->hwc_h1h.h1h_buf = h1h_buf; 98 } 99 memcpy(&hwc->hwc_h1h.h1h_buf[hwc->w_off], buf, sz); 100 hwc->w_off += sz; 101 return 0; 102} 103 104 105static enum lsquic_header_status 106save_pseudo_header (struct header_writer_ctx *hwc, enum pseudo_header ph, 107 const char *val, unsigned val_len) 108{ 109 if (0 == (hwc->pseh_mask & BIT(ph))) 110 { 111 assert(!hwc->pseh_bufs[ph]); 112 hwc->pseh_bufs[ph] = malloc(val_len + 1); 113 if (!hwc->pseh_bufs[ph]) 114 return LSQUIC_HDR_ERR_NOMEM; 115 hwc->pseh_mask |= BIT(ph); 116 memcpy(hwc->pseh_bufs[ph], val, val_len); 117 hwc->pseh_bufs[ph][val_len] = '\0'; 118 return LSQUIC_HDR_OK; 119 } 120 else 121 { 122 LSQ_INFO("header %u is already present", ph); 123 return LSQUIC_HDR_ERR_DUPLICATE_PSDO_HDR; 124 } 125} 126 127 128static enum lsquic_header_status 129add_pseudo_header (struct header_writer_ctx *hwc, const char *name, 130 unsigned name_len, const char *val, unsigned val_len) 131{ 132 if (!(hwc->hwc_flags & HWC_EXPECT_COLON)) 133 { 134 LSQ_INFO("unexpected colon"); 135 return LSQUIC_HDR_ERR_MISPLACED_PSDO_HDR; 136 } 137 138 switch (name_len) 139 { 140 case 5: 141 if (0 == memcmp(name, ":path", 5)) 142 return save_pseudo_header(hwc, PSEH_PATH, val, val_len); 143 break; 144 case 7: 145 switch (name[2]) 146 { 147 case 'c': 148 if (0 == memcmp(name, ":scheme", 7)) 149 return save_pseudo_header(hwc, PSEH_SCHEME, val, val_len); 150 break; 151 case 'e': 152 if (0 == memcmp(name, ":method", 7)) 153 return save_pseudo_header(hwc, PSEH_METHOD, val, val_len); 154 break; 155 case 't': 156 if (0 == memcmp(name, ":status", 7)) 157 return save_pseudo_header(hwc, PSEH_STATUS, val, val_len); 158 break; 159 } 160 break; 161 case 10: 162 if (0 == memcmp(name, ":authority", 10)) 163 return save_pseudo_header(hwc, PSEH_AUTHORITY, val, val_len); 164 break; 165 } 166 167 LSQ_INFO("unknown pseudo-header `%.*s'", name_len, name); 168 return LSQUIC_HDR_ERR_UNKNOWN_PSDO_HDR; 169} 170 171 172#define HTTP_CODE_LEN 3 173 174static const char * 175code_str_to_reason (const char code_str[HTTP_CODE_LEN]) 176{ 177 /* RFC 7231, Section 6: */ 178 static const char *const http_reason_phrases[] = 179 { 180 #define HTTP_REASON_CODE(code, reason) [code - 100] = reason 181 HTTP_REASON_CODE(100, "Continue"), 182 HTTP_REASON_CODE(101, "Switching Protocols"), 183 HTTP_REASON_CODE(200, "OK"), 184 HTTP_REASON_CODE(201, "Created"), 185 HTTP_REASON_CODE(202, "Accepted"), 186 HTTP_REASON_CODE(203, "Non-Authoritative Information"), 187 HTTP_REASON_CODE(204, "No Content"), 188 HTTP_REASON_CODE(205, "Reset Content"), 189 HTTP_REASON_CODE(206, "Partial Content"), 190 HTTP_REASON_CODE(300, "Multiple Choices"), 191 HTTP_REASON_CODE(301, "Moved Permanently"), 192 HTTP_REASON_CODE(302, "Found"), 193 HTTP_REASON_CODE(303, "See Other"), 194 HTTP_REASON_CODE(304, "Not Modified"), 195 HTTP_REASON_CODE(305, "Use Proxy"), 196 HTTP_REASON_CODE(307, "Temporary Redirect"), 197 HTTP_REASON_CODE(400, "Bad Request"), 198 HTTP_REASON_CODE(401, "Unauthorized"), 199 HTTP_REASON_CODE(402, "Payment Required"), 200 HTTP_REASON_CODE(403, "Forbidden"), 201 HTTP_REASON_CODE(404, "Not Found"), 202 HTTP_REASON_CODE(405, "Method Not Allowed"), 203 HTTP_REASON_CODE(406, "Not Acceptable"), 204 HTTP_REASON_CODE(407, "Proxy Authentication Required"), 205 HTTP_REASON_CODE(408, "Request Timeout"), 206 HTTP_REASON_CODE(409, "Conflict"), 207 HTTP_REASON_CODE(410, "Gone"), 208 HTTP_REASON_CODE(411, "Length Required"), 209 HTTP_REASON_CODE(412, "Precondition Failed"), 210 HTTP_REASON_CODE(413, "Payload Too Large"), 211 HTTP_REASON_CODE(414, "URI Too Long"), 212 HTTP_REASON_CODE(415, "Unsupported Media Type"), 213 HTTP_REASON_CODE(416, "Range Not Satisfiable"), 214 HTTP_REASON_CODE(417, "Expectation Failed"), 215 HTTP_REASON_CODE(426, "Upgrade Required"), 216 HTTP_REASON_CODE(500, "Internal Server Error"), 217 HTTP_REASON_CODE(501, "Not Implemented"), 218 HTTP_REASON_CODE(502, "Bad Gateway"), 219 HTTP_REASON_CODE(503, "Service Unavailable"), 220 HTTP_REASON_CODE(504, "Gateway Timeout"), 221 HTTP_REASON_CODE(505, "HTTP Version Not Supported"), 222 #undef HTTP_REASON_CODE 223 }; 224 225 long code; 226 char code_buf[HTTP_CODE_LEN + 1]; 227 228 memcpy(code_buf, code_str, HTTP_CODE_LEN); 229 code_buf[HTTP_CODE_LEN] = '\0'; 230 code = strtol(code_buf, NULL, 10) - 100; 231 if (code > 0 && code < (long) (sizeof(http_reason_phrases) / 232 sizeof(http_reason_phrases[0]))) 233 return http_reason_phrases[code]; 234 else 235 return NULL; 236} 237 238 239static enum lsquic_header_status 240convert_response_pseudo_headers (struct header_writer_ctx *hwc) 241{ 242 if ((hwc->pseh_mask & REQUIRED_SERVER_PSEH) != REQUIRED_SERVER_PSEH) 243 { 244 LSQ_INFO("not all response pseudo-headers are specified"); 245 return LSQUIC_HDR_ERR_INCOMPL_RESP_PSDO_HDR; 246 } 247 if (hwc->pseh_mask & ALL_REQUEST_PSEH) 248 { 249 LSQ_INFO("response pseudo-headers contain request-only headers"); 250 return LSQUIC_HDR_ERR_UNNEC_REQ_PSDO_HDR; 251 } 252 253 const char *code_str, *reason; 254 int code_len; 255 256 code_str = HWC_PSEH_VAL(hwc, PSEH_STATUS); 257 code_len = HWC_PSEH_LEN(hwc, PSEH_STATUS); 258 259#define HWC_UH_WRITE(h, buf, sz) do { \ 260 if (0 != hwc_uh_write(h, buf, sz)) \ 261 return LSQUIC_HDR_ERR_NOMEM; \ 262} while (0) 263 264 HWC_UH_WRITE(hwc, "HTTP/1.1 ", 9); 265 HWC_UH_WRITE(hwc, code_str, code_len); 266 if (HTTP_CODE_LEN == code_len && (reason = code_str_to_reason(code_str))) 267 { 268 HWC_UH_WRITE(hwc, " ", 1); 269 HWC_UH_WRITE(hwc, reason, strlen(reason)); 270 HWC_UH_WRITE(hwc, "\r\n", 2); 271 } 272 else 273 HWC_UH_WRITE(hwc, " \r\n", 3); 274 if (hwc->max_headers_sz && hwc->w_off > hwc->max_headers_sz) 275 { 276 LSQ_INFO("headers too large"); 277 return LSQUIC_HDR_ERR_HEADERS_TOO_LARGE; 278 } 279 return LSQUIC_HDR_OK; 280 281#undef HWC_UH_WRITE 282} 283 284 285static enum lsquic_header_status 286convert_request_pseudo_headers (struct header_writer_ctx *hwc) 287{ 288 if ((hwc->pseh_mask & REQUIRED_REQUEST_PSEH) != REQUIRED_REQUEST_PSEH) 289 { 290 LSQ_INFO("not all request pseudo-headers are specified"); 291 return LSQUIC_HDR_ERR_INCOMPL_REQ_PSDO_HDR; 292 } 293 if (hwc->pseh_mask & ALL_SERVER_PSEH) 294 { 295 LSQ_INFO("request pseudo-headers contain response-only headers"); 296 return LSQUIC_HDR_ERR_UNNEC_RESP_PSDO_HDR; 297 } 298 299#define HWC_UH_WRITE(h, buf, sz) do { \ 300 if (0 != hwc_uh_write(h, buf, sz)) \ 301 return LSQUIC_HDR_ERR_NOMEM; \ 302} while (0) 303 304 HWC_UH_WRITE(hwc, HWC_PSEH_VAL(hwc, PSEH_METHOD), HWC_PSEH_LEN(hwc, PSEH_METHOD)); 305 HWC_UH_WRITE(hwc, " ", 1); 306 HWC_UH_WRITE(hwc, HWC_PSEH_VAL(hwc, PSEH_PATH), HWC_PSEH_LEN(hwc, PSEH_PATH)); 307 HWC_UH_WRITE(hwc, " HTTP/1.1\r\n", 11); 308 309 if (hwc->max_headers_sz && hwc->w_off > hwc->max_headers_sz) 310 { 311 LSQ_INFO("headers too large"); 312 return LSQUIC_HDR_ERR_HEADERS_TOO_LARGE; 313 } 314 315 return 0; 316 317#undef HWC_UH_WRITE 318} 319 320 321static enum lsquic_header_status 322convert_pseudo_headers (struct header_writer_ctx *hwc) 323{ 324 /* We are *reading* the message. Thus, a server expects a request, and a 325 * client expects a response. Unless we receive a push promise from the 326 * server, in which case this should also be a request. 327 */ 328 if (hwc->hwc_flags & (HWC_SERVER|HWC_PUSH_PROMISE)) 329 return convert_request_pseudo_headers(hwc); 330 else 331 return convert_response_pseudo_headers(hwc); 332} 333 334 335static enum lsquic_header_status 336save_cookie (struct header_writer_ctx *hwc, const char *val, unsigned val_len) 337{ 338 char *cookie_val; 339 340 if (0 == hwc->cookie_sz) 341 { 342 hwc->cookie_nalloc = hwc->cookie_sz = val_len; 343 cookie_val = malloc(hwc->cookie_nalloc); 344 if (!cookie_val) 345 return LSQUIC_HDR_ERR_NOMEM; 346 hwc->cookie_val = cookie_val; 347 memcpy(hwc->cookie_val, val, val_len); 348 } 349 else 350 { 351 hwc->cookie_sz += val_len + 2 /* "; " */; 352 if (hwc->cookie_sz > hwc->cookie_nalloc) 353 { 354 hwc->cookie_nalloc = hwc->cookie_nalloc * 2 + val_len + 2; 355 cookie_val = realloc(hwc->cookie_val, hwc->cookie_nalloc); 356 if (!cookie_val) 357 return LSQUIC_HDR_ERR_NOMEM; 358 hwc->cookie_val = cookie_val; 359 } 360 memcpy(hwc->cookie_val + hwc->cookie_sz - val_len - 2, "; ", 2); 361 memcpy(hwc->cookie_val + hwc->cookie_sz - val_len, val, val_len); 362 } 363 364 return 0; 365} 366 367 368static enum lsquic_header_status 369add_real_header (struct header_writer_ctx *hwc, const char *name, 370 unsigned name_len, const char *val, unsigned val_len) 371{ 372 enum lsquic_header_status err; 373 unsigned i; 374 int n_upper; 375 376 if (hwc->hwc_flags & HWC_EXPECT_COLON) 377 { 378 if (0 != (err = convert_pseudo_headers(hwc))) 379 return err; 380 hwc->hwc_flags &= ~HWC_EXPECT_COLON; 381 } 382 383 if (4 == name_len && 0 == memcmp(name, "host", 4)) 384 hwc->hwc_flags |= HWC_SEEN_HOST; 385 386 n_upper = 0; 387 for (i = 0; i < name_len; ++i) 388 n_upper += isupper(name[i]); 389 if (n_upper > 0) 390 { 391 LSQ_INFO("Header name `%.*s' contains uppercase letters", 392 name_len, name); 393 return LSQUIC_HDR_ERR_UPPERCASE_HEADER; 394 } 395 396 if (6 == name_len && memcmp(name, "cookie", 6) == 0) 397 { 398 return save_cookie(hwc, val, val_len); 399 } 400 401#define HWC_UH_WRITE(h, buf, sz) do { \ 402 if (0 != hwc_uh_write(h, buf, sz)) \ 403 return LSQUIC_HDR_ERR_NOMEM; \ 404} while (0) 405 406 HWC_UH_WRITE(hwc, name, name_len); 407 HWC_UH_WRITE(hwc, ": ", 2); 408 HWC_UH_WRITE(hwc, val, val_len); 409 HWC_UH_WRITE(hwc, "\r\n", 2); 410 411 if (hwc->max_headers_sz && hwc->w_off > hwc->max_headers_sz) 412 { 413 LSQ_INFO("headers too large"); 414 return LSQUIC_HDR_ERR_HEADERS_TOO_LARGE; 415 } 416 417 return 0; 418 419#undef HWC_UH_WRITE 420} 421 422 423static enum lsquic_header_status 424add_header_to_uh (struct header_writer_ctx *hwc, const char *name, 425 unsigned name_len, const char *val, unsigned val_len) 426{ 427 LSQ_DEBUG("Got header '%.*s': '%.*s'", name_len, name, val_len, val); 428 if (':' == name[0]) 429 return add_pseudo_header(hwc, name, name_len, val, val_len); 430 else 431 return add_real_header(hwc, name, name_len, val, val_len); 432} 433 434 435static enum lsquic_header_status 436h1h_finish_hset (struct header_writer_ctx *hwc) 437{ 438 enum lsquic_header_status st; 439 440 if (hwc->hwc_flags & HWC_EXPECT_COLON) 441 { 442 st = convert_pseudo_headers(hwc); 443 if (0 != st) 444 return st; 445 hwc->hwc_flags &= ~HWC_EXPECT_COLON; 446 } 447 448#define HWC_UH_WRITE(h, buf, sz) do { \ 449 st = hwc_uh_write(h, buf, sz); \ 450 if (0 != st) \ 451 return st; \ 452} while (0) 453 454 if ((hwc->pseh_mask & BIT(PSEH_AUTHORITY)) && 455 0 == (hwc->hwc_flags & HWC_SEEN_HOST)) 456 { 457 LSQ_DEBUG("Setting 'Host: %.*s'", HWC_PSEH_LEN(hwc, PSEH_AUTHORITY), 458 HWC_PSEH_VAL(hwc, PSEH_AUTHORITY)); 459 HWC_UH_WRITE(hwc, "Host: ", 6); 460 HWC_UH_WRITE(hwc, HWC_PSEH_VAL(hwc, PSEH_AUTHORITY), 461 HWC_PSEH_LEN(hwc, PSEH_AUTHORITY)); 462 HWC_UH_WRITE(hwc, "\r\n", 2); 463 } 464 465 if (hwc->cookie_val) 466 { 467 LSQ_DEBUG("Setting 'Cookie: %.*s'", hwc->cookie_sz, hwc->cookie_val); 468 HWC_UH_WRITE(hwc, "Cookie: ", 8); 469 HWC_UH_WRITE(hwc, hwc->cookie_val, hwc->cookie_sz); 470 HWC_UH_WRITE(hwc, "\r\n", 2); 471 } 472 473 HWC_UH_WRITE(hwc, "\r\n", 2 + 1 /* NUL byte */); 474 hwc->w_off -= 1; /* Do not count NUL byte */ 475 hwc->hwc_h1h.h1h_size = hwc->w_off; 476 477 if (hwc->max_headers_sz && hwc->w_off > hwc->max_headers_sz) 478 { 479 LSQ_INFO("headers too large"); 480 return LSQUIC_HDR_ERR_HEADERS_TOO_LARGE; 481 } 482 483 return LSQUIC_HDR_OK; 484} 485 486#define HWC_PTR(data_in) (struct header_writer_ctx *) \ 487 ((unsigned char *) (hset) - offsetof(struct header_writer_ctx, hwc_h1h)) 488 489static enum lsquic_header_status 490h1h_process_header (void *hset, unsigned name_idx, 491 const char *name, unsigned name_len, 492 const char *value, unsigned value_len) 493{ 494 struct header_writer_ctx *const hwc = HWC_PTR(hset); 495 if (name) 496 return add_header_to_uh(hwc, name, name_len, value, value_len); 497 else 498 return h1h_finish_hset(hwc); 499} 500 501 502static void 503h1h_discard_header_set (void *hset) 504{ 505 struct header_writer_ctx *const hwc = HWC_PTR(hset); 506 unsigned i; 507 508 for (i = 0; i < sizeof(hwc->pseh_bufs) / sizeof(hwc->pseh_bufs[0]); ++i) 509 if (hwc->pseh_bufs[i]) 510 free(hwc->pseh_bufs[i]); 511 if (hwc->cookie_val) 512 free(hwc->cookie_val); 513 free(hwc->hwc_h1h.h1h_buf); 514 free(hwc); 515} 516 517 518static const struct lsquic_hset_if http1x_if = 519{ 520 .hsi_create_header_set = h1h_create_header_set, 521 .hsi_process_header = h1h_process_header, 522 .hsi_discard_header_set = h1h_discard_header_set, 523}; 524 525const struct lsquic_hset_if *const lsquic_http1x_if = &http1x_if; 526