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#include "dynhds.h" 27#include "strcase.h" 28 29/* The last 3 #include files should be in this order */ 30#ifdef USE_NGHTTP2 31#include <stdint.h> 32#include <nghttp2/nghttp2.h> 33#endif /* USE_NGHTTP2 */ 34#include "curl_printf.h" 35#include "curl_memory.h" 36#include "memdebug.h" 37 38 39static struct dynhds_entry * 40entry_new(const char *name, size_t namelen, 41 const char *value, size_t valuelen, int opts) 42{ 43 struct dynhds_entry *e; 44 char *p; 45 46 DEBUGASSERT(name); 47 DEBUGASSERT(value); 48 e = calloc(1, sizeof(*e) + namelen + valuelen + 2); 49 if(!e) 50 return NULL; 51 e->name = p = ((char *)e) + sizeof(*e); 52 memcpy(p, name, namelen); 53 e->namelen = namelen; 54 e->value = p += namelen + 1; /* leave a \0 at the end of name */ 55 memcpy(p, value, valuelen); 56 e->valuelen = valuelen; 57 if(opts & DYNHDS_OPT_LOWERCASE) 58 Curl_strntolower(e->name, e->name, e->namelen); 59 return e; 60} 61 62static struct dynhds_entry * 63entry_append(struct dynhds_entry *e, 64 const char *value, size_t valuelen) 65{ 66 struct dynhds_entry *e2; 67 size_t valuelen2 = e->valuelen + 1 + valuelen; 68 char *p; 69 70 DEBUGASSERT(value); 71 e2 = calloc(1, sizeof(*e) + e->namelen + valuelen2 + 2); 72 if(!e2) 73 return NULL; 74 e2->name = p = ((char *)e2) + sizeof(*e2); 75 memcpy(p, e->name, e->namelen); 76 e2->namelen = e->namelen; 77 e2->value = p += e->namelen + 1; /* leave a \0 at the end of name */ 78 memcpy(p, e->value, e->valuelen); 79 p += e->valuelen; 80 p[0] = ' '; 81 memcpy(p + 1, value, valuelen); 82 e2->valuelen = valuelen2; 83 return e2; 84} 85 86static void entry_free(struct dynhds_entry *e) 87{ 88 free(e); 89} 90 91void Curl_dynhds_init(struct dynhds *dynhds, size_t max_entries, 92 size_t max_strs_size) 93{ 94 DEBUGASSERT(dynhds); 95 DEBUGASSERT(max_strs_size); 96 dynhds->hds = NULL; 97 dynhds->hds_len = dynhds->hds_allc = dynhds->strs_len = 0; 98 dynhds->max_entries = max_entries; 99 dynhds->max_strs_size = max_strs_size; 100 dynhds->opts = 0; 101} 102 103void Curl_dynhds_free(struct dynhds *dynhds) 104{ 105 DEBUGASSERT(dynhds); 106 if(dynhds->hds && dynhds->hds_len) { 107 size_t i; 108 DEBUGASSERT(dynhds->hds); 109 for(i = 0; i < dynhds->hds_len; ++i) { 110 entry_free(dynhds->hds[i]); 111 } 112 } 113 Curl_safefree(dynhds->hds); 114 dynhds->hds_len = dynhds->hds_allc = dynhds->strs_len = 0; 115} 116 117void Curl_dynhds_reset(struct dynhds *dynhds) 118{ 119 DEBUGASSERT(dynhds); 120 if(dynhds->hds_len) { 121 size_t i; 122 DEBUGASSERT(dynhds->hds); 123 for(i = 0; i < dynhds->hds_len; ++i) { 124 entry_free(dynhds->hds[i]); 125 dynhds->hds[i] = NULL; 126 } 127 } 128 dynhds->hds_len = dynhds->strs_len = 0; 129} 130 131size_t Curl_dynhds_count(struct dynhds *dynhds) 132{ 133 return dynhds->hds_len; 134} 135 136void Curl_dynhds_set_opts(struct dynhds *dynhds, int opts) 137{ 138 dynhds->opts = opts; 139} 140 141struct dynhds_entry *Curl_dynhds_getn(struct dynhds *dynhds, size_t n) 142{ 143 DEBUGASSERT(dynhds); 144 return (n < dynhds->hds_len)? dynhds->hds[n] : NULL; 145} 146 147struct dynhds_entry *Curl_dynhds_get(struct dynhds *dynhds, const char *name, 148 size_t namelen) 149{ 150 size_t i; 151 for(i = 0; i < dynhds->hds_len; ++i) { 152 if(dynhds->hds[i]->namelen == namelen && 153 strncasecompare(dynhds->hds[i]->name, name, namelen)) { 154 return dynhds->hds[i]; 155 } 156 } 157 return NULL; 158} 159 160struct dynhds_entry *Curl_dynhds_cget(struct dynhds *dynhds, const char *name) 161{ 162 return Curl_dynhds_get(dynhds, name, strlen(name)); 163} 164 165CURLcode Curl_dynhds_add(struct dynhds *dynhds, 166 const char *name, size_t namelen, 167 const char *value, size_t valuelen) 168{ 169 struct dynhds_entry *entry = NULL; 170 CURLcode result = CURLE_OUT_OF_MEMORY; 171 172 DEBUGASSERT(dynhds); 173 if(dynhds->max_entries && dynhds->hds_len >= dynhds->max_entries) 174 return CURLE_OUT_OF_MEMORY; 175 if(dynhds->strs_len + namelen + valuelen > dynhds->max_strs_size) 176 return CURLE_OUT_OF_MEMORY; 177 178entry = entry_new(name, namelen, value, valuelen, dynhds->opts); 179 if(!entry) 180 goto out; 181 182 if(dynhds->hds_len + 1 >= dynhds->hds_allc) { 183 size_t nallc = dynhds->hds_len + 16; 184 struct dynhds_entry **nhds; 185 186 if(dynhds->max_entries && nallc > dynhds->max_entries) 187 nallc = dynhds->max_entries; 188 189 nhds = calloc(nallc, sizeof(struct dynhds_entry *)); 190 if(!nhds) 191 goto out; 192 if(dynhds->hds) { 193 memcpy(nhds, dynhds->hds, 194 dynhds->hds_len * sizeof(struct dynhds_entry *)); 195 Curl_safefree(dynhds->hds); 196 } 197 dynhds->hds = nhds; 198 dynhds->hds_allc = nallc; 199 } 200 dynhds->hds[dynhds->hds_len++] = entry; 201 entry = NULL; 202 dynhds->strs_len += namelen + valuelen; 203 result = CURLE_OK; 204 205out: 206 if(entry) 207 entry_free(entry); 208 return result; 209} 210 211CURLcode Curl_dynhds_cadd(struct dynhds *dynhds, 212 const char *name, const char *value) 213{ 214 return Curl_dynhds_add(dynhds, name, strlen(name), value, strlen(value)); 215} 216 217CURLcode Curl_dynhds_h1_add_line(struct dynhds *dynhds, 218 const char *line, size_t line_len) 219{ 220 const char *p; 221 const char *name; 222 size_t namelen; 223 const char *value; 224 size_t valuelen, i; 225 226 if(!line || !line_len) 227 return CURLE_OK; 228 229 if((line[0] == ' ') || (line[0] == '\t')) { 230 struct dynhds_entry *e, *e2; 231 /* header continuation, yikes! */ 232 if(!dynhds->hds_len) 233 return CURLE_BAD_FUNCTION_ARGUMENT; 234 235 while(line_len && ISBLANK(line[0])) { 236 ++line; 237 --line_len; 238 } 239 if(!line_len) 240 return CURLE_BAD_FUNCTION_ARGUMENT; 241 e = dynhds->hds[dynhds->hds_len-1]; 242 e2 = entry_append(e, line, line_len); 243 if(!e2) 244 return CURLE_OUT_OF_MEMORY; 245 dynhds->hds[dynhds->hds_len-1] = e2; 246 entry_free(e); 247 return CURLE_OK; 248 } 249 else { 250 p = memchr(line, ':', line_len); 251 if(!p) 252 return CURLE_BAD_FUNCTION_ARGUMENT; 253 name = line; 254 namelen = p - line; 255 p++; /* move past the colon */ 256 for(i = namelen + 1; i < line_len; ++i, ++p) { 257 if(!ISBLANK(*p)) 258 break; 259 } 260 value = p; 261 valuelen = line_len - i; 262 263 p = memchr(value, '\r', valuelen); 264 if(!p) 265 p = memchr(value, '\n', valuelen); 266 if(p) 267 valuelen = (size_t)(p - value); 268 269 return Curl_dynhds_add(dynhds, name, namelen, value, valuelen); 270 } 271} 272 273CURLcode Curl_dynhds_h1_cadd_line(struct dynhds *dynhds, const char *line) 274{ 275 return Curl_dynhds_h1_add_line(dynhds, line, line? strlen(line) : 0); 276} 277 278#ifdef DEBUGBUILD 279/* used by unit2602.c */ 280 281bool Curl_dynhds_contains(struct dynhds *dynhds, 282 const char *name, size_t namelen) 283{ 284 return !!Curl_dynhds_get(dynhds, name, namelen); 285} 286 287bool Curl_dynhds_ccontains(struct dynhds *dynhds, const char *name) 288{ 289 return Curl_dynhds_contains(dynhds, name, strlen(name)); 290} 291 292size_t Curl_dynhds_count_name(struct dynhds *dynhds, 293 const char *name, size_t namelen) 294{ 295 size_t n = 0; 296 if(dynhds->hds_len) { 297 size_t i; 298 for(i = 0; i < dynhds->hds_len; ++i) { 299 if((namelen == dynhds->hds[i]->namelen) && 300 strncasecompare(name, dynhds->hds[i]->name, namelen)) 301 ++n; 302 } 303 } 304 return n; 305} 306 307size_t Curl_dynhds_ccount_name(struct dynhds *dynhds, const char *name) 308{ 309 return Curl_dynhds_count_name(dynhds, name, strlen(name)); 310} 311 312CURLcode Curl_dynhds_set(struct dynhds *dynhds, 313 const char *name, size_t namelen, 314 const char *value, size_t valuelen) 315{ 316 Curl_dynhds_remove(dynhds, name, namelen); 317 return Curl_dynhds_add(dynhds, name, namelen, value, valuelen); 318} 319 320size_t Curl_dynhds_remove(struct dynhds *dynhds, 321 const char *name, size_t namelen) 322{ 323 size_t n = 0; 324 if(dynhds->hds_len) { 325 size_t i, len; 326 for(i = 0; i < dynhds->hds_len; ++i) { 327 if((namelen == dynhds->hds[i]->namelen) && 328 strncasecompare(name, dynhds->hds[i]->name, namelen)) { 329 ++n; 330 --dynhds->hds_len; 331 dynhds->strs_len -= (dynhds->hds[i]->namelen + 332 dynhds->hds[i]->valuelen); 333 entry_free(dynhds->hds[i]); 334 len = dynhds->hds_len - i; /* remaining entries */ 335 if(len) { 336 memmove(&dynhds->hds[i], &dynhds->hds[i + 1], 337 len * sizeof(dynhds->hds[i])); 338 } 339 --i; /* do this index again */ 340 } 341 } 342 } 343 return n; 344} 345 346size_t Curl_dynhds_cremove(struct dynhds *dynhds, const char *name) 347{ 348 return Curl_dynhds_remove(dynhds, name, strlen(name)); 349} 350 351#endif 352 353CURLcode Curl_dynhds_h1_dprint(struct dynhds *dynhds, struct dynbuf *dbuf) 354{ 355 CURLcode result = CURLE_OK; 356 size_t i; 357 358 if(!dynhds->hds_len) 359 return result; 360 361 for(i = 0; i < dynhds->hds_len; ++i) { 362 result = Curl_dyn_addf(dbuf, "%.*s: %.*s\r\n", 363 (int)dynhds->hds[i]->namelen, dynhds->hds[i]->name, 364 (int)dynhds->hds[i]->valuelen, dynhds->hds[i]->value); 365 if(result) 366 break; 367 } 368 369 return result; 370} 371 372#ifdef USE_NGHTTP2 373 374nghttp2_nv *Curl_dynhds_to_nva(struct dynhds *dynhds, size_t *pcount) 375{ 376 nghttp2_nv *nva = calloc(1, sizeof(nghttp2_nv) * dynhds->hds_len); 377 size_t i; 378 379 *pcount = 0; 380 if(!nva) 381 return NULL; 382 383 for(i = 0; i < dynhds->hds_len; ++i) { 384 struct dynhds_entry *e = dynhds->hds[i]; 385 DEBUGASSERT(e); 386 nva[i].name = (unsigned char *)e->name; 387 nva[i].namelen = e->namelen; 388 nva[i].value = (unsigned char *)e->value; 389 nva[i].valuelen = e->valuelen; 390 nva[i].flags = NGHTTP2_NV_FLAG_NONE; 391 } 392 *pcount = dynhds->hds_len; 393 return nva; 394} 395 396#endif /* USE_NGHTTP2 */ 397