headers.c (13446B)
1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 * SPDX-License-Identifier: curl 22 * 23 ***************************************************************************/ 24 25 #include "curl_setup.h" 26 27 #include "urldata.h" 28 #include "strdup.h" 29 #include "sendf.h" 30 #include "headers.h" 31 #include "curlx/strparse.h" 32 33 /* The last 3 #include files should be in this order */ 34 #include "curl_printf.h" 35 #include "curl_memory.h" 36 #include "memdebug.h" 37 38 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HEADERS_API) 39 40 /* Generate the curl_header struct for the user. This function MUST assign all 41 struct fields in the output struct. */ 42 static void copy_header_external(struct Curl_header_store *hs, 43 size_t index, 44 size_t amount, 45 struct Curl_llist_node *e, 46 struct curl_header *hout) 47 { 48 struct curl_header *h = hout; 49 h->name = hs->name; 50 h->value = hs->value; 51 h->amount = amount; 52 h->index = index; 53 /* this will randomly OR a reserved bit for the sole purpose of making it 54 impossible for applications to do == comparisons, as that would otherwise 55 be very tempting and then lead to the reserved bits not being reserved 56 anymore. */ 57 h->origin = (unsigned int)(hs->type | (1 << 27)); 58 h->anchor = e; 59 } 60 61 /* public API */ 62 CURLHcode curl_easy_header(CURL *easy, 63 const char *name, 64 size_t nameindex, 65 unsigned int type, 66 int request, 67 struct curl_header **hout) 68 { 69 struct Curl_llist_node *e; 70 struct Curl_llist_node *e_pick = NULL; 71 struct Curl_easy *data = easy; 72 size_t match = 0; 73 size_t amount = 0; 74 struct Curl_header_store *hs = NULL; 75 struct Curl_header_store *pick = NULL; 76 if(!name || !hout || !data || 77 (type > (CURLH_HEADER|CURLH_TRAILER|CURLH_CONNECT|CURLH_1XX| 78 CURLH_PSEUDO)) || !type || (request < -1)) 79 return CURLHE_BAD_ARGUMENT; 80 if(!Curl_llist_count(&data->state.httphdrs)) 81 return CURLHE_NOHEADERS; /* no headers available */ 82 if(request > data->state.requests) 83 return CURLHE_NOREQUEST; 84 if(request == -1) 85 request = data->state.requests; 86 87 /* we need a first round to count amount of this header */ 88 for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) { 89 hs = Curl_node_elem(e); 90 if(curl_strequal(hs->name, name) && 91 (hs->type & type) && 92 (hs->request == request)) { 93 amount++; 94 pick = hs; 95 e_pick = e; 96 } 97 } 98 if(!amount) 99 return CURLHE_MISSING; 100 else if(nameindex >= amount) 101 return CURLHE_BADINDEX; 102 103 if(nameindex == amount - 1) 104 /* if the last or only occurrence is what's asked for, then we know it */ 105 hs = pick; 106 else { 107 for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) { 108 hs = Curl_node_elem(e); 109 if(curl_strequal(hs->name, name) && 110 (hs->type & type) && 111 (hs->request == request) && 112 (match++ == nameindex)) { 113 e_pick = e; 114 break; 115 } 116 } 117 if(!e) /* this should not happen */ 118 return CURLHE_MISSING; 119 } 120 /* this is the name we want */ 121 copy_header_external(hs, nameindex, amount, e_pick, 122 &data->state.headerout[0]); 123 *hout = &data->state.headerout[0]; 124 return CURLHE_OK; 125 } 126 127 /* public API */ 128 struct curl_header *curl_easy_nextheader(CURL *easy, 129 unsigned int type, 130 int request, 131 struct curl_header *prev) 132 { 133 struct Curl_easy *data = easy; 134 struct Curl_llist_node *pick; 135 struct Curl_llist_node *e; 136 struct Curl_header_store *hs; 137 size_t amount = 0; 138 size_t index = 0; 139 140 if(request > data->state.requests) 141 return NULL; 142 if(request == -1) 143 request = data->state.requests; 144 145 if(prev) { 146 pick = prev->anchor; 147 if(!pick) 148 /* something is wrong */ 149 return NULL; 150 pick = Curl_node_next(pick); 151 } 152 else 153 pick = Curl_llist_head(&data->state.httphdrs); 154 155 if(pick) { 156 /* make sure it is the next header of the desired type */ 157 do { 158 hs = Curl_node_elem(pick); 159 if((hs->type & type) && (hs->request == request)) 160 break; 161 pick = Curl_node_next(pick); 162 } while(pick); 163 } 164 165 if(!pick) 166 /* no more headers available */ 167 return NULL; 168 169 hs = Curl_node_elem(pick); 170 171 /* count number of occurrences of this name within the mask and figure out 172 the index for the currently selected entry */ 173 for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) { 174 struct Curl_header_store *check = Curl_node_elem(e); 175 if(curl_strequal(hs->name, check->name) && 176 (check->request == request) && 177 (check->type & type)) 178 amount++; 179 if(e == pick) 180 index = amount - 1; 181 } 182 183 copy_header_external(hs, index, amount, pick, 184 &data->state.headerout[1]); 185 return &data->state.headerout[1]; 186 } 187 188 static CURLcode namevalue(char *header, size_t hlen, unsigned int type, 189 char **name, char **value) 190 { 191 char *end = header + hlen - 1; /* point to the last byte */ 192 DEBUGASSERT(hlen); 193 *name = header; 194 195 if(type == CURLH_PSEUDO) { 196 if(*header != ':') 197 return CURLE_BAD_FUNCTION_ARGUMENT; 198 header++; 199 } 200 201 /* Find the end of the header name */ 202 while(*header && (*header != ':')) 203 ++header; 204 205 if(*header) 206 /* Skip over colon, null it */ 207 *header++ = 0; 208 else 209 return CURLE_BAD_FUNCTION_ARGUMENT; 210 211 /* skip all leading blank letters */ 212 while(ISBLANK(*header)) 213 header++; 214 215 *value = header; 216 217 /* skip all trailing space letters */ 218 while((end > header) && ISBLANK(*end)) 219 *end-- = 0; /* null-terminate */ 220 return CURLE_OK; 221 } 222 223 static CURLcode unfold_value(struct Curl_easy *data, const char *value, 224 size_t vlen) /* length of the incoming header */ 225 { 226 struct Curl_header_store *hs; 227 struct Curl_header_store *newhs; 228 size_t olen; /* length of the old value */ 229 size_t oalloc; /* length of the old name + value + separator */ 230 size_t offset; 231 DEBUGASSERT(data->state.prevhead); 232 hs = data->state.prevhead; 233 olen = strlen(hs->value); 234 offset = hs->value - hs->buffer; 235 oalloc = olen + offset + 1; 236 237 /* skip all trailing space letters */ 238 while(vlen && ISBLANK(value[vlen - 1])) 239 vlen--; 240 241 /* save only one leading space */ 242 while((vlen > 1) && ISBLANK(value[0]) && ISBLANK(value[1])) { 243 vlen--; 244 value++; 245 } 246 247 /* since this header block might move in the realloc below, it needs to 248 first be unlinked from the list and then re-added again after the 249 realloc */ 250 Curl_node_remove(&hs->node); 251 252 /* new size = struct + new value length + old name+value length */ 253 newhs = Curl_saferealloc(hs, sizeof(*hs) + vlen + oalloc + 1); 254 if(!newhs) 255 return CURLE_OUT_OF_MEMORY; 256 /* ->name and ->value point into ->buffer (to keep the header allocation 257 in a single memory block), which now potentially have moved. Adjust 258 them. */ 259 newhs->name = newhs->buffer; 260 newhs->value = &newhs->buffer[offset]; 261 262 /* put the data at the end of the previous data, not the newline */ 263 memcpy(&newhs->value[olen], value, vlen); 264 newhs->value[olen + vlen] = 0; /* null-terminate at newline */ 265 266 /* insert this node into the list of headers */ 267 Curl_llist_append(&data->state.httphdrs, newhs, &newhs->node); 268 data->state.prevhead = newhs; 269 return CURLE_OK; 270 } 271 272 273 /* 274 * Curl_headers_push() gets passed a full HTTP header to store. It gets called 275 * immediately before the header callback. The header is CRLF terminated. 276 */ 277 CURLcode Curl_headers_push(struct Curl_easy *data, const char *header, 278 unsigned char type) 279 { 280 char *value = NULL; 281 char *name = NULL; 282 char *end; 283 size_t hlen; /* length of the incoming header */ 284 struct Curl_header_store *hs; 285 CURLcode result = CURLE_OUT_OF_MEMORY; 286 287 if((header[0] == '\r') || (header[0] == '\n')) 288 /* ignore the body separator */ 289 return CURLE_OK; 290 291 end = strchr(header, '\r'); 292 if(!end) { 293 end = strchr(header, '\n'); 294 if(!end) 295 /* neither CR nor LF as terminator is not a valid header */ 296 return CURLE_WEIRD_SERVER_REPLY; 297 } 298 hlen = end - header; 299 300 if((header[0] == ' ') || (header[0] == '\t')) { 301 if(data->state.prevhead) 302 /* line folding, append value to the previous header's value */ 303 return unfold_value(data, header, hlen); 304 else { 305 /* cannot unfold without a previous header. Instead of erroring, just 306 pass the leading blanks. */ 307 while(hlen && ISBLANK(*header)) { 308 header++; 309 hlen--; 310 } 311 if(!hlen) 312 return CURLE_WEIRD_SERVER_REPLY; 313 } 314 } 315 if(Curl_llist_count(&data->state.httphdrs) >= MAX_HTTP_RESP_HEADER_COUNT) { 316 failf(data, "Too many response headers, %d is max", 317 MAX_HTTP_RESP_HEADER_COUNT); 318 return CURLE_TOO_LARGE; 319 } 320 321 hs = calloc(1, sizeof(*hs) + hlen); 322 if(!hs) 323 return CURLE_OUT_OF_MEMORY; 324 memcpy(hs->buffer, header, hlen); 325 hs->buffer[hlen] = 0; /* null-terminate */ 326 327 result = namevalue(hs->buffer, hlen, type, &name, &value); 328 if(!result) { 329 hs->name = name; 330 hs->value = value; 331 hs->type = type; 332 hs->request = data->state.requests; 333 334 /* insert this node into the list of headers */ 335 Curl_llist_append(&data->state.httphdrs, hs, &hs->node); 336 data->state.prevhead = hs; 337 } 338 else { 339 failf(data, "Invalid response header"); 340 free(hs); 341 } 342 return result; 343 } 344 345 /* 346 * Curl_headers_reset(). Reset the headers subsystem. 347 */ 348 static void headers_reset(struct Curl_easy *data) 349 { 350 Curl_llist_init(&data->state.httphdrs, NULL); 351 data->state.prevhead = NULL; 352 } 353 354 struct hds_cw_collect_ctx { 355 struct Curl_cwriter super; 356 }; 357 358 static CURLcode hds_cw_collect_write(struct Curl_easy *data, 359 struct Curl_cwriter *writer, int type, 360 const char *buf, size_t blen) 361 { 362 if((type & CLIENTWRITE_HEADER) && !(type & CLIENTWRITE_STATUS)) { 363 unsigned char htype = (unsigned char) 364 (type & CLIENTWRITE_CONNECT ? CURLH_CONNECT : 365 (type & CLIENTWRITE_1XX ? CURLH_1XX : 366 (type & CLIENTWRITE_TRAILER ? CURLH_TRAILER : 367 CURLH_HEADER))); 368 CURLcode result = Curl_headers_push(data, buf, htype); 369 CURL_TRC_WRITE(data, "header_collect pushed(type=%x, len=%zu) -> %d", 370 htype, blen, result); 371 if(result) 372 return result; 373 } 374 return Curl_cwriter_write(data, writer->next, type, buf, blen); 375 } 376 377 static const struct Curl_cwtype hds_cw_collect = { 378 "hds-collect", 379 NULL, 380 Curl_cwriter_def_init, 381 hds_cw_collect_write, 382 Curl_cwriter_def_close, 383 sizeof(struct hds_cw_collect_ctx) 384 }; 385 386 CURLcode Curl_headers_init(struct Curl_easy *data) 387 { 388 struct Curl_cwriter *writer; 389 CURLcode result; 390 391 if(data->conn && (data->conn->handler->protocol & PROTO_FAMILY_HTTP)) { 392 /* avoid installing it twice */ 393 if(Curl_cwriter_get_by_name(data, hds_cw_collect.name)) 394 return CURLE_OK; 395 396 result = Curl_cwriter_create(&writer, data, &hds_cw_collect, 397 CURL_CW_PROTOCOL); 398 if(result) 399 return result; 400 401 result = Curl_cwriter_add(data, writer); 402 if(result) { 403 Curl_cwriter_free(data, writer); 404 return result; 405 } 406 } 407 return CURLE_OK; 408 } 409 410 /* 411 * Curl_headers_cleanup(). Free all stored headers and associated memory. 412 */ 413 CURLcode Curl_headers_cleanup(struct Curl_easy *data) 414 { 415 struct Curl_llist_node *e; 416 struct Curl_llist_node *n; 417 418 for(e = Curl_llist_head(&data->state.httphdrs); e; e = n) { 419 struct Curl_header_store *hs = Curl_node_elem(e); 420 n = Curl_node_next(e); 421 free(hs); 422 } 423 headers_reset(data); 424 return CURLE_OK; 425 } 426 427 #else /* HTTP-disabled builds below */ 428 429 CURLHcode curl_easy_header(CURL *easy, 430 const char *name, 431 size_t index, 432 unsigned int origin, 433 int request, 434 struct curl_header **hout) 435 { 436 (void)easy; 437 (void)name; 438 (void)index; 439 (void)origin; 440 (void)request; 441 (void)hout; 442 return CURLHE_NOT_BUILT_IN; 443 } 444 445 struct curl_header *curl_easy_nextheader(CURL *easy, 446 unsigned int type, 447 int request, 448 struct curl_header *prev) 449 { 450 (void)easy; 451 (void)type; 452 (void)request; 453 (void)prev; 454 return NULL; 455 } 456 #endif