Coverage Report

Created: 2026-05-21 06:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/trafficserver/src/proxy/hdrs/MIME.cc
Line
Count
Source
1
/** @file
2
3
  A brief file description
4
5
  @section license License
6
7
  Licensed to the Apache Software Foundation (ASF) under one
8
  or more contributor license agreements.  See the NOTICE file
9
  distributed with this work for additional information
10
  regarding copyright ownership.  The ASF licenses this file
11
  to you under the Apache License, Version 2.0 (the
12
  "License"); you may not use this file except in compliance
13
  with the License.  You may obtain a copy of the License at
14
15
      http://www.apache.org/licenses/LICENSE-2.0
16
17
  Unless required by applicable law or agreed to in writing, software
18
  distributed under the License is distributed on an "AS IS" BASIS,
19
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
  See the License for the specific language governing permissions and
21
  limitations under the License.
22
 */
23
24
#include "tscore/ink_defs.h"
25
#include "tscore/ink_platform.h"
26
#include "tscore/ink_memory.h"
27
28
#include "tsutil/StringCompare.h"
29
30
#include <cassert>
31
#include <cctype>
32
#include <cstdio>
33
#include <cstring>
34
#include <cctype>
35
#include <algorithm>
36
#include <string_view>
37
#include "proxy/hdrs/MIME.h"
38
#include "proxy/hdrs/HdrHeap.h"
39
#include "proxy/hdrs/HdrToken.h"
40
#include "proxy/hdrs/HdrUtils.h"
41
#include "proxy/hdrs/HttpCompat.h"
42
43
using swoc::TextView;
44
45
/***********************************************************************
46
 *                                                                     *
47
 *                    C O M P I L E    O P T I O N S                   *
48
 *                                                                     *
49
 ***********************************************************************/
50
#define TRACK_FIELD_FIND_CALLS            0
51
#define TRACK_COOKING                     0
52
#define MIME_FORMAT_DATE_USE_LOOKUP_TABLE 1
53
54
/***********************************************************************
55
 *                                                                     *
56
 *                          C O N S T A N T S                          *
57
 *                                                                     *
58
 ***********************************************************************/
59
60
namespace
61
{
62
constexpr std::array<std::string_view, 7> day_names = {
63
  "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
64
};
65
66
constexpr std::array<std::string_view, 12> month_names = {
67
  "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
68
};
69
70
template <size_t count>
71
consteval std::array<uint32_t, count>
72
make_packed(const std::array<std::string_view, count> &names)
73
{
74
  std::array<uint32_t, count> packed{};
75
76
  auto tl = [](char c) -> char { return (c >= 'A' && c <= 'Z') ? (c + 32) : c; };
77
78
  for (size_t i = 0; i < count; ++i) {
79
    const auto    &sv = names[i];
80
    const uint32_t c0 = tl(static_cast<unsigned char>(sv[0]));
81
    const uint32_t c1 = tl(static_cast<unsigned char>(sv[1]));
82
    const uint32_t c2 = tl(static_cast<unsigned char>(sv[2]));
83
    packed[i]         = (c0 << 16) | (c1 << 8) | c2;
84
  }
85
  return packed;
86
}
87
88
constexpr std::array<uint32_t, day_names.size()>   day_names_packed   = make_packed(day_names);
89
constexpr std::array<uint32_t, month_names.size()> month_names_packed = make_packed(month_names);
90
91
// Case-insensitive match of first 3 characters of input string against array of names
92
// Longer strings will match if their first 3 characters match - this is intentional for
93
// matching non-standard day/month names like "Thursday" or "September".
94
template <size_t count>
95
__attribute__((always_inline)) constexpr int
96
match_3char_ci(const std::string_view s, const std::array<uint32_t, count> &names_packed)
97
0
{
98
0
  if (s.size() < 3) {
99
0
    return -1;
100
0
  }
101
102
0
  auto           tl     = [](char c) -> char { return (c >= 'A' && c <= 'Z') ? (c + 32) : c; };
Unexecuted instantiation: MIME.cc:(anonymous namespace)::match_3char_ci<7ul>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::array<unsigned int, 7ul> const&)::{lambda(char)#1}::operator()(char) const
Unexecuted instantiation: MIME.cc:(anonymous namespace)::match_3char_ci<12ul>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::array<unsigned int, 12ul> const&)::{lambda(char)#1}::operator()(char) const
103
0
  const uint32_t packed = (tl(s[0]) << 16) | (tl(s[1]) << 8) | tl(s[2]);
104
105
0
  for (size_t i = 0; i < count; i++) {
106
0
    if (packed == names_packed[i]) {
107
0
      return i;
108
0
    }
109
0
  }
110
0
  return -1;
111
0
}
Unexecuted instantiation: MIME.cc:int (anonymous namespace)::match_3char_ci<7ul>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::array<unsigned int, 7ul> const&)
Unexecuted instantiation: MIME.cc:int (anonymous namespace)::match_3char_ci<12ul>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::array<unsigned int, 12ul> const&)
112
} // namespace
113
114
struct MDY {
115
  uint8_t  m;
116
  uint8_t  d;
117
  uint16_t y;
118
};
119
120
static MDY   *_days_to_mdy_fast_lookup_table = nullptr;
121
static time_t _days_to_mdy_fast_lookup_table_first_day;
122
static time_t _days_to_mdy_fast_lookup_table_last_day;
123
124
/***********************************************************************
125
 *                                                                     *
126
 *                             G L O B A L S                           *
127
 *                                                                     *
128
 ***********************************************************************/
129
c_str_view MIME_FIELD_ACCEPT;
130
c_str_view MIME_FIELD_ACCEPT_CHARSET;
131
c_str_view MIME_FIELD_ACCEPT_ENCODING;
132
c_str_view MIME_FIELD_ACCEPT_LANGUAGE;
133
c_str_view MIME_FIELD_ACCEPT_RANGES;
134
c_str_view MIME_FIELD_AGE;
135
c_str_view MIME_FIELD_ALLOW;
136
c_str_view MIME_FIELD_APPROVED;
137
c_str_view MIME_FIELD_AUTHORIZATION;
138
c_str_view MIME_FIELD_BYTES;
139
c_str_view MIME_FIELD_CACHE_CONTROL;
140
c_str_view MIME_FIELD_CLIENT_IP;
141
c_str_view MIME_FIELD_CONNECTION;
142
c_str_view MIME_FIELD_CONTENT_BASE;
143
c_str_view MIME_FIELD_CONTENT_ENCODING;
144
c_str_view MIME_FIELD_CONTENT_LANGUAGE;
145
c_str_view MIME_FIELD_CONTENT_LENGTH;
146
c_str_view MIME_FIELD_CONTENT_LOCATION;
147
c_str_view MIME_FIELD_CONTENT_MD5;
148
c_str_view MIME_FIELD_CONTENT_RANGE;
149
c_str_view MIME_FIELD_CONTENT_TYPE;
150
c_str_view MIME_FIELD_CONTROL;
151
c_str_view MIME_FIELD_COOKIE;
152
c_str_view MIME_FIELD_DATE;
153
c_str_view MIME_FIELD_DISTRIBUTION;
154
c_str_view MIME_FIELD_ETAG;
155
c_str_view MIME_FIELD_EXPECT;
156
c_str_view MIME_FIELD_EXPIRES;
157
c_str_view MIME_FIELD_FOLLOWUP_TO;
158
c_str_view MIME_FIELD_FROM;
159
c_str_view MIME_FIELD_HOST;
160
c_str_view MIME_FIELD_IF_MATCH;
161
c_str_view MIME_FIELD_IF_MODIFIED_SINCE;
162
c_str_view MIME_FIELD_IF_NONE_MATCH;
163
c_str_view MIME_FIELD_IF_RANGE;
164
c_str_view MIME_FIELD_IF_UNMODIFIED_SINCE;
165
c_str_view MIME_FIELD_KEEP_ALIVE;
166
c_str_view MIME_FIELD_KEYWORDS;
167
c_str_view MIME_FIELD_LAST_MODIFIED;
168
c_str_view MIME_FIELD_LINES;
169
c_str_view MIME_FIELD_LOCATION;
170
c_str_view MIME_FIELD_MAX_FORWARDS;
171
c_str_view MIME_FIELD_MESSAGE_ID;
172
c_str_view MIME_FIELD_NEWSGROUPS;
173
c_str_view MIME_FIELD_ORGANIZATION;
174
c_str_view MIME_FIELD_PATH;
175
c_str_view MIME_FIELD_PRAGMA;
176
c_str_view MIME_FIELD_PROXY_AUTHENTICATE;
177
c_str_view MIME_FIELD_PROXY_AUTHORIZATION;
178
c_str_view MIME_FIELD_PROXY_CONNECTION;
179
c_str_view MIME_FIELD_PUBLIC;
180
c_str_view MIME_FIELD_RANGE;
181
c_str_view MIME_FIELD_REFERENCES;
182
c_str_view MIME_FIELD_REFERER;
183
c_str_view MIME_FIELD_REPLY_TO;
184
c_str_view MIME_FIELD_RETRY_AFTER;
185
c_str_view MIME_FIELD_SENDER;
186
c_str_view MIME_FIELD_SERVER;
187
c_str_view MIME_FIELD_SET_COOKIE;
188
c_str_view MIME_FIELD_STRICT_TRANSPORT_SECURITY;
189
c_str_view MIME_FIELD_SUBJECT;
190
c_str_view MIME_FIELD_SUMMARY;
191
c_str_view MIME_FIELD_TE;
192
c_str_view MIME_FIELD_TRANSFER_ENCODING;
193
c_str_view MIME_FIELD_UPGRADE;
194
c_str_view MIME_FIELD_USER_AGENT;
195
c_str_view MIME_FIELD_VARY;
196
c_str_view MIME_FIELD_VIA;
197
c_str_view MIME_FIELD_WARNING;
198
c_str_view MIME_FIELD_WWW_AUTHENTICATE;
199
c_str_view MIME_FIELD_XREF;
200
c_str_view MIME_FIELD_ATS_INTERNAL;
201
c_str_view MIME_FIELD_X_ID;
202
c_str_view MIME_FIELD_X_FORWARDED_FOR;
203
c_str_view MIME_FIELD_FORWARDED;
204
c_str_view MIME_FIELD_SEC_WEBSOCKET_KEY;
205
c_str_view MIME_FIELD_SEC_WEBSOCKET_VERSION;
206
c_str_view MIME_FIELD_HTTP2_SETTINGS;
207
c_str_view MIME_FIELD_EARLY_DATA;
208
209
c_str_view MIME_VALUE_BYTES;
210
c_str_view MIME_VALUE_CHUNKED;
211
c_str_view MIME_VALUE_CLOSE;
212
c_str_view MIME_VALUE_COMPRESS;
213
c_str_view MIME_VALUE_DEFLATE;
214
c_str_view MIME_VALUE_GZIP;
215
c_str_view MIME_VALUE_BROTLI;
216
c_str_view MIME_VALUE_ZSTD;
217
c_str_view MIME_VALUE_IDENTITY;
218
c_str_view MIME_VALUE_KEEP_ALIVE;
219
c_str_view MIME_VALUE_MAX_AGE;
220
c_str_view MIME_VALUE_MAX_STALE;
221
c_str_view MIME_VALUE_MIN_FRESH;
222
c_str_view MIME_VALUE_MUST_REVALIDATE;
223
c_str_view MIME_VALUE_NONE;
224
c_str_view MIME_VALUE_NO_CACHE;
225
c_str_view MIME_VALUE_NO_STORE;
226
c_str_view MIME_VALUE_NO_TRANSFORM;
227
c_str_view MIME_VALUE_ONLY_IF_CACHED;
228
c_str_view MIME_VALUE_PRIVATE;
229
c_str_view MIME_VALUE_PROXY_REVALIDATE;
230
c_str_view MIME_VALUE_PUBLIC;
231
c_str_view MIME_VALUE_S_MAXAGE;
232
c_str_view MIME_VALUE_NEED_REVALIDATE_ONCE;
233
c_str_view MIME_VALUE_WEBSOCKET;
234
c_str_view MIME_VALUE_H2C;
235
236
// Cache-control: extension "need-revalidate-once" is used internally by T.S.
237
// to invalidate a document, and it is not returned/forwarded.
238
// If a cached document has this extension set (ie, is invalidated),
239
// then the T.S. needs to revalidate the document once before returning it.
240
// After a successful revalidation, the extension will be removed by T.S.
241
// To set or unset this directive should be done via the following two
242
// function:
243
//      set_cooked_cc_need_revalidate_once()
244
//      unset_cooked_cc_need_revalidate_once()
245
// To test, use regular Cache-control testing functions, eg,
246
//      is_cache_control_set(HTTP_VALUE_NEED_REVALIDATE_ONCE)
247
248
int MIME_WKSIDX_ACCEPT;
249
int MIME_WKSIDX_ACCEPT_CHARSET;
250
int MIME_WKSIDX_ACCEPT_ENCODING;
251
int MIME_WKSIDX_ACCEPT_LANGUAGE;
252
int MIME_WKSIDX_ACCEPT_RANGES;
253
int MIME_WKSIDX_AGE;
254
int MIME_WKSIDX_ALLOW;
255
int MIME_WKSIDX_APPROVED;
256
int MIME_WKSIDX_AUTHORIZATION;
257
int MIME_WKSIDX_BYTES;
258
int MIME_WKSIDX_CACHE_CONTROL;
259
int MIME_WKSIDX_CLIENT_IP;
260
int MIME_WKSIDX_CONNECTION;
261
int MIME_WKSIDX_CONTENT_BASE;
262
int MIME_WKSIDX_CONTENT_ENCODING;
263
int MIME_WKSIDX_CONTENT_LANGUAGE;
264
int MIME_WKSIDX_CONTENT_LENGTH;
265
int MIME_WKSIDX_CONTENT_LOCATION;
266
int MIME_WKSIDX_CONTENT_MD5;
267
int MIME_WKSIDX_CONTENT_RANGE;
268
int MIME_WKSIDX_CONTENT_TYPE;
269
int MIME_WKSIDX_CONTROL;
270
int MIME_WKSIDX_COOKIE;
271
int MIME_WKSIDX_DATE;
272
int MIME_WKSIDX_DISTRIBUTION;
273
int MIME_WKSIDX_ETAG;
274
int MIME_WKSIDX_EXPECT;
275
int MIME_WKSIDX_EXPIRES;
276
int MIME_WKSIDX_FOLLOWUP_TO;
277
int MIME_WKSIDX_FROM;
278
int MIME_WKSIDX_HOST;
279
int MIME_WKSIDX_IF_MATCH;
280
int MIME_WKSIDX_IF_MODIFIED_SINCE;
281
int MIME_WKSIDX_IF_NONE_MATCH;
282
int MIME_WKSIDX_IF_RANGE;
283
int MIME_WKSIDX_IF_UNMODIFIED_SINCE;
284
int MIME_WKSIDX_KEEP_ALIVE;
285
int MIME_WKSIDX_KEYWORDS;
286
int MIME_WKSIDX_LAST_MODIFIED;
287
int MIME_WKSIDX_LINES;
288
int MIME_WKSIDX_LOCATION;
289
int MIME_WKSIDX_MAX_FORWARDS;
290
int MIME_WKSIDX_MESSAGE_ID;
291
int MIME_WKSIDX_NEWSGROUPS;
292
int MIME_WKSIDX_ORGANIZATION;
293
int MIME_WKSIDX_PATH;
294
int MIME_WKSIDX_PRAGMA;
295
int MIME_WKSIDX_PROXY_AUTHENTICATE;
296
int MIME_WKSIDX_PROXY_AUTHORIZATION;
297
int MIME_WKSIDX_PROXY_CONNECTION;
298
int MIME_WKSIDX_PUBLIC;
299
int MIME_WKSIDX_RANGE;
300
int MIME_WKSIDX_REFERENCES;
301
int MIME_WKSIDX_REFERER;
302
int MIME_WKSIDX_REPLY_TO;
303
int MIME_WKSIDX_RETRY_AFTER;
304
int MIME_WKSIDX_SENDER;
305
int MIME_WKSIDX_SERVER;
306
int MIME_WKSIDX_SET_COOKIE;
307
int MIME_WKSIDX_STRICT_TRANSPORT_SECURITY;
308
int MIME_WKSIDX_SUBJECT;
309
int MIME_WKSIDX_SUMMARY;
310
int MIME_WKSIDX_TE;
311
int MIME_WKSIDX_TRANSFER_ENCODING;
312
int MIME_WKSIDX_UPGRADE;
313
int MIME_WKSIDX_USER_AGENT;
314
int MIME_WKSIDX_VARY;
315
int MIME_WKSIDX_VIA;
316
int MIME_WKSIDX_WARNING;
317
int MIME_WKSIDX_WWW_AUTHENTICATE;
318
int MIME_WKSIDX_XREF;
319
int MIME_WKSIDX_ATS_INTERNAL;
320
int MIME_WKSIDX_X_ID;
321
int MIME_WKSIDX_X_FORWARDED_FOR;
322
int MIME_WKSIDX_FORWARDED;
323
int MIME_WKSIDX_SEC_WEBSOCKET_KEY;
324
int MIME_WKSIDX_SEC_WEBSOCKET_VERSION;
325
int MIME_WKSIDX_HTTP2_SETTINGS;
326
int MIME_WKSIDX_EARLY_DATA;
327
328
namespace
329
{
330
DbgCtl dbg_ctl_http{"http"};
331
332
} // end anonymous namespace
333
334
/***********************************************************************
335
 *                                                                     *
336
 *                 U T I L I T Y    R O U T I N E S                    *
337
 *                                                                     *
338
 ***********************************************************************/
339
inline static int
340
is_digit(char c)
341
0
{
342
0
  return ((c <= '9') && (c >= '0'));
343
0
}
344
345
inline static int
346
is_ws(char c)
347
0
{
348
0
  return ((c == ParseRules::CHAR_SP) || (c == ParseRules::CHAR_HT));
349
0
}
350
351
/***********************************************************************
352
 *                                                                     *
353
 *                    P R E S E N C E    B I T S                       *
354
 *                                                                     *
355
 ***********************************************************************/
356
uint64_t
357
mime_field_presence_mask(const char *well_known_str)
358
0
{
359
0
  return hdrtoken_wks_to_mask(well_known_str);
360
0
}
361
362
uint64_t
363
mime_field_presence_mask(int well_known_str_index)
364
0
{
365
0
  return hdrtoken_index_to_mask(well_known_str_index);
366
0
}
367
368
int
369
mime_field_presence_get(MIMEHdrImpl *h, const char *well_known_str)
370
0
{
371
0
  uint64_t mask = mime_field_presence_mask(well_known_str);
372
0
  return ((mask == 0) ? 1 : ((h->m_presence_bits & mask) == 0 ? 0 : 1));
373
0
}
374
375
int
376
mime_field_presence_get(MIMEHdrImpl *h, int well_known_str_index)
377
0
{
378
0
  const char *wks = hdrtoken_index_to_wks(well_known_str_index);
379
0
  return mime_field_presence_get(h, wks);
380
0
}
381
382
void
383
mime_hdr_presence_set(MIMEHdrImpl *h, const char *well_known_str)
384
0
{
385
0
  uint64_t mask = mime_field_presence_mask(well_known_str);
386
0
  if (mask != 0) {
387
0
    h->m_presence_bits |= mask;
388
0
  }
389
0
}
390
391
void
392
mime_hdr_presence_set(MIMEHdrImpl *h, int well_known_str_index)
393
0
{
394
0
  const char *wks = hdrtoken_index_to_wks(well_known_str_index);
395
0
  mime_hdr_presence_set(h, wks);
396
0
}
397
398
void
399
mime_hdr_presence_unset(MIMEHdrImpl *h, const char *well_known_str)
400
0
{
401
0
  uint64_t mask = mime_field_presence_mask(well_known_str);
402
0
  if (mask != 0) {
403
0
    h->m_presence_bits &= (~mask);
404
0
  }
405
0
}
406
407
void
408
mime_hdr_presence_unset(MIMEHdrImpl *h, int well_known_str_index)
409
0
{
410
0
  const char *wks = hdrtoken_index_to_wks(well_known_str_index);
411
0
  mime_hdr_presence_unset(h, wks);
412
0
}
413
414
/***********************************************************************
415
 *                                                                     *
416
 *                  S L O T    A C C E L E R A T O R S                 *
417
 *                                                                     *
418
 ***********************************************************************/
419
inline void
420
mime_hdr_init_accelerators_and_presence_bits(MIMEHdrImpl *mh)
421
0
{
422
0
  mh->m_presence_bits        = 0;
423
0
  mh->m_slot_accelerators[0] = 0xFFFFFFFF;
424
0
  mh->m_slot_accelerators[1] = 0xFFFFFFFF;
425
0
  mh->m_slot_accelerators[2] = 0xFFFFFFFF;
426
0
  mh->m_slot_accelerators[3] = 0xFFFFFFFF;
427
0
}
428
429
inline uint32_t
430
mime_hdr_get_accelerator_slotnum(MIMEHdrImpl *mh, int32_t slot_id)
431
0
{
432
0
  ink_assert((slot_id != MIME_SLOTID_NONE) && (slot_id < 32));
433
434
0
  uint32_t word_index = slot_id / 8;                         // 4 words of 8 slots
435
0
  uint32_t word       = mh->m_slot_accelerators[word_index]; // 8 slots of 4 bits each
436
0
  uint32_t nybble     = slot_id % 8;                         // which of the 8 nybbles?
437
0
  uint32_t slot       = ((word >> (nybble * 4)) & 15);       // grab the 4 bit slotnum
438
0
  return slot;
439
0
}
440
441
inline void
442
mime_hdr_set_accelerator_slotnum(MIMEHdrImpl *mh, int32_t slot_id, uint32_t slot_num)
443
0
{
444
0
  ink_assert((slot_id != MIME_SLOTID_NONE) && (slot_id < 32));
445
0
  ink_assert(slot_num < 16);
446
447
0
  uint32_t word_index = slot_id / 8;                         // 4 words of 8 slots
448
0
  uint32_t word       = mh->m_slot_accelerators[word_index]; // 8 slots of 4 bits each
449
0
  uint32_t nybble     = slot_id % 8;                         // which of the 8 nybbles?
450
0
  uint32_t shift      = nybble * 4;                          // shift in chunks of 4 bits
451
0
  uint32_t mask       = ~(MIME_FIELD_SLOTNUM_MASK << shift); // mask to zero out old slot
452
0
  uint32_t graft      = (slot_num << shift);                 // plug to insert into slot
453
0
  uint32_t new_word   = (word & mask) | graft;               // new value
454
455
0
  mh->m_slot_accelerators[word_index] = new_word;
456
0
}
457
458
inline void
459
mime_hdr_set_accelerators_and_presence_bits(MIMEHdrImpl *mh, MIMEField *field)
460
0
{
461
0
  int       slot_id;
462
0
  ptrdiff_t slot_num;
463
0
  if (field->m_wks_idx < 0) {
464
0
    return;
465
0
  }
466
467
0
  ink_assert(mh);
468
469
0
  mime_hdr_presence_set(mh, field->m_wks_idx);
470
471
0
  slot_id = hdrtoken_index_to_slotid(field->m_wks_idx);
472
0
  if (slot_id != MIME_SLOTID_NONE) {
473
0
    if (mh->m_first_fblock.contains(field)) {
474
0
      slot_num = (field - &(mh->m_first_fblock.m_field_slots[0]));
475
      // contains() assure that the field is in the block, and the calculated
476
      // slot_num will be between 0 and 15, which seem valid.
477
      // However, strangely, this function regards slot number 14 and 15 as
478
      // unknown for some reason that is not clear. It might be a bug.
479
      // The block below is left to keep the original behavior. See also TS-4316.
480
0
      if (slot_num >= MIME_FIELD_SLOTNUM_UNKNOWN) {
481
0
        slot_num = MIME_FIELD_SLOTNUM_UNKNOWN;
482
0
      }
483
0
    } else {
484
0
      slot_num = MIME_FIELD_SLOTNUM_UNKNOWN;
485
0
    }
486
0
    mime_hdr_set_accelerator_slotnum(mh, slot_id, slot_num);
487
0
  }
488
0
}
489
490
inline void
491
mime_hdr_unset_accelerators_and_presence_bits(MIMEHdrImpl *mh, MIMEField *field)
492
0
{
493
0
  int slot_id;
494
0
  if (field->m_wks_idx < 0) {
495
0
    return;
496
0
  }
497
498
0
  mime_hdr_presence_unset(mh, field->m_wks_idx);
499
500
0
  slot_id = hdrtoken_index_to_slotid(field->m_wks_idx);
501
0
  if (slot_id != MIME_SLOTID_NONE) {
502
0
    mime_hdr_set_accelerator_slotnum(mh, slot_id, MIME_FIELD_SLOTNUM_MAX);
503
0
  }
504
0
}
505
506
/// Reset data in the header.
507
/// Clear all the presence bits and accelerators.
508
/// Update all the m_wks_idx values, presence bits and accelerators.
509
inline void
510
mime_hdr_reset_accelerators_and_presence_bits(MIMEHdrImpl *mh)
511
0
{
512
0
  mime_hdr_init_accelerators_and_presence_bits(mh);
513
514
0
  for (MIMEFieldBlockImpl *fblock = &(mh->m_first_fblock); fblock != nullptr; fblock = fblock->m_next) {
515
0
    for (MIMEField *field = fblock->m_field_slots, *limit = field + fblock->m_freetop; field < limit; ++field) {
516
0
      if (field->is_live()) {
517
0
        field->m_wks_idx = hdrtoken_tokenize(field->m_ptr_name, field->m_len_name);
518
0
        if (field->is_dup_head()) {
519
0
          mime_hdr_set_accelerators_and_presence_bits(mh, field);
520
0
        }
521
0
      }
522
0
    }
523
0
  }
524
0
}
525
526
int
527
checksum_block(const char *s, int len)
528
0
{
529
0
  int sum = 0;
530
0
  while (len--) {
531
0
    sum ^= *s++;
532
0
  }
533
0
  return sum;
534
0
}
535
536
#ifdef ENABLE_MIME_SANITY_CHECK
537
void
538
mime_hdr_sanity_check(MIMEHdrImpl *mh)
539
{
540
  MIMEFieldBlockImpl *fblock, *blk, *last_fblock;
541
  MIMEField          *field, *next_dup;
542
  uint32_t            slot_index, index;
543
  uint64_t            masksum;
544
545
  ink_assert(mh != nullptr);
546
547
  masksum     = 0;
548
  slot_index  = 0;
549
  last_fblock = nullptr;
550
551
  for (fblock = &(mh->m_first_fblock); fblock != nullptr; fblock = fblock->m_next) {
552
    for (index = 0; index < fblock->m_freetop; index++) {
553
      field = &(fblock->m_field_slots[index]);
554
555
      if (field->is_live()) {
556
        // dummy operations just to make sure deref doesn't crash
557
        checksum_block(field->m_ptr_name, field->m_len_name);
558
        if (field->m_ptr_value) {
559
          checksum_block(field->m_ptr_value, field->m_len_value);
560
        }
561
562
        if (field->m_n_v_raw_printable) {
563
          int total_len = field->m_len_name + field->m_len_value + field->m_n_v_raw_printable_pad;
564
          checksum_block(field->m_ptr_name, total_len);
565
        }
566
        // walk the dup list, quickly checking each cell
567
        if (field->m_next_dup != nullptr) {
568
          int field_slotnum = mime_hdr_field_slotnum(mh, field);
569
570
          for (next_dup = field->m_next_dup; next_dup; next_dup = next_dup->m_next_dup) {
571
            int next_slotnum = mime_hdr_field_slotnum(mh, next_dup);
572
            ink_release_assert((next_dup->m_flags & MIME_FIELD_SLOT_FLAGS_DUP_HEAD) == 0);
573
            ink_release_assert((next_dup->m_readiness == MIME_FIELD_SLOT_READINESS_LIVE));
574
            ink_release_assert(next_dup->m_wks_idx == field->m_wks_idx);
575
            ink_release_assert(next_dup->m_len_name == field->m_len_name);
576
            ink_release_assert(strncasecmp(field->m_ptr_name, next_dup->m_ptr_name, field->m_len_name) == 0);
577
            ink_release_assert(next_slotnum > field_slotnum);
578
          }
579
        }
580
        // if this is a well known string, check presence bits & slot accelerators
581
        if (field->m_wks_idx >= 0) {
582
          const char *wks = hdrtoken_index_to_wks(field->m_wks_idx);
583
          int         len = hdrtoken_index_to_length(field->m_wks_idx);
584
585
          if (field->m_len_name != len || strncasecmp(field->m_ptr_name, wks, field->m_len_name) != 0) {
586
            Warning("Encountered WKS hash collision on '%.*s'", field->m_len_name, field->m_ptr_name);
587
          }
588
589
          uint64_t mask  = mime_field_presence_mask(field->m_wks_idx);
590
          masksum       |= mask;
591
592
          int32_t slot_id = hdrtoken_index_to_slotid(field->m_wks_idx);
593
          if ((slot_id != MIME_SLOTID_NONE) && (slot_index < MIME_FIELD_SLOTNUM_UNKNOWN) &&
594
              (field->m_flags & MIME_FIELD_SLOT_FLAGS_DUP_HEAD)) {
595
            uint32_t slot_num = mime_hdr_get_accelerator_slotnum(mh, slot_id);
596
            if (slot_num <= 14) {
597
              ink_release_assert(slot_num == slot_index);
598
            }
599
          }
600
        } else {
601
          int idx = hdrtoken_tokenize(field->m_ptr_name, field->m_len_name, nullptr);
602
          ink_release_assert(idx < 0);
603
        }
604
605
        // verify that the next dup pointer points to a block in this list
606
        if (field->m_next_dup) {
607
          bool found = false;
608
          for (blk = &(mh->m_first_fblock); blk != nullptr; blk = blk->m_next) {
609
            const char *addr = reinterpret_cast<const char *>(field->m_next_dup);
610
            if ((addr >= reinterpret_cast<const char *>(blk)) &&
611
                (addr < reinterpret_cast<const char *>(blk) + sizeof(MIMEFieldBlockImpl))) {
612
              found = true;
613
              break;
614
            }
615
          }
616
          ink_release_assert(found);
617
        }
618
        // re-find the field --- should always find the head dup
619
        MIMEField *mf = mime_hdr_field_find(mh, field->m_ptr_name, field->m_len_name);
620
        ink_release_assert(mf != nullptr);
621
        if (mf == field) {
622
          ink_release_assert((field->m_flags & MIME_FIELD_SLOT_FLAGS_DUP_HEAD) != 0);
623
        } else {
624
          ink_release_assert((field->m_flags & MIME_FIELD_SLOT_FLAGS_DUP_HEAD) == 0);
625
        }
626
      }
627
628
      ++slot_index;
629
    }
630
    last_fblock = fblock;
631
  }
632
633
  ink_release_assert(last_fblock == mh->m_fblock_list_tail);
634
  ink_release_assert(masksum == mh->m_presence_bits);
635
}
636
#endif
637
638
void
639
mime_init()
640
0
{
641
0
  static int init = 1;
642
643
0
  if (init) {
644
0
    init = 0;
645
646
0
    hdrtoken_init();
647
648
0
    MIME_FIELD_ACCEPT                    = hdrtoken_string_to_wks_sv("Accept");
649
0
    MIME_FIELD_ACCEPT_CHARSET            = hdrtoken_string_to_wks_sv("Accept-Charset");
650
0
    MIME_FIELD_ACCEPT_ENCODING           = hdrtoken_string_to_wks_sv("Accept-Encoding");
651
0
    MIME_FIELD_ACCEPT_LANGUAGE           = hdrtoken_string_to_wks_sv("Accept-Language");
652
0
    MIME_FIELD_ACCEPT_RANGES             = hdrtoken_string_to_wks_sv("Accept-Ranges");
653
0
    MIME_FIELD_AGE                       = hdrtoken_string_to_wks_sv("Age");
654
0
    MIME_FIELD_ALLOW                     = hdrtoken_string_to_wks_sv("Allow");
655
0
    MIME_FIELD_APPROVED                  = hdrtoken_string_to_wks_sv("Approved");
656
0
    MIME_FIELD_AUTHORIZATION             = hdrtoken_string_to_wks_sv("Authorization");
657
0
    MIME_FIELD_BYTES                     = hdrtoken_string_to_wks_sv("Bytes");
658
0
    MIME_FIELD_CACHE_CONTROL             = hdrtoken_string_to_wks_sv("Cache-Control");
659
0
    MIME_FIELD_CLIENT_IP                 = hdrtoken_string_to_wks_sv("Client-ip");
660
0
    MIME_FIELD_CONNECTION                = hdrtoken_string_to_wks_sv("Connection");
661
0
    MIME_FIELD_CONTENT_BASE              = hdrtoken_string_to_wks_sv("Content-Base");
662
0
    MIME_FIELD_CONTENT_ENCODING          = hdrtoken_string_to_wks_sv("Content-Encoding");
663
0
    MIME_FIELD_CONTENT_LANGUAGE          = hdrtoken_string_to_wks_sv("Content-Language");
664
0
    MIME_FIELD_CONTENT_LENGTH            = hdrtoken_string_to_wks_sv("Content-Length");
665
0
    MIME_FIELD_CONTENT_LOCATION          = hdrtoken_string_to_wks_sv("Content-Location");
666
0
    MIME_FIELD_CONTENT_MD5               = hdrtoken_string_to_wks_sv("Content-MD5");
667
0
    MIME_FIELD_CONTENT_RANGE             = hdrtoken_string_to_wks_sv("Content-Range");
668
0
    MIME_FIELD_CONTENT_TYPE              = hdrtoken_string_to_wks_sv("Content-Type");
669
0
    MIME_FIELD_CONTROL                   = hdrtoken_string_to_wks_sv("Control");
670
0
    MIME_FIELD_COOKIE                    = hdrtoken_string_to_wks_sv("Cookie");
671
0
    MIME_FIELD_DATE                      = hdrtoken_string_to_wks_sv("Date");
672
0
    MIME_FIELD_DISTRIBUTION              = hdrtoken_string_to_wks_sv("Distribution");
673
0
    MIME_FIELD_ETAG                      = hdrtoken_string_to_wks_sv("Etag");
674
0
    MIME_FIELD_EXPECT                    = hdrtoken_string_to_wks_sv("Expect");
675
0
    MIME_FIELD_EXPIRES                   = hdrtoken_string_to_wks_sv("Expires");
676
0
    MIME_FIELD_FOLLOWUP_TO               = hdrtoken_string_to_wks_sv("Followup-To");
677
0
    MIME_FIELD_FROM                      = hdrtoken_string_to_wks_sv("From");
678
0
    MIME_FIELD_HOST                      = hdrtoken_string_to_wks_sv("Host");
679
0
    MIME_FIELD_IF_MATCH                  = hdrtoken_string_to_wks_sv("If-Match");
680
0
    MIME_FIELD_IF_MODIFIED_SINCE         = hdrtoken_string_to_wks_sv("If-Modified-Since");
681
0
    MIME_FIELD_IF_NONE_MATCH             = hdrtoken_string_to_wks_sv("If-None-Match");
682
0
    MIME_FIELD_IF_RANGE                  = hdrtoken_string_to_wks_sv("If-Range");
683
0
    MIME_FIELD_IF_UNMODIFIED_SINCE       = hdrtoken_string_to_wks_sv("If-Unmodified-Since");
684
0
    MIME_FIELD_KEEP_ALIVE                = hdrtoken_string_to_wks_sv("Keep-Alive");
685
0
    MIME_FIELD_KEYWORDS                  = hdrtoken_string_to_wks_sv("Keywords");
686
0
    MIME_FIELD_LAST_MODIFIED             = hdrtoken_string_to_wks_sv("Last-Modified");
687
0
    MIME_FIELD_LINES                     = hdrtoken_string_to_wks_sv("Lines");
688
0
    MIME_FIELD_LOCATION                  = hdrtoken_string_to_wks_sv("Location");
689
0
    MIME_FIELD_MAX_FORWARDS              = hdrtoken_string_to_wks_sv("Max-Forwards");
690
0
    MIME_FIELD_MESSAGE_ID                = hdrtoken_string_to_wks_sv("Message-ID");
691
0
    MIME_FIELD_NEWSGROUPS                = hdrtoken_string_to_wks_sv("Newsgroups");
692
0
    MIME_FIELD_ORGANIZATION              = hdrtoken_string_to_wks_sv("Organization");
693
0
    MIME_FIELD_PATH                      = hdrtoken_string_to_wks_sv("Path");
694
0
    MIME_FIELD_PRAGMA                    = hdrtoken_string_to_wks_sv("Pragma");
695
0
    MIME_FIELD_PROXY_AUTHENTICATE        = hdrtoken_string_to_wks_sv("Proxy-Authenticate");
696
0
    MIME_FIELD_PROXY_AUTHORIZATION       = hdrtoken_string_to_wks_sv("Proxy-Authorization");
697
0
    MIME_FIELD_PROXY_CONNECTION          = hdrtoken_string_to_wks_sv("Proxy-Connection");
698
0
    MIME_FIELD_PUBLIC                    = hdrtoken_string_to_wks_sv("Public");
699
0
    MIME_FIELD_RANGE                     = hdrtoken_string_to_wks_sv("Range");
700
0
    MIME_FIELD_REFERENCES                = hdrtoken_string_to_wks_sv("References");
701
0
    MIME_FIELD_REFERER                   = hdrtoken_string_to_wks_sv("Referer");
702
0
    MIME_FIELD_REPLY_TO                  = hdrtoken_string_to_wks_sv("Reply-To");
703
0
    MIME_FIELD_RETRY_AFTER               = hdrtoken_string_to_wks_sv("Retry-After");
704
0
    MIME_FIELD_SENDER                    = hdrtoken_string_to_wks_sv("Sender");
705
0
    MIME_FIELD_SERVER                    = hdrtoken_string_to_wks_sv("Server");
706
0
    MIME_FIELD_SET_COOKIE                = hdrtoken_string_to_wks_sv("Set-Cookie");
707
0
    MIME_FIELD_STRICT_TRANSPORT_SECURITY = hdrtoken_string_to_wks_sv("Strict-Transport-Security");
708
0
    MIME_FIELD_SUBJECT                   = hdrtoken_string_to_wks_sv("Subject");
709
0
    MIME_FIELD_SUMMARY                   = hdrtoken_string_to_wks_sv("Summary");
710
0
    MIME_FIELD_TE                        = hdrtoken_string_to_wks_sv("TE");
711
0
    MIME_FIELD_TRANSFER_ENCODING         = hdrtoken_string_to_wks_sv("Transfer-Encoding");
712
0
    MIME_FIELD_UPGRADE                   = hdrtoken_string_to_wks_sv("Upgrade");
713
0
    MIME_FIELD_USER_AGENT                = hdrtoken_string_to_wks_sv("User-Agent");
714
0
    MIME_FIELD_VARY                      = hdrtoken_string_to_wks_sv("Vary");
715
0
    MIME_FIELD_VIA                       = hdrtoken_string_to_wks_sv("Via");
716
0
    MIME_FIELD_WARNING                   = hdrtoken_string_to_wks_sv("Warning");
717
0
    MIME_FIELD_WWW_AUTHENTICATE          = hdrtoken_string_to_wks_sv("Www-Authenticate");
718
0
    MIME_FIELD_XREF                      = hdrtoken_string_to_wks_sv("Xref");
719
0
    MIME_FIELD_ATS_INTERNAL              = hdrtoken_string_to_wks_sv("@Ats-Internal");
720
0
    MIME_FIELD_X_ID                      = hdrtoken_string_to_wks_sv("X-ID");
721
0
    MIME_FIELD_X_FORWARDED_FOR           = hdrtoken_string_to_wks_sv("X-Forwarded-For");
722
0
    MIME_FIELD_FORWARDED                 = hdrtoken_string_to_wks_sv("Forwarded");
723
0
    MIME_FIELD_SEC_WEBSOCKET_KEY         = hdrtoken_string_to_wks_sv("Sec-WebSocket-Key");
724
0
    MIME_FIELD_SEC_WEBSOCKET_VERSION     = hdrtoken_string_to_wks_sv("Sec-WebSocket-Version");
725
0
    MIME_FIELD_HTTP2_SETTINGS            = hdrtoken_string_to_wks_sv("HTTP2-Settings");
726
0
    MIME_FIELD_EARLY_DATA                = hdrtoken_string_to_wks_sv("Early-Data");
727
728
0
    MIME_WKSIDX_ACCEPT                    = hdrtoken_wks_to_index(MIME_FIELD_ACCEPT.c_str());
729
0
    MIME_WKSIDX_ACCEPT_CHARSET            = hdrtoken_wks_to_index(MIME_FIELD_ACCEPT_CHARSET.c_str());
730
0
    MIME_WKSIDX_ACCEPT_ENCODING           = hdrtoken_wks_to_index(MIME_FIELD_ACCEPT_ENCODING.c_str());
731
0
    MIME_WKSIDX_ACCEPT_LANGUAGE           = hdrtoken_wks_to_index(MIME_FIELD_ACCEPT_LANGUAGE.c_str());
732
0
    MIME_WKSIDX_ACCEPT_RANGES             = hdrtoken_wks_to_index(MIME_FIELD_ACCEPT_RANGES.c_str());
733
0
    MIME_WKSIDX_AGE                       = hdrtoken_wks_to_index(MIME_FIELD_AGE.c_str());
734
0
    MIME_WKSIDX_ALLOW                     = hdrtoken_wks_to_index(MIME_FIELD_ALLOW.c_str());
735
0
    MIME_WKSIDX_APPROVED                  = hdrtoken_wks_to_index(MIME_FIELD_APPROVED.c_str());
736
0
    MIME_WKSIDX_AUTHORIZATION             = hdrtoken_wks_to_index(MIME_FIELD_AUTHORIZATION.c_str());
737
0
    MIME_WKSIDX_BYTES                     = hdrtoken_wks_to_index(MIME_FIELD_BYTES.c_str());
738
0
    MIME_WKSIDX_CACHE_CONTROL             = hdrtoken_wks_to_index(MIME_FIELD_CACHE_CONTROL.c_str());
739
0
    MIME_WKSIDX_CLIENT_IP                 = hdrtoken_wks_to_index(MIME_FIELD_CLIENT_IP.c_str());
740
0
    MIME_WKSIDX_CONNECTION                = hdrtoken_wks_to_index(MIME_FIELD_CONNECTION.c_str());
741
0
    MIME_WKSIDX_CONTENT_BASE              = hdrtoken_wks_to_index(MIME_FIELD_CONTENT_BASE.c_str());
742
0
    MIME_WKSIDX_CONTENT_ENCODING          = hdrtoken_wks_to_index(MIME_FIELD_CONTENT_ENCODING.c_str());
743
0
    MIME_WKSIDX_CONTENT_LANGUAGE          = hdrtoken_wks_to_index(MIME_FIELD_CONTENT_LANGUAGE.c_str());
744
0
    MIME_WKSIDX_CONTENT_LENGTH            = hdrtoken_wks_to_index(MIME_FIELD_CONTENT_LENGTH.c_str());
745
0
    MIME_WKSIDX_CONTENT_LOCATION          = hdrtoken_wks_to_index(MIME_FIELD_CONTENT_LOCATION.c_str());
746
0
    MIME_WKSIDX_CONTENT_MD5               = hdrtoken_wks_to_index(MIME_FIELD_CONTENT_MD5.c_str());
747
0
    MIME_WKSIDX_CONTENT_RANGE             = hdrtoken_wks_to_index(MIME_FIELD_CONTENT_RANGE.c_str());
748
0
    MIME_WKSIDX_CONTENT_TYPE              = hdrtoken_wks_to_index(MIME_FIELD_CONTENT_TYPE.c_str());
749
0
    MIME_WKSIDX_CONTROL                   = hdrtoken_wks_to_index(MIME_FIELD_CONTROL.c_str());
750
0
    MIME_WKSIDX_COOKIE                    = hdrtoken_wks_to_index(MIME_FIELD_COOKIE.c_str());
751
0
    MIME_WKSIDX_DATE                      = hdrtoken_wks_to_index(MIME_FIELD_DATE.c_str());
752
0
    MIME_WKSIDX_DISTRIBUTION              = hdrtoken_wks_to_index(MIME_FIELD_DISTRIBUTION.c_str());
753
0
    MIME_WKSIDX_ETAG                      = hdrtoken_wks_to_index(MIME_FIELD_ETAG.c_str());
754
0
    MIME_WKSIDX_EXPECT                    = hdrtoken_wks_to_index(MIME_FIELD_EXPECT.c_str());
755
0
    MIME_WKSIDX_EXPIRES                   = hdrtoken_wks_to_index(MIME_FIELD_EXPIRES.c_str());
756
0
    MIME_WKSIDX_FOLLOWUP_TO               = hdrtoken_wks_to_index(MIME_FIELD_FOLLOWUP_TO.c_str());
757
0
    MIME_WKSIDX_FROM                      = hdrtoken_wks_to_index(MIME_FIELD_FROM.c_str());
758
0
    MIME_WKSIDX_HOST                      = hdrtoken_wks_to_index(MIME_FIELD_HOST.c_str());
759
0
    MIME_WKSIDX_IF_MATCH                  = hdrtoken_wks_to_index(MIME_FIELD_IF_MATCH.c_str());
760
0
    MIME_WKSIDX_IF_MODIFIED_SINCE         = hdrtoken_wks_to_index(MIME_FIELD_IF_MODIFIED_SINCE.c_str());
761
0
    MIME_WKSIDX_IF_NONE_MATCH             = hdrtoken_wks_to_index(MIME_FIELD_IF_NONE_MATCH.c_str());
762
0
    MIME_WKSIDX_IF_RANGE                  = hdrtoken_wks_to_index(MIME_FIELD_IF_RANGE.c_str());
763
0
    MIME_WKSIDX_IF_UNMODIFIED_SINCE       = hdrtoken_wks_to_index(MIME_FIELD_IF_UNMODIFIED_SINCE.c_str());
764
0
    MIME_WKSIDX_KEEP_ALIVE                = hdrtoken_wks_to_index(MIME_FIELD_KEEP_ALIVE.c_str());
765
0
    MIME_WKSIDX_KEYWORDS                  = hdrtoken_wks_to_index(MIME_FIELD_KEYWORDS.c_str());
766
0
    MIME_WKSIDX_LAST_MODIFIED             = hdrtoken_wks_to_index(MIME_FIELD_LAST_MODIFIED.c_str());
767
0
    MIME_WKSIDX_LINES                     = hdrtoken_wks_to_index(MIME_FIELD_LINES.c_str());
768
0
    MIME_WKSIDX_LOCATION                  = hdrtoken_wks_to_index(MIME_FIELD_LOCATION.c_str());
769
0
    MIME_WKSIDX_MAX_FORWARDS              = hdrtoken_wks_to_index(MIME_FIELD_MAX_FORWARDS.c_str());
770
0
    MIME_WKSIDX_MESSAGE_ID                = hdrtoken_wks_to_index(MIME_FIELD_MESSAGE_ID.c_str());
771
0
    MIME_WKSIDX_NEWSGROUPS                = hdrtoken_wks_to_index(MIME_FIELD_NEWSGROUPS.c_str());
772
0
    MIME_WKSIDX_ORGANIZATION              = hdrtoken_wks_to_index(MIME_FIELD_ORGANIZATION.c_str());
773
0
    MIME_WKSIDX_PATH                      = hdrtoken_wks_to_index(MIME_FIELD_PATH.c_str());
774
0
    MIME_WKSIDX_PRAGMA                    = hdrtoken_wks_to_index(MIME_FIELD_PRAGMA.c_str());
775
0
    MIME_WKSIDX_PROXY_AUTHENTICATE        = hdrtoken_wks_to_index(MIME_FIELD_PROXY_AUTHENTICATE.c_str());
776
0
    MIME_WKSIDX_PROXY_AUTHORIZATION       = hdrtoken_wks_to_index(MIME_FIELD_PROXY_AUTHORIZATION.c_str());
777
0
    MIME_WKSIDX_PROXY_CONNECTION          = hdrtoken_wks_to_index(MIME_FIELD_PROXY_CONNECTION.c_str());
778
0
    MIME_WKSIDX_PUBLIC                    = hdrtoken_wks_to_index(MIME_FIELD_PUBLIC.c_str());
779
0
    MIME_WKSIDX_RANGE                     = hdrtoken_wks_to_index(MIME_FIELD_RANGE.c_str());
780
0
    MIME_WKSIDX_REFERENCES                = hdrtoken_wks_to_index(MIME_FIELD_REFERENCES.c_str());
781
0
    MIME_WKSIDX_REFERER                   = hdrtoken_wks_to_index(MIME_FIELD_REFERER.c_str());
782
0
    MIME_WKSIDX_REPLY_TO                  = hdrtoken_wks_to_index(MIME_FIELD_REPLY_TO.c_str());
783
0
    MIME_WKSIDX_RETRY_AFTER               = hdrtoken_wks_to_index(MIME_FIELD_RETRY_AFTER.c_str());
784
0
    MIME_WKSIDX_SENDER                    = hdrtoken_wks_to_index(MIME_FIELD_SENDER.c_str());
785
0
    MIME_WKSIDX_SERVER                    = hdrtoken_wks_to_index(MIME_FIELD_SERVER.c_str());
786
0
    MIME_WKSIDX_SET_COOKIE                = hdrtoken_wks_to_index(MIME_FIELD_SET_COOKIE.c_str());
787
0
    MIME_WKSIDX_STRICT_TRANSPORT_SECURITY = hdrtoken_wks_to_index(MIME_FIELD_STRICT_TRANSPORT_SECURITY.c_str());
788
0
    MIME_WKSIDX_SUBJECT                   = hdrtoken_wks_to_index(MIME_FIELD_SUBJECT.c_str());
789
0
    MIME_WKSIDX_SUMMARY                   = hdrtoken_wks_to_index(MIME_FIELD_SUMMARY.c_str());
790
0
    MIME_WKSIDX_TE                        = hdrtoken_wks_to_index(MIME_FIELD_TE.c_str());
791
0
    MIME_WKSIDX_TRANSFER_ENCODING         = hdrtoken_wks_to_index(MIME_FIELD_TRANSFER_ENCODING.c_str());
792
0
    MIME_WKSIDX_UPGRADE                   = hdrtoken_wks_to_index(MIME_FIELD_UPGRADE.c_str());
793
0
    MIME_WKSIDX_USER_AGENT                = hdrtoken_wks_to_index(MIME_FIELD_USER_AGENT.c_str());
794
0
    MIME_WKSIDX_VARY                      = hdrtoken_wks_to_index(MIME_FIELD_VARY.c_str());
795
0
    MIME_WKSIDX_VIA                       = hdrtoken_wks_to_index(MIME_FIELD_VIA.c_str());
796
0
    MIME_WKSIDX_WARNING                   = hdrtoken_wks_to_index(MIME_FIELD_WARNING.c_str());
797
0
    MIME_WKSIDX_WWW_AUTHENTICATE          = hdrtoken_wks_to_index(MIME_FIELD_WWW_AUTHENTICATE.c_str());
798
0
    MIME_WKSIDX_XREF                      = hdrtoken_wks_to_index(MIME_FIELD_XREF.c_str());
799
0
    MIME_WKSIDX_X_ID                      = hdrtoken_wks_to_index(MIME_FIELD_X_ID.c_str());
800
0
    MIME_WKSIDX_X_FORWARDED_FOR           = hdrtoken_wks_to_index(MIME_FIELD_X_FORWARDED_FOR.c_str());
801
0
    MIME_WKSIDX_FORWARDED                 = hdrtoken_wks_to_index(MIME_FIELD_FORWARDED.c_str());
802
0
    MIME_WKSIDX_SEC_WEBSOCKET_KEY         = hdrtoken_wks_to_index(MIME_FIELD_SEC_WEBSOCKET_KEY.c_str());
803
0
    MIME_WKSIDX_SEC_WEBSOCKET_VERSION     = hdrtoken_wks_to_index(MIME_FIELD_SEC_WEBSOCKET_VERSION.c_str());
804
0
    MIME_WKSIDX_HTTP2_SETTINGS            = hdrtoken_wks_to_index(MIME_FIELD_HTTP2_SETTINGS.c_str());
805
0
    MIME_WKSIDX_EARLY_DATA                = hdrtoken_wks_to_index(MIME_FIELD_EARLY_DATA.c_str());
806
807
0
    MIME_VALUE_BYTES                = hdrtoken_string_to_wks_sv("bytes");
808
0
    MIME_VALUE_CHUNKED              = hdrtoken_string_to_wks_sv("chunked");
809
0
    MIME_VALUE_CLOSE                = hdrtoken_string_to_wks_sv("close");
810
0
    MIME_VALUE_COMPRESS             = hdrtoken_string_to_wks_sv("compress");
811
0
    MIME_VALUE_DEFLATE              = hdrtoken_string_to_wks_sv("deflate");
812
0
    MIME_VALUE_GZIP                 = hdrtoken_string_to_wks_sv("gzip");
813
0
    MIME_VALUE_BROTLI               = hdrtoken_string_to_wks_sv("br");
814
0
    MIME_VALUE_ZSTD                 = hdrtoken_string_to_wks_sv("zstd");
815
0
    MIME_VALUE_IDENTITY             = hdrtoken_string_to_wks_sv("identity");
816
0
    MIME_VALUE_KEEP_ALIVE           = hdrtoken_string_to_wks_sv("keep-alive");
817
0
    MIME_VALUE_MAX_AGE              = hdrtoken_string_to_wks_sv("max-age");
818
0
    MIME_VALUE_MAX_STALE            = hdrtoken_string_to_wks_sv("max-stale");
819
0
    MIME_VALUE_MIN_FRESH            = hdrtoken_string_to_wks_sv("min-fresh");
820
0
    MIME_VALUE_MUST_REVALIDATE      = hdrtoken_string_to_wks_sv("must-revalidate");
821
0
    MIME_VALUE_NONE                 = hdrtoken_string_to_wks_sv("none");
822
0
    MIME_VALUE_NO_CACHE             = hdrtoken_string_to_wks_sv("no-cache");
823
0
    MIME_VALUE_NO_STORE             = hdrtoken_string_to_wks_sv("no-store");
824
0
    MIME_VALUE_NO_TRANSFORM         = hdrtoken_string_to_wks_sv("no-transform");
825
0
    MIME_VALUE_ONLY_IF_CACHED       = hdrtoken_string_to_wks_sv("only-if-cached");
826
0
    MIME_VALUE_PRIVATE              = hdrtoken_string_to_wks_sv("private");
827
0
    MIME_VALUE_PROXY_REVALIDATE     = hdrtoken_string_to_wks_sv("proxy-revalidate");
828
0
    MIME_VALUE_PUBLIC               = hdrtoken_string_to_wks_sv("public");
829
0
    MIME_VALUE_S_MAXAGE             = hdrtoken_string_to_wks_sv("s-maxage");
830
0
    MIME_VALUE_NEED_REVALIDATE_ONCE = hdrtoken_string_to_wks_sv("need-revalidate-once");
831
0
    MIME_VALUE_WEBSOCKET            = hdrtoken_string_to_wks_sv("websocket");
832
0
    MIME_VALUE_H2C                  = hdrtoken_string_to_wks_sv(MIME_UPGRADE_H2C_TOKEN);
833
834
0
    mime_init_date_format_table();
835
0
    mime_init_cache_control_cooking_masks();
836
0
  }
837
0
}
838
839
void
840
mime_init_cache_control_cooking_masks()
841
0
{
842
0
  static struct {
843
0
    const char *name;
844
0
    uint32_t    mask;
845
0
  } cc_mask_table[] = {
846
0
    {"max-age",              MIME_COOKED_MASK_CC_MAX_AGE             },
847
0
    {"no-cache",             MIME_COOKED_MASK_CC_NO_CACHE            },
848
0
    {"no-store",             MIME_COOKED_MASK_CC_NO_STORE            },
849
0
    {"no-transform",         MIME_COOKED_MASK_CC_NO_TRANSFORM        },
850
0
    {"max-stale",            MIME_COOKED_MASK_CC_MAX_STALE           },
851
0
    {"min-fresh",            MIME_COOKED_MASK_CC_MIN_FRESH           },
852
0
    {"only-if-cached",       MIME_COOKED_MASK_CC_ONLY_IF_CACHED      },
853
0
    {"public",               MIME_COOKED_MASK_CC_PUBLIC              },
854
0
    {"private",              MIME_COOKED_MASK_CC_PRIVATE             },
855
0
    {"must-revalidate",      MIME_COOKED_MASK_CC_MUST_REVALIDATE     },
856
0
    {"proxy-revalidate",     MIME_COOKED_MASK_CC_PROXY_REVALIDATE    },
857
0
    {"s-maxage",             MIME_COOKED_MASK_CC_S_MAXAGE            },
858
0
    {"need-revalidate-once", MIME_COOKED_MASK_CC_NEED_REVALIDATE_ONCE},
859
0
    {nullptr,                0                                       }
860
0
  };
861
862
0
  for (int i = 0; cc_mask_table[i].name != nullptr; i++) {
863
0
    const char         *wks                      = hdrtoken_string_to_wks(cc_mask_table[i].name);
864
0
    HdrTokenHeapPrefix *p                        = hdrtoken_wks_to_prefix(wks);
865
0
    p->wks_type_specific.u.cache_control.cc_mask = cc_mask_table[i].mask;
866
0
  }
867
0
}
868
869
void
870
mime_init_date_format_table()
871
0
{
872
  ////////////////////////////////////////////////////////////////
873
  // to speed up the days_since_epoch to m/d/y conversion, we   //
874
  // use a pre-computed lookup table to support the common case //
875
  // of dates that are +/- one year from today --- this code    //
876
  // builds the lookup table during the first call.             //
877
  ////////////////////////////////////////////////////////////////
878
879
0
  time_t now_secs;
880
0
  time_t i, now_days, first_days, last_days, num_days;
881
0
  int    m = 0, d = 0, y = 0;
882
883
0
  time(&now_secs);
884
0
  now_days   = static_cast<time_t>(now_secs / (60 * 60 * 24));
885
0
  first_days = now_days - 366;
886
0
  last_days  = now_days + 366;
887
0
  num_days   = last_days - first_days + 1;
888
889
0
  _days_to_mdy_fast_lookup_table           = static_cast<MDY *>(ats_malloc(num_days * sizeof(MDY)));
890
0
  _days_to_mdy_fast_lookup_table_first_day = first_days;
891
0
  _days_to_mdy_fast_lookup_table_last_day  = last_days;
892
893
0
  for (i = 0; i < num_days; i++) {
894
0
    mime_days_since_epoch_to_mdy_slowcase(first_days + i, &m, &d, &y);
895
0
    _days_to_mdy_fast_lookup_table[i].m = m;
896
0
    _days_to_mdy_fast_lookup_table[i].d = d;
897
0
    _days_to_mdy_fast_lookup_table[i].y = y;
898
0
  }
899
0
}
900
901
MIMEHdrImpl *
902
mime_hdr_create(HdrHeap *heap)
903
0
{
904
0
  MIMEHdrImpl *mh;
905
906
0
  mh = (MIMEHdrImpl *)heap->allocate_obj(sizeof(MIMEHdrImpl), HdrHeapObjType::MIME_HEADER);
907
0
  mime_hdr_init(mh);
908
0
  return mh;
909
0
}
910
911
void
912
_mime_hdr_field_block_init(MIMEFieldBlockImpl *fblock)
913
0
{
914
0
  fblock->m_freetop = 0;
915
0
  fblock->m_next    = nullptr;
916
917
#ifdef BLOCK_INIT_PARANOIA
918
  int i;
919
920
  // FIX: Could eliminate this initialization loop if we assumed
921
  //      every slot above the freetop of the block was garbage;
922
  //      but to be safe, and help debugging, for now we are eating
923
  //      the cost of initializing all slots in a block.
924
925
  for (i = 0; i < MIME_FIELD_BLOCK_SLOTS; i++) {
926
    MIMEField *field   = &(fblock->m_field_slots[i]);
927
    field->m_readiness = MIME_FIELD_SLOT_READINESS_EMPTY;
928
  }
929
#endif
930
0
}
931
932
void
933
mime_hdr_cooked_stuff_init(MIMEHdrImpl *mh, MIMEField *changing_field_or_null)
934
0
{
935
  // to be safe, reinitialize unless you know this call is for other cooked field
936
0
  if ((changing_field_or_null == nullptr) || (changing_field_or_null->m_wks_idx != MIME_WKSIDX_PRAGMA)) {
937
0
    mh->m_cooked_stuff.m_cache_control.m_mask           = 0;
938
0
    mh->m_cooked_stuff.m_cache_control.m_secs_max_age   = 0;
939
0
    mh->m_cooked_stuff.m_cache_control.m_secs_s_maxage  = 0;
940
0
    mh->m_cooked_stuff.m_cache_control.m_secs_max_stale = 0;
941
0
    mh->m_cooked_stuff.m_cache_control.m_secs_min_fresh = 0;
942
0
  }
943
0
  if ((changing_field_or_null == nullptr) || (changing_field_or_null->m_wks_idx != MIME_WKSIDX_CACHE_CONTROL)) {
944
0
    mh->m_cooked_stuff.m_pragma.m_no_cache = false;
945
0
  }
946
0
}
947
948
void
949
mime_hdr_init(MIMEHdrImpl *mh)
950
0
{
951
0
  mime_hdr_init_accelerators_and_presence_bits(mh);
952
953
0
  mime_hdr_cooked_stuff_init(mh, nullptr);
954
955
  // first header is inline: fake an object header for uniformity
956
0
  obj_init_header((HdrHeapObjImpl *)&(mh->m_first_fblock), HdrHeapObjType::FIELD_BLOCK, sizeof(MIMEFieldBlockImpl), 0);
957
958
0
  _mime_hdr_field_block_init(&(mh->m_first_fblock));
959
0
  mh->m_fblock_list_tail = &(mh->m_first_fblock);
960
961
0
  MIME_HDR_SANITY_CHECK(mh);
962
0
}
963
964
MIMEFieldBlockImpl *
965
_mime_field_block_copy(MIMEFieldBlockImpl *s_fblock, HdrHeap * /* s_heap ATS_UNUSED */, HdrHeap *d_heap)
966
0
{
967
0
  MIMEFieldBlockImpl *d_fblock;
968
969
0
  d_fblock = (MIMEFieldBlockImpl *)d_heap->allocate_obj(sizeof(MIMEFieldBlockImpl), HdrHeapObjType::FIELD_BLOCK);
970
0
  memcpy(d_fblock, s_fblock, sizeof(MIMEFieldBlockImpl));
971
0
  return d_fblock;
972
0
}
973
974
void
975
_mime_field_block_destroy(HdrHeap *heap, MIMEFieldBlockImpl *fblock)
976
0
{
977
0
  heap->deallocate_obj(fblock);
978
0
}
979
980
void
981
mime_hdr_destroy_field_block_list(HdrHeap *heap, MIMEFieldBlockImpl *head)
982
0
{
983
0
  MIMEFieldBlockImpl *next;
984
985
0
  while (head != nullptr) {
986
0
    next = head->m_next;
987
0
    _mime_field_block_destroy(heap, head);
988
0
    head = next;
989
0
  }
990
0
}
991
992
void
993
mime_hdr_destroy(HdrHeap *heap, MIMEHdrImpl *mh)
994
0
{
995
0
  mime_hdr_destroy_field_block_list(heap, mh->m_first_fblock.m_next);
996
997
  // INKqa11458: if we deallocate mh here and call TSMLocRelease
998
  // again, the plugin fails in assert. We leave deallocating to
999
  // the plugin using TSMLocRelease
1000
1001
  // heap->deallocate_obj(mh);
1002
0
}
1003
1004
void
1005
mime_hdr_copy_onto(MIMEHdrImpl *s_mh, HdrHeap *s_heap, MIMEHdrImpl *d_mh, HdrHeap *d_heap, bool inherit_strs)
1006
0
{
1007
0
  int                 block_count;
1008
0
  MIMEFieldBlockImpl *s_fblock, *d_fblock, *prev_d_fblock;
1009
1010
  // If there are chained field blocks beyond the first one, we're just going to
1011
  //   destroy them.  Ideally, we'd use them if the copied in header needed
1012
  //   extra blocks.  It's too late in the Tomcat code cycle to implement
1013
  //   reuse.
1014
0
  if (d_mh->m_first_fblock.m_next) {
1015
0
    mime_hdr_destroy_field_block_list(d_heap, d_mh->m_first_fblock.m_next);
1016
0
  }
1017
1018
0
  ink_assert(((char *)&(s_mh->m_first_fblock.m_field_slots[MIME_FIELD_BLOCK_SLOTS]) - (char *)s_mh) == sizeof(struct MIMEHdrImpl));
1019
1020
0
  int   top             = s_mh->m_first_fblock.m_freetop;
1021
0
  char *end             = reinterpret_cast<char *>(&(s_mh->m_first_fblock.m_field_slots[top]));
1022
0
  int   bytes_below_top = end - reinterpret_cast<char *>(s_mh);
1023
1024
  // copies useful part of enclosed first block too
1025
0
  memcpy(d_mh, s_mh, bytes_below_top);
1026
1027
0
  if (d_mh->m_first_fblock.m_next == nullptr) // common case: no other block
1028
0
  {
1029
0
    d_mh->m_fblock_list_tail = &(d_mh->m_first_fblock);
1030
0
    block_count              = 1;
1031
0
  } else // uncommon case: block list exists
1032
0
  {
1033
0
    prev_d_fblock = &(d_mh->m_first_fblock);
1034
0
    block_count   = 1;
1035
0
    for (s_fblock = s_mh->m_first_fblock.m_next; s_fblock != nullptr; s_fblock = s_fblock->m_next) {
1036
0
      ++block_count;
1037
0
      d_fblock              = _mime_field_block_copy(s_fblock, s_heap, d_heap);
1038
0
      prev_d_fblock->m_next = d_fblock;
1039
0
      prev_d_fblock         = d_fblock;
1040
0
    }
1041
0
    d_mh->m_fblock_list_tail = prev_d_fblock;
1042
0
  }
1043
1044
0
  if (inherit_strs) {
1045
0
    d_heap->inherit_string_heaps(s_heap);
1046
0
  }
1047
1048
0
  mime_hdr_field_block_list_adjust(block_count, &(s_mh->m_first_fblock), &(d_mh->m_first_fblock));
1049
1050
0
  MIME_HDR_SANITY_CHECK(s_mh);
1051
0
  MIME_HDR_SANITY_CHECK(d_mh);
1052
0
}
1053
1054
MIMEHdrImpl *
1055
mime_hdr_clone(MIMEHdrImpl *s_mh, HdrHeap *s_heap, HdrHeap *d_heap, bool inherit_strs)
1056
0
{
1057
0
  MIMEHdrImpl *d_mh;
1058
1059
0
  d_mh = mime_hdr_create(d_heap);
1060
0
  mime_hdr_copy_onto(s_mh, s_heap, d_mh, d_heap, inherit_strs);
1061
0
  return d_mh;
1062
0
}
1063
1064
/** Move a pointer from one list to another, keeping the relative offset.
1065
 * @return A pointer that has the same relative offset to @a dest_base as
1066
 * @a dest_ptr does to @a src_base.
1067
 */
1068
static inline MIMEField *
1069
rebase(MIMEField *dest_ptr,  ///< Original pointer into @src_base memory.
1070
       void      *dest_base, ///< New base pointer.
1071
       void      *src_base   ///< Original base pointer.
1072
)
1073
0
{
1074
0
  return reinterpret_cast<MIMEField *>(reinterpret_cast<char *>(dest_ptr) +
1075
0
                                       (static_cast<char *>(dest_base) - static_cast<char *>(src_base)));
1076
0
}
1077
1078
static inline void
1079
relocate(MIMEField *field, MIMEFieldBlockImpl *dest_block, MIMEFieldBlockImpl *src_block)
1080
0
{
1081
0
  for (; src_block; src_block = src_block->m_next, dest_block = dest_block->m_next) {
1082
0
    ink_release_assert(dest_block);
1083
1084
0
    if (field->m_next_dup >= src_block->m_field_slots && field->m_next_dup < src_block->m_field_slots + src_block->m_freetop) {
1085
0
      field->m_next_dup = rebase(field->m_next_dup, dest_block->m_field_slots, src_block->m_field_slots);
1086
0
      return;
1087
0
    }
1088
0
  }
1089
0
}
1090
1091
void
1092
mime_hdr_field_block_list_adjust(int /* block_count ATS_UNUSED */, MIMEFieldBlockImpl *old_list, MIMEFieldBlockImpl *new_list)
1093
0
{
1094
0
  for (MIMEFieldBlockImpl *new_blk = new_list; new_blk; new_blk = new_blk->m_next) {
1095
0
    for (MIMEField *field = new_blk->m_field_slots, *end = field + new_blk->m_freetop; field != end; ++field) {
1096
0
      if (field->is_live() && field->m_next_dup) {
1097
0
        relocate(field, new_list, old_list);
1098
0
      }
1099
0
    }
1100
0
  }
1101
0
}
1102
1103
int
1104
mime_hdr_length_get(MIMEHdrImpl *mh)
1105
0
{
1106
0
  unsigned int        length, index;
1107
0
  MIMEFieldBlockImpl *fblock;
1108
0
  MIMEField          *field;
1109
1110
0
  length = 2;
1111
1112
0
  for (fblock = &(mh->m_first_fblock); fblock != nullptr; fblock = fblock->m_next) {
1113
0
    for (index = 0; index < fblock->m_freetop; index++) {
1114
0
      field = &(fblock->m_field_slots[index]);
1115
0
      if (field->is_live()) {
1116
0
        length += mime_field_length_get(field);
1117
0
      }
1118
0
    }
1119
0
  }
1120
1121
0
  return length;
1122
0
}
1123
1124
void
1125
mime_hdr_fields_clear(HdrHeap *heap, MIMEHdrImpl *mh)
1126
0
{
1127
0
  mime_hdr_destroy_field_block_list(heap, mh->m_first_fblock.m_next);
1128
0
  mime_hdr_init(mh);
1129
0
}
1130
1131
MIMEField *
1132
_mime_hdr_field_list_search_by_wks(MIMEHdrImpl *mh, int wks_idx)
1133
0
{
1134
0
  MIMEFieldBlockImpl *fblock;
1135
0
  MIMEField          *field, *too_far_field;
1136
1137
0
  ink_assert(hdrtoken_is_valid_wks_idx(wks_idx));
1138
1139
0
  for (fblock = &(mh->m_first_fblock); fblock != nullptr; fblock = fblock->m_next) {
1140
0
    field = &(fblock->m_field_slots[0]);
1141
1142
0
    too_far_field = &(fblock->m_field_slots[fblock->m_freetop]);
1143
0
    while (field < too_far_field) {
1144
0
      if (field->is_live() && (field->m_wks_idx == wks_idx)) {
1145
0
        return field;
1146
0
      }
1147
0
      ++field;
1148
0
    }
1149
0
  }
1150
1151
0
  return nullptr;
1152
0
}
1153
1154
MIMEField *
1155
_mime_hdr_field_list_search_by_string(MIMEHdrImpl *mh, std::string_view field_name)
1156
0
{
1157
0
  MIMEFieldBlockImpl *fblock;
1158
0
  MIMEField          *field, *too_far_field;
1159
1160
0
  ink_assert(mh);
1161
0
  for (fblock = &(mh->m_first_fblock); fblock != nullptr; fblock = fblock->m_next) {
1162
0
    field = &(fblock->m_field_slots[0]);
1163
1164
0
    too_far_field = &(fblock->m_field_slots[fblock->m_freetop]);
1165
0
    while (field < too_far_field) {
1166
0
      if (field->is_live() &&
1167
0
          ts::iequals(std::string_view{field->m_ptr_name, static_cast<std::string_view::size_type>(field->m_len_name)},
1168
0
                      field_name)) {
1169
0
        return field;
1170
0
      }
1171
0
      ++field;
1172
0
    }
1173
0
  }
1174
1175
0
  return nullptr;
1176
0
}
1177
1178
MIMEField *
1179
_mime_hdr_field_list_search_by_slotnum(MIMEHdrImpl *mh, int slotnum)
1180
0
{
1181
0
  unsigned int        block_num, block_index;
1182
0
  MIMEFieldBlockImpl *fblock;
1183
1184
0
  if (slotnum < MIME_FIELD_BLOCK_SLOTS) {
1185
0
    fblock      = &(mh->m_first_fblock);
1186
0
    block_index = slotnum;
1187
0
    if (block_index >= fblock->m_freetop) {
1188
0
      return nullptr;
1189
0
    } else {
1190
0
      return &(fblock->m_field_slots[block_index]);
1191
0
    }
1192
0
  } else {
1193
0
    block_num   = slotnum / MIME_FIELD_BLOCK_SLOTS;
1194
0
    block_index = slotnum % MIME_FIELD_BLOCK_SLOTS;
1195
1196
0
    fblock = &(mh->m_first_fblock);
1197
0
    while (block_num-- && fblock) {
1198
0
      fblock = fblock->m_next;
1199
0
    }
1200
0
    if ((fblock == nullptr) || (block_index >= fblock->m_freetop)) {
1201
0
      return nullptr;
1202
0
    } else {
1203
0
      return &(fblock->m_field_slots[block_index]);
1204
0
    }
1205
0
  }
1206
0
}
1207
1208
MIMEField *
1209
mime_hdr_field_find(MIMEHdrImpl *mh, std::string_view field_name)
1210
0
{
1211
0
  HdrTokenHeapPrefix *token_info;
1212
0
  const bool          is_wks = hdrtoken_is_wks(field_name.data());
1213
1214
0
  ink_assert(!field_name.empty());
1215
1216
  ////////////////////////////////////////////
1217
  // do presence check and slot accelerator //
1218
  ////////////////////////////////////////////
1219
1220
#if TRACK_FIELD_FIND_CALLS
1221
  Dbg(dbg_ctl_http, "mime_hdr_field_find(hdr 0x%X, field %.*s): is_wks = %d", mh, static_cast<int>(field_name.length()),
1222
      field_name.data(), is_wks);
1223
#endif
1224
1225
0
  if (is_wks) {
1226
0
    token_info = hdrtoken_wks_to_prefix(field_name.data());
1227
0
    if ((token_info->wks_info.mask) && ((mh->m_presence_bits & token_info->wks_info.mask) == 0)) {
1228
#if TRACK_FIELD_FIND_CALLS
1229
      Dbg(dbg_ctl_http, "mime_hdr_field_find(hdr 0x%X, field %.*s): MISS (due to presence bits)", mh, field_name_len,
1230
          field_name_str);
1231
#endif
1232
0
      return nullptr;
1233
0
    }
1234
1235
0
    int32_t slot_id = token_info->wks_info.slotid;
1236
1237
0
    if (slot_id != MIME_SLOTID_NONE) {
1238
0
      uint32_t slotnum = mime_hdr_get_accelerator_slotnum(mh, slot_id);
1239
1240
0
      if (slotnum != MIME_FIELD_SLOTNUM_UNKNOWN) {
1241
0
        MIMEField *f = _mime_hdr_field_list_search_by_slotnum(mh, slotnum);
1242
0
        ink_assert((f == nullptr) || f->is_live());
1243
#if TRACK_FIELD_FIND_CALLS
1244
        Dbg(dbg_ctl_http, "mime_hdr_field_find(hdr 0x%X, field %.*s): %s (due to slot accelerators)", mh,
1245
            static_cast<int>(field_name.length()), field_name.data(), (f ? "HIT" : "MISS"));
1246
#endif
1247
0
        return f;
1248
0
      } else {
1249
#if TRACK_FIELD_FIND_CALLS
1250
        Dbg(dbg_ctl_http, "mime_hdr_field_find(hdr 0x%X, field %.*s): UNKNOWN (slot too big)", mh,
1251
            static_cast<int>(field_name.length()), field_name.data());
1252
#endif
1253
0
      }
1254
0
    }
1255
1256
    ///////////////////////////////////////////////////////////////////////////
1257
    // search by well-known string index or by case-insensitive string match //
1258
    ///////////////////////////////////////////////////////////////////////////
1259
1260
0
    MIMEField *f = _mime_hdr_field_list_search_by_wks(mh, token_info->wks_idx);
1261
0
    ink_assert((f == nullptr) || f->is_live());
1262
#if TRACK_FIELD_FIND_CALLS
1263
    Dbg(dbg_ctl_http, "mime_hdr_field_find(hdr 0x%X, field %.*s): %s (due to WKS list walk)", mh,
1264
        static_cast<int>(field_name.length()), field_name.data(), (f ? "HIT" : "MISS"));
1265
#endif
1266
0
    return f;
1267
0
  } else {
1268
0
    MIMEField *f = _mime_hdr_field_list_search_by_string(mh, field_name);
1269
1270
0
    ink_assert((f == nullptr) || f->is_live());
1271
#if TRACK_FIELD_FIND_CALLS
1272
    Dbg(dbg_ctl_http, "mime_hdr_field_find(hdr 0x%X, field %.*s): %s (due to strcmp list walk)", mh,
1273
        static_cast<int>(field_name.length()), field_name.data(), (f ? "HIT" : "MISS"));
1274
#endif
1275
0
    return f;
1276
0
  }
1277
0
}
1278
1279
MIMEField *
1280
mime_hdr_field_get(MIMEHdrImpl *mh, int idx)
1281
0
{
1282
0
  unsigned int        index;
1283
0
  MIMEFieldBlockImpl *fblock;
1284
0
  MIMEField          *field;
1285
0
  int                 got_idx;
1286
1287
0
  got_idx = -1;
1288
1289
0
  for (fblock = &(mh->m_first_fblock); fblock != nullptr; fblock = fblock->m_next) {
1290
0
    for (index = 0; index < fblock->m_freetop; index++) {
1291
0
      field = &(fblock->m_field_slots[index]);
1292
0
      if (field->is_live()) {
1293
0
        ++got_idx;
1294
0
      }
1295
0
      if (got_idx == idx) {
1296
0
        return field;
1297
0
      }
1298
0
    }
1299
0
  }
1300
1301
0
  return nullptr;
1302
0
}
1303
1304
MIMEField *
1305
mime_hdr_field_get_slotnum(MIMEHdrImpl *mh, int slotnum)
1306
0
{
1307
0
  return _mime_hdr_field_list_search_by_slotnum(mh, slotnum);
1308
0
}
1309
1310
int
1311
mime_hdr_fields_count(MIMEHdrImpl *mh)
1312
0
{
1313
0
  unsigned int        index;
1314
0
  MIMEFieldBlockImpl *fblock;
1315
0
  MIMEField          *field;
1316
0
  int                 count;
1317
1318
0
  count = 0;
1319
1320
0
  for (fblock = &(mh->m_first_fblock); fblock != nullptr; fblock = fblock->m_next) {
1321
0
    for (index = 0; index < fblock->m_freetop; index++) {
1322
0
      field = &(fblock->m_field_slots[index]);
1323
0
      if (field->is_live()) {
1324
0
        ++count;
1325
0
      }
1326
0
    }
1327
0
  }
1328
1329
0
  return count;
1330
0
}
1331
1332
void
1333
mime_field_init(MIMEField *field)
1334
0
{
1335
0
  memset(field, 0, sizeof(MIMEField));
1336
0
  field->m_readiness = MIME_FIELD_SLOT_READINESS_DETACHED;
1337
0
  field->m_wks_idx   = -1;
1338
0
}
1339
1340
MIMEField *
1341
mime_field_create(HdrHeap *heap, MIMEHdrImpl *mh)
1342
0
{
1343
0
  MIMEField          *field;
1344
0
  MIMEFieldBlockImpl *tail_fblock, *new_fblock;
1345
1346
0
  tail_fblock = mh->m_fblock_list_tail;
1347
0
  if (tail_fblock->m_freetop >= MIME_FIELD_BLOCK_SLOTS) {
1348
0
    new_fblock = (MIMEFieldBlockImpl *)heap->allocate_obj(sizeof(MIMEFieldBlockImpl), HdrHeapObjType::FIELD_BLOCK);
1349
0
    _mime_hdr_field_block_init(new_fblock);
1350
0
    tail_fblock->m_next    = new_fblock;
1351
0
    tail_fblock            = new_fblock;
1352
0
    mh->m_fblock_list_tail = new_fblock;
1353
0
  }
1354
1355
0
  field = &(tail_fblock->m_field_slots[tail_fblock->m_freetop]);
1356
0
  ++tail_fblock->m_freetop;
1357
1358
0
  mime_field_init(field);
1359
1360
0
  return field;
1361
0
}
1362
1363
MIMEField *
1364
mime_field_create_named(HdrHeap *heap, MIMEHdrImpl *mh, std::string_view name)
1365
0
{
1366
0
  MIMEField *field              = mime_field_create(heap, mh);
1367
0
  int        field_name_wks_idx = hdrtoken_tokenize(name.data(), static_cast<int>(name.length()));
1368
0
  mime_field_name_set(heap, mh, field, field_name_wks_idx, name, true);
1369
0
  return field;
1370
0
}
1371
1372
void
1373
mime_hdr_field_attach(MIMEHdrImpl *mh, MIMEField *field, int check_for_dups, MIMEField *prev_dup)
1374
0
{
1375
0
  MIME_HDR_SANITY_CHECK(mh);
1376
1377
0
  if (!field->is_detached()) {
1378
0
    return;
1379
0
  }
1380
1381
0
  ink_assert(field->m_ptr_name != nullptr);
1382
1383
  //////////////////////////////////////////////////
1384
  // if we don't know the head dup, or are given  //
1385
  // a non-head dup, then search for the head dup //
1386
  //////////////////////////////////////////////////
1387
1388
0
  if (check_for_dups || (prev_dup && (!prev_dup->is_dup_head()))) {
1389
0
    std::string_view name{field->name_get()};
1390
0
    prev_dup = mime_hdr_field_find(mh, name);
1391
0
    ink_assert((prev_dup == nullptr) || (prev_dup->is_dup_head()));
1392
0
  }
1393
1394
0
  field->m_readiness = MIME_FIELD_SLOT_READINESS_LIVE;
1395
1396
  ////////////////////////////////////////////////////////////////////
1397
  // now, attach the new field --- if there are dups, make sure the //
1398
  // field is patched into the dup list in increasing slot order to //
1399
  // maintain the invariant that dups are chained in slot order     //
1400
  ////////////////////////////////////////////////////////////////////
1401
1402
0
  if (prev_dup) {
1403
0
    MIMEField *next_dup;
1404
0
    int        field_slotnum, prev_slotnum, next_slotnum;
1405
1406
    /////////////////////////////////////////////////////////////////
1407
    // walk down dup list looking for the last dup in slot-order   //
1408
    // before this field object --- meaning a dup before the field //
1409
    // in slot order who either has no next dup, or whose next dup //
1410
    // is numerically after the field in slot order.               //
1411
    /////////////////////////////////////////////////////////////////
1412
1413
0
    field_slotnum = mime_hdr_field_slotnum(mh, field);
1414
0
    prev_slotnum  = mime_hdr_field_slotnum(mh, prev_dup);
1415
0
    next_dup      = prev_dup->m_next_dup;
1416
0
    next_slotnum  = (next_dup ? mime_hdr_field_slotnum(mh, next_dup) : -1);
1417
1418
0
    ink_assert(field_slotnum != prev_slotnum);
1419
1420
0
    while (prev_slotnum < field_slotnum) // break if prev after field
1421
0
    {
1422
0
      if (next_dup == nullptr) {
1423
0
        break; // no next dup, we're done
1424
0
      }
1425
0
      if (next_slotnum > field_slotnum) {
1426
0
        break; // next dup is after us, we're done
1427
0
      }
1428
0
      prev_dup     = next_dup;
1429
0
      prev_slotnum = next_slotnum;
1430
0
      next_dup     = prev_dup->m_next_dup;
1431
0
    }
1432
1433
    /////////////////////////////////////////////////////
1434
    // we get here if the prev_slotnum > field_slotnum //
1435
    // (meaning we're now the first dup in the list),  //
1436
    // or when we've found the correct prev and next   //
1437
    /////////////////////////////////////////////////////
1438
1439
0
    if (prev_slotnum > field_slotnum) // we are now the head
1440
0
    {
1441
      /////////////////////////////////////////////////////////////
1442
      // here, it turns out that "prev_dup" is actually after    //
1443
      // "field" in the list of fields --- so, prev_dup is a bit //
1444
      // of a misnomer, it is actually, the NEXT field!          //
1445
      /////////////////////////////////////////////////////////////
1446
1447
0
      field->m_flags    = (field->m_flags | MIME_FIELD_SLOT_FLAGS_DUP_HEAD);
1448
0
      field->m_next_dup = prev_dup;
1449
0
      prev_dup->m_flags = (prev_dup->m_flags & ~MIME_FIELD_SLOT_FLAGS_DUP_HEAD);
1450
0
      mime_hdr_set_accelerators_and_presence_bits(mh, field);
1451
0
    } else // patch us after prev, and before next
1452
0
    {
1453
0
      ink_assert(prev_slotnum < field_slotnum);
1454
0
      ink_assert((next_dup == nullptr) || (next_slotnum > field_slotnum));
1455
0
      field->m_flags = (field->m_flags & ~MIME_FIELD_SLOT_FLAGS_DUP_HEAD);
1456
0
      ink_assert((next_dup == nullptr) || next_dup->is_live());
1457
0
      prev_dup->m_next_dup = field;
1458
0
      field->m_next_dup    = next_dup;
1459
0
    }
1460
0
  } else {
1461
0
    field->m_flags = (field->m_flags | MIME_FIELD_SLOT_FLAGS_DUP_HEAD);
1462
0
    mime_hdr_set_accelerators_and_presence_bits(mh, field);
1463
0
  }
1464
1465
  // Now keep the cooked cache consistent
1466
0
  ink_assert(field->is_live());
1467
0
  if (field->m_ptr_value && field->is_cooked()) {
1468
0
    mh->recompute_cooked_stuff(field);
1469
0
  }
1470
1471
0
  MIME_HDR_SANITY_CHECK(mh);
1472
0
}
1473
1474
void
1475
mime_hdr_field_detach(MIMEHdrImpl *mh, MIMEField *field, bool detach_all_dups)
1476
0
{
1477
0
  ink_assert(mh);
1478
0
  MIMEField *next_dup = field->m_next_dup;
1479
1480
  // If this field is already detached, there's nothing to do. There must
1481
  // not be a dup list if we detached correctly.
1482
0
  if (field->is_detached()) {
1483
0
    ink_assert(next_dup == nullptr);
1484
0
    return;
1485
0
  }
1486
1487
0
  ink_assert(field->is_live());
1488
0
  MIME_HDR_SANITY_CHECK(mh);
1489
1490
  // Normally, this function is called with the current dup list head,
1491
  // so, we need to update the accelerators after the patch out.  But, if
1492
  // this function is ever called in the middle of a dup list, we need
1493
  // to walk the list to find the previous dup in the list to patch out
1494
  // the dup being detached.
1495
1496
0
  if (field->m_flags & MIME_FIELD_SLOT_FLAGS_DUP_HEAD) // head of list?
1497
0
  {
1498
0
    if (!next_dup) // only child
1499
0
    {
1500
0
      mime_hdr_unset_accelerators_and_presence_bits(mh, field);
1501
0
    } else // next guy is dup head
1502
0
    {
1503
0
      next_dup->m_flags |= MIME_FIELD_SLOT_FLAGS_DUP_HEAD;
1504
0
      mime_hdr_set_accelerators_and_presence_bits(mh, next_dup);
1505
0
    }
1506
0
  } else // need to walk list to find and patch out from predecessor
1507
0
  {
1508
0
    std::string_view name{field->name_get()};
1509
0
    MIMEField       *prev = mime_hdr_field_find(mh, name);
1510
1511
0
    while (prev && (prev->m_next_dup != field)) {
1512
0
      prev = prev->m_next_dup;
1513
0
    }
1514
0
    ink_assert(prev != nullptr);
1515
1516
0
    if (prev->m_next_dup == field) {
1517
0
      prev->m_next_dup = next_dup;
1518
0
    }
1519
0
  }
1520
1521
  // Field is now detached and alone
1522
0
  field->m_readiness = MIME_FIELD_SLOT_READINESS_DETACHED;
1523
0
  field->m_next_dup  = nullptr;
1524
1525
  // Because we changed the values through detaching,update the cooked cache
1526
0
  if (field->is_cooked()) {
1527
0
    mh->recompute_cooked_stuff(field);
1528
0
  }
1529
1530
0
  MIME_HDR_SANITY_CHECK(mh);
1531
1532
  // At this point, the list should be back to a valid state, either the
1533
  // next dup detached and the accelerators set to the next dup (if any),
1534
  // or an interior dup detached and patched around.  If we are requested
1535
  // to delete the whole dup list, we tail-recurse to delete it.
1536
1537
0
  if (detach_all_dups && next_dup) {
1538
0
    mime_hdr_field_detach(mh, next_dup, detach_all_dups);
1539
0
  }
1540
0
}
1541
1542
void
1543
mime_hdr_field_delete(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, bool delete_all_dups)
1544
0
{
1545
0
  if (delete_all_dups) {
1546
0
    while (field) {
1547
0
      MIMEField *next = field->m_next_dup;
1548
0
      mime_hdr_field_delete(heap, mh, field, false);
1549
0
      field = next;
1550
0
    }
1551
0
  } else {
1552
0
    heap->free_string(field->m_ptr_name, field->m_len_name);
1553
0
    heap->free_string(field->m_ptr_value, field->m_len_value);
1554
1555
0
    MIME_HDR_SANITY_CHECK(mh);
1556
0
    mime_hdr_field_detach(mh, field, false);
1557
1558
0
    MIME_HDR_SANITY_CHECK(mh);
1559
0
    mime_field_destroy(mh, field);
1560
1561
0
    MIMEFieldBlockImpl *prev_block        = nullptr;
1562
0
    bool                can_destroy_block = true;
1563
0
    for (auto fblock = &(mh->m_first_fblock); fblock != nullptr; fblock = fblock->m_next) {
1564
0
      if (prev_block != nullptr) {
1565
0
        if (fblock->m_freetop == MIME_FIELD_BLOCK_SLOTS && fblock->contains(field)) {
1566
          // Check if fields in all slots are deleted
1567
0
          for (auto &m_field_slot : fblock->m_field_slots) {
1568
0
            if (m_field_slot.m_readiness != MIME_FIELD_SLOT_READINESS_DELETED) {
1569
0
              can_destroy_block = false;
1570
0
              break;
1571
0
            }
1572
0
          }
1573
          // Destroy a block and maintain the chain
1574
0
          if (can_destroy_block) {
1575
0
            prev_block->m_next = fblock->m_next;
1576
0
            _mime_field_block_destroy(heap, fblock);
1577
0
            if (prev_block->m_next == nullptr) {
1578
0
              mh->m_fblock_list_tail = prev_block;
1579
0
            }
1580
0
          }
1581
0
          break;
1582
0
        }
1583
0
      }
1584
0
      prev_block = fblock;
1585
0
    }
1586
0
  }
1587
1588
0
  MIME_HDR_SANITY_CHECK(mh);
1589
0
}
1590
1591
auto
1592
MIMEHdrImpl::find(MIMEField const *field) -> iterator
1593
0
{
1594
0
  for (MIMEFieldBlockImpl *fblock = &m_first_fblock; fblock != nullptr; fblock = fblock->m_next) {
1595
0
    if (fblock->contains(field)) {
1596
0
      return {fblock, unsigned(field - fblock->m_field_slots)};
1597
0
    }
1598
0
  }
1599
0
  return {};
1600
0
}
1601
1602
// This function needs to be removed - use of it indicates poorly implemented code.
1603
// That code should be updated to use the field iterators, which are much more performant.
1604
int
1605
mime_hdr_field_slotnum(MIMEHdrImpl *mh, MIMEField *field)
1606
0
{
1607
0
  int                 slots_so_far;
1608
0
  MIMEFieldBlockImpl *fblock;
1609
1610
0
  slots_so_far = 0;
1611
0
  for (fblock = &(mh->m_first_fblock); fblock != nullptr; fblock = fblock->m_next) {
1612
0
    if (fblock->contains(field)) {
1613
0
      MIMEField *first      = &(fblock->m_field_slots[0]);
1614
0
      ptrdiff_t  block_slot = field - first; // in units of MIMEField
1615
0
      return slots_so_far + block_slot;
1616
0
    }
1617
0
    slots_so_far += MIME_FIELD_BLOCK_SLOTS;
1618
0
  }
1619
0
  return -1;
1620
0
}
1621
1622
MIMEField *
1623
mime_hdr_prepare_for_value_set(HdrHeap *heap, MIMEHdrImpl *mh, std::string_view name)
1624
0
{
1625
0
  int        wks_idx;
1626
0
  MIMEField *field;
1627
1628
0
  field = mime_hdr_field_find(mh, name);
1629
1630
  //////////////////////////////////////////////////////////////////////
1631
  // this function returns with exactly one attached field created,   //
1632
  // ready to have its value set.                                     //
1633
  //                                                                  //
1634
  // on return from field_find, there are 3 possibilities:            //
1635
  //   no field found:      create attached, named field              //
1636
  //   field found w/dups:  delete list, create attached, named field //
1637
  //   dupless field found: return the field for mutation             //
1638
  //////////////////////////////////////////////////////////////////////
1639
1640
0
  if (field == nullptr) // no fields of this name
1641
0
  {
1642
0
    wks_idx = hdrtoken_tokenize(name.data(), static_cast<int>(name.length()));
1643
0
    field   = mime_field_create(heap, mh);
1644
0
    mime_field_name_set(heap, mh, field, wks_idx, name, true);
1645
0
    mime_hdr_field_attach(mh, field, 0, nullptr);
1646
1647
0
  } else if (field->m_next_dup) // list of more than 1 field
1648
0
  {
1649
0
    wks_idx = field->m_wks_idx;
1650
0
    mime_hdr_field_delete(heap, mh, field, true);
1651
0
    field = mime_field_create(heap, mh);
1652
0
    mime_field_name_set(heap, mh, field, wks_idx, name, true);
1653
0
    mime_hdr_field_attach(mh, field, 0, nullptr);
1654
0
  }
1655
0
  return field;
1656
0
}
1657
1658
void
1659
mime_field_destroy(MIMEHdrImpl * /* mh ATS_UNUSED */, MIMEField *field)
1660
0
{
1661
0
  ink_assert(field->m_readiness == MIME_FIELD_SLOT_READINESS_DETACHED);
1662
0
  field->m_readiness = MIME_FIELD_SLOT_READINESS_DELETED;
1663
0
}
1664
1665
std::string_view
1666
MIMEField::name_get() const
1667
0
{
1668
0
  if (m_wks_idx >= 0) {
1669
0
    return {hdrtoken_index_to_wks(m_wks_idx), m_len_name};
1670
0
  }
1671
0
  return {m_ptr_name, m_len_name};
1672
0
}
1673
1674
void
1675
mime_field_name_set(HdrHeap *heap, MIMEHdrImpl * /* mh ATS_UNUSED */, MIMEField *field, int16_t name_wks_idx_or_neg1,
1676
                    std::string_view name, bool must_copy_string)
1677
0
{
1678
0
  ink_assert(field->m_readiness == MIME_FIELD_SLOT_READINESS_DETACHED);
1679
1680
0
  field->m_wks_idx = name_wks_idx_or_neg1;
1681
0
  mime_str_u16_set(heap, name, &(field->m_ptr_name), &(field->m_len_name), must_copy_string);
1682
1683
0
  if ((name_wks_idx_or_neg1 == MIME_WKSIDX_CACHE_CONTROL) || (name_wks_idx_or_neg1 == MIME_WKSIDX_PRAGMA)) {
1684
0
    field->m_flags |= MIME_FIELD_SLOT_FLAGS_COOKED;
1685
0
  }
1686
0
}
1687
1688
int
1689
MIMEField::value_get_index(std::string_view value) const
1690
0
{
1691
0
  int  retval = -1;
1692
0
  auto length{static_cast<int>(value.length())};
1693
1694
  // if field doesn't support commas and there is just one instance, just compare the value
1695
0
  if (!this->supports_commas() && !this->has_dups()) {
1696
0
    if (this->m_len_value == static_cast<uint32_t>(length) && strncasecmp(value.data(), this->m_ptr_value, length) == 0) {
1697
0
      retval = 0;
1698
0
    }
1699
0
  } else {
1700
0
    HdrCsvIter  iter;
1701
0
    int         tok_len;
1702
0
    int         index = 0;
1703
0
    const char *tok   = iter.get_first(this, &tok_len);
1704
1705
0
    while (tok) {
1706
0
      if (tok_len == length && strncasecmp(tok, value.data(), length) == 0) {
1707
0
        retval = index;
1708
0
        break;
1709
0
      } else {
1710
0
        index++;
1711
0
      }
1712
0
      tok = iter.get_next(&tok_len);
1713
0
    }
1714
0
  }
1715
1716
0
  return retval;
1717
0
}
1718
1719
std::string_view
1720
MIMEField::value_get() const
1721
0
{
1722
0
  return {m_ptr_value, m_len_value};
1723
0
}
1724
1725
int32_t
1726
mime_field_value_get_int(const MIMEField *field)
1727
0
{
1728
0
  std::string_view value{field->value_get()};
1729
1730
0
  return mime_parse_int(value.data(), value.data() + value.size());
1731
0
}
1732
1733
uint32_t
1734
mime_field_value_get_uint(const MIMEField *field)
1735
0
{
1736
0
  std::string_view value{field->value_get()};
1737
1738
0
  return mime_parse_uint(value.data(), value.data() + value.size());
1739
0
}
1740
1741
int64_t
1742
mime_field_value_get_int64(const MIMEField *field)
1743
0
{
1744
0
  std::string_view value{field->value_get()};
1745
1746
0
  return mime_parse_int64(value.data(), value.data() + value.size());
1747
0
}
1748
1749
time_t
1750
mime_field_value_get_date(const MIMEField *field)
1751
0
{
1752
0
  std::string_view value{field->value_get()};
1753
1754
0
  return mime_parse_date(value.data(), value.data() + value.size());
1755
0
}
1756
1757
const char *
1758
mime_field_value_get_comma_val(const MIMEField *field, int *length, int idx)
1759
0
{
1760
  // some fields (like Date) contain commas but should not be ripped apart
1761
0
  if (!field->supports_commas()) {
1762
0
    if (idx == 0) {
1763
0
      auto value{field->value_get()};
1764
0
      *length = static_cast<int>(value.size());
1765
0
      return value.data();
1766
0
    }
1767
0
    return nullptr;
1768
0
  } else {
1769
0
    Str    *str;
1770
0
    StrList list(false);
1771
1772
0
    mime_field_value_get_comma_list(field, &list);
1773
0
    str = list.get_idx(idx);
1774
0
    if (str != nullptr) {
1775
0
      *length = static_cast<int>(str->len);
1776
0
      return str->str;
1777
0
    } else {
1778
0
      *length = 0;
1779
0
      return nullptr;
1780
0
    }
1781
0
  }
1782
0
}
1783
1784
int
1785
mime_field_value_get_comma_val_count(const MIMEField *field)
1786
0
{
1787
  // some fields (like Date) contain commas but should not be ripped apart
1788
0
  if (!field->supports_commas()) {
1789
0
    return ((field->m_len_value == 0) ? 0 : 1);
1790
0
  } else {
1791
0
    StrList list(false);
1792
0
    int     count = mime_field_value_get_comma_list(field, &list);
1793
0
    return count;
1794
0
  }
1795
0
}
1796
1797
int
1798
mime_field_value_get_comma_list(const MIMEField *field, StrList *list)
1799
0
{
1800
0
  std::string_view value{field->value_get()};
1801
1802
  // if field doesn't support commas, don't rip apart.
1803
0
  if (!field->supports_commas()) {
1804
0
    list->append_string(value.data(), static_cast<int>(value.size()));
1805
0
  } else {
1806
0
    HttpCompat::parse_tok_list(list, 1, value.data(), static_cast<int>(value.size()), ',');
1807
0
  }
1808
1809
0
  return list->count;
1810
0
}
1811
1812
const char *
1813
mime_field_value_str_from_strlist(HdrHeap *heap, int *new_str_len_return, StrList *list)
1814
0
{
1815
0
  Str  *cell;
1816
0
  char *new_value, *dest;
1817
0
  int   i, new_value_len;
1818
  // This works, because all strings are from the same heap when it is "split" into the list.
1819
0
  HdrHeap::HeapGuard guard(heap, list->head->str);
1820
1821
0
  new_value_len = 0;
1822
1823
  // (1) walk the StrList cells, summing each cell's string lengths,
1824
  //     and add 2 bytes for each ", " between cells
1825
0
  cell = list->head;
1826
0
  for (i = 0; i < list->count; i++) {
1827
0
    new_value_len += cell->len;
1828
0
    cell           = cell->next;
1829
0
  }
1830
0
  if (list->count > 1) {
1831
0
    new_value_len += (2 * (list->count - 1));
1832
0
  }
1833
1834
  // (2) allocate new heap string
1835
0
  new_value = heap->allocate_str(new_value_len);
1836
1837
  // (3) copy string pieces into new heap string
1838
0
  dest = new_value;
1839
0
  cell = list->head;
1840
0
  for (i = 0; i < list->count; i++) {
1841
0
    if (i != 0) {
1842
0
      *dest++ = ',';
1843
0
      *dest++ = ' ';
1844
0
    }
1845
0
    memcpy(dest, cell->str, cell->len);
1846
0
    dest += cell->len;
1847
0
    cell  = cell->next;
1848
0
  }
1849
0
  ink_assert(dest - new_value == new_value_len);
1850
1851
0
  *new_str_len_return = new_value_len;
1852
0
  return new_value;
1853
0
}
1854
1855
void
1856
mime_field_value_set_comma_val(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, int idx, std::string_view new_piece)
1857
0
{
1858
0
  int     len;
1859
0
  Str    *cell;
1860
0
  StrList list(false);
1861
1862
  // (1) rip the value into tokens, keeping surrounding quotes, but not whitespace
1863
0
  HttpCompat::parse_tok_list(&list, 0, field->m_ptr_value, field->m_len_value, ',');
1864
1865
  // (2) if desired index isn't valid, then don't change the field
1866
0
  if ((idx < 0) || (idx >= list.count)) {
1867
0
    return;
1868
0
  }
1869
1870
  // (3) mutate cell idx
1871
0
  cell = list.get_idx(idx);
1872
0
  ink_assert(cell != nullptr);
1873
0
  cell->str = new_piece.data();
1874
0
  cell->len = new_piece.length();
1875
1876
  // (4) reassemble the new string
1877
0
  field->m_ptr_value = mime_field_value_str_from_strlist(heap, &len, &list);
1878
0
  field->m_len_value = len;
1879
1880
  // (5) keep stuff fields consistent
1881
0
  field->m_n_v_raw_printable = 0;
1882
0
  if (field->is_live() && field->is_cooked()) {
1883
0
    mh->recompute_cooked_stuff(field);
1884
0
  }
1885
0
}
1886
1887
void
1888
mime_field_value_delete_comma_val(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, int idx)
1889
0
{
1890
0
  int     len;
1891
0
  Str    *cell;
1892
0
  StrList list(false);
1893
1894
  // (1) rip the value into tokens, keeping surrounding quotes, but not whitespace
1895
0
  HttpCompat::parse_tok_list(&list, 0, field->m_ptr_value, field->m_len_value, ',');
1896
1897
  // (2) if desired index isn't valid, then don't change the field
1898
0
  if ((idx < 0) || (idx >= list.count)) {
1899
0
    return;
1900
0
  }
1901
1902
  // (3) delete cell idx
1903
0
  cell = list.get_idx(idx);
1904
0
  list.detach(cell);
1905
1906
  /**********************************************/
1907
  /*   Fix for bug INKqa09752                   */
1908
  /*                                            */
1909
  /*   If this is the last value                */
1910
  /*   in the field, set the m_ptr_val to NULL  */
1911
  /**********************************************/
1912
1913
0
  if (list.count == 0) {
1914
0
    field->m_ptr_value = nullptr;
1915
0
    field->m_len_value = 0;
1916
0
  } else {
1917
    /************************************/
1918
    /*   End Fix for bug INKqa09752     */
1919
    /************************************/
1920
1921
    // (4) reassemble the new string
1922
0
    field->m_ptr_value = mime_field_value_str_from_strlist(heap, &len, &list);
1923
0
    field->m_len_value = len;
1924
0
  }
1925
1926
  // (5) keep stuff fields consistent
1927
0
  field->m_n_v_raw_printable = 0;
1928
0
  if (field->is_live() && field->is_cooked()) {
1929
0
    mh->recompute_cooked_stuff(field);
1930
0
  }
1931
0
}
1932
1933
void
1934
mime_field_value_insert_comma_val(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, int idx, std::string_view new_piece)
1935
0
{
1936
0
  int     len;
1937
0
  Str    *cell, *prev;
1938
0
  StrList list(false);
1939
1940
  // (1) rip the value into tokens, keeping surrounding quotes, but not whitespace
1941
0
  HttpCompat::parse_tok_list(&list, 0, field->m_ptr_value, field->m_len_value, ',');
1942
1943
  // (2) if desired index isn't valid, then don't change the field
1944
0
  if (idx < 0) {
1945
0
    idx = list.count;
1946
0
  }
1947
0
  if (idx > list.count) {
1948
0
    return;
1949
0
  }
1950
1951
  // (3) create a new cell
1952
0
  cell = list.new_cell(new_piece.data(), static_cast<int>(new_piece.length()));
1953
1954
  // (4) patch new cell into list at the right place
1955
0
  if (idx == 0) {
1956
0
    list.prepend(cell);
1957
0
  } else {
1958
0
    prev = list.get_idx(idx - 1);
1959
0
    list.add_after(prev, cell);
1960
0
  }
1961
1962
  // (5) reassemble the new string
1963
0
  field->m_ptr_value = mime_field_value_str_from_strlist(heap, &len, &list);
1964
0
  field->m_len_value = len;
1965
1966
  // (6) keep stuff fields consistent
1967
0
  field->m_n_v_raw_printable = 0;
1968
0
  if (field->is_live() && field->is_cooked()) {
1969
0
    mh->recompute_cooked_stuff(field);
1970
0
  }
1971
0
}
1972
1973
void
1974
mime_field_value_extend_comma_val(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, int idx, std::string_view new_piece)
1975
0
{
1976
0
  Str    *cell;
1977
0
  StrList list(false);
1978
0
  int     trimmed, len;
1979
0
  size_t  extended_len;
1980
0
  char   *dest, *temp_ptr, temp_buf[128];
1981
1982
  // (1) rip the value into tokens, keeping surrounding quotes, but not whitespace
1983
0
  HttpCompat::parse_tok_list(&list, 0, field->m_ptr_value, field->m_len_value, ',');
1984
1985
  // (2) if desired index isn't valid, then don't change the field
1986
0
  if ((idx < 0) || (idx >= list.count)) {
1987
0
    return;
1988
0
  }
1989
1990
  // (3) get the cell we want to modify
1991
0
  cell = list.get_idx(idx);
1992
0
  ink_assert(cell != nullptr);
1993
1994
  // (4) trim quotes if any
1995
0
  if ((cell->len >= 2) && (cell->str[0] == '\"') && (cell->str[cell->len - 1] == '\"')) {
1996
0
    trimmed    = 1;
1997
0
    cell->str += 1;
1998
0
    cell->len -= 2;
1999
0
  } else {
2000
0
    trimmed = 0;
2001
0
  }
2002
2003
  // (5) compute length of extended token
2004
0
  auto new_piece_len{static_cast<int>(new_piece.length())};
2005
0
  extended_len = cell->len + new_piece_len + (trimmed ? 2 : 0);
2006
2007
  // (6) allocate temporary space to construct new value
2008
0
  if (extended_len <= sizeof(temp_buf)) {
2009
0
    temp_ptr = temp_buf;
2010
0
  } else {
2011
0
    temp_ptr = static_cast<char *>(ats_malloc(extended_len));
2012
0
  }
2013
2014
  // (7) construct new extended token
2015
0
  dest = temp_ptr;
2016
0
  if (trimmed) {
2017
0
    *dest++ = '\"';
2018
0
  }
2019
0
  memcpy(dest, cell->str, cell->len);
2020
0
  dest += cell->len;
2021
0
  memcpy(dest, new_piece.data(), new_piece_len);
2022
0
  dest += new_piece_len;
2023
0
  if (trimmed) {
2024
0
    *dest++ = '\"';
2025
0
  }
2026
0
  ink_assert((size_t)(dest - temp_ptr) == extended_len);
2027
2028
  // (8) assign the new token to the cell
2029
0
  cell->str = temp_ptr;
2030
0
  cell->len = extended_len;
2031
2032
  // (9) reassemble the new string
2033
0
  field->m_ptr_value = mime_field_value_str_from_strlist(heap, &len, &list);
2034
0
  field->m_len_value = len;
2035
2036
  // (10) keep stuff fields consistent
2037
0
  field->m_n_v_raw_printable = 0;
2038
0
  if (field->is_live() && field->is_cooked()) {
2039
0
    mh->recompute_cooked_stuff(field);
2040
0
  }
2041
2042
  // (11) free up any temporary storage
2043
0
  if (extended_len > sizeof(temp_buf)) {
2044
0
    ats_free(temp_ptr);
2045
0
  }
2046
0
}
2047
2048
void
2049
mime_field_value_set(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, std::string_view value, bool must_copy_string)
2050
0
{
2051
0
  heap->free_string(field->m_ptr_value, field->m_len_value);
2052
2053
0
  if (must_copy_string && value.data()) {
2054
0
    field->m_ptr_value = heap->duplicate_str(value.data(), static_cast<int>(value.length()));
2055
0
  } else {
2056
0
    field->m_ptr_value = nullptr;
2057
0
  }
2058
2059
0
  field->m_len_value         = static_cast<int>(value.length());
2060
0
  field->m_n_v_raw_printable = 0;
2061
2062
  // Now keep the cooked cache consistent
2063
0
  if (field->is_live() && field->is_cooked()) {
2064
0
    mh->recompute_cooked_stuff(field);
2065
0
  }
2066
0
}
2067
2068
void
2069
mime_field_value_set_int(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, int32_t value)
2070
0
{
2071
0
  char buf[16];
2072
0
  int  len = mime_format_int(buf, value, sizeof(buf));
2073
0
  mime_field_value_set(heap, mh, field, std::string_view{buf, static_cast<std::string_view::size_type>(len)}, true);
2074
0
}
2075
2076
void
2077
mime_field_value_set_uint(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, uint32_t value)
2078
0
{
2079
0
  char buf[16];
2080
0
  int  len = mime_format_uint(buf, value, sizeof(buf));
2081
0
  mime_field_value_set(heap, mh, field, std::string_view{buf, static_cast<std::string_view::size_type>(len)}, true);
2082
0
}
2083
2084
void
2085
mime_field_value_set_int64(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, int64_t value)
2086
0
{
2087
0
  char buf[21];
2088
0
  int  len = mime_format_int64(buf, value, sizeof(buf));
2089
0
  mime_field_value_set(heap, mh, field, std::string_view{buf, static_cast<std::string_view::size_type>(len)}, true);
2090
0
}
2091
2092
void
2093
mime_field_value_set_date(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, time_t value)
2094
0
{
2095
0
  char buf[33];
2096
0
  int  len = mime_format_date(buf, value);
2097
0
  mime_field_value_set(heap, mh, field, std::string_view{buf, static_cast<std::string_view::size_type>(len)}, true);
2098
0
}
2099
2100
void
2101
mime_field_name_value_set(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, int16_t name_wks_idx_or_neg1, std::string_view name,
2102
                          std::string_view value, int n_v_raw_printable, int n_v_raw_length, bool must_copy_strings)
2103
0
{
2104
0
  auto         name_length{static_cast<int>(name.length())};
2105
0
  auto         value_length{static_cast<int>(value.length())};
2106
0
  unsigned int n_v_raw_pad = n_v_raw_length - (name_length + value_length);
2107
2108
0
  ink_assert(field->m_readiness == MIME_FIELD_SLOT_READINESS_DETACHED);
2109
2110
0
  if (must_copy_strings) {
2111
0
    mime_field_name_set(heap, mh, field, name_wks_idx_or_neg1, name, true);
2112
0
    mime_field_value_set(heap, mh, field, value, true);
2113
0
  } else {
2114
0
    field->m_wks_idx   = name_wks_idx_or_neg1;
2115
0
    field->m_ptr_name  = name.data();
2116
0
    field->m_ptr_value = value.data();
2117
0
    field->m_len_name  = name_length;
2118
0
    field->m_len_value = value_length;
2119
0
    if (n_v_raw_printable && (n_v_raw_pad <= 7)) {
2120
0
      field->m_n_v_raw_printable     = n_v_raw_printable;
2121
0
      field->m_n_v_raw_printable_pad = n_v_raw_pad;
2122
0
    } else {
2123
0
      field->m_n_v_raw_printable = 0;
2124
0
    }
2125
2126
    // Now keep the cooked cache consistent
2127
0
    if ((name_wks_idx_or_neg1 == MIME_WKSIDX_CACHE_CONTROL) || (name_wks_idx_or_neg1 == MIME_WKSIDX_PRAGMA)) {
2128
0
      field->m_flags |= MIME_FIELD_SLOT_FLAGS_COOKED;
2129
0
    }
2130
0
    if (field->is_live() && field->is_cooked()) {
2131
0
      mh->recompute_cooked_stuff(field);
2132
0
    }
2133
0
  }
2134
0
}
2135
2136
void
2137
mime_field_value_append(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, std::string_view value, bool prepend_comma,
2138
                        const char separator)
2139
0
{
2140
0
  auto length{static_cast<int>(value.length())};
2141
0
  int  new_length = field->m_len_value + length;
2142
0
  if (prepend_comma && field->m_len_value) {
2143
0
    new_length += 2;
2144
0
  }
2145
2146
  // Start by trying expand the string we already  have
2147
0
  char *new_str = heap->expand_str(field->m_ptr_value, field->m_len_value, new_length);
2148
2149
0
  if (new_str == nullptr) {
2150
    // Expansion failed.  Create a new string and copy over the value contents
2151
0
    new_str = heap->allocate_str(new_length);
2152
0
    memcpy(new_str, field->m_ptr_value, field->m_len_value);
2153
0
  }
2154
2155
0
  char *ptr = new_str + field->m_len_value;
2156
0
  if (prepend_comma && field->m_len_value) {
2157
0
    *ptr++ = separator;
2158
0
    *ptr++ = ' ';
2159
0
  }
2160
2161
0
  memcpy(ptr, value.data(), length);
2162
2163
0
  field->m_ptr_value         = new_str;
2164
0
  field->m_len_value         = new_length;
2165
0
  field->m_n_v_raw_printable = 0;
2166
2167
  // Now keep the cooked cache consistent
2168
0
  if (field->is_live() && field->is_cooked()) {
2169
0
    mh->recompute_cooked_stuff(field);
2170
0
  }
2171
0
}
2172
2173
std::tuple<MIMEField *, std::string_view, std::string_view>
2174
MIMEHdr::get_host_port_values()
2175
0
{
2176
0
  MIMEField       *field = this->field_find(static_cast<std::string_view>(MIME_FIELD_HOST));
2177
0
  std::string_view host, port;
2178
2179
0
  if (field) {
2180
0
    swoc::TextView b{field->m_ptr_value, static_cast<size_t>(field->m_len_value)};
2181
2182
0
    if (b) {
2183
0
      if ('[' == *b) {
2184
0
        auto idx = b.find(']');
2185
0
        if (idx < b.size() - 1 && b[idx + 1] == ':') {
2186
0
          host = b.take_prefix(idx + 1);
2187
0
          port = b;
2188
0
        } else {
2189
0
          host = b;
2190
0
        }
2191
0
      } else {
2192
0
        host = b.take_prefix_at(':');
2193
0
        port = b;
2194
0
      }
2195
0
    } else {
2196
0
      field = nullptr; // no value in field, signal fail.
2197
0
    }
2198
0
  }
2199
0
  return std::make_tuple(field, host, port);
2200
0
}
2201
2202
/***********************************************************************
2203
 *                                                                     *
2204
 *                          P A R S E R                                *
2205
 *                                                                     *
2206
 ***********************************************************************/
2207
2208
void
2209
MIMEScanner::init()
2210
0
{
2211
0
  m_state = INITIAL_PARSE_STATE;
2212
  // Ugly, but required because of how proxy allocation works - that leaves the instance in a
2213
  // random state, so even assigning to it can crash. Because this method substitutes for a real
2214
  // constructor in the proxy allocation system, call the CTOR here. Any memory that gets allocated
2215
  // is supposed to be cleaned up by calling @c clear on this object.
2216
0
  new (&m_line) std::string;
2217
0
}
2218
2219
MIMEScanner &
2220
MIMEScanner::append(TextView text)
2221
0
{
2222
0
  m_line += text;
2223
0
  return *this;
2224
0
}
2225
2226
ParseResult
2227
MIMEScanner::get(TextView &input, TextView &output, bool &output_shares_input, bool eof_p, ScanType scan_type)
2228
0
{
2229
0
  ParseResult zret = ParseResult::CONT;
2230
  // Need this for handling dangling CR.
2231
0
  static const char RAW_CR{ParseRules::CHAR_CR};
2232
2233
0
  auto text = input;
2234
0
  while (ParseResult::CONT == zret && !text.empty()) {
2235
0
    switch (m_state) {
2236
0
    case MimeParseState::BEFORE: // waiting to find a field.
2237
0
      m_line.resize(0);          // any caller should already be done with the buffer
2238
0
      if (ParseRules::is_cr(*text)) {
2239
0
        ++text;
2240
0
        if (!text.empty() && ParseRules::is_lf(*text)) {
2241
          // optimize a bit - this happens >99% of the time after a CR.
2242
0
          ++text;
2243
0
          zret = ParseResult::DONE;
2244
0
        } else {
2245
0
          m_state = MimeParseState::FOUND_CR;
2246
0
        }
2247
0
      } else if (ParseRules::is_lf(*text)) {
2248
0
        ++text;
2249
0
        zret = ParseResult::DONE; // Required by regression test.
2250
0
      } else {
2251
        // consume this character in the next state.
2252
0
        m_state = MimeParseState::INSIDE;
2253
0
      }
2254
0
      break;
2255
0
    case MimeParseState::FOUND_CR:
2256
      // Looking for a field and found a CR, which should mean terminating the header.
2257
0
      if (ParseRules::is_lf(*text)) {
2258
0
        ++text;
2259
0
        zret = ParseResult::DONE;
2260
0
      } else {
2261
        // This really should be an error (spec doesn't permit lone CR) but the regression tests
2262
        // require it.
2263
0
        this->append(TextView(&RAW_CR, 1)); // This is to fix a core dump of the icc 19.1 compiler when {&RAW_CR, 1} is used
2264
0
        m_state = MimeParseState::INSIDE;
2265
0
      }
2266
0
      break;
2267
0
    case MimeParseState::INSIDE: {
2268
0
      auto lf_off = text.find(ParseRules::CHAR_LF);
2269
0
      if (lf_off != TextView::npos) {
2270
0
        text.remove_prefix(lf_off + 1); // drop up to and including LF
2271
0
        if (ScanType::LINE == scan_type) {
2272
0
          zret    = ParseResult::OK;
2273
0
          m_state = MimeParseState::BEFORE;
2274
0
        } else {
2275
0
          m_state = MimeParseState::AFTER; // looking for line folding.
2276
0
        }
2277
0
      } else { // no EOL, consume all text without changing state.
2278
0
        text.remove_prefix(text.size());
2279
0
      }
2280
0
    } break;
2281
0
    case MimeParseState::AFTER:
2282
      // After a LF, the next line might be a continuation / folded line. That's indicated by a
2283
      // starting whitespace. If that's the case, back up over the preceding CR/LF with space and
2284
      // pretend it's the same line.
2285
0
      if (ParseRules::is_ws(*text)) { // folded line.
2286
0
        char *unfold = const_cast<char *>(text.data() - 1);
2287
0
        *unfold--    = ' ';
2288
0
        if (ParseRules::is_cr(*unfold)) {
2289
0
          *unfold = ' ';
2290
0
        }
2291
0
        m_state = MimeParseState::INSIDE; // back inside the field.
2292
0
      } else {
2293
0
        m_state = MimeParseState::BEFORE; // field terminated.
2294
0
        zret    = ParseResult::OK;
2295
0
      }
2296
0
      break;
2297
0
    }
2298
0
  }
2299
2300
0
  TextView parsed_text{input.data(), text.data()};
2301
0
  bool     save_parsed_text_p = !parsed_text.empty();
2302
2303
0
  if (ParseResult::CONT == zret) {
2304
    // data ran out before we got a clear final result. There a number of things we need to check
2305
    // and possibly adjust that result. It's less complex to do this cleanup than handle all of
2306
    // these checks in the parser state machine.
2307
0
    if (eof_p) {
2308
      // Should never return PARSE_CONT if we've hit EOF.
2309
0
      if (parsed_text.empty()) {
2310
        // all input previously consumed. If we're between fields, that's cool.
2311
0
        if (MimeParseState::INSIDE != m_state) {
2312
0
          m_state = MimeParseState::BEFORE; // probably not needed...
2313
0
          zret    = ParseResult::DONE;
2314
0
        } else {
2315
0
          zret = ParseResult::ERROR; // unterminated field.
2316
0
        }
2317
0
      } else if (MimeParseState::AFTER == m_state) {
2318
        // Special case it seems - need to accept the final field even if there's no header
2319
        // terminating CR LF. This is only reasonable after absolute end of input (EOF) because
2320
        // otherwise this might be a multiline field where we haven't seen the next leading space.
2321
0
        m_state = MimeParseState::BEFORE;
2322
0
        zret    = ParseResult::OK;
2323
0
      } else {
2324
        // Partial input, no field / line CR LF
2325
0
        zret = ParseResult::ERROR; // Unterminated field.
2326
0
      }
2327
0
    } else if (!parsed_text.empty()) {
2328
0
      if (MimeParseState::INSIDE == m_state) {
2329
        // Inside a field but more data is expected. Save what we've got.
2330
0
        this->append(parsed_text);  // Do this here to force appending.
2331
0
        save_parsed_text_p = false; // don't double append.
2332
0
      } else if (MimeParseState::AFTER == m_state) {
2333
        // After a field but we still have data. Need to parse it too.
2334
0
        m_state = MimeParseState::BEFORE;
2335
0
        zret    = ParseResult::OK;
2336
0
      }
2337
0
    }
2338
0
  }
2339
2340
0
  if (save_parsed_text_p && !m_line.empty()) {
2341
    // If we're already accumulating, continue to do so if we have data.
2342
0
    this->append(parsed_text);
2343
0
  }
2344
2345
  // adjust out arguments.
2346
0
  output_shares_input = true;
2347
0
  if (ParseResult::CONT != zret) {
2348
0
    if (!m_line.empty()) {
2349
0
      output              = m_line; // cleared when called with state MimeParseState::BEFORE
2350
0
      output_shares_input = false;
2351
0
    } else {
2352
0
      output = parsed_text;
2353
0
    }
2354
0
  }
2355
2356
  // Make sure there are no null characters in the input scanned so far
2357
0
  if (zret != ParseResult::ERROR && TextView::npos != parsed_text.find('\0')) {
2358
0
    zret = ParseResult::ERROR;
2359
0
  }
2360
2361
0
  input.remove_prefix(parsed_text.size());
2362
0
  return zret;
2363
0
}
2364
2365
void
2366
_mime_parser_init(MIMEParser *parser)
2367
0
{
2368
0
  parser->m_field       = 0;
2369
0
  parser->m_field_flags = 0;
2370
0
  parser->m_value       = -1;
2371
0
}
2372
//////////////////////////////////////////////////////
2373
// init     first time structure setup              //
2374
// clear    resets an already-initialized structure //
2375
//////////////////////////////////////////////////////
2376
void
2377
mime_parser_init(MIMEParser *parser)
2378
0
{
2379
0
  parser->m_scanner.init();
2380
0
  _mime_parser_init(parser);
2381
0
}
2382
2383
void
2384
mime_parser_clear(MIMEParser *parser)
2385
0
{
2386
0
  parser->m_scanner.clear();
2387
0
  _mime_parser_init(parser);
2388
0
}
2389
2390
ParseResult
2391
mime_parser_parse(MIMEParser *parser, HdrHeap *heap, MIMEHdrImpl *mh, const char **real_s, const char *real_e,
2392
                  bool must_copy_strings, bool eof, bool remove_ws_from_field_name, size_t max_hdr_field_size)
2393
0
{
2394
0
  ParseResult err;
2395
0
  bool        line_is_real;
2396
2397
0
  MIMEScanner *scanner = &parser->m_scanner;
2398
2399
0
  while (true) {
2400
    ////////////////////////////////////////////////////////////////////////////
2401
    // get a name:value line, with all continuation lines glued into one line //
2402
    ////////////////////////////////////////////////////////////////////////////
2403
2404
0
    TextView text{*real_s, real_e};
2405
0
    TextView parsed;
2406
0
    err     = scanner->get(text, parsed, line_is_real, eof, MIMEScanner::ScanType::FIELD);
2407
0
    *real_s = text.data();
2408
0
    if (err != ParseResult::OK) {
2409
0
      return err;
2410
0
    }
2411
2412
    //////////////////////////////////////////////////
2413
    // if got a LF or CR on its own, end the header //
2414
    //////////////////////////////////////////////////
2415
2416
0
    if ((parsed.size() >= 2) && (parsed[0] == ParseRules::CHAR_CR) && (parsed[1] == ParseRules::CHAR_LF)) {
2417
0
      return ParseResult::DONE;
2418
0
    }
2419
2420
0
    if ((parsed.size() >= 1) && (parsed[0] == ParseRules::CHAR_LF)) {
2421
0
      return ParseResult::DONE;
2422
0
    }
2423
2424
    /////////////////////////////////////////////
2425
    // find pointers into the name:value field //
2426
    /////////////////////////////////////////////
2427
2428
    /**
2429
     * Fix for INKqa09141. The is_token function fails for '@' character.
2430
     * Header names starting with '@' signs are valid headers. Hence we
2431
     * have to add one more check to see if the first parameter is '@'
2432
     * character then, the header name is valid.
2433
     **/
2434
0
    if ((!ParseRules::is_token(*parsed)) && (*parsed != '@')) {
2435
0
      continue; // toss away garbage line
2436
0
    }
2437
2438
    // find name last
2439
0
    auto field_value = parsed; // need parsed as is later on.
2440
0
    auto field_name  = field_value.split_prefix_at(':');
2441
0
    if (field_name.empty()) {
2442
0
      continue; // toss away garbage line
2443
0
    }
2444
2445
    // RFC7230 section 3.2.4:
2446
    // No whitespace is allowed between the header field-name and colon.  In
2447
    // the past, differences in the handling of such whitespace have led to
2448
    // security vulnerabilities in request routing and response handling.  A
2449
    // server MUST reject any received request message that contains
2450
    // whitespace between a header field-name and colon with a response code
2451
    // of 400 (Bad Request).
2452
    // A proxy MUST remove any such whitespace from a response message before
2453
    // forwarding the message downstream.
2454
0
    bool raw_print_field = true;
2455
0
    if (is_ws(field_name.back())) {
2456
0
      if (!remove_ws_from_field_name) {
2457
0
        return ParseResult::ERROR;
2458
0
      }
2459
0
      field_name.rtrim_if(&ParseRules::is_ws);
2460
0
      raw_print_field = false;
2461
0
    } else if (parsed.suffix(2) != "\r\n") {
2462
0
      raw_print_field = false;
2463
0
    }
2464
2465
    // find value first
2466
0
    field_value.ltrim_if(&ParseRules::is_ws);
2467
0
    field_value.rtrim_if(&ParseRules::is_wslfcr);
2468
2469
    // Make sure the name + value is not longer than configured max_hdr_field_size
2470
0
    if (field_name.size() + field_value.size() > max_hdr_field_size) {
2471
0
      return ParseResult::ERROR;
2472
0
    }
2473
2474
    //    int total_line_length = (int)(field_line_last - field_line_first + 1);
2475
2476
    //////////////////////////////////////////////////////////////////////
2477
    // if we can't leave the name & value in the real buffer, copy them //
2478
    //////////////////////////////////////////////////////////////////////
2479
2480
0
    if (must_copy_strings || (!line_is_real)) {
2481
0
      char     *dup   = heap->duplicate_str(parsed.data(), parsed.size());
2482
0
      ptrdiff_t delta = dup - parsed.data();
2483
0
      field_name.assign(field_name.data() + delta, field_name.size());
2484
0
      field_value.assign(field_value.data() + delta, field_value.size());
2485
0
    }
2486
    ///////////////////////
2487
    // tokenize the name //
2488
    ///////////////////////
2489
2490
0
    int field_name_wks_idx = hdrtoken_tokenize(field_name.data(), field_name.size());
2491
2492
0
    if (field_name_wks_idx < 0) {
2493
0
      for (auto i : field_name) {
2494
0
        if (!ParseRules::is_http_field_name(i)) {
2495
0
          return ParseResult::ERROR;
2496
0
        }
2497
0
      }
2498
0
    }
2499
2500
    // RFC 9110 Section 5.5. Field Values
2501
0
    for (char i : field_value) {
2502
      // FIXME: ParseRules::is_http_field_value() should be used but the implementation looks wrong
2503
0
      if (ParseRules::is_control(i)) {
2504
0
        return ParseResult::ERROR;
2505
0
      }
2506
0
    }
2507
2508
    ///////////////////////////////////////////
2509
    // build and insert the new field object //
2510
    ///////////////////////////////////////////
2511
2512
0
    MIMEField *field = mime_field_create(heap, mh);
2513
0
    mime_field_name_value_set(heap, mh, field, field_name_wks_idx, field_name, field_value, raw_print_field, parsed.size(), false);
2514
0
    mime_hdr_field_attach(mh, field, 1, nullptr);
2515
0
  }
2516
0
}
2517
2518
void
2519
mime_hdr_describe(HdrHeapObjImpl *raw, bool recurse)
2520
0
{
2521
0
  MIMEFieldBlockImpl *fblock;
2522
0
  MIMEHdrImpl        *obj = (MIMEHdrImpl *)raw;
2523
2524
0
  Dbg(dbg_ctl_http, "\t[PBITS: 0x%08X%08X, SLACC: 0x%04X%04X%04X%04X, HEADBLK: %p, TAILBLK: %p]",
2525
0
      (uint32_t)((obj->m_presence_bits >> 32) & (TOK_64_CONST(0xFFFFFFFF))),
2526
0
      (uint32_t)((obj->m_presence_bits >> 0) & (TOK_64_CONST(0xFFFFFFFF))), obj->m_slot_accelerators[0],
2527
0
      obj->m_slot_accelerators[1], obj->m_slot_accelerators[2], obj->m_slot_accelerators[3], &(obj->m_first_fblock),
2528
0
      obj->m_fblock_list_tail);
2529
2530
0
  Dbg(dbg_ctl_http, "\t[CBITS: 0x%08X, T_MAXAGE: %d, T_SMAXAGE: %d, T_MAXSTALE: %d, T_MINFRESH: %d, PNO$: %d]",
2531
0
      obj->m_cooked_stuff.m_cache_control.m_mask, obj->m_cooked_stuff.m_cache_control.m_secs_max_age,
2532
0
      obj->m_cooked_stuff.m_cache_control.m_secs_s_maxage, obj->m_cooked_stuff.m_cache_control.m_secs_max_stale,
2533
0
      obj->m_cooked_stuff.m_cache_control.m_secs_min_fresh, obj->m_cooked_stuff.m_pragma.m_no_cache);
2534
0
  for (fblock = &(obj->m_first_fblock); fblock != nullptr; fblock = fblock->m_next) {
2535
0
    if (recurse || (fblock == &(obj->m_first_fblock))) {
2536
0
      obj_describe((HdrHeapObjImpl *)fblock, recurse);
2537
0
    }
2538
0
  }
2539
0
}
2540
2541
void
2542
mime_field_block_describe(HdrHeapObjImpl *raw, bool /* recurse ATS_UNUSED */)
2543
0
{
2544
0
  unsigned int                 i;
2545
0
  static constexpr const char *readiness_names[] = {"EMPTY", "DETACHED", "LIVE", "DELETED"};
2546
2547
0
  MIMEFieldBlockImpl *obj = (MIMEFieldBlockImpl *)raw;
2548
2549
0
  Dbg(dbg_ctl_http, "[FREETOP: %d, NEXTBLK: %p]", obj->m_freetop, obj->m_next);
2550
2551
0
  for (i = 0; i < obj->m_freetop; i++) {
2552
0
    MIMEField *f = &(obj->m_field_slots[i]);
2553
0
    Dbg(dbg_ctl_http, "\tSLOT #%2d (%p), %-8s", i, f, readiness_names[f->m_readiness]);
2554
2555
0
    switch (f->m_readiness) {
2556
0
    case MIME_FIELD_SLOT_READINESS_EMPTY:
2557
0
      break;
2558
0
    case MIME_FIELD_SLOT_READINESS_DETACHED:
2559
0
    case MIME_FIELD_SLOT_READINESS_LIVE:
2560
0
    case MIME_FIELD_SLOT_READINESS_DELETED:
2561
0
      Dbg(dbg_ctl_http, "[N: \"%.*s\", N_LEN: %d, N_IDX: %d, ", f->m_len_name, (f->m_ptr_name ? f->m_ptr_name : "NULL"),
2562
0
          f->m_len_name, f->m_wks_idx);
2563
0
      Dbg(dbg_ctl_http, "V: \"%.*s\", V_LEN: %d, ", f->m_len_value, (f->m_ptr_value ? f->m_ptr_value : "NULL"), f->m_len_value);
2564
0
      Dbg(dbg_ctl_http, "NEXTDUP: %p, RAW: %d, RAWLEN: %d, F: %d]", f->m_next_dup, f->m_n_v_raw_printable,
2565
0
          f->m_len_name + f->m_len_value + f->m_n_v_raw_printable_pad, f->m_flags);
2566
0
      break;
2567
0
    }
2568
0
    Dbg(dbg_ctl_http, "\n");
2569
0
  }
2570
0
}
2571
2572
int
2573
mime_hdr_print(MIMEHdrImpl const *mh, char *buf_start, int buf_length, int *buf_index_inout, int *buf_chars_to_skip_inout)
2574
0
{
2575
0
  MIMEFieldBlockImpl const *fblock;
2576
0
  MIMEField const          *field;
2577
0
  uint32_t                  index;
2578
2579
0
#define SIMPLE_MIME_HDR_PRINT
2580
0
#ifdef SIMPLE_MIME_HDR_PRINT
2581
0
  for (fblock = &(mh->m_first_fblock); fblock != nullptr; fblock = fblock->m_next) {
2582
0
    for (index = 0; index < fblock->m_freetop; index++) {
2583
0
      field = &(fblock->m_field_slots[index]);
2584
0
      if (field->is_live()) {
2585
0
        if (!mime_field_print(field, buf_start, buf_length, buf_index_inout, buf_chars_to_skip_inout)) {
2586
0
          return 0;
2587
0
        }
2588
0
      }
2589
0
    }
2590
0
  }
2591
#else
2592
  // FIX: if not raw_printable, need to print with mime_field_print,
2593
  //      not mime_mem_print
2594
  for (fblock = &(mh->m_first_fblock); fblock != NULL; fblock = fblock->m_next) {
2595
    const char *contig_start = NULL;
2596
    int         this_length, contig_length = 0;
2597
    for (index = 0; index < fblock->m_freetop; index++) {
2598
      field       = &(fblock->m_field_slots[index]);
2599
      this_length = field->m_len_name + field->m_len_value + field->m_n_v_raw_printable_pad;
2600
      if (field->is_live()) {
2601
        if ((field->m_ptr_name == contig_start + contig_length) && field->m_n_v_raw_printable &&
2602
            ((buf_index_inout == NULL) || (contig_length + this_length <= buf_length - *buf_index_inout))) {
2603
          contig_length += this_length;
2604
        } else {
2605
          if (contig_length > 0) {
2606
            if (!mime_mem_print(std::string_view{contig_start, static_cast<std::string_view::size_type>(contig_length)}, buf_start,
2607
                                buf_length, buf_index_inout, buf_chars_to_skip_inout))
2608
              return 0;
2609
          }
2610
          contig_start  = field->m_ptr_name;
2611
          contig_length = this_length;
2612
        }
2613
      }
2614
    }
2615
2616
    if (contig_length > 0) {
2617
      if (!mime_mem_print(std::string_view{ontig_start, static_cast<std::string_view::size_type>(contig_length)}, buf_start,
2618
                          buf_length, buf_index_inout, buf_chars_to_skip_inout))
2619
        return 0;
2620
    }
2621
  }
2622
#endif
2623
2624
0
  if (!mime_mem_print("\r\n"sv, buf_start, buf_length, buf_index_inout, buf_chars_to_skip_inout)) {
2625
0
    return 0;
2626
0
  }
2627
2628
0
  return 1;
2629
0
}
2630
2631
namespace
2632
{
2633
int
2634
mime_mem_print_(std::string_view src, char *buf_start, int buf_length, int *buf_index_inout, int *buf_chars_to_skip_inout,
2635
                int (*char_transform)(int char_in))
2636
0
{
2637
0
  if (buf_start == nullptr) { // this case should only be used by test_header
2638
0
    ink_release_assert(buf_index_inout == nullptr);
2639
0
    ink_release_assert(buf_chars_to_skip_inout == nullptr);
2640
0
    for (auto c : src) {
2641
0
      putchar(c);
2642
0
    }
2643
0
    return 1;
2644
0
  }
2645
2646
0
  ink_assert(src.data() != nullptr);
2647
2648
0
  if (*buf_chars_to_skip_inout > 0) {
2649
0
    if (*buf_chars_to_skip_inout >= static_cast<int>(src.length())) {
2650
0
      *buf_chars_to_skip_inout -= static_cast<int>(src.length());
2651
0
      return 1;
2652
0
    } else {
2653
0
      src.remove_prefix(*buf_chars_to_skip_inout);
2654
0
      *buf_chars_to_skip_inout = 0;
2655
0
    }
2656
0
  }
2657
2658
0
  auto src_l{static_cast<int>(src.length())};
2659
0
  int  copy_l = std::min(buf_length - *buf_index_inout, src_l);
2660
0
  if (copy_l > 0) {
2661
0
    buf_start += *buf_index_inout;
2662
0
    auto src_d{src.data()};
2663
0
    std::transform(src_d, src_d + copy_l, buf_start, char_transform);
2664
0
    *buf_index_inout += copy_l;
2665
0
  }
2666
0
  return (src_l == copy_l);
2667
0
}
2668
2669
int
2670
to_same_char(int ch)
2671
0
{
2672
0
  return ch;
2673
0
}
2674
2675
} // end anonymous namespace
2676
2677
int
2678
mime_mem_print(std::string_view src, char *buf_start, int buf_length, int *buf_index_inout, int *buf_chars_to_skip_inout)
2679
0
{
2680
0
  return mime_mem_print_(src, buf_start, buf_length, buf_index_inout, buf_chars_to_skip_inout, to_same_char);
2681
0
}
2682
2683
int
2684
mime_mem_print_lc(std::string_view src, char *buf_start, int buf_length, int *buf_index_inout, int *buf_chars_to_skip_inout)
2685
0
{
2686
0
  return mime_mem_print_(src, buf_start, buf_length, buf_index_inout, buf_chars_to_skip_inout, std::tolower);
2687
0
}
2688
2689
int
2690
mime_field_print(MIMEField const *field, char *buf_start, int buf_length, int *buf_index_inout, int *buf_chars_to_skip_inout)
2691
0
{
2692
0
#define TRY(x) \
2693
0
  if (!x)      \
2694
0
  return 0
2695
2696
0
  int total_len;
2697
2698
  // Don't print names that begin with an '@'.
2699
0
  if (field->m_ptr_name[0] == '@') {
2700
0
    return 1;
2701
0
  }
2702
2703
0
  if (field->m_n_v_raw_printable) {
2704
0
    total_len = field->m_len_name + field->m_len_value + field->m_n_v_raw_printable_pad;
2705
2706
0
    if ((buf_start != nullptr) && (*buf_chars_to_skip_inout == 0) && (total_len <= (buf_length - *buf_index_inout))) {
2707
0
      buf_start += *buf_index_inout;
2708
0
      memcpy(buf_start, field->m_ptr_name, total_len);
2709
0
      *buf_index_inout += total_len;
2710
2711
0
    } else {
2712
0
      TRY(mime_mem_print(std::string_view{field->m_ptr_name, static_cast<std::string_view::size_type>(total_len)}, buf_start,
2713
0
                         buf_length, buf_index_inout, buf_chars_to_skip_inout));
2714
0
    }
2715
0
  } else {
2716
0
    total_len = field->m_len_name + field->m_len_value + 2 + 2;
2717
2718
    // try to handle on fast path
2719
2720
0
    if ((buf_start != nullptr) && (*buf_chars_to_skip_inout == 0) && (total_len <= (buf_length - *buf_index_inout))) {
2721
0
      buf_start += *buf_index_inout;
2722
2723
0
      memcpy(buf_start, field->m_ptr_name, field->m_len_name);
2724
0
      buf_start += field->m_len_name;
2725
2726
0
      buf_start[0]  = ':';
2727
0
      buf_start[1]  = ' ';
2728
0
      buf_start    += 2;
2729
2730
0
      memcpy(buf_start, field->m_ptr_value, field->m_len_value);
2731
0
      buf_start += field->m_len_value;
2732
2733
0
      buf_start[0] = '\r';
2734
0
      buf_start[1] = '\n';
2735
2736
0
      *buf_index_inout += total_len;
2737
0
    } else {
2738
0
      TRY(mime_mem_print(std::string_view{field->m_ptr_name, static_cast<std::string_view::size_type>(field->m_len_name)},
2739
0
                         buf_start, buf_length, buf_index_inout, buf_chars_to_skip_inout));
2740
0
      TRY(mime_mem_print(": "sv, buf_start, buf_length, buf_index_inout, buf_chars_to_skip_inout));
2741
0
      TRY(mime_mem_print(std::string_view{field->m_ptr_value, static_cast<std::string_view::size_type>(field->m_len_value)},
2742
0
                         buf_start, buf_length, buf_index_inout, buf_chars_to_skip_inout));
2743
0
      TRY(mime_mem_print("\r\n"sv, buf_start, buf_length, buf_index_inout, buf_chars_to_skip_inout));
2744
0
    }
2745
0
  }
2746
2747
0
  return 1;
2748
2749
0
#undef TRY
2750
0
}
2751
2752
const char *
2753
mime_str_u16_set(HdrHeap *heap, std::string_view src, const char **d_str, uint16_t *d_len, bool must_copy)
2754
0
{
2755
0
  auto s_len{static_cast<int>(src.length())};
2756
0
  ink_assert(s_len >= 0 && s_len < UINT16_MAX);
2757
  // INKqa08287 - keep track of free string space.
2758
  //  INVARIANT: passed in result pointers must be to
2759
  //    either NULL or be valid ptr for a string already
2760
  //    the string heaps
2761
0
  heap->free_string(*d_str, *d_len);
2762
2763
0
  auto s_str{src.data()};
2764
0
  if (must_copy && s_str) {
2765
0
    s_str = heap->duplicate_str(s_str, s_len);
2766
0
  }
2767
0
  *d_str = s_str;
2768
0
  *d_len = s_len;
2769
0
  return s_str;
2770
0
}
2771
2772
int
2773
mime_field_length_get(MIMEField *field)
2774
0
{
2775
0
  if (field->m_n_v_raw_printable) {
2776
0
    return (field->m_len_name + field->m_len_value + field->m_n_v_raw_printable_pad);
2777
0
  } else {
2778
0
    return (field->m_len_name + field->m_len_value + 4); // add ": \r\n"
2779
0
  }
2780
0
}
2781
2782
int
2783
mime_format_int(char *buf, int32_t val, size_t buf_len)
2784
0
{
2785
0
  return ink_fast_itoa(val, buf, buf_len);
2786
0
}
2787
2788
int
2789
mime_format_uint(char *buf, uint32_t val, size_t buf_len)
2790
0
{
2791
0
  return ink_fast_uitoa(val, buf, buf_len);
2792
0
}
2793
2794
int
2795
mime_format_int64(char *buf, int64_t val, size_t buf_len)
2796
0
{
2797
0
  return ink_fast_ltoa(val, buf, buf_len);
2798
0
}
2799
2800
void
2801
mime_days_since_epoch_to_mdy_slowcase(time_t days_since_jan_1_1970, int *m_return, int *d_return, int *y_return)
2802
0
{
2803
0
  static constexpr int DAYS_OFFSET = 25508;
2804
2805
0
  static const char months[] = {
2806
0
    2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,
2807
0
    2,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,
2808
0
    4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,
2809
0
    5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  6,
2810
0
    6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  7,
2811
0
    7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  8,
2812
0
    8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  9,  9,
2813
0
    9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  10, 10,
2814
0
    10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11,
2815
0
    11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 0,  0,  0,
2816
0
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1,  1,  1,
2817
0
    1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1};
2818
2819
0
  static constexpr int days[12] = {305, 336, -1, 30, 60, 91, 121, 152, 183, 213, 244, 274};
2820
2821
0
  time_t mday, year, month, d, dp;
2822
2823
0
  mday = days_since_jan_1_1970;
2824
2825
  /* guess the year and refine the guess */
2826
0
  year = mday / 365 + 69;
2827
0
  d = dp = (year * 365) + (year / 4) - (year / 100) + (year / 100 + 3) / 4 - DAYS_OFFSET - 1;
2828
2829
0
  while (dp < mday) {
2830
0
    d     = dp;
2831
0
    year += 1;
2832
0
    dp    = (year * 365) + (year / 4) - (year / 100) + (year / 100 + 3) / 4 - DAYS_OFFSET - 1;
2833
0
  }
2834
2835
  /* convert the days */
2836
0
  d = mday - d;
2837
0
  if ((d < 0) || (d > 366)) {
2838
0
    ink_assert(!"bad date");
2839
0
  } else {
2840
0
    month = months[d];
2841
0
    if (month > 1) {
2842
0
      year -= 1;
2843
0
    }
2844
2845
0
    mday  = d - days[month] - 1;
2846
0
    year += 1900;
2847
2848
    // coverity[Y2K38_SAFETY:FALSE]
2849
0
    *m_return = month;
2850
    // coverity[Y2K38_SAFETY:FALSE]
2851
0
    *d_return = mday;
2852
    // coverity[Y2K38_SAFETY:FALSE]
2853
0
    *y_return = year;
2854
0
  }
2855
0
}
2856
2857
void
2858
mime_days_since_epoch_to_mdy(time_t days_since_jan_1_1970, int *m_return, int *d_return, int *y_return)
2859
0
{
2860
0
  ink_assert(_days_to_mdy_fast_lookup_table != nullptr);
2861
2862
  /////////////////////////////////////////////////////////////
2863
  // if we have a fast lookup entry for this date, return it //
2864
  /////////////////////////////////////////////////////////////
2865
2866
0
  if ((days_since_jan_1_1970 >= _days_to_mdy_fast_lookup_table_first_day) &&
2867
0
      (days_since_jan_1_1970 <= _days_to_mdy_fast_lookup_table_last_day)) {
2868
    ////////////////////////////////////////////////////////////////
2869
    // to speed up the days_since_epoch to m/d/y conversion, we   //
2870
    // use a pre-computed lookup table to support the common case //
2871
    // of dates that are +/- one year from today.                 //
2872
    ////////////////////////////////////////////////////////////////
2873
2874
0
    time_t i  = days_since_jan_1_1970 - _days_to_mdy_fast_lookup_table_first_day;
2875
0
    *m_return = _days_to_mdy_fast_lookup_table[i].m;
2876
0
    *d_return = _days_to_mdy_fast_lookup_table[i].d;
2877
0
    *y_return = _days_to_mdy_fast_lookup_table[i].y;
2878
0
    return;
2879
0
  }
2880
  ////////////////////////////////////
2881
  // otherwise, return the slow way //
2882
  ////////////////////////////////////
2883
2884
0
  mime_days_since_epoch_to_mdy_slowcase(days_since_jan_1_1970, m_return, d_return, y_return);
2885
0
}
2886
2887
int
2888
mime_format_date(char *buffer, time_t value)
2889
0
{
2890
  // must be 3 characters!
2891
0
  static constexpr const char *daystrs[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
2892
2893
  // must be 3 characters!
2894
0
  static constexpr const char *monthstrs[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
2895
2896
0
  static constexpr const char *digitstrs[] = {
2897
0
    "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
2898
0
    "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39",
2899
0
    "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59",
2900
0
    "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79",
2901
0
    "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99",
2902
0
  };
2903
2904
0
  char *buf;
2905
0
  int   sec, min, hour, wday, mday = 0, year = 0, month = 0;
2906
2907
0
  buf = buffer;
2908
2909
0
  sec    = static_cast<int>(value % 60);
2910
0
  value /= 60;
2911
0
  min    = static_cast<int>(value % 60);
2912
0
  value /= 60;
2913
0
  hour   = static_cast<int>(value % 24);
2914
0
  value /= 24;
2915
2916
  /* Jan 1, 1970 was a Thursday */
2917
0
  wday = static_cast<int>((4 + value) % 7);
2918
2919
/* value is days since Jan 1, 1970 */
2920
0
#if MIME_FORMAT_DATE_USE_LOOKUP_TABLE
2921
0
  mime_days_since_epoch_to_mdy(value, &month, &mday, &year);
2922
#else
2923
  mime_days_since_epoch_to_mdy_slowcase(value, &month, &mday, &year);
2924
#endif
2925
2926
  /* arrange the date in the buffer */
2927
0
  ink_assert((mday >= 0) && (mday <= 99));
2928
0
  ink_assert((hour >= 0) && (hour <= 99));
2929
0
  ink_assert((min >= 0) && (min <= 99));
2930
0
  ink_assert((sec >= 0) && (sec <= 99));
2931
2932
  /* the day string */
2933
0
  const char *three_char_day  = daystrs[wday];
2934
0
  buf[0]                      = three_char_day[0];
2935
0
  buf[1]                      = three_char_day[1];
2936
0
  buf[2]                      = three_char_day[2];
2937
0
  buf                        += 3;
2938
2939
0
  buf[0]  = ',';
2940
0
  buf[1]  = ' ';
2941
0
  buf    += 2;
2942
2943
  /* the day of month */
2944
0
  buf[0]  = digitstrs[mday][0];
2945
0
  buf[1]  = digitstrs[mday][1];
2946
0
  buf[2]  = ' ';
2947
0
  buf    += 3;
2948
2949
  /* the month string */
2950
0
  const char *three_char_month  = monthstrs[month];
2951
0
  buf[0]                        = three_char_month[0];
2952
0
  buf[1]                        = three_char_month[1];
2953
0
  buf[2]                        = three_char_month[2];
2954
0
  buf                          += 3;
2955
2956
  /* the year */
2957
0
  buf[0] = ' ';
2958
2959
0
  if ((year >= 2000) && (year <= 2009)) {
2960
0
    buf[1] = '2';
2961
0
    buf[2] = '0';
2962
0
    buf[3] = '0';
2963
0
    buf[4] = (year - 2000) + '0';
2964
0
  } else if ((year >= 1990) && (year <= 1999)) {
2965
0
    buf[1] = '1';
2966
0
    buf[2] = '9';
2967
0
    buf[3] = '9';
2968
0
    buf[4] = (year - 1990) + '0';
2969
0
  } else {
2970
0
    buf[4]  = (year % 10) + '0';
2971
0
    year   /= 10;
2972
0
    buf[3]  = (year % 10) + '0';
2973
0
    year   /= 10;
2974
0
    buf[2]  = (year % 10) + '0';
2975
0
    year   /= 10;
2976
0
    buf[1]  = (year % 10) + '0';
2977
0
  }
2978
0
  buf[5]  = ' ';
2979
0
  buf    += 6;
2980
2981
  /* the hour */
2982
0
  buf[0]  = digitstrs[hour][0];
2983
0
  buf[1]  = digitstrs[hour][1];
2984
0
  buf[2]  = ':';
2985
0
  buf    += 3;
2986
2987
  /* the minute */
2988
0
  buf[0]  = digitstrs[min][0];
2989
0
  buf[1]  = digitstrs[min][1];
2990
0
  buf[2]  = ':';
2991
0
  buf    += 3;
2992
2993
  /* the second */
2994
0
  buf[0]  = digitstrs[sec][0];
2995
0
  buf[1]  = digitstrs[sec][1];
2996
0
  buf[2]  = ' ';
2997
0
  buf    += 3;
2998
2999
  /* the timezone string */
3000
0
  buf[0]  = 'G';
3001
0
  buf[1]  = 'M';
3002
0
  buf[2]  = 'T';
3003
0
  buf[3]  = '\0';
3004
0
  buf    += 3;
3005
3006
0
  return buf - buffer; // not counting NUL
3007
0
}
3008
3009
int32_t
3010
mime_parse_int(const char *buf, const char *end)
3011
0
{
3012
0
  int32_t num;
3013
0
  bool    negative;
3014
3015
0
  if (!buf || (buf == end)) {
3016
0
    return 0;
3017
0
  }
3018
3019
0
  if (is_digit(*buf)) { // fast case
3020
0
    num = *buf++ - '0';
3021
0
    while ((buf != end) && is_digit(*buf)) {
3022
0
      if (num != INT_MAX) {
3023
0
        int new_num = (num * 10) + (*buf++ - '0');
3024
3025
0
        num = (new_num < num ? INT_MAX : new_num); // Check for overflow
3026
0
      } else {
3027
0
        ++buf; // Skip the remaining (valid) digits since we reached MAX/MIN_INT
3028
0
      }
3029
0
    }
3030
3031
0
    return num;
3032
0
  } else {
3033
0
    num      = 0;
3034
0
    negative = false;
3035
3036
0
    while ((buf != end) && ParseRules::is_space(*buf)) {
3037
0
      buf += 1;
3038
0
    }
3039
3040
0
    if ((buf != end) && (*buf == '-')) {
3041
0
      negative  = true;
3042
0
      buf      += 1;
3043
0
    }
3044
    // NOTE: we first compute the value as negative then correct the
3045
    // sign back to positive. This enables us to correctly parse MININT.
3046
0
    while ((buf != end) && is_digit(*buf)) {
3047
0
      if (num != INT_MIN) {
3048
0
        int new_num = (num * 10) - (*buf++ - '0');
3049
3050
0
        num = (new_num > num ? INT_MIN : new_num); // Check for overflow, so to speak, see above re: negative
3051
0
      } else {
3052
0
        ++buf; // Skip the remaining (valid) digits since we reached MAX/MIN_INT
3053
0
      }
3054
0
    }
3055
3056
0
    if (!negative) {
3057
0
      num = -num;
3058
0
    }
3059
3060
0
    return num;
3061
0
  }
3062
0
}
3063
3064
uint32_t
3065
mime_parse_uint(const char *buf, const char *end)
3066
0
{
3067
0
  uint32_t num;
3068
3069
0
  if (!buf || (buf == end)) {
3070
0
    return 0;
3071
0
  }
3072
3073
0
  if (is_digit(*buf)) // fast case
3074
0
  {
3075
0
    num = *buf++ - '0';
3076
0
    while ((buf != end) && is_digit(*buf)) {
3077
0
      num = (num * 10) + (*buf++ - '0');
3078
0
    }
3079
0
    return num;
3080
0
  } else {
3081
0
    num = 0;
3082
0
    while ((buf != end) && ParseRules::is_space(*buf)) {
3083
0
      buf += 1;
3084
0
    }
3085
0
    while ((buf != end) && is_digit(*buf)) {
3086
0
      num = (num * 10) + (*buf++ - '0');
3087
0
    }
3088
0
    return num;
3089
0
  }
3090
0
}
3091
3092
int64_t
3093
mime_parse_int64(const char *buf, const char *end)
3094
0
{
3095
0
  int64_t num;
3096
0
  bool    negative;
3097
3098
0
  if (!buf || (buf == end)) {
3099
0
    return 0;
3100
0
  }
3101
3102
0
  if (is_digit(*buf)) // fast case
3103
0
  {
3104
0
    num = *buf++ - '0';
3105
0
    while ((buf != end) && is_digit(*buf)) {
3106
0
      num = (num * 10) + (*buf++ - '0');
3107
0
    }
3108
0
    return num;
3109
0
  } else {
3110
0
    num      = 0;
3111
0
    negative = false;
3112
3113
0
    while ((buf != end) && ParseRules::is_space(*buf)) {
3114
0
      buf += 1;
3115
0
    }
3116
3117
0
    if ((buf != end) && (*buf == '-')) {
3118
0
      negative  = true;
3119
0
      buf      += 1;
3120
0
    }
3121
    // NOTE: we first compute the value as negative then correct the
3122
    // sign back to positive. This enables us to correctly parse MININT.
3123
0
    while ((buf != end) && is_digit(*buf)) {
3124
0
      num = (num * 10) - (*buf++ - '0');
3125
0
    }
3126
3127
0
    if (!negative) {
3128
0
      num = -num;
3129
0
    }
3130
3131
0
    return num;
3132
0
  }
3133
0
}
3134
3135
/*-------------------------------------------------------------------------
3136
3137
  mime_parse_rfc822_date_fastcase (const char *buf, int length, struct tm *tp)
3138
3139
  This routine is a fast case parser for date strings that are guaranteed
3140
  to be in the rfc822/rfc1123 format.  It uses integer binary searches to
3141
  convert daya and month names into integral indices.
3142
3143
  This routine only supports rfc822/rfc1123 dates.  It must be called
3144
  with NO leading whitespace, with a length >= 29 characters, and with
3145
  a comma in buf[3].  The caller MUST ensure these conditions.
3146
3147
        Sun, 06 Nov 1994 08:49:37 GMT
3148
        01234567890123456789012345678
3149
3150
  This function handles the common case dates fast.  The day and month
3151
  are processed as 3 character integers before falling to DFA.  This
3152
  table shows string, hash (24 bit values of 3 characters), and the
3153
  resulting string index.
3154
3155
        Fri 0x467269 5    Apr 0x417072 3
3156
        Mon 0x4D6F6E 1    Aug 0x417567 7
3157
        Sat 0x536174 6    Dec 0x446563 11
3158
        Sun 0x53756E 0    Feb 0x466562 1
3159
        Thu 0x546875 4    Jan 0x4A616E 0
3160
        Tue 0x547565 2    Jul 0x4A756C 6
3161
        Wed 0x576564 3    Jun 0x4A756E 5
3162
                          Mar 0x4D6172 2
3163
                          May 0x4D6179 4
3164
                          Nov 0x4E6F76 10
3165
                          Oct 0x4F6374 9
3166
                          Sep 0x536570 8
3167
3168
  -------------------------------------------------------------------------*/
3169
int
3170
mime_parse_rfc822_date_fastcase(const char *buf, int length, struct tm *tp)
3171
0
{
3172
0
  unsigned int three_char_wday, three_char_mon;
3173
3174
0
  ink_assert(length >= 29);
3175
0
  ink_assert(!is_ws(buf[0]));
3176
0
  ink_assert(buf[3] == ',');
3177
3178
  ////////////////////////////
3179
  // binary search for wday //
3180
  ////////////////////////////
3181
0
  tp->tm_wday     = -1;
3182
0
  three_char_wday = (buf[0] << 16) | (buf[1] << 8) | buf[2];
3183
0
  if (three_char_wday <= 0x53756E) {
3184
0
    if (three_char_wday == 0x467269) {
3185
0
      tp->tm_wday = 5;
3186
0
    } else if (three_char_wday == 0x4D6F6E) {
3187
0
      tp->tm_wday = 1;
3188
0
    } else if (three_char_wday == 0x536174) {
3189
0
      tp->tm_wday = 6;
3190
0
    } else if (three_char_wday == 0x53756E) {
3191
0
      tp->tm_wday = 0;
3192
0
    }
3193
0
  } else {
3194
0
    if (three_char_wday == 0x546875) {
3195
0
      tp->tm_wday = 4;
3196
0
    } else if (three_char_wday == 0x547565) {
3197
0
      tp->tm_wday = 2;
3198
0
    } else if (three_char_wday == 0x576564) {
3199
0
      tp->tm_wday = 3;
3200
0
    }
3201
0
  }
3202
0
  if (tp->tm_wday < 0) {
3203
0
    tp->tm_wday = match_3char_ci({buf, 3}, day_names_packed);
3204
0
    if (tp->tm_wday < 0) {
3205
0
      return 0;
3206
0
    }
3207
0
  }
3208
  //////////////////////////
3209
  // extract day of month //
3210
  //////////////////////////
3211
0
  tp->tm_mday = (buf[5] - '0') * 10 + (buf[6] - '0');
3212
3213
  /////////////////////////////
3214
  // binary search for month //
3215
  /////////////////////////////
3216
0
  tp->tm_mon     = -1;
3217
0
  three_char_mon = (buf[8] << 16) | (buf[9] << 8) | buf[10];
3218
0
  if (three_char_mon <= 0x4A756C) {
3219
0
    if (three_char_mon <= 0x446563) {
3220
0
      if (three_char_mon == 0x417072) {
3221
0
        tp->tm_mon = 3;
3222
0
      } else if (three_char_mon == 0x417567) {
3223
0
        tp->tm_mon = 7;
3224
0
      } else if (three_char_mon == 0x446563) {
3225
0
        tp->tm_mon = 11;
3226
0
      }
3227
0
    } else {
3228
0
      if (three_char_mon == 0x466562) {
3229
0
        tp->tm_mon = 1;
3230
0
      } else if (three_char_mon == 0x4A616E) {
3231
0
        tp->tm_mon = 0;
3232
0
      } else if (three_char_mon == 0x4A756C) {
3233
0
        tp->tm_mon = 6;
3234
0
      }
3235
0
    }
3236
0
  } else {
3237
0
    if (three_char_mon <= 0x4D6179) {
3238
0
      if (three_char_mon == 0x4A756E) {
3239
0
        tp->tm_mon = 5;
3240
0
      } else if (three_char_mon == 0x4D6172) {
3241
0
        tp->tm_mon = 2;
3242
0
      } else if (three_char_mon == 0x4D6179) {
3243
0
        tp->tm_mon = 4;
3244
0
      }
3245
0
    } else {
3246
0
      if (three_char_mon == 0x4E6F76) {
3247
0
        tp->tm_mon = 10;
3248
0
      } else if (three_char_mon == 0x4F6374) {
3249
0
        tp->tm_mon = 9;
3250
0
      } else if (three_char_mon == 0x536570) {
3251
0
        tp->tm_mon = 8;
3252
0
      }
3253
0
    }
3254
0
  }
3255
0
  if (tp->tm_mon < 0) {
3256
0
    tp->tm_mon = match_3char_ci({buf + 8, 3}, month_names_packed);
3257
0
    if (tp->tm_mon < 0) {
3258
0
      return 0;
3259
0
    }
3260
0
  }
3261
  //////////////////
3262
  // extract year //
3263
  //////////////////
3264
0
  tp->tm_year = ((buf[12] - '0') * 1000 + (buf[13] - '0') * 100 + (buf[14] - '0') * 10 + (buf[15] - '0')) - 1900;
3265
3266
  //////////////////
3267
  // extract time //
3268
  //////////////////
3269
0
  tp->tm_hour = (buf[17] - '0') * 10 + (buf[18] - '0');
3270
0
  tp->tm_min  = (buf[20] - '0') * 10 + (buf[21] - '0');
3271
0
  tp->tm_sec  = (buf[23] - '0') * 10 + (buf[24] - '0');
3272
0
  if ((buf[19] != ':') || (buf[22] != ':')) {
3273
0
    return 0;
3274
0
  }
3275
0
  return 1;
3276
0
}
3277
3278
/*-------------------------------------------------------------------------
3279
   Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123
3280
   Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
3281
   Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format
3282
   6 Nov 1994 08:49:37 GMT        ; NNTP-style date
3283
  -------------------------------------------------------------------------*/
3284
time_t
3285
mime_parse_date(const char *buf, const char *end)
3286
0
{
3287
0
  static constexpr int DAYS_OFFSET = 25508;
3288
0
  static constexpr int days[12]    = {305, 336, -1, 30, 60, 91, 121, 152, 183, 213, 244, 274};
3289
3290
0
  struct tm tp;
3291
0
  time_t    t;
3292
0
  int       year;
3293
0
  int       month;
3294
0
  int       mday;
3295
3296
0
  if (!buf) {
3297
0
    return static_cast<time_t>(0);
3298
0
  }
3299
3300
0
  while ((buf != end) && is_ws(*buf)) {
3301
0
    buf += 1;
3302
0
  }
3303
3304
0
  if ((buf != end) && is_digit(*buf)) { // NNTP date
3305
0
    if (!mime_parse_mday(buf, end, &tp.tm_mday)) {
3306
0
      return static_cast<time_t>(0);
3307
0
    }
3308
0
    if (!mime_parse_month(buf, end, &tp.tm_mon)) {
3309
0
      return static_cast<time_t>(0);
3310
0
    }
3311
0
    if (!mime_parse_year(buf, end, &tp.tm_year)) {
3312
0
      return static_cast<time_t>(0);
3313
0
    }
3314
0
    if (!mime_parse_time(buf, end, &tp.tm_hour, &tp.tm_min, &tp.tm_sec)) {
3315
0
      return static_cast<time_t>(0);
3316
0
    }
3317
0
  } else if (end && (end - buf >= 29) && (buf[3] == ',')) {
3318
0
    if (!mime_parse_rfc822_date_fastcase(buf, end - buf, &tp)) {
3319
0
      return static_cast<time_t>(0);
3320
0
    }
3321
0
  } else {
3322
0
    if (!mime_parse_day(buf, end, &tp.tm_wday)) {
3323
0
      return static_cast<time_t>(0);
3324
0
    }
3325
3326
0
    while ((buf != end) && is_ws(*buf)) {
3327
0
      buf += 1;
3328
0
    }
3329
3330
0
    if ((buf != end) && ((*buf == ',') || is_digit(*buf))) {
3331
      // RFC 822 or RFC 850 time format
3332
0
      if (!mime_parse_mday(buf, end, &tp.tm_mday)) {
3333
0
        return static_cast<time_t>(0);
3334
0
      }
3335
0
      if (!mime_parse_month(buf, end, &tp.tm_mon)) {
3336
0
        return static_cast<time_t>(0);
3337
0
      }
3338
0
      if (!mime_parse_year(buf, end, &tp.tm_year)) {
3339
0
        return static_cast<time_t>(0);
3340
0
      }
3341
0
      if (!mime_parse_time(buf, end, &tp.tm_hour, &tp.tm_min, &tp.tm_sec)) {
3342
0
        return static_cast<time_t>(0);
3343
0
      }
3344
      // ignore timezone specifier...should always be GMT anyways
3345
0
    } else {
3346
      // ANSI C's asctime format
3347
0
      if (!mime_parse_month(buf, end, &tp.tm_mon)) {
3348
0
        return static_cast<time_t>(0);
3349
0
      }
3350
0
      if (!mime_parse_mday(buf, end, &tp.tm_mday)) {
3351
0
        return static_cast<time_t>(0);
3352
0
      }
3353
0
      if (!mime_parse_time(buf, end, &tp.tm_hour, &tp.tm_min, &tp.tm_sec)) {
3354
0
        return static_cast<time_t>(0);
3355
0
      }
3356
0
      if (!mime_parse_year(buf, end, &tp.tm_year)) {
3357
0
        return static_cast<time_t>(0);
3358
0
      }
3359
0
    }
3360
0
  }
3361
3362
0
  year  = tp.tm_year;
3363
0
  month = tp.tm_mon;
3364
0
  mday  = tp.tm_mday;
3365
3366
  // what should we do?
3367
0
  if (year > 137) {
3368
0
    return static_cast<time_t>(INT_MAX);
3369
0
  }
3370
0
  if (year < 70) {
3371
0
    return static_cast<time_t>(0);
3372
0
  }
3373
3374
0
  mday += days[month];
3375
  /* month base == march */
3376
0
  if (month < 2) {
3377
0
    year -= 1;
3378
0
  }
3379
0
  mday += (year * 365) + (year / 4) - (year / 100) + (year / 100 + 3) / 4;
3380
0
  mday -= DAYS_OFFSET;
3381
3382
0
  t = ((mday * 24 + tp.tm_hour) * 60 + tp.tm_min) * 60 + tp.tm_sec;
3383
3384
0
  return t;
3385
0
}
3386
3387
bool
3388
mime_parse_day(const char *&buf, const char *end, int *day)
3389
0
{
3390
0
  const char *e;
3391
3392
0
  while ((buf != end) && *buf && !ParseRules::is_alpha(*buf)) {
3393
0
    buf += 1;
3394
0
  }
3395
3396
0
  e = buf;
3397
0
  while ((e != end) && *e && ParseRules::is_alpha(*e)) {
3398
0
    e += 1;
3399
0
  }
3400
3401
0
  *day = match_3char_ci({buf, static_cast<size_t>(e - buf)}, day_names_packed);
3402
0
  if (*day < 0) {
3403
0
    return false;
3404
0
  } else {
3405
0
    buf = e;
3406
0
    return true;
3407
0
  }
3408
0
}
3409
3410
bool
3411
mime_parse_month(const char *&buf, const char *end, int *month)
3412
0
{
3413
0
  const char *e;
3414
3415
0
  while ((buf != end) && *buf && !ParseRules::is_alpha(*buf)) {
3416
0
    buf += 1;
3417
0
  }
3418
3419
0
  e = buf;
3420
0
  while ((e != end) && *e && ParseRules::is_alpha(*e)) {
3421
0
    e += 1;
3422
0
  }
3423
3424
0
  *month = match_3char_ci({buf, static_cast<size_t>(e - buf)}, month_names_packed);
3425
0
  if (*month < 0) {
3426
0
    return false;
3427
0
  } else {
3428
0
    buf = e;
3429
0
    return true;
3430
0
  }
3431
0
}
3432
3433
bool
3434
mime_parse_mday(const char *&buf, const char *end, int *mday)
3435
0
{
3436
0
  return mime_parse_integer(buf, end, mday);
3437
0
}
3438
3439
bool
3440
mime_parse_year(const char *&buf, const char *end, int *year)
3441
0
{
3442
0
  int val;
3443
3444
0
  while ((buf != end) && *buf && !is_digit(*buf)) {
3445
0
    buf += 1;
3446
0
  }
3447
3448
0
  if ((buf == end) || (*buf == '\0')) {
3449
0
    return false;
3450
0
  }
3451
3452
0
  val = 0;
3453
3454
0
  while ((buf != end) && *buf && is_digit(*buf)) {
3455
0
    val = (val * 10) + (*buf++ - '0');
3456
0
  }
3457
3458
0
  if (val >= 1900) {
3459
0
    val -= 1900;
3460
0
  } else if (val < 70) {
3461
0
    val += 100;
3462
0
  }
3463
3464
0
  *year = val;
3465
3466
0
  return true;
3467
0
}
3468
3469
bool
3470
mime_parse_time(const char *&buf, const char *end, int *hour, int *min, int *sec)
3471
0
{
3472
0
  if (!mime_parse_integer(buf, end, hour)) {
3473
0
    return false;
3474
0
  }
3475
0
  if (!mime_parse_integer(buf, end, min)) {
3476
0
    return false;
3477
0
  }
3478
0
  if (!mime_parse_integer(buf, end, sec)) {
3479
0
    return false;
3480
0
  }
3481
0
  return true;
3482
0
}
3483
3484
// This behaves slightly different than mime_parse_int(), int that we actually
3485
// return a "bool" for success / failure on "reasonable" parsing. This kinda
3486
// dumb, because we have two interfaces, where one does not move along the
3487
// buf pointer, but this one does (and the ones using this function do).
3488
bool
3489
mime_parse_integer(const char *&buf, const char *end, int *integer)
3490
0
{
3491
0
  while ((buf != end) && *buf && !is_digit(*buf) && (*buf != '-')) {
3492
0
    buf += 1;
3493
0
  }
3494
3495
0
  if ((buf == end) || (*buf == '\0')) {
3496
0
    return false;
3497
0
  }
3498
3499
0
  int32_t num;
3500
0
  bool    negative;
3501
3502
  // This code is copied verbatim from mime_parse_int ... Sigh. Maybe amc is right, and
3503
  // we really need to clean this up. But, as such, we should redo all these interfaces,
3504
  // and that's a big undertaking (and we'd want to move these strings all to string_view's).
3505
0
  if (is_digit(*buf)) { // fast case
3506
0
    num = *buf++ - '0';
3507
0
    while ((buf != end) && is_digit(*buf)) {
3508
0
      if (num != INT_MAX) {
3509
0
        int new_num = (num * 10) + (*buf++ - '0');
3510
3511
0
        num = (new_num < num ? INT_MAX : new_num); // Check for overflow
3512
0
      } else {
3513
0
        ++buf; // Skip the remaining (valid) digits since we reached MAX/MIN_INT
3514
0
      }
3515
0
    }
3516
0
  } else {
3517
0
    num      = 0;
3518
0
    negative = false;
3519
3520
0
    while ((buf != end) && ParseRules::is_space(*buf)) {
3521
0
      buf += 1;
3522
0
    }
3523
3524
0
    if ((buf != end) && (*buf == '-')) {
3525
0
      negative  = true;
3526
0
      buf      += 1;
3527
0
    }
3528
    // NOTE: we first compute the value as negative then correct the
3529
    // sign back to positive. This enables us to correctly parse MININT.
3530
0
    while ((buf != end) && is_digit(*buf)) {
3531
0
      if (num != INT_MIN) {
3532
0
        int new_num = (num * 10) - (*buf++ - '0');
3533
3534
0
        num = (new_num > num ? INT_MIN : new_num); // Check for overflow, so to speak, see above re: negative
3535
0
      } else {
3536
0
        ++buf; // Skip the remaining (valid) digits since we reached MAX/MIN_INT
3537
0
      }
3538
0
    }
3539
3540
0
    if (!negative) {
3541
0
      num = -num;
3542
0
    }
3543
0
  }
3544
3545
0
  *integer = num;
3546
3547
0
  return true;
3548
0
}
3549
3550
/***********************************************************************
3551
 *                                                                     *
3552
 *                        M A R S H A L I N G                          *
3553
 *                                                                     *
3554
 ***********************************************************************/
3555
int
3556
MIMEFieldBlockImpl::marshal(MarshalXlate *ptr_xlate, int num_ptr, MarshalXlate *str_xlate, int num_str)
3557
0
{
3558
  // printf("FieldBlockImpl:marshal  num_ptr = %d  num_str = %d\n", num_ptr, num_str);
3559
0
  HDR_MARSHAL_PTR(m_next, MIMEFieldBlockImpl, ptr_xlate, num_ptr);
3560
3561
0
  if ((num_str == 1) && (num_ptr == 1)) {
3562
0
    for (uint32_t index = 0; index < m_freetop; index++) {
3563
0
      MIMEField *field = &(m_field_slots[index]);
3564
3565
0
      if (field->is_live()) {
3566
0
        HDR_MARSHAL_STR_1(field->m_ptr_name, str_xlate);
3567
0
        HDR_MARSHAL_STR_1(field->m_ptr_value, str_xlate);
3568
0
        if (field->m_next_dup) {
3569
0
          HDR_MARSHAL_PTR_1(field->m_next_dup, MIMEField, ptr_xlate);
3570
0
        }
3571
0
      }
3572
0
    }
3573
0
  } else {
3574
0
    for (uint32_t index = 0; index < m_freetop; index++) {
3575
0
      MIMEField *field = &(m_field_slots[index]);
3576
3577
0
      if (field->is_live()) {
3578
0
        HDR_MARSHAL_STR(field->m_ptr_name, str_xlate, num_str);
3579
0
        HDR_MARSHAL_STR(field->m_ptr_value, str_xlate, num_str);
3580
0
        if (field->m_next_dup) {
3581
0
          HDR_MARSHAL_PTR(field->m_next_dup, MIMEField, ptr_xlate, num_ptr);
3582
0
        }
3583
0
      }
3584
0
    }
3585
0
  }
3586
0
  return 0;
3587
0
}
3588
3589
void
3590
MIMEFieldBlockImpl::unmarshal(intptr_t offset)
3591
0
{
3592
0
  HDR_UNMARSHAL_PTR(m_next, MIMEFieldBlockImpl, offset);
3593
3594
0
  for (uint32_t index = 0; index < m_freetop; index++) {
3595
0
    MIMEField *field = &(m_field_slots[index]);
3596
3597
0
    if (field->is_live()) {
3598
0
      HDR_UNMARSHAL_STR(field->m_ptr_name, offset);
3599
0
      HDR_UNMARSHAL_STR(field->m_ptr_value, offset);
3600
0
      if (field->m_next_dup) {
3601
0
        HDR_UNMARSHAL_PTR(field->m_next_dup, MIMEField, offset);
3602
0
      }
3603
0
    } else {
3604
      // Clear out other types of slots
3605
0
      field->m_readiness = MIME_FIELD_SLOT_READINESS_EMPTY;
3606
0
    }
3607
0
  }
3608
0
}
3609
3610
void
3611
MIMEFieldBlockImpl::move_strings(HdrStrHeap *new_heap)
3612
0
{
3613
0
  for (uint32_t index = 0; index < m_freetop; index++) {
3614
0
    MIMEField *field = &(m_field_slots[index]);
3615
3616
0
    if (field->is_live() || field->is_detached()) {
3617
      // FIX ME - Should do the field in one shot and preserve
3618
      //   raw_printable if it's set
3619
0
      field->m_n_v_raw_printable = 0;
3620
3621
0
      HDR_MOVE_STR(field->m_ptr_name, field->m_len_name);
3622
0
      HDR_MOVE_STR(field->m_ptr_value, field->m_len_value);
3623
0
    }
3624
0
  }
3625
0
}
3626
3627
size_t
3628
MIMEFieldBlockImpl::strings_length()
3629
0
{
3630
0
  size_t ret = 0;
3631
3632
0
  for (uint32_t index = 0; index < m_freetop; index++) {
3633
0
    MIMEField *field = &(m_field_slots[index]);
3634
3635
0
    if (field->m_readiness == MIME_FIELD_SLOT_READINESS_LIVE || field->m_readiness == MIME_FIELD_SLOT_READINESS_DETACHED) {
3636
0
      ret += field->m_len_name;
3637
0
      ret += field->m_len_value;
3638
0
    }
3639
0
  }
3640
0
  return ret;
3641
0
}
3642
3643
bool
3644
MIMEFieldBlockImpl::contains(const MIMEField *field)
3645
0
{
3646
0
  MIMEField *first = &(m_field_slots[0]);
3647
0
  MIMEField *last  = &(m_field_slots[MIME_FIELD_BLOCK_SLOTS - 1]);
3648
0
  return (field >= first) && (field <= last);
3649
0
}
3650
3651
void
3652
MIMEFieldBlockImpl::check_strings(HeapCheck *heaps, int num_heaps)
3653
0
{
3654
0
  for (uint32_t index = 0; index < m_freetop; index++) {
3655
0
    MIMEField *field = &(m_field_slots[index]);
3656
3657
0
    if (field->is_live() || field->is_detached()) {
3658
      // FIX ME - Should check raw printing characters as well
3659
0
      CHECK_STR(field->m_ptr_name, field->m_len_name, heaps, num_heaps);
3660
0
      CHECK_STR(field->m_ptr_value, field->m_len_value, heaps, num_heaps);
3661
0
    }
3662
0
  }
3663
0
}
3664
3665
int
3666
MIMEHdrImpl::marshal(MarshalXlate *ptr_xlate, int num_ptr, MarshalXlate *str_xlate, int num_str)
3667
0
{
3668
  // printf("MIMEHdrImpl:marshal  num_ptr = %d  num_str = %d\n", num_ptr, num_str);
3669
0
  HDR_MARSHAL_PTR(m_fblock_list_tail, MIMEFieldBlockImpl, ptr_xlate, num_ptr);
3670
0
  return m_first_fblock.marshal(ptr_xlate, num_ptr, str_xlate, num_str);
3671
0
}
3672
3673
void
3674
MIMEHdrImpl::unmarshal(intptr_t offset)
3675
0
{
3676
0
  HDR_UNMARSHAL_PTR(m_fblock_list_tail, MIMEFieldBlockImpl, offset);
3677
0
  m_first_fblock.unmarshal(offset);
3678
0
}
3679
3680
void
3681
MIMEHdrImpl::move_strings(HdrStrHeap *new_heap)
3682
0
{
3683
0
  m_first_fblock.move_strings(new_heap);
3684
0
}
3685
3686
size_t
3687
MIMEHdrImpl::strings_length()
3688
0
{
3689
0
  return m_first_fblock.strings_length();
3690
0
}
3691
3692
void
3693
MIMEHdrImpl::check_strings(HeapCheck *heaps, int num_heaps)
3694
0
{
3695
0
  m_first_fblock.check_strings(heaps, num_heaps);
3696
0
}
3697
3698
void
3699
MIMEHdrImpl::recompute_accelerators_and_presence_bits()
3700
0
{
3701
0
  mime_hdr_reset_accelerators_and_presence_bits(this);
3702
0
}
3703
3704
/***********************************************************************
3705
 *                                                                     *
3706
 *                 C O O K E D    V A L U E    C A C H E               *
3707
 *                                                                     *
3708
 ***********************************************************************/
3709
3710
////////////////////////////////////////////////////////
3711
// we need to recook the cooked values cache when:    //
3712
//                                                    //
3713
//      setting the value and the field is live       //
3714
//      attaching the field and the field isn't empty //
3715
//      detaching the field                           //
3716
////////////////////////////////////////////////////////
3717
3718
void
3719
MIMEHdrImpl::recompute_cooked_stuff(MIMEField *changing_field_or_null, const std::string_view *targeted_headers,
3720
                                    size_t targeted_headers_count)
3721
0
{
3722
0
  int         len, tlen;
3723
0
  const char *s;
3724
0
  const char *c;
3725
0
  const char *e;
3726
0
  const char *token_wks;
3727
0
  MIMEField  *field;
3728
0
  uint32_t    mask = 0;
3729
3730
0
  mime_hdr_cooked_stuff_init(this, changing_field_or_null);
3731
3732
  /////////////////////////////////////////////////////////////////////////////
3733
  // (1) cook the Cache-Control header (or targeted variant) if present     //
3734
  /////////////////////////////////////////////////////////////////////////////
3735
3736
  // to be safe, recompute unless you know this call is for other cooked field
3737
0
  if ((changing_field_or_null == nullptr) || (changing_field_or_null->m_wks_idx != MIME_WKSIDX_PRAGMA)) {
3738
0
    ink_assert(targeted_headers != nullptr || targeted_headers_count == 0);
3739
0
    field = nullptr;
3740
3741
    // Check for targeted cache control headers first (in priority order).
3742
0
    for (size_t i = 0; i < targeted_headers_count; ++i) {
3743
0
      field = mime_hdr_field_find(this, targeted_headers[i]);
3744
0
      if (field) {
3745
0
        break;
3746
0
      }
3747
0
    }
3748
3749
    // If no targeted header was found, fall back to standard Cache-Control.
3750
0
    if (!field) {
3751
0
      field = mime_hdr_field_find(this, static_cast<std::string_view>(MIME_FIELD_CACHE_CONTROL));
3752
0
    }
3753
3754
0
    if (field) {
3755
      // try pathpaths first -- unlike most other fastpaths, this one
3756
      // is probably more useful for polygraph than for the real world
3757
0
      if (!field->has_dups()) {
3758
0
        auto val{field->value_get()};
3759
0
        if (ptr_len_casecmp(val.data(), val.length(), "public", 6) == 0) {
3760
0
          mask                                   = MIME_COOKED_MASK_CC_PUBLIC;
3761
0
          m_cooked_stuff.m_cache_control.m_mask |= mask;
3762
0
        } else if (ptr_len_casecmp(val.data(), val.length(), "private,no-cache", 16) == 0) {
3763
0
          mask                                   = MIME_COOKED_MASK_CC_PRIVATE | MIME_COOKED_MASK_CC_NO_CACHE;
3764
0
          m_cooked_stuff.m_cache_control.m_mask |= mask;
3765
0
        }
3766
0
      }
3767
3768
0
      if (mask == 0) {
3769
0
        HdrCsvIter csv_iter;
3770
3771
0
        for (s = csv_iter.get_first(field, &len); s != nullptr; s = csv_iter.get_next(&len)) {
3772
0
          e = s + len;
3773
          // Store set mask bits from this CSV value so we can clear them if needed.
3774
0
          uint32_t csv_value_mask = 0;
3775
3776
0
          for (c = s; (c < e) && (ParseRules::is_token(*c)); c++) {
3777
0
            ;
3778
0
          }
3779
0
          tlen = c - s;
3780
3781
          // If >= 0 then this is a well known token
3782
0
          if (hdrtoken_tokenize(s, tlen, &token_wks) >= 0) {
3783
#if TRACK_COOKING
3784
            Dbg(dbg_ctl_http, "recompute_cooked_stuff: got field '%s'", token_wks);
3785
#endif
3786
3787
0
            HdrTokenHeapPrefix *p                  = hdrtoken_wks_to_prefix(token_wks);
3788
0
            mask                                   = p->wks_type_specific.u.cache_control.cc_mask;
3789
0
            m_cooked_stuff.m_cache_control.m_mask |= mask;
3790
0
            csv_value_mask                        |= mask;
3791
3792
#if TRACK_COOKING
3793
            Dbg(dbg_ctl_http, "                        set mask 0x%0X", mask);
3794
#endif
3795
3796
0
            if (mask & (MIME_COOKED_MASK_CC_MAX_AGE | MIME_COOKED_MASK_CC_S_MAXAGE | MIME_COOKED_MASK_CC_MAX_STALE |
3797
0
                        MIME_COOKED_MASK_CC_MIN_FRESH)) {
3798
0
              int value;
3799
              // Per RFC 7230 Section 3.2.3, there should be no whitespace around '='.
3800
0
              const char *value_start = c;
3801
3802
              // Check if the next character is '=' (no space allowed before '=').
3803
0
              if (c < e && *c == '=') {
3804
0
                ++c; // Move past the '='
3805
3806
                // Again: no whitespace after the '=' either. Keep in mind that values can be negative.
3807
0
                bool valid_syntax = (c < e) && (is_digit(*c) || *c == '-');
3808
3809
0
                if (valid_syntax) {
3810
                  // Reset to value_start to let mime_parse_integer do its work.
3811
0
                  c = value_start;
3812
0
                  if (mime_parse_integer(c, e, &value)) {
3813
#if TRACK_COOKING
3814
                    Dbg(dbg_ctl_http, "                        set integer value %d", value);
3815
#endif
3816
0
                    if (token_wks == MIME_VALUE_MAX_AGE.c_str()) {
3817
0
                      m_cooked_stuff.m_cache_control.m_secs_max_age = value;
3818
0
                    } else if (token_wks == MIME_VALUE_MIN_FRESH.c_str()) {
3819
0
                      m_cooked_stuff.m_cache_control.m_secs_min_fresh = value;
3820
0
                    } else if (token_wks == MIME_VALUE_MAX_STALE.c_str()) {
3821
0
                      m_cooked_stuff.m_cache_control.m_secs_max_stale = value;
3822
0
                    } else if (token_wks == MIME_VALUE_S_MAXAGE.c_str()) {
3823
0
                      m_cooked_stuff.m_cache_control.m_secs_s_maxage = value;
3824
0
                    }
3825
0
                  } else {
3826
#if TRACK_COOKING
3827
                    Dbg(dbg_ctl_http, "                        set integer value %d", INT_MAX);
3828
#endif
3829
0
                    if (token_wks == MIME_VALUE_MAX_STALE.c_str()) {
3830
0
                      m_cooked_stuff.m_cache_control.m_secs_max_stale = INT_MAX;
3831
0
                    }
3832
0
                  }
3833
0
                } else {
3834
                  // Syntax is malformed (e.g., whitespace after '=', quotes around value, or no value).
3835
                  // Treat this as unrecognized and clear the mask.
3836
0
                  csv_value_mask                         = 0;
3837
0
                  m_cooked_stuff.m_cache_control.m_mask &= ~mask;
3838
0
                }
3839
0
              } else {
3840
                // No '=' found, or whitespace before '='. This is malformed.
3841
                // For directives that require values, this is an error.
3842
                // Clear the mask for this directive.
3843
0
                csv_value_mask                         = 0;
3844
0
                m_cooked_stuff.m_cache_control.m_mask &= ~mask;
3845
0
              }
3846
0
            }
3847
3848
            // Detect whether there is any more non-whitespace content after the
3849
            // directive. This indicates an unrecognized or malformed directive.
3850
            // This can happen, for instance, if the host uses semicolons
3851
            // instead of commas as separators which is against RFC 7234 (see
3852
            // issue #12029). Regardless of the cause, this means we need to
3853
            // ignore the directive and clear any mask bits we set from it.
3854
0
            while (c < e && ParseRules::is_ws(*c)) {
3855
0
              ++c;
3856
0
            }
3857
0
            if (c < e) {
3858
              // There's non-whitespace content that wasn't parsed. This means
3859
              // that we cannot really understand what this directive is.
3860
              // Per RFC 7234 Section 5.2: "A cache MUST ignore unrecognized cache
3861
              // directives."
3862
0
              if (csv_value_mask != 0) {
3863
                // Reverse the mask that we set above.
3864
0
                m_cooked_stuff.m_cache_control.m_mask &= ~csv_value_mask;
3865
0
              }
3866
0
            }
3867
0
          }
3868
0
        }
3869
0
      }
3870
0
    }
3871
0
  }
3872
  ///////////////////////////////////////////
3873
  // (2) cook the Pragma header if present //
3874
  ///////////////////////////////////////////
3875
3876
0
  if ((changing_field_or_null == nullptr) || (changing_field_or_null->m_wks_idx != MIME_WKSIDX_CACHE_CONTROL)) {
3877
0
    field = mime_hdr_field_find(this, static_cast<std::string_view>(MIME_FIELD_PRAGMA));
3878
0
    if (field) {
3879
0
      if (!field->has_dups()) { // try fastpath first
3880
0
        auto val{field->value_get()};
3881
0
        if (ptr_len_casecmp(val.data(), val.length(), "no-cache", 8) == 0) {
3882
0
          m_cooked_stuff.m_pragma.m_no_cache = true;
3883
0
          return;
3884
0
        }
3885
0
      }
3886
3887
0
      {
3888
0
        HdrCsvIter csv_iter;
3889
3890
0
        for (s = csv_iter.get_first(field, &len); s != nullptr; s = csv_iter.get_next(&len)) {
3891
0
          e = s + len;
3892
0
          for (c = s; (c < e) && (ParseRules::is_token(*c)); c++) {
3893
0
            ;
3894
0
          }
3895
0
          tlen = c - s;
3896
3897
0
          if (hdrtoken_tokenize(s, tlen, &token_wks) >= 0) {
3898
0
            if (token_wks == MIME_VALUE_NO_CACHE.c_str()) {
3899
0
              m_cooked_stuff.m_pragma.m_no_cache = true;
3900
0
            }
3901
0
          }
3902
0
        }
3903
0
      }
3904
0
    }
3905
0
  }
3906
0
}