Mercurial > hg > nginx-quic
comparison src/event/ngx_event_quic_transport.c @ 7690:ae35ccba7aa6 quic
Extracted transport part of the code into separate file.
All code dealing with serializing/deserializing
is moved int srv/event/ngx_event_quic_transport.c/h file.
All macros for dealing with data are internal to source file.
The header file exposes frame types and error codes.
The exported functions are currently packet header parsers and writers
and frames parser/writer.
The ngx_quic_header_t structure is updated with 'log' member. This avoids
passing extra argument to parsing functions that need to report errors.
author | Vladimir Homutov <vl@nginx.com> |
---|---|
date | Wed, 18 Mar 2020 12:58:27 +0300 |
parents | |
children | 78540e2160d0 |
comparison
equal
deleted
inserted
replaced
7689:61f9b873e2e7 | 7690:ae35ccba7aa6 |
---|---|
1 | |
2 /* | |
3 * Copyright (C) Nginx, Inc. | |
4 */ | |
5 | |
6 | |
7 #include <ngx_config.h> | |
8 #include <ngx_core.h> | |
9 #include <ngx_event.h> | |
10 | |
11 | |
12 #if (NGX_HAVE_NONALIGNED) | |
13 | |
14 #define ngx_quic_parse_uint16(p) ntohs(*(uint16_t *) (p)) | |
15 #define ngx_quic_parse_uint32(p) ntohl(*(uint32_t *) (p)) | |
16 | |
17 #define ngx_quic_write_uint16 ngx_quic_write_uint16_aligned | |
18 #define ngx_quic_write_uint32 ngx_quic_write_uint32_aligned | |
19 | |
20 #else | |
21 | |
22 #define ngx_quic_parse_uint16(p) ((p)[0] << 8 | (p)[1]) | |
23 #define ngx_quic_parse_uint32(p) \ | |
24 ((uint32_t) (p)[0] << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3]) | |
25 | |
26 #define ngx_quic_write_uint16(p, s) \ | |
27 ((p)[0] = (u_char) ((s) >> 8), \ | |
28 (p)[1] = (u_char) (s), \ | |
29 (p) + sizeof(uint16_t)) | |
30 | |
31 #define ngx_quic_write_uint32(p, s) \ | |
32 ((p)[0] = (u_char) ((s) >> 24), \ | |
33 (p)[1] = (u_char) ((s) >> 16), \ | |
34 (p)[2] = (u_char) ((s) >> 8), \ | |
35 (p)[3] = (u_char) (s), \ | |
36 (p) + sizeof(uint32_t)) | |
37 | |
38 #endif | |
39 | |
40 | |
41 #define ngx_quic_write_uint16_aligned(p, s) \ | |
42 (*(uint16_t *) (p) = htons((uint16_t) (s)), (p) + sizeof(uint16_t)) | |
43 | |
44 #define ngx_quic_write_uint32_aligned(p, s) \ | |
45 (*(uint32_t *) (p) = htonl((uint32_t) (s)), (p) + sizeof(uint32_t)) | |
46 | |
47 #define ngx_quic_varint_len(value) \ | |
48 ((value) <= 63 ? 1 \ | |
49 : ((uint32_t) value) <= 16383 ? 2 \ | |
50 : ((uint64_t) value) <= 1073741823 ? 4 \ | |
51 : 8) | |
52 | |
53 | |
54 static uint64_t ngx_quic_parse_int(u_char **pos); | |
55 static void ngx_quic_build_int(u_char **pos, uint64_t value); | |
56 | |
57 static size_t ngx_quic_create_ack(u_char *p, ngx_quic_ack_frame_t *ack); | |
58 static size_t ngx_quic_create_crypto(u_char *p, | |
59 ngx_quic_crypto_frame_t *crypto); | |
60 static size_t ngx_quic_create_stream(u_char *p, ngx_quic_stream_frame_t *sf); | |
61 | |
62 | |
63 /* literal errors indexed by corresponding value */ | |
64 static char *ngx_quic_errors[] = { | |
65 "NO_ERROR", | |
66 "INTERNAL_ERROR", | |
67 "SERVER_BUSY", | |
68 "FLOW_CONTROL_ERROR", | |
69 "STREAM_LIMIT_ERROR", | |
70 "STREAM_STATE_ERROR", | |
71 "FINAL_SIZE_ERROR", | |
72 "FRAME_ENCODING_ERROR", | |
73 "TRANSPORT_PARAMETER_ERROR", | |
74 "CONNECTION_ID_LIMIT_ERROR", | |
75 "PROTOCOL_VIOLATION", | |
76 "INVALID_TOKEN", | |
77 "", | |
78 "CRYPTO_BUFFER_EXCEEDED", | |
79 "CRYPTO_ERROR", | |
80 }; | |
81 | |
82 | |
83 static uint64_t | |
84 ngx_quic_parse_int(u_char **pos) | |
85 { | |
86 u_char *p; | |
87 uint64_t value; | |
88 ngx_uint_t len; | |
89 | |
90 p = *pos; | |
91 len = 1 << ((*p & 0xc0) >> 6); | |
92 value = *p++ & 0x3f; | |
93 | |
94 while (--len) { | |
95 value = (value << 8) + *p++; | |
96 } | |
97 | |
98 *pos = p; | |
99 return value; | |
100 } | |
101 | |
102 | |
103 static void | |
104 ngx_quic_build_int(u_char **pos, uint64_t value) | |
105 { | |
106 u_char *p; | |
107 ngx_uint_t len;//, len2; | |
108 | |
109 p = *pos; | |
110 len = 0; | |
111 | |
112 while (value >> ((1 << len) * 8 - 2)) { | |
113 len++; | |
114 } | |
115 | |
116 *p = len << 6; | |
117 | |
118 // len2 = | |
119 len = (1 << len); | |
120 len--; | |
121 *p |= value >> (len * 8); | |
122 p++; | |
123 | |
124 while (len) { | |
125 *p++ = value >> ((len-- - 1) * 8); | |
126 } | |
127 | |
128 *pos = p; | |
129 // return len2; | |
130 } | |
131 | |
132 | |
133 u_char * | |
134 ngx_quic_error_text(uint64_t error_code) | |
135 { | |
136 return (u_char *) ngx_quic_errors[error_code]; | |
137 } | |
138 | |
139 | |
140 ngx_int_t | |
141 ngx_quic_parse_long_header(ngx_quic_header_t *pkt) | |
142 { | |
143 u_char *p; | |
144 | |
145 p = pkt->data; | |
146 | |
147 ngx_quic_hexdump0(pkt->log, "long input", pkt->data, pkt->len); | |
148 | |
149 if (!(p[0] & NGX_QUIC_PKT_LONG)) { | |
150 ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "not a long packet"); | |
151 return NGX_ERROR; | |
152 } | |
153 | |
154 pkt->flags = *p++; | |
155 | |
156 pkt->version = ngx_quic_parse_uint32(p); | |
157 p += sizeof(uint32_t); | |
158 | |
159 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, pkt->log, 0, | |
160 "quic flags:%xi version:%xD", pkt->flags, pkt->version); | |
161 | |
162 if (pkt->version != quic_version) { | |
163 ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "unsupported quic version"); | |
164 return NGX_ERROR; | |
165 } | |
166 | |
167 pkt->dcid.len = *p++; | |
168 pkt->dcid.data = p; | |
169 p += pkt->dcid.len; | |
170 | |
171 pkt->scid.len = *p++; | |
172 pkt->scid.data = p; | |
173 p += pkt->scid.len; | |
174 | |
175 pkt->raw->pos = p; | |
176 | |
177 return NGX_OK; | |
178 } | |
179 | |
180 | |
181 size_t | |
182 ngx_quic_create_long_header(ngx_quic_header_t *pkt, ngx_str_t *out, | |
183 size_t pkt_len, u_char **pnp) | |
184 { | |
185 u_char *p, *start; | |
186 | |
187 p = start = out->data; | |
188 | |
189 *p++ = pkt->flags; | |
190 | |
191 p = ngx_quic_write_uint32(p, quic_version); | |
192 | |
193 *p++ = pkt->scid.len; | |
194 p = ngx_cpymem(p, pkt->scid.data, pkt->scid.len); | |
195 | |
196 *p++ = pkt->dcid.len; | |
197 p = ngx_cpymem(p, pkt->dcid.data, pkt->dcid.len); | |
198 | |
199 if (pkt->level == ssl_encryption_initial) { | |
200 ngx_quic_build_int(&p, pkt->token.len); | |
201 } | |
202 | |
203 ngx_quic_build_int(&p, pkt_len + 1); // length (inc. pnl) | |
204 | |
205 *pnp = p; | |
206 | |
207 *p++ = (uint64_t) (*pkt->number); | |
208 | |
209 return p - start; | |
210 } | |
211 | |
212 | |
213 ngx_int_t | |
214 ngx_quic_parse_short_header(ngx_quic_header_t *pkt, ngx_str_t *dcid) | |
215 { | |
216 u_char *p; | |
217 | |
218 p = pkt->data; | |
219 | |
220 ngx_quic_hexdump0(pkt->log, "short input", pkt->data, pkt->len); | |
221 | |
222 if ((p[0] & NGX_QUIC_PKT_LONG)) { | |
223 ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "not a short packet"); | |
224 return NGX_ERROR; | |
225 } | |
226 | |
227 pkt->flags = *p++; | |
228 | |
229 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pkt->log, 0, | |
230 "quic flags:%xi", pkt->flags); | |
231 | |
232 if (ngx_memcmp(p, dcid->data, dcid->len) != 0) { | |
233 ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "unexpected quic dcid"); | |
234 return NGX_ERROR; | |
235 } | |
236 | |
237 pkt->dcid.len = dcid->len; | |
238 pkt->dcid.data = p; | |
239 p += pkt->dcid.len; | |
240 | |
241 pkt->raw->pos = p; | |
242 | |
243 return NGX_OK; | |
244 } | |
245 | |
246 | |
247 ngx_int_t | |
248 ngx_quic_parse_initial_header(ngx_quic_header_t *pkt) | |
249 { | |
250 u_char *p; | |
251 ngx_int_t plen; | |
252 | |
253 p = pkt->raw->pos; | |
254 | |
255 pkt->token.len = ngx_quic_parse_int(&p); | |
256 pkt->token.data = p; | |
257 | |
258 p += pkt->token.len; | |
259 | |
260 plen = ngx_quic_parse_int(&p); | |
261 | |
262 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pkt->log, 0, | |
263 "quic packet length: %d", plen); | |
264 | |
265 if (plen > pkt->data + pkt->len - p) { | |
266 ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "truncated initial packet"); | |
267 return NGX_ERROR; | |
268 } | |
269 | |
270 pkt->raw->pos = p; | |
271 pkt->len = plen; | |
272 | |
273 ngx_quic_hexdump0(pkt->log, "DCID", pkt->dcid.data, pkt->dcid.len); | |
274 ngx_quic_hexdump0(pkt->log, "SCID", pkt->scid.data, pkt->scid.len); | |
275 ngx_quic_hexdump0(pkt->log, "token", pkt->token.data, pkt->token.len); | |
276 | |
277 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pkt->log, 0, | |
278 "quic packet length: %d", plen); | |
279 | |
280 return NGX_OK; | |
281 } | |
282 | |
283 | |
284 ngx_int_t | |
285 ngx_quic_parse_handshake_header(ngx_quic_header_t *pkt) | |
286 { | |
287 u_char *p; | |
288 ngx_int_t plen; | |
289 | |
290 p = pkt->raw->pos; | |
291 | |
292 plen = ngx_quic_parse_int(&p); | |
293 | |
294 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pkt->log, 0, | |
295 "quic packet length: %d", plen); | |
296 | |
297 if (plen > pkt->data + pkt->len - p) { | |
298 ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "truncated handshake packet"); | |
299 return NGX_ERROR; | |
300 } | |
301 | |
302 pkt->raw->pos = p; | |
303 pkt->len = plen; | |
304 | |
305 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pkt->log, 0, | |
306 "quic packet length: %d", plen); | |
307 | |
308 return NGX_OK; | |
309 } | |
310 | |
311 | |
312 #define ngx_quic_stream_bit_off(val) (((val) & 0x04) ? 1 : 0) | |
313 #define ngx_quic_stream_bit_len(val) (((val) & 0x02) ? 1 : 0) | |
314 #define ngx_quic_stream_bit_fin(val) (((val) & 0x01) ? 1 : 0) | |
315 | |
316 ssize_t | |
317 ngx_quic_parse_frame(u_char *start, u_char *end, ngx_quic_frame_t *frame) | |
318 { | |
319 u_char *p; | |
320 | |
321 size_t npad; | |
322 | |
323 p = start; | |
324 | |
325 frame->type = *p++; // TODO: check overflow (p < end) | |
326 | |
327 switch (frame->type) { | |
328 | |
329 case NGX_QUIC_FT_CRYPTO: | |
330 frame->u.crypto.offset = *p++; | |
331 frame->u.crypto.len = ngx_quic_parse_int(&p); | |
332 frame->u.crypto.data = p; | |
333 p += frame->u.crypto.len; | |
334 | |
335 break; | |
336 | |
337 case NGX_QUIC_FT_PADDING: | |
338 npad = 0; | |
339 while (p < end && *p == NGX_QUIC_FT_PADDING) { // XXX | |
340 p++; npad++; | |
341 } | |
342 | |
343 break; | |
344 | |
345 case NGX_QUIC_FT_ACK: | |
346 case NGX_QUIC_FT_ACK_ECN: | |
347 | |
348 frame->u.ack.largest = ngx_quic_parse_int(&p); | |
349 frame->u.ack.delay = ngx_quic_parse_int(&p); | |
350 frame->u.ack.range_count =ngx_quic_parse_int(&p); | |
351 frame->u.ack.first_range =ngx_quic_parse_int(&p); | |
352 | |
353 if (frame->u.ack.range_count) { | |
354 frame->u.ack.ranges[0] = ngx_quic_parse_int(&p); | |
355 } | |
356 | |
357 if (frame->type ==NGX_QUIC_FT_ACK_ECN) { | |
358 return NGX_ERROR; | |
359 } | |
360 | |
361 break; | |
362 | |
363 case NGX_QUIC_FT_PING: | |
364 break; | |
365 | |
366 case NGX_QUIC_FT_NEW_CONNECTION_ID: | |
367 | |
368 frame->u.ncid.seqnum = ngx_quic_parse_int(&p); | |
369 frame->u.ncid.retire = ngx_quic_parse_int(&p); | |
370 frame->u.ncid.len = *p++; | |
371 ngx_memcpy(frame->u.ncid.cid, p, frame->u.ncid.len); | |
372 p += frame->u.ncid.len; | |
373 | |
374 ngx_memcpy(frame->u.ncid.srt, p, 16); | |
375 p += 16; | |
376 | |
377 break; | |
378 | |
379 case NGX_QUIC_FT_CONNECTION_CLOSE: | |
380 | |
381 frame->u.close.error_code = ngx_quic_parse_int(&p); | |
382 frame->u.close.frame_type = ngx_quic_parse_int(&p); // not in 0x1d CC | |
383 frame->u.close.reason.len = ngx_quic_parse_int(&p); | |
384 frame->u.close.reason.data = p; | |
385 p += frame->u.close.reason.len; | |
386 | |
387 if (frame->u.close.error_code > NGX_QUIC_ERR_LAST) { | |
388 frame->u.close.error_code = NGX_QUIC_ERR_LAST; | |
389 } | |
390 break; | |
391 | |
392 case NGX_QUIC_FT_STREAM0: | |
393 case NGX_QUIC_FT_STREAM1: | |
394 case NGX_QUIC_FT_STREAM2: | |
395 case NGX_QUIC_FT_STREAM3: | |
396 case NGX_QUIC_FT_STREAM4: | |
397 case NGX_QUIC_FT_STREAM5: | |
398 case NGX_QUIC_FT_STREAM6: | |
399 case NGX_QUIC_FT_STREAM7: | |
400 | |
401 frame->u.stream.type = frame->type; | |
402 | |
403 frame->u.stream.off = ngx_quic_stream_bit_off(frame->type); | |
404 frame->u.stream.len = ngx_quic_stream_bit_len(frame->type); | |
405 frame->u.stream.fin = ngx_quic_stream_bit_fin(frame->type); | |
406 | |
407 frame->u.stream.stream_id = ngx_quic_parse_int(&p); | |
408 if (frame->type & 0x04) { | |
409 frame->u.stream.offset = ngx_quic_parse_int(&p); | |
410 } else { | |
411 frame->u.stream.offset = 0; | |
412 } | |
413 | |
414 if (frame->type & 0x02) { | |
415 frame->u.stream.length = ngx_quic_parse_int(&p); | |
416 } else { | |
417 frame->u.stream.length = end - p; /* up to packet end */ | |
418 } | |
419 | |
420 frame->u.stream.data = p; | |
421 | |
422 p += frame->u.stream.length; | |
423 | |
424 break; | |
425 | |
426 default: | |
427 return NGX_ERROR; | |
428 } | |
429 | |
430 return p - start; | |
431 } | |
432 | |
433 | |
434 ssize_t | |
435 ngx_quic_create_frame(u_char *p, u_char *end, ngx_quic_frame_t *f) | |
436 { | |
437 // TODO: handle end arg | |
438 | |
439 switch (f->type) { | |
440 case NGX_QUIC_FT_ACK: | |
441 return ngx_quic_create_ack(p, &f->u.ack); | |
442 | |
443 case NGX_QUIC_FT_CRYPTO: | |
444 return ngx_quic_create_crypto(p, &f->u.crypto); | |
445 | |
446 case NGX_QUIC_FT_STREAM0: | |
447 case NGX_QUIC_FT_STREAM1: | |
448 case NGX_QUIC_FT_STREAM2: | |
449 case NGX_QUIC_FT_STREAM3: | |
450 case NGX_QUIC_FT_STREAM4: | |
451 case NGX_QUIC_FT_STREAM5: | |
452 case NGX_QUIC_FT_STREAM6: | |
453 case NGX_QUIC_FT_STREAM7: | |
454 return ngx_quic_create_stream(p, &f->u.stream); | |
455 | |
456 default: | |
457 /* BUG: unsupported frame type generated */ | |
458 return NGX_ERROR; | |
459 } | |
460 } | |
461 | |
462 | |
463 size_t | |
464 ngx_quic_frame_len(ngx_quic_frame_t *frame) | |
465 { | |
466 switch (frame->type) { | |
467 case NGX_QUIC_FT_ACK: | |
468 return ngx_quic_create_ack(NULL, &frame->u.ack); | |
469 case NGX_QUIC_FT_CRYPTO: | |
470 return ngx_quic_create_crypto(NULL, &frame->u.crypto); | |
471 | |
472 case NGX_QUIC_FT_STREAM0: | |
473 case NGX_QUIC_FT_STREAM1: | |
474 case NGX_QUIC_FT_STREAM2: | |
475 case NGX_QUIC_FT_STREAM3: | |
476 case NGX_QUIC_FT_STREAM4: | |
477 case NGX_QUIC_FT_STREAM5: | |
478 case NGX_QUIC_FT_STREAM6: | |
479 case NGX_QUIC_FT_STREAM7: | |
480 return ngx_quic_create_stream(NULL, &frame->u.stream); | |
481 default: | |
482 /* BUG: unsupported frame type generated */ | |
483 return 0; | |
484 } | |
485 } | |
486 | |
487 | |
488 static size_t | |
489 ngx_quic_create_ack(u_char *p, ngx_quic_ack_frame_t *ack) | |
490 { | |
491 size_t len; | |
492 | |
493 /* minimal ACK packet */ | |
494 | |
495 if (p == NULL) { | |
496 len = ngx_quic_varint_len(NGX_QUIC_FT_ACK); | |
497 len += ngx_quic_varint_len(ack->pn); | |
498 len += ngx_quic_varint_len(0); | |
499 len += ngx_quic_varint_len(0); | |
500 len += ngx_quic_varint_len(ack->pn); | |
501 | |
502 return len; | |
503 } | |
504 | |
505 ngx_quic_build_int(&p, NGX_QUIC_FT_ACK); | |
506 ngx_quic_build_int(&p, ack->pn); | |
507 ngx_quic_build_int(&p, 0); | |
508 ngx_quic_build_int(&p, 0); | |
509 ngx_quic_build_int(&p, ack->pn); | |
510 | |
511 return 5; | |
512 } | |
513 | |
514 | |
515 static size_t | |
516 ngx_quic_create_crypto(u_char *p, ngx_quic_crypto_frame_t *crypto) | |
517 { | |
518 size_t len; | |
519 u_char *start; | |
520 | |
521 if (p == NULL) { | |
522 len = ngx_quic_varint_len(NGX_QUIC_FT_CRYPTO); | |
523 len += ngx_quic_varint_len(crypto->offset); | |
524 len += ngx_quic_varint_len(crypto->len); | |
525 len += crypto->len; | |
526 | |
527 return len; | |
528 } | |
529 | |
530 start = p; | |
531 | |
532 ngx_quic_build_int(&p, NGX_QUIC_FT_CRYPTO); | |
533 ngx_quic_build_int(&p, crypto->offset); | |
534 ngx_quic_build_int(&p, crypto->len); | |
535 p = ngx_cpymem(p, crypto->data, crypto->len); | |
536 | |
537 return p - start; | |
538 } | |
539 | |
540 | |
541 static size_t | |
542 ngx_quic_create_stream(u_char *p, ngx_quic_stream_frame_t *sf) | |
543 { | |
544 size_t len; | |
545 u_char *start; | |
546 | |
547 if (!sf->len) { | |
548 #if 0 | |
549 ngx_log_error(NGX_LOG_INFO, log, 0, | |
550 "attempt to generate a stream frame without length"); | |
551 #endif | |
552 // XXX: handle error in caller | |
553 return NGX_ERROR; | |
554 } | |
555 | |
556 if (p == NULL) { | |
557 len = ngx_quic_varint_len(sf->type); | |
558 | |
559 if (sf->off) { | |
560 len += ngx_quic_varint_len(sf->offset); | |
561 } | |
562 | |
563 len += ngx_quic_varint_len(sf->stream_id); | |
564 | |
565 /* length is always present in generated frames */ | |
566 len += ngx_quic_varint_len(sf->length); | |
567 | |
568 len += sf->length; | |
569 | |
570 return len; | |
571 } | |
572 | |
573 start = p; | |
574 | |
575 ngx_quic_build_int(&p, sf->type); | |
576 ngx_quic_build_int(&p, sf->stream_id); | |
577 | |
578 if (sf->off) { | |
579 ngx_quic_build_int(&p, sf->offset); | |
580 } | |
581 | |
582 /* length is always present in generated frames */ | |
583 ngx_quic_build_int(&p, sf->length); | |
584 | |
585 p = ngx_cpymem(p, sf->data, sf->length); | |
586 | |
587 return p - start; | |
588 } |