/src/trafficserver/lib/ls-hpack/lshpack.cc
Line | Count | Source |
1 | | /** @file |
2 | | |
3 | | Implementations of LiteSpeed functions used by ATS for hpack. |
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 | | /* |
25 | | MIT License |
26 | | |
27 | | Copyright (c) 2018 - 2023 LiteSpeed Technologies Inc |
28 | | |
29 | | Permission is hereby granted, free of charge, to any person obtaining a copy |
30 | | of this software and associated documentation files (the "Software"), to deal |
31 | | in the Software without restriction, including without limitation the rights |
32 | | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
33 | | copies of the Software, and to permit persons to whom the Software is |
34 | | furnished to do so, subject to the following conditions: |
35 | | |
36 | | The above copyright notice and this permission notice shall be included in all |
37 | | copies or substantial portions of the Software. |
38 | | |
39 | | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
40 | | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
41 | | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
42 | | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
43 | | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
44 | | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
45 | | SOFTWARE. |
46 | | */ |
47 | | |
48 | | /** ATS |
49 | | * This is the minimal implementation from lshpack.c, lshpack.h, and |
50 | | * lsxpack_header.h required from LiteSpeed to perform hpack given the ATS API |
51 | | * needs. |
52 | | */ |
53 | | |
54 | | #include "huff-tables.h" |
55 | | #include <cstdint> |
56 | | #include <cstring> |
57 | | |
58 | | |
59 | | namespace litespeed { |
60 | | |
61 | | namespace |
62 | | { |
63 | | |
64 | | constexpr int64_t LSHPACK_ERR_MORE_BUF = -3; |
65 | | |
66 | | struct decode_status { |
67 | | uint8_t state; |
68 | | uint8_t eos; |
69 | | }; |
70 | | |
71 | | enum { |
72 | | HPACK_HUFFMAN_FLAG_ACCEPTED = 0x01, |
73 | | HPACK_HUFFMAN_FLAG_SYM = 0x02, |
74 | | HPACK_HUFFMAN_FLAG_FAIL = 0x04, |
75 | | }; |
76 | | |
77 | | char * |
78 | | hdec_huff_dec4bits(uint8_t src_4bits, char *dst, struct decode_status *status) |
79 | 0 | { |
80 | 0 | const struct decode_el cur_dec_code = decode_tables[status->state][src_4bits]; |
81 | 0 | if (cur_dec_code.flags & HPACK_HUFFMAN_FLAG_FAIL) { |
82 | 0 | return nullptr; // failed |
83 | 0 | } |
84 | 0 | if (cur_dec_code.flags & HPACK_HUFFMAN_FLAG_SYM) { |
85 | 0 | *dst = cur_dec_code.sym; |
86 | 0 | dst++; |
87 | 0 | } |
88 | |
|
89 | 0 | status->state = cur_dec_code.state; |
90 | 0 | status->eos = ((cur_dec_code.flags & HPACK_HUFFMAN_FLAG_ACCEPTED) != 0); |
91 | 0 | return dst; |
92 | 0 | } |
93 | | |
94 | | } // anonymous namespace |
95 | | |
96 | | int64_t |
97 | | lshpack_enc_huff_encode(uint8_t const *src, uint8_t const *const src_end, uint8_t *dst, uint32_t dst_len) |
98 | 0 | { |
99 | 0 | uint8_t *p_dst = dst; |
100 | 0 | uint8_t *dst_end = p_dst + dst_len; |
101 | 0 | uintptr_t bits = 0; |
102 | 0 | unsigned bits_used = 0, adj; |
103 | 0 | struct encode_el cur_enc_code; |
104 | 0 | const struct henc *henc; |
105 | 0 | uint16_t idx; |
106 | |
|
107 | 0 | while (src + sizeof(bits) * 8 / 5 + sizeof(idx) < src_end && p_dst + sizeof(bits) <= dst_end) { |
108 | 0 | memcpy(&idx, src, 2); |
109 | 0 | henc = &hencs[idx]; |
110 | 0 | src += 2; |
111 | 0 | while (bits_used + henc->lens < sizeof(bits) * 8) { |
112 | 0 | bits <<= henc->lens; |
113 | 0 | bits |= henc->code; |
114 | 0 | bits_used += henc->lens; |
115 | 0 | memcpy(&idx, src, 2); |
116 | 0 | henc = &hencs[idx]; |
117 | 0 | src += 2; |
118 | 0 | } |
119 | 0 | if (henc->lens < 64) { |
120 | | // Prevent undefined behavior of shifting the full number of bits. |
121 | 0 | if (bits_used > 0) { |
122 | 0 | bits <<= sizeof(bits) * 8 - bits_used; |
123 | 0 | } |
124 | 0 | bits_used = henc->lens - (sizeof(bits) * 8 - bits_used); |
125 | 0 | bits |= henc->code >> bits_used; |
126 | 0 | #if UINTPTR_MAX == 18446744073709551615ull |
127 | 0 | *p_dst++ = bits >> 56; |
128 | 0 | *p_dst++ = bits >> 48; |
129 | 0 | *p_dst++ = bits >> 40; |
130 | 0 | *p_dst++ = bits >> 32; |
131 | 0 | #endif |
132 | 0 | *p_dst++ = bits >> 24; |
133 | 0 | *p_dst++ = bits >> 16; |
134 | 0 | *p_dst++ = bits >> 8; |
135 | 0 | *p_dst++ = bits; |
136 | 0 | bits = henc->code; /* OK not to clear high bits */ |
137 | 0 | } else { |
138 | 0 | src -= 2; |
139 | 0 | break; |
140 | 0 | } |
141 | 0 | } |
142 | |
|
143 | 0 | while (src != src_end) { |
144 | 0 | cur_enc_code = encode_table[*src++]; |
145 | 0 | if (bits_used + cur_enc_code.bits < sizeof(bits) * 8) { |
146 | 0 | bits <<= cur_enc_code.bits; |
147 | 0 | bits |= cur_enc_code.code; |
148 | 0 | bits_used += cur_enc_code.bits; |
149 | 0 | continue; |
150 | 0 | } else if (p_dst + sizeof(bits) <= dst_end) { |
151 | | // Prevent undefined behavior of shifting the full number of bits. |
152 | 0 | if (bits_used > 0) { |
153 | 0 | bits <<= sizeof(bits) * 8 - bits_used; |
154 | 0 | } |
155 | 0 | bits_used = cur_enc_code.bits - (sizeof(bits) * 8 - bits_used); |
156 | 0 | bits |= cur_enc_code.code >> bits_used; |
157 | 0 | #if UINTPTR_MAX == 18446744073709551615ull |
158 | 0 | *p_dst++ = bits >> 56; |
159 | 0 | *p_dst++ = bits >> 48; |
160 | 0 | *p_dst++ = bits >> 40; |
161 | 0 | *p_dst++ = bits >> 32; |
162 | 0 | #endif |
163 | 0 | *p_dst++ = bits >> 24; |
164 | 0 | *p_dst++ = bits >> 16; |
165 | 0 | *p_dst++ = bits >> 8; |
166 | 0 | *p_dst++ = bits; |
167 | 0 | bits = cur_enc_code.code; /* OK not to clear high bits */ |
168 | 0 | } else { |
169 | 0 | return -1; |
170 | 0 | } |
171 | 0 | } |
172 | | |
173 | 0 | adj = bits_used + (-bits_used & 7); /* Round up to 8 */ |
174 | 0 | if (bits_used && p_dst + (adj >> 3) <= dst_end) { |
175 | 0 | bits <<= -bits_used & 7; /* Align to byte boundary */ |
176 | 0 | bits |= ((1 << (-bits_used & 7)) - 1); /* EOF */ |
177 | 0 | switch (adj >> 3) { /* Write out */ |
178 | 0 | #if UINTPTR_MAX == 18446744073709551615ull |
179 | 0 | case 8: |
180 | 0 | *p_dst++ = bits >> 56; |
181 | 0 | [[fallthrough]]; |
182 | 0 | case 7: |
183 | 0 | *p_dst++ = bits >> 48; |
184 | 0 | [[fallthrough]]; |
185 | 0 | case 6: |
186 | 0 | *p_dst++ = bits >> 40; |
187 | 0 | [[fallthrough]]; |
188 | 0 | case 5: |
189 | 0 | *p_dst++ = bits >> 32; |
190 | 0 | [[fallthrough]]; |
191 | 0 | #endif |
192 | 0 | case 4: |
193 | 0 | *p_dst++ = bits >> 24; |
194 | 0 | [[fallthrough]]; |
195 | 0 | case 3: |
196 | 0 | *p_dst++ = bits >> 16; |
197 | 0 | [[fallthrough]]; |
198 | 0 | case 2: |
199 | 0 | *p_dst++ = bits >> 8; |
200 | 0 | [[fallthrough]]; |
201 | 0 | default: |
202 | 0 | *p_dst++ = bits; |
203 | 0 | } |
204 | 0 | return p_dst - dst; |
205 | 0 | } else if (p_dst + (adj >> 3) <= dst_end) { |
206 | 0 | return p_dst - dst; |
207 | 0 | } else { |
208 | 0 | return -1; |
209 | 0 | } |
210 | 0 | } |
211 | | |
212 | | // Implementation taken from LiteSpeed: |
213 | | // lshpack_dec_huff_decode_full |
214 | | int64_t |
215 | | lshpack_dec_huff_decode_full(uint8_t const *src, uint32_t src_len, char *dst, uint32_t dst_len) |
216 | 0 | { |
217 | 0 | const uint8_t *p_src = src; |
218 | 0 | const uint8_t *const src_end = src + src_len; |
219 | 0 | char *p_dst = dst; |
220 | 0 | char *dst_end = dst + dst_len; |
221 | 0 | struct decode_status status = {0, 1}; |
222 | |
|
223 | 0 | while (p_src != src_end) { |
224 | 0 | if (p_dst == dst_end) { |
225 | 0 | return LSHPACK_ERR_MORE_BUF; |
226 | 0 | } |
227 | 0 | if ((p_dst = hdec_huff_dec4bits(*p_src >> 4, p_dst, &status)) == nullptr) { |
228 | 0 | return -1; |
229 | 0 | } |
230 | 0 | if (p_dst == dst_end) { |
231 | 0 | return LSHPACK_ERR_MORE_BUF; |
232 | 0 | } |
233 | 0 | if ((p_dst = hdec_huff_dec4bits(*p_src & 0xf, p_dst, &status)) == nullptr) { |
234 | 0 | return -1; |
235 | 0 | } |
236 | 0 | ++p_src; |
237 | 0 | } |
238 | | |
239 | 0 | if (!status.eos) { |
240 | 0 | return -1; |
241 | 0 | } |
242 | | |
243 | 0 | return p_dst - dst; |
244 | 0 | } |
245 | | |
246 | | } // namespace litespeed |