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 "strcase.h" 30#include "headers.h" 31 32/* The last 3 #include files should be in this order */ 33#include "curl_printf.h" 34#include "curl_memory.h" 35#include "memdebug.h" 36 37#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HEADERS_API) 38 39/* Generate the curl_header struct for the user. This function MUST assign all 40 struct fields in the output struct. */ 41static void copy_header_external(struct Curl_header_store *hs, 42 size_t index, 43 size_t amount, 44 struct Curl_llist_element *e, 45 struct curl_header *hout) 46{ 47 struct curl_header *h = hout; 48 h->name = hs->name; 49 h->value = hs->value; 50 h->amount = amount; 51 h->index = index; 52 /* this will randomly OR a reserved bit for the sole purpose of making it 53 impossible for applications to do == comparisons, as that would otherwise 54 be very tempting and then lead to the reserved bits not being reserved 55 anymore. */ 56 h->origin = hs->type | (1<<27); 57 h->anchor = e; 58} 59 60/* public API */ 61CURLHcode curl_easy_header(CURL *easy, 62 const char *name, 63 size_t nameindex, 64 unsigned int type, 65 int request, 66 struct curl_header **hout) 67{ 68 struct Curl_llist_element *e; 69 struct Curl_llist_element *e_pick = NULL; 70 struct Curl_easy *data = easy; 71 size_t match = 0; 72 size_t amount = 0; 73 struct Curl_header_store *hs = NULL; 74 struct Curl_header_store *pick = NULL; 75 if(!name || !hout || !data || 76 (type > (CURLH_HEADER|CURLH_TRAILER|CURLH_CONNECT|CURLH_1XX| 77 CURLH_PSEUDO)) || !type || (request < -1)) 78 return CURLHE_BAD_ARGUMENT; 79 if(!Curl_llist_count(&data->state.httphdrs)) 80 return CURLHE_NOHEADERS; /* no headers available */ 81 if(request > data->state.requests) 82 return CURLHE_NOREQUEST; 83 if(request == -1) 84 request = data->state.requests; 85 86 /* we need a first round to count amount of this header */ 87 for(e = data->state.httphdrs.head; e; e = e->next) { 88 hs = e->ptr; 89 if(strcasecompare(hs->name, name) && 90 (hs->type & type) && 91 (hs->request == request)) { 92 amount++; 93 pick = hs; 94 e_pick = e; 95 } 96 } 97 if(!amount) 98 return CURLHE_MISSING; 99 else if(nameindex >= amount) 100 return CURLHE_BADINDEX; 101 102 if(nameindex == amount - 1) 103 /* if the last or only occurrence is what's asked for, then we know it */ 104 hs = pick; 105 else { 106 for(e = data->state.httphdrs.head; e; e = e->next) { 107 hs = e->ptr; 108 if(strcasecompare(hs->name, name) && 109 (hs->type & type) && 110 (hs->request == request) && 111 (match++ == nameindex)) { 112 e_pick = e; 113 break; 114 } 115 } 116 if(!e) /* this shouldn't happen */ 117 return CURLHE_MISSING; 118 } 119 /* this is the name we want */ 120 copy_header_external(hs, nameindex, amount, e_pick, 121 &data->state.headerout[0]); 122 *hout = &data->state.headerout[0]; 123 return CURLHE_OK; 124} 125 126/* public API */ 127struct curl_header *curl_easy_nextheader(CURL *easy, 128 unsigned int type, 129 int request, 130 struct curl_header *prev) 131{ 132 struct Curl_easy *data = easy; 133 struct Curl_llist_element *pick; 134 struct Curl_llist_element *e; 135 struct Curl_header_store *hs; 136 size_t amount = 0; 137 size_t index = 0; 138 139 if(request > data->state.requests) 140 return NULL; 141 if(request == -1) 142 request = data->state.requests; 143 144 if(prev) { 145 pick = prev->anchor; 146 if(!pick) 147 /* something is wrong */ 148 return NULL; 149 pick = pick->next; 150 } 151 else 152 pick = data->state.httphdrs.head; 153 154 if(pick) { 155 /* make sure it is the next header of the desired type */ 156 do { 157 hs = pick->ptr; 158 if((hs->type & type) && (hs->request == request)) 159 break; 160 pick = pick->next; 161 } while(pick); 162 } 163 164 if(!pick) 165 /* no more headers available */ 166 return NULL; 167 168 hs = pick->ptr; 169 170 /* count number of occurrences of this name within the mask and figure out 171 the index for the currently selected entry */ 172 for(e = data->state.httphdrs.head; e; e = e->next) { 173 struct Curl_header_store *check = e->ptr; 174 if(strcasecompare(hs->name, check->name) && 175 (check->request == request) && 176 (check->type & type)) 177 amount++; 178 if(e == pick) 179 index = amount - 1; 180 } 181 182 copy_header_external(hs, index, amount, pick, 183 &data->state.headerout[1]); 184 return &data->state.headerout[1]; 185} 186 187static CURLcode namevalue(char *header, size_t hlen, unsigned int type, 188 char **name, char **value) 189{ 190 char *end = header + hlen - 1; /* point to the last byte */ 191 DEBUGASSERT(hlen); 192 *name = header; 193 194 if(type == CURLH_PSEUDO) { 195 if(*header != ':') 196 return CURLE_BAD_FUNCTION_ARGUMENT; 197 header++; 198 } 199 200 /* Find the end of the header name */ 201 while(*header && (*header != ':')) 202 ++header; 203 204 if(*header) 205 /* Skip over colon, null it */ 206 *header++ = 0; 207 else 208 return CURLE_BAD_FUNCTION_ARGUMENT; 209 210 /* skip all leading space letters */ 211 while(*header && ISBLANK(*header)) 212 header++; 213 214 *value = header; 215 216 /* skip all trailing space letters */ 217 while((end > header) && ISSPACE(*end)) 218 *end-- = 0; /* nul terminate */ 219 return CURLE_OK; 220} 221 222static CURLcode unfold_value(struct Curl_easy *data, const char *value, 223 size_t vlen) /* length of the incoming header */ 224{ 225 struct Curl_header_store *hs; 226 struct Curl_header_store *newhs; 227 size_t olen; /* length of the old value */ 228 size_t oalloc; /* length of the old name + value + separator */ 229 size_t offset; 230 DEBUGASSERT(data->state.prevhead); 231 hs = data->state.prevhead; 232 olen = strlen(hs->value); 233 offset = hs->value - hs->buffer; 234 oalloc = olen + offset + 1; 235 236 /* skip all trailing space letters */ 237 while(vlen && ISSPACE(value[vlen - 1])) 238 vlen--; 239 240 /* save only one leading space */ 241 while((vlen > 1) && ISBLANK(value[0]) && ISBLANK(value[1])) { 242 vlen--; 243 value++; 244 } 245 246 /* since this header block might move in the realloc below, it needs to 247 first be unlinked from the list and then re-added again after the 248 realloc */ 249 Curl_llist_remove(&data->state.httphdrs, &hs->node, NULL); 250 251 /* new size = struct + new value length + old name+value length */ 252 newhs = Curl_saferealloc(hs, sizeof(*hs) + vlen + oalloc + 1); 253 if(!newhs) 254 return CURLE_OUT_OF_MEMORY; 255 /* ->name' and ->value point into ->buffer (to keep the header allocation 256 in a single memory block), which now potentially have moved. Adjust 257 them. */ 258 newhs->name = newhs->buffer; 259 newhs->value = &newhs->buffer[offset]; 260 261 /* put the data at the end of the previous data, not the newline */ 262 memcpy(&newhs->value[olen], value, vlen); 263 newhs->value[olen + vlen] = 0; /* null-terminate at newline */ 264 265 /* insert this node into the list of headers */ 266 Curl_llist_insert_next(&data->state.httphdrs, data->state.httphdrs.tail, 267 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 */ 277CURLcode 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 /* Can't 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 316 hs = calloc(1, sizeof(*hs) + hlen); 317 if(!hs) 318 return CURLE_OUT_OF_MEMORY; 319 memcpy(hs->buffer, header, hlen); 320 hs->buffer[hlen] = 0; /* nul terminate */ 321 322 result = namevalue(hs->buffer, hlen, type, &name, &value); 323 if(!result) { 324 hs->name = name; 325 hs->value = value; 326 hs->type = type; 327 hs->request = data->state.requests; 328 329 /* insert this node into the list of headers */ 330 Curl_llist_insert_next(&data->state.httphdrs, data->state.httphdrs.tail, 331 hs, &hs->node); 332 data->state.prevhead = hs; 333 } 334 else 335 free(hs); 336 return result; 337} 338 339/* 340 * Curl_headers_init(). Init the headers subsystem. 341 */ 342static void headers_init(struct Curl_easy *data) 343{ 344 Curl_llist_init(&data->state.httphdrs, NULL); 345 data->state.prevhead = NULL; 346} 347 348/* 349 * Curl_headers_cleanup(). Free all stored headers and associated memory. 350 */ 351CURLcode Curl_headers_cleanup(struct Curl_easy *data) 352{ 353 struct Curl_llist_element *e; 354 struct Curl_llist_element *n; 355 356 for(e = data->state.httphdrs.head; e; e = n) { 357 struct Curl_header_store *hs = e->ptr; 358 n = e->next; 359 free(hs); 360 } 361 headers_init(data); 362 return CURLE_OK; 363} 364 365#else /* HTTP-disabled builds below */ 366 367CURLHcode curl_easy_header(CURL *easy, 368 const char *name, 369 size_t index, 370 unsigned int origin, 371 int request, 372 struct curl_header **hout) 373{ 374 (void)easy; 375 (void)name; 376 (void)index; 377 (void)origin; 378 (void)request; 379 (void)hout; 380 return CURLHE_NOT_BUILT_IN; 381} 382 383struct curl_header *curl_easy_nextheader(CURL *easy, 384 unsigned int type, 385 int request, 386 struct curl_header *prev) 387{ 388 (void)easy; 389 (void)type; 390 (void)request; 391 (void)prev; 392 return NULL; 393} 394#endif 395