1/*
2 * nghttp2 - HTTP/2 C Library
3 *
4 * Copyright (c) 2013 Tatsuhiro Tsujikawa
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 */
25#include "nghttp2_frame.h"
26
27#include <string.h>
28#include <assert.h>
29#include <stdio.h>
30#include <errno.h>
31
32#include "nghttp2_helper.h"
33#include "nghttp2_net.h"
34#include "nghttp2_priority_spec.h"
35#include "nghttp2_debug.h"
36
37void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd) {
38  nghttp2_put_uint32be(&buf[0], (uint32_t)(hd->length << 8));
39  buf[3] = hd->type;
40  buf[4] = hd->flags;
41  nghttp2_put_uint32be(&buf[5], (uint32_t)hd->stream_id);
42  /* ignore hd->reserved for now */
43}
44
45void nghttp2_frame_unpack_frame_hd(nghttp2_frame_hd *hd, const uint8_t *buf) {
46  hd->length = nghttp2_get_uint32(&buf[0]) >> 8;
47  hd->type = buf[3];
48  hd->flags = buf[4];
49  hd->stream_id = nghttp2_get_uint32(&buf[5]) & NGHTTP2_STREAM_ID_MASK;
50  hd->reserved = 0;
51}
52
53void nghttp2_frame_hd_init(nghttp2_frame_hd *hd, size_t length, uint8_t type,
54                           uint8_t flags, int32_t stream_id) {
55  hd->length = length;
56  hd->type = type;
57  hd->flags = flags;
58  hd->stream_id = stream_id;
59  hd->reserved = 0;
60}
61
62void nghttp2_frame_headers_init(nghttp2_headers *frame, uint8_t flags,
63                                int32_t stream_id, nghttp2_headers_category cat,
64                                const nghttp2_priority_spec *pri_spec,
65                                nghttp2_nv *nva, size_t nvlen) {
66  nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_HEADERS, flags, stream_id);
67  frame->padlen = 0;
68  frame->nva = nva;
69  frame->nvlen = nvlen;
70  frame->cat = cat;
71
72  if (pri_spec) {
73    frame->pri_spec = *pri_spec;
74  } else {
75    nghttp2_priority_spec_default_init(&frame->pri_spec);
76  }
77}
78
79void nghttp2_frame_headers_free(nghttp2_headers *frame, nghttp2_mem *mem) {
80  nghttp2_nv_array_del(frame->nva, mem);
81}
82
83void nghttp2_frame_priority_init(nghttp2_priority *frame, int32_t stream_id,
84                                 const nghttp2_priority_spec *pri_spec) {
85  nghttp2_frame_hd_init(&frame->hd, NGHTTP2_PRIORITY_SPECLEN, NGHTTP2_PRIORITY,
86                        NGHTTP2_FLAG_NONE, stream_id);
87  frame->pri_spec = *pri_spec;
88}
89
90void nghttp2_frame_priority_free(nghttp2_priority *frame) { (void)frame; }
91
92void nghttp2_frame_rst_stream_init(nghttp2_rst_stream *frame, int32_t stream_id,
93                                   uint32_t error_code) {
94  nghttp2_frame_hd_init(&frame->hd, 4, NGHTTP2_RST_STREAM, NGHTTP2_FLAG_NONE,
95                        stream_id);
96  frame->error_code = error_code;
97}
98
99void nghttp2_frame_rst_stream_free(nghttp2_rst_stream *frame) { (void)frame; }
100
101void nghttp2_frame_settings_init(nghttp2_settings *frame, uint8_t flags,
102                                 nghttp2_settings_entry *iv, size_t niv) {
103  nghttp2_frame_hd_init(&frame->hd, niv * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH,
104                        NGHTTP2_SETTINGS, flags, 0);
105  frame->niv = niv;
106  frame->iv = iv;
107}
108
109void nghttp2_frame_settings_free(nghttp2_settings *frame, nghttp2_mem *mem) {
110  nghttp2_mem_free(mem, frame->iv);
111}
112
113void nghttp2_frame_push_promise_init(nghttp2_push_promise *frame, uint8_t flags,
114                                     int32_t stream_id,
115                                     int32_t promised_stream_id,
116                                     nghttp2_nv *nva, size_t nvlen) {
117  nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_PUSH_PROMISE, flags, stream_id);
118  frame->padlen = 0;
119  frame->nva = nva;
120  frame->nvlen = nvlen;
121  frame->promised_stream_id = promised_stream_id;
122  frame->reserved = 0;
123}
124
125void nghttp2_frame_push_promise_free(nghttp2_push_promise *frame,
126                                     nghttp2_mem *mem) {
127  nghttp2_nv_array_del(frame->nva, mem);
128}
129
130void nghttp2_frame_ping_init(nghttp2_ping *frame, uint8_t flags,
131                             const uint8_t *opaque_data) {
132  nghttp2_frame_hd_init(&frame->hd, 8, NGHTTP2_PING, flags, 0);
133  if (opaque_data) {
134    memcpy(frame->opaque_data, opaque_data, sizeof(frame->opaque_data));
135  } else {
136    memset(frame->opaque_data, 0, sizeof(frame->opaque_data));
137  }
138}
139
140void nghttp2_frame_ping_free(nghttp2_ping *frame) { (void)frame; }
141
142void nghttp2_frame_goaway_init(nghttp2_goaway *frame, int32_t last_stream_id,
143                               uint32_t error_code, uint8_t *opaque_data,
144                               size_t opaque_data_len) {
145  nghttp2_frame_hd_init(&frame->hd, 8 + opaque_data_len, NGHTTP2_GOAWAY,
146                        NGHTTP2_FLAG_NONE, 0);
147  frame->last_stream_id = last_stream_id;
148  frame->error_code = error_code;
149  frame->opaque_data = opaque_data;
150  frame->opaque_data_len = opaque_data_len;
151  frame->reserved = 0;
152}
153
154void nghttp2_frame_goaway_free(nghttp2_goaway *frame, nghttp2_mem *mem) {
155  nghttp2_mem_free(mem, frame->opaque_data);
156}
157
158void nghttp2_frame_window_update_init(nghttp2_window_update *frame,
159                                      uint8_t flags, int32_t stream_id,
160                                      int32_t window_size_increment) {
161  nghttp2_frame_hd_init(&frame->hd, 4, NGHTTP2_WINDOW_UPDATE, flags, stream_id);
162  frame->window_size_increment = window_size_increment;
163  frame->reserved = 0;
164}
165
166void nghttp2_frame_window_update_free(nghttp2_window_update *frame) {
167  (void)frame;
168}
169
170size_t nghttp2_frame_trail_padlen(nghttp2_frame *frame, size_t padlen) {
171  /* We have iframe->padlen == 0, but iframe->frame.hd.flags may have
172     NGHTTP2_FLAG_PADDED set.  This happens when receiving
173     CONTINUATION frame, since we don't reset flags after HEADERS was
174     received. */
175  if (padlen == 0) {
176    return 0;
177  }
178  return padlen - ((frame->hd.flags & NGHTTP2_FLAG_PADDED) > 0);
179}
180
181void nghttp2_frame_data_init(nghttp2_data *frame, uint8_t flags,
182                             int32_t stream_id) {
183  /* At this moment, the length of DATA frame is unknown */
184  nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_DATA, flags, stream_id);
185  frame->padlen = 0;
186}
187
188void nghttp2_frame_data_free(nghttp2_data *frame) { (void)frame; }
189
190void nghttp2_frame_extension_init(nghttp2_extension *frame, uint8_t type,
191                                  uint8_t flags, int32_t stream_id,
192                                  void *payload) {
193  nghttp2_frame_hd_init(&frame->hd, 0, type, flags, stream_id);
194  frame->payload = payload;
195}
196
197void nghttp2_frame_extension_free(nghttp2_extension *frame) { (void)frame; }
198
199void nghttp2_frame_altsvc_init(nghttp2_extension *frame, int32_t stream_id,
200                               uint8_t *origin, size_t origin_len,
201                               uint8_t *field_value, size_t field_value_len) {
202  nghttp2_ext_altsvc *altsvc;
203
204  nghttp2_frame_hd_init(&frame->hd, 2 + origin_len + field_value_len,
205                        NGHTTP2_ALTSVC, NGHTTP2_FLAG_NONE, stream_id);
206
207  altsvc = frame->payload;
208  altsvc->origin = origin;
209  altsvc->origin_len = origin_len;
210  altsvc->field_value = field_value;
211  altsvc->field_value_len = field_value_len;
212}
213
214void nghttp2_frame_altsvc_free(nghttp2_extension *frame, nghttp2_mem *mem) {
215  nghttp2_ext_altsvc *altsvc;
216
217  altsvc = frame->payload;
218  if (altsvc == NULL) {
219    return;
220  }
221  /* We use the same buffer for altsvc->origin and
222     altsvc->field_value. */
223  nghttp2_mem_free(mem, altsvc->origin);
224}
225
226void nghttp2_frame_origin_init(nghttp2_extension *frame,
227                               nghttp2_origin_entry *ov, size_t nov) {
228  nghttp2_ext_origin *origin;
229  size_t payloadlen = 0;
230  size_t i;
231
232  for (i = 0; i < nov; ++i) {
233    payloadlen += 2 + ov[i].origin_len;
234  }
235
236  nghttp2_frame_hd_init(&frame->hd, payloadlen, NGHTTP2_ORIGIN,
237                        NGHTTP2_FLAG_NONE, 0);
238
239  origin = frame->payload;
240  origin->ov = ov;
241  origin->nov = nov;
242}
243
244void nghttp2_frame_origin_free(nghttp2_extension *frame, nghttp2_mem *mem) {
245  nghttp2_ext_origin *origin;
246
247  origin = frame->payload;
248  if (origin == NULL) {
249    return;
250  }
251  /* We use the same buffer for all resources pointed by the field of
252     origin directly or indirectly. */
253  nghttp2_mem_free(mem, origin->ov);
254}
255
256void nghttp2_frame_priority_update_init(nghttp2_extension *frame,
257                                        int32_t stream_id, uint8_t *field_value,
258                                        size_t field_value_len) {
259  nghttp2_ext_priority_update *priority_update;
260
261  nghttp2_frame_hd_init(&frame->hd, 4 + field_value_len,
262                        NGHTTP2_PRIORITY_UPDATE, NGHTTP2_FLAG_NONE, 0);
263
264  priority_update = frame->payload;
265  priority_update->stream_id = stream_id;
266  priority_update->field_value = field_value;
267  priority_update->field_value_len = field_value_len;
268}
269
270void nghttp2_frame_priority_update_free(nghttp2_extension *frame,
271                                        nghttp2_mem *mem) {
272  nghttp2_ext_priority_update *priority_update;
273
274  priority_update = frame->payload;
275  if (priority_update == NULL) {
276    return;
277  }
278  nghttp2_mem_free(mem, priority_update->field_value);
279}
280
281size_t nghttp2_frame_priority_len(uint8_t flags) {
282  if (flags & NGHTTP2_FLAG_PRIORITY) {
283    return NGHTTP2_PRIORITY_SPECLEN;
284  }
285
286  return 0;
287}
288
289size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame) {
290  return nghttp2_frame_priority_len(frame->hd.flags);
291}
292
293/*
294 * Call this function after payload was serialized, but not before
295 * changing buf->pos and serializing frame header.
296 *
297 * This function assumes bufs->cur points to the last buf chain of the
298 * frame(s).
299 *
300 * This function serializes frame header for HEADERS/PUSH_PROMISE and
301 * handles their successive CONTINUATION frames.
302 *
303 * We don't process any padding here.
304 */
305static int frame_pack_headers_shared(nghttp2_bufs *bufs,
306                                     nghttp2_frame_hd *frame_hd) {
307  nghttp2_buf *buf;
308  nghttp2_buf_chain *ci, *ce;
309  nghttp2_frame_hd hd;
310
311  buf = &bufs->head->buf;
312
313  hd = *frame_hd;
314  hd.length = nghttp2_buf_len(buf);
315
316  DEBUGF("send: HEADERS/PUSH_PROMISE, payloadlen=%zu\n", hd.length);
317
318  /* We have multiple frame buffers, which means one or more
319     CONTINUATION frame is involved. Remove END_HEADERS flag from the
320     first frame. */
321  if (bufs->head != bufs->cur) {
322    hd.flags = (uint8_t)(hd.flags & ~NGHTTP2_FLAG_END_HEADERS);
323  }
324
325  buf->pos -= NGHTTP2_FRAME_HDLEN;
326  nghttp2_frame_pack_frame_hd(buf->pos, &hd);
327
328  if (bufs->head != bufs->cur) {
329    /* 2nd and later frames are CONTINUATION frames. */
330    hd.type = NGHTTP2_CONTINUATION;
331    /* We don't have no flags except for last CONTINUATION */
332    hd.flags = NGHTTP2_FLAG_NONE;
333
334    ce = bufs->cur;
335
336    for (ci = bufs->head->next; ci != ce; ci = ci->next) {
337      buf = &ci->buf;
338
339      hd.length = nghttp2_buf_len(buf);
340
341      DEBUGF("send: int CONTINUATION, payloadlen=%zu\n", hd.length);
342
343      buf->pos -= NGHTTP2_FRAME_HDLEN;
344      nghttp2_frame_pack_frame_hd(buf->pos, &hd);
345    }
346
347    buf = &ci->buf;
348    hd.length = nghttp2_buf_len(buf);
349    /* Set END_HEADERS flag for last CONTINUATION */
350    hd.flags = NGHTTP2_FLAG_END_HEADERS;
351
352    DEBUGF("send: last CONTINUATION, payloadlen=%zu\n", hd.length);
353
354    buf->pos -= NGHTTP2_FRAME_HDLEN;
355    nghttp2_frame_pack_frame_hd(buf->pos, &hd);
356  }
357
358  return 0;
359}
360
361int nghttp2_frame_pack_headers(nghttp2_bufs *bufs, nghttp2_headers *frame,
362                               nghttp2_hd_deflater *deflater) {
363  size_t nv_offset;
364  int rv;
365  nghttp2_buf *buf;
366
367  assert(bufs->head == bufs->cur);
368
369  nv_offset = nghttp2_frame_headers_payload_nv_offset(frame);
370
371  buf = &bufs->cur->buf;
372
373  buf->pos += nv_offset;
374  buf->last = buf->pos;
375
376  /* This call will adjust buf->last to the correct position */
377  rv = nghttp2_hd_deflate_hd_bufs(deflater, bufs, frame->nva, frame->nvlen);
378
379  if (rv == NGHTTP2_ERR_BUFFER_ERROR) {
380    rv = NGHTTP2_ERR_HEADER_COMP;
381  }
382
383  buf->pos -= nv_offset;
384
385  if (rv != 0) {
386    return rv;
387  }
388
389  if (frame->hd.flags & NGHTTP2_FLAG_PRIORITY) {
390    nghttp2_frame_pack_priority_spec(buf->pos, &frame->pri_spec);
391  }
392
393  frame->padlen = 0;
394  frame->hd.length = nghttp2_bufs_len(bufs);
395
396  return frame_pack_headers_shared(bufs, &frame->hd);
397}
398
399void nghttp2_frame_pack_priority_spec(uint8_t *buf,
400                                      const nghttp2_priority_spec *pri_spec) {
401  nghttp2_put_uint32be(buf, (uint32_t)pri_spec->stream_id);
402  if (pri_spec->exclusive) {
403    buf[0] |= 0x80;
404  }
405  buf[4] = (uint8_t)(pri_spec->weight - 1);
406}
407
408void nghttp2_frame_unpack_priority_spec(nghttp2_priority_spec *pri_spec,
409                                        const uint8_t *payload) {
410  int32_t dep_stream_id;
411  uint8_t exclusive;
412  int32_t weight;
413
414  dep_stream_id = nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK;
415  exclusive = (payload[0] & 0x80) > 0;
416  weight = payload[4] + 1;
417
418  nghttp2_priority_spec_init(pri_spec, dep_stream_id, weight, exclusive);
419}
420
421void nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame,
422                                          const uint8_t *payload) {
423  if (frame->hd.flags & NGHTTP2_FLAG_PRIORITY) {
424    nghttp2_frame_unpack_priority_spec(&frame->pri_spec, payload);
425  } else {
426    nghttp2_priority_spec_default_init(&frame->pri_spec);
427  }
428
429  frame->nva = NULL;
430  frame->nvlen = 0;
431}
432
433void nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame) {
434  nghttp2_buf *buf;
435
436  assert(bufs->head == bufs->cur);
437
438  buf = &bufs->head->buf;
439
440  assert(nghttp2_buf_avail(buf) >= NGHTTP2_PRIORITY_SPECLEN);
441
442  buf->pos -= NGHTTP2_FRAME_HDLEN;
443
444  nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
445
446  nghttp2_frame_pack_priority_spec(buf->last, &frame->pri_spec);
447
448  buf->last += NGHTTP2_PRIORITY_SPECLEN;
449}
450
451void nghttp2_frame_unpack_priority_payload(nghttp2_priority *frame,
452                                           const uint8_t *payload) {
453  nghttp2_frame_unpack_priority_spec(&frame->pri_spec, payload);
454}
455
456void nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs,
457                                   nghttp2_rst_stream *frame) {
458  nghttp2_buf *buf;
459
460  assert(bufs->head == bufs->cur);
461
462  buf = &bufs->head->buf;
463
464  assert(nghttp2_buf_avail(buf) >= 4);
465
466  buf->pos -= NGHTTP2_FRAME_HDLEN;
467
468  nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
469
470  nghttp2_put_uint32be(buf->last, frame->error_code);
471  buf->last += 4;
472}
473
474void nghttp2_frame_unpack_rst_stream_payload(nghttp2_rst_stream *frame,
475                                             const uint8_t *payload) {
476  frame->error_code = nghttp2_get_uint32(payload);
477}
478
479int nghttp2_frame_pack_settings(nghttp2_bufs *bufs, nghttp2_settings *frame) {
480  nghttp2_buf *buf;
481
482  assert(bufs->head == bufs->cur);
483
484  buf = &bufs->head->buf;
485
486  if (nghttp2_buf_avail(buf) < frame->hd.length) {
487    return NGHTTP2_ERR_FRAME_SIZE_ERROR;
488  }
489
490  buf->pos -= NGHTTP2_FRAME_HDLEN;
491
492  nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
493
494  buf->last +=
495      nghttp2_frame_pack_settings_payload(buf->last, frame->iv, frame->niv);
496
497  return 0;
498}
499
500size_t nghttp2_frame_pack_settings_payload(uint8_t *buf,
501                                           const nghttp2_settings_entry *iv,
502                                           size_t niv) {
503  size_t i;
504  for (i = 0; i < niv; ++i, buf += NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) {
505    nghttp2_put_uint16be(buf, (uint16_t)iv[i].settings_id);
506    nghttp2_put_uint32be(buf + 2, iv[i].value);
507  }
508  return NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH * niv;
509}
510
511void nghttp2_frame_unpack_settings_payload(nghttp2_settings *frame,
512                                           nghttp2_settings_entry *iv,
513                                           size_t niv) {
514  frame->iv = iv;
515  frame->niv = niv;
516}
517
518void nghttp2_frame_unpack_settings_entry(nghttp2_settings_entry *iv,
519                                         const uint8_t *payload) {
520  iv->settings_id = nghttp2_get_uint16(&payload[0]);
521  iv->value = nghttp2_get_uint32(&payload[2]);
522}
523
524int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr,
525                                           size_t *niv_ptr,
526                                           const uint8_t *payload,
527                                           size_t payloadlen,
528                                           nghttp2_mem *mem) {
529  size_t i;
530
531  *niv_ptr = payloadlen / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH;
532
533  if (*niv_ptr == 0) {
534    *iv_ptr = NULL;
535
536    return 0;
537  }
538
539  *iv_ptr =
540      nghttp2_mem_malloc(mem, (*niv_ptr) * sizeof(nghttp2_settings_entry));
541
542  if (*iv_ptr == NULL) {
543    return NGHTTP2_ERR_NOMEM;
544  }
545
546  for (i = 0; i < *niv_ptr; ++i) {
547    size_t off = i * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH;
548    nghttp2_frame_unpack_settings_entry(&(*iv_ptr)[i], &payload[off]);
549  }
550
551  return 0;
552}
553
554int nghttp2_frame_pack_push_promise(nghttp2_bufs *bufs,
555                                    nghttp2_push_promise *frame,
556                                    nghttp2_hd_deflater *deflater) {
557  size_t nv_offset = 4;
558  int rv;
559  nghttp2_buf *buf;
560
561  assert(bufs->head == bufs->cur);
562
563  buf = &bufs->cur->buf;
564
565  buf->pos += nv_offset;
566  buf->last = buf->pos;
567
568  /* This call will adjust buf->last to the correct position */
569  rv = nghttp2_hd_deflate_hd_bufs(deflater, bufs, frame->nva, frame->nvlen);
570
571  if (rv == NGHTTP2_ERR_BUFFER_ERROR) {
572    rv = NGHTTP2_ERR_HEADER_COMP;
573  }
574
575  buf->pos -= nv_offset;
576
577  if (rv != 0) {
578    return rv;
579  }
580
581  nghttp2_put_uint32be(buf->pos, (uint32_t)frame->promised_stream_id);
582
583  frame->padlen = 0;
584  frame->hd.length = nghttp2_bufs_len(bufs);
585
586  return frame_pack_headers_shared(bufs, &frame->hd);
587}
588
589void nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame,
590                                               const uint8_t *payload) {
591  frame->promised_stream_id =
592      nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK;
593  frame->nva = NULL;
594  frame->nvlen = 0;
595}
596
597void nghttp2_frame_pack_ping(nghttp2_bufs *bufs, nghttp2_ping *frame) {
598  nghttp2_buf *buf;
599
600  assert(bufs->head == bufs->cur);
601
602  buf = &bufs->head->buf;
603
604  assert(nghttp2_buf_avail(buf) >= 8);
605
606  buf->pos -= NGHTTP2_FRAME_HDLEN;
607
608  nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
609
610  buf->last =
611      nghttp2_cpymem(buf->last, frame->opaque_data, sizeof(frame->opaque_data));
612}
613
614void nghttp2_frame_unpack_ping_payload(nghttp2_ping *frame,
615                                       const uint8_t *payload) {
616  memcpy(frame->opaque_data, payload, sizeof(frame->opaque_data));
617}
618
619int nghttp2_frame_pack_goaway(nghttp2_bufs *bufs, nghttp2_goaway *frame) {
620  int rv;
621  nghttp2_buf *buf;
622
623  assert(bufs->head == bufs->cur);
624
625  buf = &bufs->head->buf;
626
627  buf->pos -= NGHTTP2_FRAME_HDLEN;
628
629  nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
630
631  nghttp2_put_uint32be(buf->last, (uint32_t)frame->last_stream_id);
632  buf->last += 4;
633
634  nghttp2_put_uint32be(buf->last, frame->error_code);
635  buf->last += 4;
636
637  rv = nghttp2_bufs_add(bufs, frame->opaque_data, frame->opaque_data_len);
638
639  if (rv == NGHTTP2_ERR_BUFFER_ERROR) {
640    return NGHTTP2_ERR_FRAME_SIZE_ERROR;
641  }
642
643  if (rv != 0) {
644    return rv;
645  }
646
647  return 0;
648}
649
650void nghttp2_frame_unpack_goaway_payload(nghttp2_goaway *frame,
651                                         const uint8_t *payload,
652                                         uint8_t *var_gift_payload,
653                                         size_t var_gift_payloadlen) {
654  frame->last_stream_id = nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK;
655  frame->error_code = nghttp2_get_uint32(payload + 4);
656
657  frame->opaque_data = var_gift_payload;
658  frame->opaque_data_len = var_gift_payloadlen;
659}
660
661int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame,
662                                         const uint8_t *payload,
663                                         size_t payloadlen, nghttp2_mem *mem) {
664  uint8_t *var_gift_payload;
665  size_t var_gift_payloadlen;
666
667  if (payloadlen > 8) {
668    var_gift_payloadlen = payloadlen - 8;
669  } else {
670    var_gift_payloadlen = 0;
671  }
672
673  if (!var_gift_payloadlen) {
674    var_gift_payload = NULL;
675  } else {
676    var_gift_payload = nghttp2_mem_malloc(mem, var_gift_payloadlen);
677
678    if (var_gift_payload == NULL) {
679      return NGHTTP2_ERR_NOMEM;
680    }
681
682    memcpy(var_gift_payload, payload + 8, var_gift_payloadlen);
683  }
684
685  nghttp2_frame_unpack_goaway_payload(frame, payload, var_gift_payload,
686                                      var_gift_payloadlen);
687
688  return 0;
689}
690
691void nghttp2_frame_pack_window_update(nghttp2_bufs *bufs,
692                                      nghttp2_window_update *frame) {
693  nghttp2_buf *buf;
694
695  assert(bufs->head == bufs->cur);
696
697  buf = &bufs->head->buf;
698
699  assert(nghttp2_buf_avail(buf) >= 4);
700
701  buf->pos -= NGHTTP2_FRAME_HDLEN;
702
703  nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
704
705  nghttp2_put_uint32be(buf->last, (uint32_t)frame->window_size_increment);
706  buf->last += 4;
707}
708
709void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame,
710                                                const uint8_t *payload) {
711  frame->window_size_increment =
712      nghttp2_get_uint32(payload) & NGHTTP2_WINDOW_SIZE_INCREMENT_MASK;
713}
714
715void nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_extension *frame) {
716  int rv;
717  nghttp2_buf *buf;
718  nghttp2_ext_altsvc *altsvc;
719
720  /* This is required with --disable-assert. */
721  (void)rv;
722
723  altsvc = frame->payload;
724
725  buf = &bufs->head->buf;
726
727  assert(nghttp2_buf_avail(buf) >=
728         2 + altsvc->origin_len + altsvc->field_value_len);
729
730  buf->pos -= NGHTTP2_FRAME_HDLEN;
731
732  nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
733
734  nghttp2_put_uint16be(buf->last, (uint16_t)altsvc->origin_len);
735  buf->last += 2;
736
737  rv = nghttp2_bufs_add(bufs, altsvc->origin, altsvc->origin_len);
738
739  assert(rv == 0);
740
741  rv = nghttp2_bufs_add(bufs, altsvc->field_value, altsvc->field_value_len);
742
743  assert(rv == 0);
744}
745
746void nghttp2_frame_unpack_altsvc_payload(nghttp2_extension *frame,
747                                         size_t origin_len, uint8_t *payload,
748                                         size_t payloadlen) {
749  nghttp2_ext_altsvc *altsvc;
750  uint8_t *p;
751
752  altsvc = frame->payload;
753  p = payload;
754
755  altsvc->origin = p;
756
757  p += origin_len;
758
759  altsvc->origin_len = origin_len;
760
761  altsvc->field_value = p;
762  altsvc->field_value_len = (size_t)(payload + payloadlen - p);
763}
764
765int nghttp2_frame_unpack_altsvc_payload2(nghttp2_extension *frame,
766                                         const uint8_t *payload,
767                                         size_t payloadlen, nghttp2_mem *mem) {
768  uint8_t *buf;
769  size_t origin_len;
770
771  if (payloadlen < 2) {
772    return NGHTTP2_FRAME_SIZE_ERROR;
773  }
774
775  origin_len = nghttp2_get_uint16(payload);
776
777  buf = nghttp2_mem_malloc(mem, payloadlen - 2);
778  if (!buf) {
779    return NGHTTP2_ERR_NOMEM;
780  }
781
782  nghttp2_cpymem(buf, payload + 2, payloadlen - 2);
783
784  nghttp2_frame_unpack_altsvc_payload(frame, origin_len, buf, payloadlen - 2);
785
786  return 0;
787}
788
789int nghttp2_frame_pack_origin(nghttp2_bufs *bufs, nghttp2_extension *frame) {
790  nghttp2_buf *buf;
791  nghttp2_ext_origin *origin;
792  nghttp2_origin_entry *orig;
793  size_t i;
794
795  origin = frame->payload;
796
797  buf = &bufs->head->buf;
798
799  if (nghttp2_buf_avail(buf) < frame->hd.length) {
800    return NGHTTP2_ERR_FRAME_SIZE_ERROR;
801  }
802
803  buf->pos -= NGHTTP2_FRAME_HDLEN;
804
805  nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
806
807  for (i = 0; i < origin->nov; ++i) {
808    orig = &origin->ov[i];
809    nghttp2_put_uint16be(buf->last, (uint16_t)orig->origin_len);
810    buf->last += 2;
811    buf->last = nghttp2_cpymem(buf->last, orig->origin, orig->origin_len);
812  }
813
814  assert(nghttp2_buf_len(buf) == NGHTTP2_FRAME_HDLEN + frame->hd.length);
815
816  return 0;
817}
818
819int nghttp2_frame_unpack_origin_payload(nghttp2_extension *frame,
820                                        const uint8_t *payload,
821                                        size_t payloadlen, nghttp2_mem *mem) {
822  nghttp2_ext_origin *origin;
823  const uint8_t *p, *end;
824  uint8_t *dst;
825  size_t originlen;
826  nghttp2_origin_entry *ov;
827  size_t nov = 0;
828  size_t len = 0;
829
830  origin = frame->payload;
831  p = end = payload;
832  if (payloadlen) {
833    end += payloadlen;
834  }
835
836  for (; p != end;) {
837    if (end - p < 2) {
838      return NGHTTP2_ERR_FRAME_SIZE_ERROR;
839    }
840    originlen = nghttp2_get_uint16(p);
841    p += 2;
842    if (originlen == 0) {
843      continue;
844    }
845    if (originlen > (size_t)(end - p)) {
846      return NGHTTP2_ERR_FRAME_SIZE_ERROR;
847    }
848    p += originlen;
849    /* 1 for terminal NULL */
850    len += originlen + 1;
851    ++nov;
852  }
853
854  if (nov == 0) {
855    origin->ov = NULL;
856    origin->nov = 0;
857
858    return 0;
859  }
860
861  len += nov * sizeof(nghttp2_origin_entry);
862
863  ov = nghttp2_mem_malloc(mem, len);
864  if (ov == NULL) {
865    return NGHTTP2_ERR_NOMEM;
866  }
867
868  origin->ov = ov;
869  origin->nov = nov;
870
871  dst = (uint8_t *)ov + nov * sizeof(nghttp2_origin_entry);
872  p = payload;
873
874  for (; p != end;) {
875    originlen = nghttp2_get_uint16(p);
876    p += 2;
877    if (originlen == 0) {
878      continue;
879    }
880    ov->origin = dst;
881    ov->origin_len = originlen;
882    dst = nghttp2_cpymem(dst, p, originlen);
883    *dst++ = '\0';
884    p += originlen;
885    ++ov;
886  }
887
888  return 0;
889}
890
891void nghttp2_frame_pack_priority_update(nghttp2_bufs *bufs,
892                                        nghttp2_extension *frame) {
893  int rv;
894  nghttp2_buf *buf;
895  nghttp2_ext_priority_update *priority_update;
896
897  /* This is required with --disable-assert. */
898  (void)rv;
899
900  priority_update = frame->payload;
901
902  buf = &bufs->head->buf;
903
904  assert(nghttp2_buf_avail(buf) >= 4 + priority_update->field_value_len);
905
906  buf->pos -= NGHTTP2_FRAME_HDLEN;
907
908  nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
909
910  nghttp2_put_uint32be(buf->last, (uint32_t)priority_update->stream_id);
911  buf->last += 4;
912
913  rv = nghttp2_bufs_add(bufs, priority_update->field_value,
914                        priority_update->field_value_len);
915
916  assert(rv == 0);
917}
918
919void nghttp2_frame_unpack_priority_update_payload(nghttp2_extension *frame,
920                                                  uint8_t *payload,
921                                                  size_t payloadlen) {
922  nghttp2_ext_priority_update *priority_update;
923
924  assert(payloadlen >= 4);
925
926  priority_update = frame->payload;
927
928  priority_update->stream_id =
929      nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK;
930
931  if (payloadlen > 4) {
932    priority_update->field_value = payload + 4;
933    priority_update->field_value_len = payloadlen - 4;
934  } else {
935    priority_update->field_value = NULL;
936    priority_update->field_value_len = 0;
937  }
938}
939
940nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv,
941                                              size_t niv, nghttp2_mem *mem) {
942  nghttp2_settings_entry *iv_copy;
943  size_t len = niv * sizeof(nghttp2_settings_entry);
944
945  if (len == 0) {
946    return NULL;
947  }
948
949  iv_copy = nghttp2_mem_malloc(mem, len);
950
951  if (iv_copy == NULL) {
952    return NULL;
953  }
954
955  memcpy(iv_copy, iv, len);
956
957  return iv_copy;
958}
959
960int nghttp2_nv_equal(const nghttp2_nv *a, const nghttp2_nv *b) {
961  if (a->namelen != b->namelen || a->valuelen != b->valuelen) {
962    return 0;
963  }
964
965  if (a->name == NULL || b->name == NULL) {
966    assert(a->namelen == 0);
967    assert(b->namelen == 0);
968  } else if (memcmp(a->name, b->name, a->namelen) != 0) {
969    return 0;
970  }
971
972  if (a->value == NULL || b->value == NULL) {
973    assert(a->valuelen == 0);
974    assert(b->valuelen == 0);
975  } else if (memcmp(a->value, b->value, a->valuelen) != 0) {
976    return 0;
977  }
978
979  return 1;
980}
981
982void nghttp2_nv_array_del(nghttp2_nv *nva, nghttp2_mem *mem) {
983  nghttp2_mem_free(mem, nva);
984}
985
986static int bytes_compar(const uint8_t *a, size_t alen, const uint8_t *b,
987                        size_t blen) {
988  int rv;
989
990  if (alen == blen) {
991    return memcmp(a, b, alen);
992  }
993
994  if (alen < blen) {
995    rv = memcmp(a, b, alen);
996
997    if (rv == 0) {
998      return -1;
999    }
1000
1001    return rv;
1002  }
1003
1004  rv = memcmp(a, b, blen);
1005
1006  if (rv == 0) {
1007    return 1;
1008  }
1009
1010  return rv;
1011}
1012
1013int nghttp2_nv_compare_name(const nghttp2_nv *lhs, const nghttp2_nv *rhs) {
1014  return bytes_compar(lhs->name, lhs->namelen, rhs->name, rhs->namelen);
1015}
1016
1017static int nv_compar(const void *lhs, const void *rhs) {
1018  const nghttp2_nv *a = (const nghttp2_nv *)lhs;
1019  const nghttp2_nv *b = (const nghttp2_nv *)rhs;
1020  int rv;
1021
1022  rv = bytes_compar(a->name, a->namelen, b->name, b->namelen);
1023
1024  if (rv == 0) {
1025    return bytes_compar(a->value, a->valuelen, b->value, b->valuelen);
1026  }
1027
1028  return rv;
1029}
1030
1031void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen) {
1032  qsort(nva, nvlen, sizeof(nghttp2_nv), nv_compar);
1033}
1034
1035int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva,
1036                          size_t nvlen, nghttp2_mem *mem) {
1037  size_t i;
1038  uint8_t *data = NULL;
1039  size_t buflen = 0;
1040  nghttp2_nv *p;
1041
1042  if (nvlen == 0) {
1043    *nva_ptr = NULL;
1044
1045    return 0;
1046  }
1047
1048  for (i = 0; i < nvlen; ++i) {
1049    /* + 1 for null-termination */
1050    if ((nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_NAME) == 0) {
1051      buflen += nva[i].namelen + 1;
1052    }
1053    if ((nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_VALUE) == 0) {
1054      buflen += nva[i].valuelen + 1;
1055    }
1056  }
1057
1058  buflen += sizeof(nghttp2_nv) * nvlen;
1059
1060  *nva_ptr = nghttp2_mem_malloc(mem, buflen);
1061
1062  if (*nva_ptr == NULL) {
1063    return NGHTTP2_ERR_NOMEM;
1064  }
1065
1066  p = *nva_ptr;
1067  data = (uint8_t *)(*nva_ptr) + sizeof(nghttp2_nv) * nvlen;
1068
1069  for (i = 0; i < nvlen; ++i) {
1070    p->flags = nva[i].flags;
1071
1072    if (nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_NAME) {
1073      p->name = nva[i].name;
1074      p->namelen = nva[i].namelen;
1075    } else {
1076      if (nva[i].namelen) {
1077        memcpy(data, nva[i].name, nva[i].namelen);
1078      }
1079      p->name = data;
1080      p->namelen = nva[i].namelen;
1081      data[p->namelen] = '\0';
1082      nghttp2_downcase(p->name, p->namelen);
1083      data += nva[i].namelen + 1;
1084    }
1085
1086    if (nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_VALUE) {
1087      p->value = nva[i].value;
1088      p->valuelen = nva[i].valuelen;
1089    } else {
1090      if (nva[i].valuelen) {
1091        memcpy(data, nva[i].value, nva[i].valuelen);
1092      }
1093      p->value = data;
1094      p->valuelen = nva[i].valuelen;
1095      data[p->valuelen] = '\0';
1096      data += nva[i].valuelen + 1;
1097    }
1098
1099    ++p;
1100  }
1101  return 0;
1102}
1103
1104int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv) {
1105  size_t i;
1106  for (i = 0; i < niv; ++i) {
1107    switch (iv[i].settings_id) {
1108    case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:
1109      break;
1110    case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
1111      break;
1112    case NGHTTP2_SETTINGS_ENABLE_PUSH:
1113      if (iv[i].value != 0 && iv[i].value != 1) {
1114        return 0;
1115      }
1116      break;
1117    case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
1118      if (iv[i].value > (uint32_t)NGHTTP2_MAX_WINDOW_SIZE) {
1119        return 0;
1120      }
1121      break;
1122    case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
1123      if (iv[i].value < NGHTTP2_MAX_FRAME_SIZE_MIN ||
1124          iv[i].value > NGHTTP2_MAX_FRAME_SIZE_MAX) {
1125        return 0;
1126      }
1127      break;
1128    case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
1129      break;
1130    case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
1131      if (iv[i].value != 0 && iv[i].value != 1) {
1132        return 0;
1133      }
1134      break;
1135    case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES:
1136      if (iv[i].value != 0 && iv[i].value != 1) {
1137        return 0;
1138      }
1139      break;
1140    }
1141  }
1142  return 1;
1143}
1144
1145static void frame_set_pad(nghttp2_buf *buf, size_t padlen, int framehd_only) {
1146  size_t trail_padlen;
1147  size_t newlen;
1148
1149  DEBUGF("send: padlen=%zu, shift left 1 bytes\n", padlen);
1150
1151  memmove(buf->pos - 1, buf->pos, NGHTTP2_FRAME_HDLEN);
1152
1153  --buf->pos;
1154
1155  buf->pos[4] |= NGHTTP2_FLAG_PADDED;
1156
1157  newlen = (nghttp2_get_uint32(buf->pos) >> 8) + padlen;
1158  nghttp2_put_uint32be(buf->pos, (uint32_t)((newlen << 8) + buf->pos[3]));
1159
1160  if (framehd_only) {
1161    return;
1162  }
1163
1164  trail_padlen = padlen - 1;
1165  buf->pos[NGHTTP2_FRAME_HDLEN] = (uint8_t)trail_padlen;
1166
1167  /* zero out padding */
1168  memset(buf->last, 0, trail_padlen);
1169  /* extend buffers trail_padlen bytes, since we ate previous padlen -
1170     trail_padlen byte(s) */
1171  buf->last += trail_padlen;
1172}
1173
1174void nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd,
1175                           size_t padlen, int framehd_only) {
1176  nghttp2_buf *buf;
1177
1178  if (padlen == 0) {
1179    DEBUGF("send: padlen = 0, nothing to do\n");
1180
1181    return;
1182  }
1183
1184  /*
1185   * We have arranged bufs like this:
1186   *
1187   *  0                   1                   2                   3
1188   *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
1189   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1190   * | |Frame header     | Frame payload...                          :
1191   * +-+-----------------+-------------------------------------------+
1192   * | |Frame header     | Frame payload...                          :
1193   * +-+-----------------+-------------------------------------------+
1194   * | |Frame header     | Frame payload...                          :
1195   * +-+-----------------+-------------------------------------------+
1196   *
1197   * We arranged padding so that it is included in the first frame
1198   * completely.  For padded frame, we are going to adjust buf->pos of
1199   * frame which includes padding and serialize (memmove) frame header
1200   * in the correct position.  Also extends buf->last to include
1201   * padding.
1202   */
1203
1204  buf = &bufs->head->buf;
1205
1206  assert(nghttp2_buf_avail(buf) >= padlen - 1);
1207
1208  frame_set_pad(buf, padlen, framehd_only);
1209
1210  hd->length += padlen;
1211  hd->flags |= NGHTTP2_FLAG_PADDED;
1212
1213  DEBUGF("send: final payloadlen=%zu, padlen=%zu\n", hd->length, padlen);
1214}
1215