Line data Source code
1 : // SPDX-License-Identifier: GPL-2.0-or-later
2 : /*
3 : * Packet interface
4 : * Copyright (C) 1999 Kunihiro Ishiguro
5 : */
6 :
7 : #include <zebra.h>
8 : #include <stddef.h>
9 : #include <pthread.h>
10 :
11 : #include "stream.h"
12 : #include "memory.h"
13 : #include "network.h"
14 : #include "prefix.h"
15 : #include "log.h"
16 : #include "frr_pthread.h"
17 : #include "lib_errors.h"
18 :
19 12 : DEFINE_MTYPE_STATIC(LIB, STREAM, "Stream");
20 12 : DEFINE_MTYPE_STATIC(LIB, STREAM_FIFO, "Stream FIFO");
21 :
22 : /* Tests whether a position is valid */
23 : #define GETP_VALID(S, G) ((G) <= (S)->endp)
24 : #define PUT_AT_VALID(S,G) GETP_VALID(S,G)
25 : #define ENDP_VALID(S, E) ((E) <= (S)->size)
26 :
27 : /* asserting sanity checks. Following must be true before
28 : * stream functions are called:
29 : *
30 : * Following must always be true of stream elements
31 : * before and after calls to stream functions:
32 : *
33 : * getp <= endp <= size
34 : *
35 : * Note that after a stream function is called following may be true:
36 : * if (getp == endp) then stream is no longer readable
37 : * if (endp == size) then stream is no longer writeable
38 : *
39 : * It is valid to put to anywhere within the size of the stream, but only
40 : * using stream_put..._at() functions.
41 : */
42 : #define STREAM_WARN_OFFSETS(S) \
43 : do { \
44 : flog_warn(EC_LIB_STREAM, \
45 : "&(struct stream): %p, size: %lu, getp: %lu, endp: %lu", \
46 : (void *)(S), (unsigned long)(S)->size, \
47 : (unsigned long)(S)->getp, (unsigned long)(S)->endp); \
48 : zlog_backtrace(LOG_WARNING); \
49 : } while (0)
50 :
51 : #define STREAM_VERIFY_SANE(S) \
52 : do { \
53 : if (!(GETP_VALID(S, (S)->getp) && ENDP_VALID(S, (S)->endp))) { \
54 : STREAM_WARN_OFFSETS(S); \
55 : } \
56 : assert(GETP_VALID(S, (S)->getp)); \
57 : assert(ENDP_VALID(S, (S)->endp)); \
58 : } while (0)
59 :
60 : #define STREAM_BOUND_WARN(S, WHAT) \
61 : do { \
62 : flog_warn(EC_LIB_STREAM, "%s: Attempt to %s out of bounds", \
63 : __func__, (WHAT)); \
64 : STREAM_WARN_OFFSETS(S); \
65 : assert(0); \
66 : } while (0)
67 :
68 : #define STREAM_BOUND_WARN2(S, WHAT) \
69 : do { \
70 : flog_warn(EC_LIB_STREAM, "%s: Attempt to %s out of bounds", \
71 : __func__, (WHAT)); \
72 : STREAM_WARN_OFFSETS(S); \
73 : } while (0)
74 :
75 : /* XXX: Deprecated macro: do not use */
76 : #define CHECK_SIZE(S, Z) \
77 : do { \
78 : if (((S)->endp + (Z)) > (S)->size) { \
79 : flog_warn( \
80 : EC_LIB_STREAM, \
81 : "CHECK_SIZE: truncating requested size %lu", \
82 : (unsigned long)(Z)); \
83 : STREAM_WARN_OFFSETS(S); \
84 : (Z) = (S)->size - (S)->endp; \
85 : } \
86 : } while (0);
87 :
88 : /* Make stream buffer. */
89 394 : struct stream *stream_new(size_t size)
90 : {
91 394 : struct stream *s;
92 :
93 394 : assert(size > 0);
94 :
95 394 : s = XMALLOC(MTYPE_STREAM, sizeof(struct stream) + size);
96 :
97 394 : s->getp = s->endp = 0;
98 394 : s->next = NULL;
99 394 : s->size = size;
100 394 : return s;
101 : }
102 :
103 : /* Free it now. */
104 398 : void stream_free(struct stream *s)
105 : {
106 398 : if (!s)
107 : return;
108 :
109 394 : XFREE(MTYPE_STREAM, s);
110 : }
111 :
112 154 : struct stream *stream_copy(struct stream *dest, const struct stream *src)
113 : {
114 154 : STREAM_VERIFY_SANE(src);
115 :
116 154 : assert(dest != NULL);
117 154 : assert(STREAM_SIZE(dest) >= src->endp);
118 :
119 154 : dest->endp = src->endp;
120 154 : dest->getp = src->getp;
121 :
122 154 : memcpy(dest->data, src->data, src->endp);
123 :
124 154 : return dest;
125 : }
126 :
127 146 : struct stream *stream_dup(const struct stream *s)
128 : {
129 146 : struct stream *snew;
130 :
131 146 : STREAM_VERIFY_SANE(s);
132 :
133 146 : snew = stream_new(s->endp);
134 :
135 146 : return (stream_copy(snew, s));
136 : }
137 :
138 5 : struct stream *stream_dupcat(const struct stream *s1, const struct stream *s2,
139 : size_t offset)
140 : {
141 5 : struct stream *new;
142 :
143 5 : STREAM_VERIFY_SANE(s1);
144 5 : STREAM_VERIFY_SANE(s2);
145 :
146 5 : if ((new = stream_new(s1->endp + s2->endp)) == NULL)
147 : return NULL;
148 :
149 5 : memcpy(new->data, s1->data, offset);
150 5 : memcpy(new->data + offset, s2->data, s2->endp);
151 5 : memcpy(new->data + offset + s2->endp, s1->data + offset,
152 5 : (s1->endp - offset));
153 5 : new->endp = s1->endp + s2->endp;
154 5 : return new;
155 : }
156 :
157 0 : size_t stream_resize_inplace(struct stream **sptr, size_t newsize)
158 : {
159 0 : struct stream *orig = *sptr;
160 :
161 0 : STREAM_VERIFY_SANE(orig);
162 :
163 0 : orig = XREALLOC(MTYPE_STREAM, orig, sizeof(struct stream) + newsize);
164 :
165 0 : orig->size = newsize;
166 :
167 0 : if (orig->endp > orig->size)
168 0 : orig->endp = orig->size;
169 0 : if (orig->getp > orig->endp)
170 0 : orig->getp = orig->endp;
171 :
172 0 : STREAM_VERIFY_SANE(orig);
173 :
174 0 : *sptr = orig;
175 0 : return orig->size;
176 : }
177 :
178 575 : size_t stream_get_getp(const struct stream *s)
179 : {
180 575 : STREAM_VERIFY_SANE(s);
181 575 : return s->getp;
182 : }
183 :
184 1084 : size_t stream_get_endp(const struct stream *s)
185 : {
186 1084 : STREAM_VERIFY_SANE(s);
187 1084 : return s->endp;
188 : }
189 :
190 0 : size_t stream_get_size(const struct stream *s)
191 : {
192 0 : STREAM_VERIFY_SANE(s);
193 0 : return s->size;
194 : }
195 :
196 : /* Stream structre' stream pointer related functions. */
197 497 : void stream_set_getp(struct stream *s, size_t pos)
198 : {
199 497 : STREAM_VERIFY_SANE(s);
200 :
201 497 : if (!GETP_VALID(s, pos)) {
202 0 : STREAM_BOUND_WARN(s, "set getp");
203 : pos = s->endp;
204 : }
205 :
206 497 : s->getp = pos;
207 497 : }
208 :
209 22 : void stream_set_endp(struct stream *s, size_t pos)
210 : {
211 22 : STREAM_VERIFY_SANE(s);
212 :
213 22 : if (!ENDP_VALID(s, pos)) {
214 0 : STREAM_BOUND_WARN(s, "set endp");
215 : return;
216 : }
217 :
218 : /*
219 : * Make sure the current read pointer is not beyond the new endp.
220 : */
221 22 : if (s->getp > pos) {
222 0 : STREAM_BOUND_WARN(s, "set endp");
223 : return;
224 : }
225 :
226 22 : s->endp = pos;
227 22 : STREAM_VERIFY_SANE(s);
228 : }
229 :
230 : /* Forward pointer. */
231 48 : void stream_forward_getp(struct stream *s, size_t size)
232 : {
233 48 : STREAM_VERIFY_SANE(s);
234 :
235 48 : if (!GETP_VALID(s, s->getp + size)) {
236 0 : STREAM_BOUND_WARN(s, "seek getp");
237 : return;
238 : }
239 :
240 48 : s->getp += size;
241 : }
242 :
243 0 : bool stream_forward_getp2(struct stream *s, size_t size)
244 : {
245 0 : STREAM_VERIFY_SANE(s);
246 :
247 0 : if (!GETP_VALID(s, s->getp + size))
248 : return false;
249 :
250 0 : s->getp += size;
251 :
252 0 : return true;
253 : }
254 :
255 0 : void stream_rewind_getp(struct stream *s, size_t size)
256 : {
257 0 : STREAM_VERIFY_SANE(s);
258 :
259 0 : if (size > s->getp || !GETP_VALID(s, s->getp - size)) {
260 0 : STREAM_BOUND_WARN(s, "rewind getp");
261 : return;
262 : }
263 :
264 0 : s->getp -= size;
265 : }
266 :
267 0 : bool stream_rewind_getp2(struct stream *s, size_t size)
268 : {
269 0 : STREAM_VERIFY_SANE(s);
270 :
271 0 : if (size > s->getp || !GETP_VALID(s, s->getp - size))
272 : return false;
273 :
274 0 : s->getp -= size;
275 :
276 0 : return true;
277 : }
278 :
279 0 : void stream_forward_endp(struct stream *s, size_t size)
280 : {
281 0 : STREAM_VERIFY_SANE(s);
282 :
283 0 : if (!ENDP_VALID(s, s->endp + size)) {
284 0 : STREAM_BOUND_WARN(s, "seek endp");
285 : return;
286 : }
287 :
288 0 : s->endp += size;
289 : }
290 :
291 0 : bool stream_forward_endp2(struct stream *s, size_t size)
292 : {
293 0 : STREAM_VERIFY_SANE(s);
294 :
295 0 : if (!ENDP_VALID(s, s->endp + size))
296 : return false;
297 :
298 0 : s->endp += size;
299 :
300 0 : return true;
301 : }
302 :
303 : /* Copy from stream to destination. */
304 195 : bool stream_get2(void *dst, struct stream *s, size_t size)
305 : {
306 195 : STREAM_VERIFY_SANE(s);
307 :
308 195 : if (STREAM_READABLE(s) < size) {
309 0 : STREAM_BOUND_WARN2(s, "get");
310 0 : return false;
311 : }
312 :
313 195 : memcpy(dst, s->data + s->getp, size);
314 195 : s->getp += size;
315 :
316 195 : return true;
317 : }
318 :
319 24 : void stream_get(void *dst, struct stream *s, size_t size)
320 : {
321 24 : STREAM_VERIFY_SANE(s);
322 :
323 24 : if (STREAM_READABLE(s) < size) {
324 0 : STREAM_BOUND_WARN(s, "get");
325 : return;
326 : }
327 :
328 24 : memcpy(dst, s->data + s->getp, size);
329 24 : s->getp += size;
330 : }
331 :
332 : /* Get next character from the stream. */
333 952 : bool stream_getc2(struct stream *s, uint8_t *byte)
334 : {
335 952 : STREAM_VERIFY_SANE(s);
336 :
337 952 : if (STREAM_READABLE(s) < sizeof(uint8_t)) {
338 0 : STREAM_BOUND_WARN2(s, "get char");
339 0 : return false;
340 : }
341 952 : *byte = s->data[s->getp++];
342 :
343 952 : return true;
344 : }
345 :
346 850 : uint8_t stream_getc(struct stream *s)
347 : {
348 850 : uint8_t c;
349 :
350 850 : STREAM_VERIFY_SANE(s);
351 :
352 850 : if (STREAM_READABLE(s) < sizeof(uint8_t)) {
353 0 : STREAM_BOUND_WARN(s, "get char");
354 : return 0;
355 : }
356 850 : c = s->data[s->getp++];
357 :
358 850 : return c;
359 : }
360 :
361 : /* Get next character from the stream. */
362 10 : uint8_t stream_getc_from(struct stream *s, size_t from)
363 : {
364 10 : uint8_t c;
365 :
366 10 : STREAM_VERIFY_SANE(s);
367 :
368 10 : if (!GETP_VALID(s, from + sizeof(uint8_t))) {
369 0 : STREAM_BOUND_WARN(s, "get char");
370 : return 0;
371 : }
372 :
373 10 : c = s->data[from];
374 :
375 10 : return c;
376 : }
377 :
378 598 : bool stream_getw2(struct stream *s, uint16_t *word)
379 : {
380 598 : STREAM_VERIFY_SANE(s);
381 :
382 598 : if (STREAM_READABLE(s) < sizeof(uint16_t)) {
383 0 : STREAM_BOUND_WARN2(s, "get ");
384 0 : return false;
385 : }
386 :
387 598 : *word = s->data[s->getp++] << 8;
388 598 : *word |= s->data[s->getp++];
389 :
390 598 : return true;
391 : }
392 :
393 : /* Get next word from the stream. */
394 446 : uint16_t stream_getw(struct stream *s)
395 : {
396 446 : uint16_t w;
397 :
398 446 : STREAM_VERIFY_SANE(s);
399 :
400 446 : if (STREAM_READABLE(s) < sizeof(uint16_t)) {
401 0 : STREAM_BOUND_WARN(s, "get ");
402 : return 0;
403 : }
404 :
405 446 : w = s->data[s->getp++] << 8;
406 446 : w |= s->data[s->getp++];
407 :
408 446 : return w;
409 : }
410 :
411 : /* Get next word from the stream. */
412 42 : uint16_t stream_getw_from(struct stream *s, size_t from)
413 : {
414 42 : uint16_t w;
415 :
416 42 : STREAM_VERIFY_SANE(s);
417 :
418 42 : if (!GETP_VALID(s, from + sizeof(uint16_t))) {
419 0 : STREAM_BOUND_WARN(s, "get ");
420 : return 0;
421 : }
422 :
423 42 : w = s->data[from++] << 8;
424 42 : w |= s->data[from];
425 :
426 42 : return w;
427 : }
428 :
429 : /* Get next 3-byte from the stream. */
430 0 : uint32_t stream_get3_from(struct stream *s, size_t from)
431 : {
432 0 : uint32_t l;
433 :
434 0 : STREAM_VERIFY_SANE(s);
435 :
436 0 : if (!GETP_VALID(s, from + 3)) {
437 0 : STREAM_BOUND_WARN(s, "get 3byte");
438 : return 0;
439 : }
440 :
441 0 : l = s->data[from++] << 16;
442 0 : l |= s->data[from++] << 8;
443 0 : l |= s->data[from];
444 :
445 0 : return l;
446 : }
447 :
448 4 : uint32_t stream_get3(struct stream *s)
449 : {
450 4 : uint32_t l;
451 :
452 4 : STREAM_VERIFY_SANE(s);
453 :
454 4 : if (STREAM_READABLE(s) < 3) {
455 0 : STREAM_BOUND_WARN(s, "get 3byte");
456 : return 0;
457 : }
458 :
459 4 : l = s->data[s->getp++] << 16;
460 4 : l |= s->data[s->getp++] << 8;
461 4 : l |= s->data[s->getp++];
462 :
463 4 : return l;
464 : }
465 :
466 : /* Get next long word from the stream. */
467 0 : uint32_t stream_getl_from(struct stream *s, size_t from)
468 : {
469 0 : uint32_t l;
470 :
471 0 : STREAM_VERIFY_SANE(s);
472 :
473 0 : if (!GETP_VALID(s, from + sizeof(uint32_t))) {
474 0 : STREAM_BOUND_WARN(s, "get long");
475 : return 0;
476 : }
477 :
478 0 : l = (unsigned)(s->data[from++]) << 24;
479 0 : l |= s->data[from++] << 16;
480 0 : l |= s->data[from++] << 8;
481 0 : l |= s->data[from];
482 :
483 0 : return l;
484 : }
485 :
486 : /* Copy from stream at specific location to destination. */
487 22 : void stream_get_from(void *dst, struct stream *s, size_t from, size_t size)
488 : {
489 22 : STREAM_VERIFY_SANE(s);
490 :
491 22 : if (!GETP_VALID(s, from + size)) {
492 0 : STREAM_BOUND_WARN(s, "get from");
493 : return;
494 : }
495 :
496 22 : memcpy(dst, s->data + from, size);
497 : }
498 :
499 1187 : bool stream_getl2(struct stream *s, uint32_t *l)
500 : {
501 1187 : STREAM_VERIFY_SANE(s);
502 :
503 1187 : if (STREAM_READABLE(s) < sizeof(uint32_t)) {
504 0 : STREAM_BOUND_WARN2(s, "get long");
505 0 : return false;
506 : }
507 :
508 1187 : *l = (unsigned int)(s->data[s->getp++]) << 24;
509 1187 : *l |= s->data[s->getp++] << 16;
510 1187 : *l |= s->data[s->getp++] << 8;
511 1187 : *l |= s->data[s->getp++];
512 :
513 1187 : return true;
514 : }
515 :
516 193 : uint32_t stream_getl(struct stream *s)
517 : {
518 193 : uint32_t l;
519 :
520 193 : STREAM_VERIFY_SANE(s);
521 :
522 193 : if (STREAM_READABLE(s) < sizeof(uint32_t)) {
523 0 : STREAM_BOUND_WARN(s, "get long");
524 : return 0;
525 : }
526 :
527 193 : l = (unsigned)(s->data[s->getp++]) << 24;
528 193 : l |= s->data[s->getp++] << 16;
529 193 : l |= s->data[s->getp++] << 8;
530 193 : l |= s->data[s->getp++];
531 :
532 193 : return l;
533 : }
534 :
535 : /* Get next quad word from the stream. */
536 0 : uint64_t stream_getq_from(struct stream *s, size_t from)
537 : {
538 0 : uint64_t q;
539 :
540 0 : STREAM_VERIFY_SANE(s);
541 :
542 0 : if (!GETP_VALID(s, from + sizeof(uint64_t))) {
543 0 : STREAM_BOUND_WARN(s, "get quad");
544 : return 0;
545 : }
546 :
547 0 : q = ((uint64_t)s->data[from++]) << 56;
548 0 : q |= ((uint64_t)s->data[from++]) << 48;
549 0 : q |= ((uint64_t)s->data[from++]) << 40;
550 0 : q |= ((uint64_t)s->data[from++]) << 32;
551 0 : q |= ((uint64_t)s->data[from++]) << 24;
552 0 : q |= ((uint64_t)s->data[from++]) << 16;
553 0 : q |= ((uint64_t)s->data[from++]) << 8;
554 0 : q |= ((uint64_t)s->data[from++]);
555 :
556 0 : return q;
557 : }
558 :
559 0 : uint64_t stream_getq(struct stream *s)
560 : {
561 0 : uint64_t q;
562 :
563 0 : STREAM_VERIFY_SANE(s);
564 :
565 0 : if (STREAM_READABLE(s) < sizeof(uint64_t)) {
566 0 : STREAM_BOUND_WARN(s, "get quad");
567 : return 0;
568 : }
569 :
570 0 : q = ((uint64_t)s->data[s->getp++]) << 56;
571 0 : q |= ((uint64_t)s->data[s->getp++]) << 48;
572 0 : q |= ((uint64_t)s->data[s->getp++]) << 40;
573 0 : q |= ((uint64_t)s->data[s->getp++]) << 32;
574 0 : q |= ((uint64_t)s->data[s->getp++]) << 24;
575 0 : q |= ((uint64_t)s->data[s->getp++]) << 16;
576 0 : q |= ((uint64_t)s->data[s->getp++]) << 8;
577 0 : q |= ((uint64_t)s->data[s->getp++]);
578 :
579 0 : return q;
580 : }
581 :
582 42 : bool stream_getq2(struct stream *s, uint64_t *q)
583 : {
584 42 : STREAM_VERIFY_SANE(s);
585 :
586 42 : if (STREAM_READABLE(s) < sizeof(uint64_t)) {
587 0 : STREAM_BOUND_WARN2(s, "get uint64");
588 0 : return false;
589 : }
590 :
591 42 : *q = ((uint64_t)s->data[s->getp++]) << 56;
592 42 : *q |= ((uint64_t)s->data[s->getp++]) << 48;
593 42 : *q |= ((uint64_t)s->data[s->getp++]) << 40;
594 42 : *q |= ((uint64_t)s->data[s->getp++]) << 32;
595 42 : *q |= ((uint64_t)s->data[s->getp++]) << 24;
596 42 : *q |= ((uint64_t)s->data[s->getp++]) << 16;
597 42 : *q |= ((uint64_t)s->data[s->getp++]) << 8;
598 42 : *q |= ((uint64_t)s->data[s->getp++]);
599 :
600 42 : return true;
601 : }
602 :
603 : /* Get next long word from the stream. */
604 4 : uint32_t stream_get_ipv4(struct stream *s)
605 : {
606 4 : uint32_t l;
607 :
608 4 : STREAM_VERIFY_SANE(s);
609 :
610 4 : if (STREAM_READABLE(s) < sizeof(uint32_t)) {
611 0 : STREAM_BOUND_WARN(s, "get ipv4");
612 : return 0;
613 : }
614 :
615 4 : memcpy(&l, s->data + s->getp, sizeof(uint32_t));
616 4 : s->getp += sizeof(uint32_t);
617 :
618 4 : return l;
619 : }
620 :
621 0 : bool stream_get_ipaddr(struct stream *s, struct ipaddr *ip)
622 : {
623 0 : uint16_t ipa_len = 0;
624 :
625 0 : STREAM_VERIFY_SANE(s);
626 :
627 : /* Get address type. */
628 0 : if (STREAM_READABLE(s) < sizeof(uint16_t)) {
629 0 : STREAM_BOUND_WARN2(s, "get ipaddr");
630 0 : return false;
631 : }
632 0 : ip->ipa_type = stream_getw(s);
633 :
634 : /* Get address value. */
635 0 : switch (ip->ipa_type) {
636 0 : case IPADDR_V4:
637 0 : ipa_len = IPV4_MAX_BYTELEN;
638 0 : break;
639 0 : case IPADDR_V6:
640 0 : ipa_len = IPV6_MAX_BYTELEN;
641 0 : break;
642 0 : case IPADDR_NONE:
643 0 : flog_err(EC_LIB_DEVELOPMENT,
644 : "%s: unknown ip address-family: %u", __func__,
645 : ip->ipa_type);
646 0 : return false;
647 : }
648 0 : if (STREAM_READABLE(s) < ipa_len) {
649 0 : STREAM_BOUND_WARN2(s, "get ipaddr");
650 0 : return false;
651 : }
652 0 : memcpy(&ip->ip, s->data + s->getp, ipa_len);
653 0 : s->getp += ipa_len;
654 :
655 0 : return true;
656 : }
657 :
658 0 : float stream_getf(struct stream *s)
659 : {
660 0 : union {
661 : float r;
662 : uint32_t d;
663 : } u;
664 0 : u.d = stream_getl(s);
665 0 : return u.r;
666 : }
667 :
668 0 : double stream_getd(struct stream *s)
669 : {
670 0 : union {
671 : double r;
672 : uint64_t d;
673 : } u;
674 0 : u.d = stream_getq(s);
675 0 : return u.r;
676 : }
677 :
678 : /* Copy from source to stream.
679 : *
680 : * XXX: This uses CHECK_SIZE and hence has funny semantics -> Size will wrap
681 : * around. This should be fixed once the stream updates are working.
682 : *
683 : * stream_write() is saner
684 : */
685 250 : void stream_put(struct stream *s, const void *src, size_t size)
686 : {
687 :
688 : /* XXX: CHECK_SIZE has strange semantics. It should be deprecated */
689 250 : CHECK_SIZE(s, size);
690 :
691 250 : STREAM_VERIFY_SANE(s);
692 :
693 250 : if (STREAM_WRITEABLE(s) < size) {
694 0 : STREAM_BOUND_WARN(s, "put");
695 : return;
696 : }
697 :
698 250 : if (src)
699 184 : memcpy(s->data + s->endp, src, size);
700 : else
701 66 : memset(s->data + s->endp, 0, size);
702 :
703 250 : s->endp += size;
704 : }
705 :
706 : /* Put character to the stream. */
707 1763 : int stream_putc(struct stream *s, uint8_t c)
708 : {
709 1763 : STREAM_VERIFY_SANE(s);
710 :
711 1763 : if (STREAM_WRITEABLE(s) < sizeof(uint8_t)) {
712 0 : STREAM_BOUND_WARN(s, "put");
713 : return 0;
714 : }
715 :
716 1763 : s->data[s->endp++] = c;
717 1763 : return sizeof(uint8_t);
718 : }
719 :
720 : /* Put word to the stream. */
721 770 : int stream_putw(struct stream *s, uint16_t w)
722 : {
723 770 : STREAM_VERIFY_SANE(s);
724 :
725 770 : if (STREAM_WRITEABLE(s) < sizeof(uint16_t)) {
726 0 : STREAM_BOUND_WARN(s, "put");
727 : return 0;
728 : }
729 :
730 770 : s->data[s->endp++] = (uint8_t)(w >> 8);
731 770 : s->data[s->endp++] = (uint8_t)w;
732 :
733 770 : return 2;
734 : }
735 :
736 : /* Put long word to the stream. */
737 4 : int stream_put3(struct stream *s, uint32_t l)
738 : {
739 4 : STREAM_VERIFY_SANE(s);
740 :
741 4 : if (STREAM_WRITEABLE(s) < 3) {
742 0 : STREAM_BOUND_WARN(s, "put");
743 : return 0;
744 : }
745 :
746 4 : s->data[s->endp++] = (uint8_t)(l >> 16);
747 4 : s->data[s->endp++] = (uint8_t)(l >> 8);
748 4 : s->data[s->endp++] = (uint8_t)l;
749 :
750 4 : return 3;
751 : }
752 :
753 : /* Put long word to the stream. */
754 1315 : int stream_putl(struct stream *s, uint32_t l)
755 : {
756 1315 : STREAM_VERIFY_SANE(s);
757 :
758 1315 : if (STREAM_WRITEABLE(s) < sizeof(uint32_t)) {
759 0 : STREAM_BOUND_WARN(s, "put");
760 : return 0;
761 : }
762 :
763 1315 : s->data[s->endp++] = (uint8_t)(l >> 24);
764 1315 : s->data[s->endp++] = (uint8_t)(l >> 16);
765 1315 : s->data[s->endp++] = (uint8_t)(l >> 8);
766 1315 : s->data[s->endp++] = (uint8_t)l;
767 :
768 1315 : return 4;
769 : }
770 :
771 : /* Put quad word to the stream. */
772 42 : int stream_putq(struct stream *s, uint64_t q)
773 : {
774 42 : STREAM_VERIFY_SANE(s);
775 :
776 42 : if (STREAM_WRITEABLE(s) < sizeof(uint64_t)) {
777 0 : STREAM_BOUND_WARN(s, "put quad");
778 : return 0;
779 : }
780 :
781 42 : s->data[s->endp++] = (uint8_t)(q >> 56);
782 42 : s->data[s->endp++] = (uint8_t)(q >> 48);
783 42 : s->data[s->endp++] = (uint8_t)(q >> 40);
784 42 : s->data[s->endp++] = (uint8_t)(q >> 32);
785 42 : s->data[s->endp++] = (uint8_t)(q >> 24);
786 42 : s->data[s->endp++] = (uint8_t)(q >> 16);
787 42 : s->data[s->endp++] = (uint8_t)(q >> 8);
788 42 : s->data[s->endp++] = (uint8_t)q;
789 :
790 42 : return 8;
791 : }
792 :
793 0 : int stream_putf(struct stream *s, float f)
794 : {
795 0 : union {
796 : float i;
797 : uint32_t o;
798 : } u;
799 0 : u.i = f;
800 0 : return stream_putl(s, u.o);
801 : }
802 :
803 0 : int stream_putd(struct stream *s, double d)
804 : {
805 0 : union {
806 : double i;
807 : uint64_t o;
808 : } u;
809 0 : u.i = d;
810 0 : return stream_putq(s, u.o);
811 : }
812 :
813 32 : int stream_putc_at(struct stream *s, size_t putp, uint8_t c)
814 : {
815 32 : STREAM_VERIFY_SANE(s);
816 :
817 32 : if (!PUT_AT_VALID(s, putp + sizeof(uint8_t))) {
818 0 : STREAM_BOUND_WARN(s, "put");
819 : return 0;
820 : }
821 :
822 32 : s->data[putp] = c;
823 :
824 32 : return 1;
825 : }
826 :
827 331 : int stream_putw_at(struct stream *s, size_t putp, uint16_t w)
828 : {
829 331 : STREAM_VERIFY_SANE(s);
830 :
831 331 : if (!PUT_AT_VALID(s, putp + sizeof(uint16_t))) {
832 0 : STREAM_BOUND_WARN(s, "put");
833 : return 0;
834 : }
835 :
836 331 : s->data[putp] = (uint8_t)(w >> 8);
837 331 : s->data[putp + 1] = (uint8_t)w;
838 :
839 331 : return 2;
840 : }
841 :
842 0 : int stream_put3_at(struct stream *s, size_t putp, uint32_t l)
843 : {
844 0 : STREAM_VERIFY_SANE(s);
845 :
846 0 : if (!PUT_AT_VALID(s, putp + 3)) {
847 0 : STREAM_BOUND_WARN(s, "put");
848 : return 0;
849 : }
850 0 : s->data[putp] = (uint8_t)(l >> 16);
851 0 : s->data[putp + 1] = (uint8_t)(l >> 8);
852 0 : s->data[putp + 2] = (uint8_t)l;
853 :
854 0 : return 3;
855 : }
856 :
857 0 : int stream_putl_at(struct stream *s, size_t putp, uint32_t l)
858 : {
859 0 : STREAM_VERIFY_SANE(s);
860 :
861 0 : if (!PUT_AT_VALID(s, putp + sizeof(uint32_t))) {
862 0 : STREAM_BOUND_WARN(s, "put");
863 : return 0;
864 : }
865 0 : s->data[putp] = (uint8_t)(l >> 24);
866 0 : s->data[putp + 1] = (uint8_t)(l >> 16);
867 0 : s->data[putp + 2] = (uint8_t)(l >> 8);
868 0 : s->data[putp + 3] = (uint8_t)l;
869 :
870 0 : return 4;
871 : }
872 :
873 0 : int stream_putq_at(struct stream *s, size_t putp, uint64_t q)
874 : {
875 0 : STREAM_VERIFY_SANE(s);
876 :
877 0 : if (!PUT_AT_VALID(s, putp + sizeof(uint64_t))) {
878 0 : STREAM_BOUND_WARN(s, "put");
879 : return 0;
880 : }
881 0 : s->data[putp] = (uint8_t)(q >> 56);
882 0 : s->data[putp + 1] = (uint8_t)(q >> 48);
883 0 : s->data[putp + 2] = (uint8_t)(q >> 40);
884 0 : s->data[putp + 3] = (uint8_t)(q >> 32);
885 0 : s->data[putp + 4] = (uint8_t)(q >> 24);
886 0 : s->data[putp + 5] = (uint8_t)(q >> 16);
887 0 : s->data[putp + 6] = (uint8_t)(q >> 8);
888 0 : s->data[putp + 7] = (uint8_t)q;
889 :
890 0 : return 8;
891 : }
892 :
893 : /* Put long word to the stream. */
894 0 : int stream_put_ipv4(struct stream *s, uint32_t l)
895 : {
896 0 : STREAM_VERIFY_SANE(s);
897 :
898 0 : if (STREAM_WRITEABLE(s) < sizeof(uint32_t)) {
899 0 : STREAM_BOUND_WARN(s, "put");
900 : return 0;
901 : }
902 0 : memcpy(s->data + s->endp, &l, sizeof(uint32_t));
903 0 : s->endp += sizeof(uint32_t);
904 :
905 0 : return sizeof(uint32_t);
906 : }
907 :
908 : /* Put long word to the stream. */
909 4 : int stream_put_in_addr(struct stream *s, const struct in_addr *addr)
910 : {
911 4 : STREAM_VERIFY_SANE(s);
912 :
913 4 : if (STREAM_WRITEABLE(s) < sizeof(uint32_t)) {
914 0 : STREAM_BOUND_WARN(s, "put");
915 : return 0;
916 : }
917 :
918 4 : memcpy(s->data + s->endp, addr, sizeof(uint32_t));
919 4 : s->endp += sizeof(uint32_t);
920 :
921 4 : return sizeof(uint32_t);
922 : }
923 :
924 0 : bool stream_put_ipaddr(struct stream *s, struct ipaddr *ip)
925 : {
926 0 : stream_putw(s, ip->ipa_type);
927 :
928 0 : switch (ip->ipa_type) {
929 0 : case IPADDR_V4:
930 0 : stream_put_in_addr(s, &ip->ipaddr_v4);
931 0 : break;
932 0 : case IPADDR_V6:
933 0 : stream_write(s, (uint8_t *)&ip->ipaddr_v6, 16);
934 0 : break;
935 0 : case IPADDR_NONE:
936 0 : flog_err(EC_LIB_DEVELOPMENT,
937 : "%s: unknown ip address-family: %u", __func__,
938 : ip->ipa_type);
939 0 : return false;
940 : }
941 :
942 : return true;
943 : }
944 :
945 : /* Put in_addr at location in the stream. */
946 0 : int stream_put_in_addr_at(struct stream *s, size_t putp,
947 : const struct in_addr *addr)
948 : {
949 0 : STREAM_VERIFY_SANE(s);
950 :
951 0 : if (!PUT_AT_VALID(s, putp + 4)) {
952 0 : STREAM_BOUND_WARN(s, "put");
953 : return 0;
954 : }
955 :
956 0 : memcpy(&s->data[putp], addr, 4);
957 0 : return 4;
958 : }
959 :
960 : /* Put in6_addr at location in the stream. */
961 20 : int stream_put_in6_addr_at(struct stream *s, size_t putp,
962 : const struct in6_addr *addr)
963 : {
964 20 : STREAM_VERIFY_SANE(s);
965 :
966 20 : if (!PUT_AT_VALID(s, putp + 16)) {
967 0 : STREAM_BOUND_WARN(s, "put");
968 : return 0;
969 : }
970 :
971 20 : memcpy(&s->data[putp], addr, 16);
972 20 : return 16;
973 : }
974 :
975 : /* Put prefix by nlri type format. */
976 6 : int stream_put_prefix_addpath(struct stream *s, const struct prefix *p,
977 : bool addpath_capable, uint32_t addpath_tx_id)
978 : {
979 6 : size_t psize;
980 6 : size_t psize_with_addpath;
981 :
982 6 : STREAM_VERIFY_SANE(s);
983 :
984 6 : psize = PSIZE(p->prefixlen);
985 :
986 6 : if (addpath_capable)
987 0 : psize_with_addpath = psize + 4;
988 : else
989 : psize_with_addpath = psize;
990 :
991 6 : if (STREAM_WRITEABLE(s) < (psize_with_addpath + sizeof(uint8_t))) {
992 0 : STREAM_BOUND_WARN(s, "put");
993 : return 0;
994 : }
995 :
996 6 : if (addpath_capable) {
997 0 : s->data[s->endp++] = (uint8_t)(addpath_tx_id >> 24);
998 0 : s->data[s->endp++] = (uint8_t)(addpath_tx_id >> 16);
999 0 : s->data[s->endp++] = (uint8_t)(addpath_tx_id >> 8);
1000 0 : s->data[s->endp++] = (uint8_t)addpath_tx_id;
1001 : }
1002 :
1003 6 : s->data[s->endp++] = p->prefixlen;
1004 6 : memcpy(s->data + s->endp, &p->u.prefix, psize);
1005 6 : s->endp += psize;
1006 :
1007 6 : return psize;
1008 : }
1009 :
1010 0 : int stream_put_prefix(struct stream *s, const struct prefix *p)
1011 : {
1012 0 : return stream_put_prefix_addpath(s, p, 0, 0);
1013 : }
1014 :
1015 : /* Put NLRI with label */
1016 0 : int stream_put_labeled_prefix(struct stream *s, const struct prefix *p,
1017 : mpls_label_t *label, bool addpath_capable,
1018 : uint32_t addpath_tx_id)
1019 : {
1020 0 : size_t psize;
1021 0 : size_t psize_with_addpath;
1022 0 : uint8_t *label_pnt = (uint8_t *)label;
1023 :
1024 0 : STREAM_VERIFY_SANE(s);
1025 :
1026 0 : psize = PSIZE(p->prefixlen);
1027 0 : psize_with_addpath = psize + (addpath_capable ? 4 : 0);
1028 :
1029 0 : if (STREAM_WRITEABLE(s) < (psize_with_addpath + 3)) {
1030 0 : STREAM_BOUND_WARN(s, "put");
1031 : return 0;
1032 : }
1033 :
1034 0 : if (addpath_capable) {
1035 0 : s->data[s->endp++] = (uint8_t)(addpath_tx_id >> 24);
1036 0 : s->data[s->endp++] = (uint8_t)(addpath_tx_id >> 16);
1037 0 : s->data[s->endp++] = (uint8_t)(addpath_tx_id >> 8);
1038 0 : s->data[s->endp++] = (uint8_t)addpath_tx_id;
1039 : }
1040 :
1041 0 : stream_putc(s, (p->prefixlen + 24));
1042 0 : stream_putc(s, label_pnt[0]);
1043 0 : stream_putc(s, label_pnt[1]);
1044 0 : stream_putc(s, label_pnt[2]);
1045 0 : memcpy(s->data + s->endp, &p->u.prefix, psize);
1046 0 : s->endp += psize;
1047 :
1048 0 : return (psize + 3);
1049 : }
1050 :
1051 : /* Read size from fd. */
1052 4 : int stream_read(struct stream *s, int fd, size_t size)
1053 : {
1054 4 : int nbytes;
1055 :
1056 4 : STREAM_VERIFY_SANE(s);
1057 :
1058 4 : if (STREAM_WRITEABLE(s) < size) {
1059 0 : STREAM_BOUND_WARN(s, "put");
1060 : return 0;
1061 : }
1062 :
1063 4 : nbytes = readn(fd, s->data + s->endp, size);
1064 :
1065 4 : if (nbytes > 0)
1066 4 : s->endp += nbytes;
1067 :
1068 4 : return nbytes;
1069 : }
1070 :
1071 624 : ssize_t stream_read_try(struct stream *s, int fd, size_t size)
1072 : {
1073 624 : ssize_t nbytes;
1074 :
1075 624 : STREAM_VERIFY_SANE(s);
1076 :
1077 624 : if (STREAM_WRITEABLE(s) < size) {
1078 0 : STREAM_BOUND_WARN(s, "put");
1079 : /* Fatal (not transient) error, since retrying will not help
1080 : (stream is too small to contain the desired data). */
1081 : return -1;
1082 : }
1083 :
1084 624 : nbytes = read(fd, s->data + s->endp, size);
1085 624 : if (nbytes >= 0) {
1086 592 : s->endp += nbytes;
1087 592 : return nbytes;
1088 : }
1089 : /* Error: was it transient (return -2) or fatal (return -1)? */
1090 32 : if (ERRNO_IO_RETRY(errno))
1091 : return -2;
1092 0 : flog_err(EC_LIB_SOCKET, "%s: read failed on fd %d: %s", __func__, fd,
1093 : safe_strerror(errno));
1094 0 : return -1;
1095 : }
1096 :
1097 : /* Read up to size bytes into the stream from the fd, using recvmsgfrom
1098 : * whose arguments match the remaining arguments to this function
1099 : */
1100 0 : ssize_t stream_recvfrom(struct stream *s, int fd, size_t size, int flags,
1101 : struct sockaddr *from, socklen_t *fromlen)
1102 : {
1103 0 : ssize_t nbytes;
1104 :
1105 0 : STREAM_VERIFY_SANE(s);
1106 :
1107 0 : if (STREAM_WRITEABLE(s) < size) {
1108 0 : STREAM_BOUND_WARN(s, "put");
1109 : /* Fatal (not transient) error, since retrying will not help
1110 : (stream is too small to contain the desired data). */
1111 : return -1;
1112 : }
1113 :
1114 0 : nbytes = recvfrom(fd, s->data + s->endp, size, flags, from, fromlen);
1115 0 : if (nbytes >= 0) {
1116 0 : s->endp += nbytes;
1117 0 : return nbytes;
1118 : }
1119 : /* Error: was it transient (return -2) or fatal (return -1)? */
1120 0 : if (ERRNO_IO_RETRY(errno))
1121 : return -2;
1122 0 : flog_err(EC_LIB_SOCKET, "%s: read failed on fd %d: %s", __func__, fd,
1123 : safe_strerror(errno));
1124 0 : return -1;
1125 : }
1126 :
1127 : /* Read up to smaller of size or SIZE_REMAIN() bytes to the stream, starting
1128 : * from endp.
1129 : * First iovec will be used to receive the data.
1130 : * Stream need not be empty.
1131 : */
1132 0 : ssize_t stream_recvmsg(struct stream *s, int fd, struct msghdr *msgh, int flags,
1133 : size_t size)
1134 : {
1135 0 : int nbytes;
1136 0 : struct iovec *iov;
1137 :
1138 0 : STREAM_VERIFY_SANE(s);
1139 0 : assert(msgh->msg_iovlen > 0);
1140 :
1141 0 : if (STREAM_WRITEABLE(s) < size) {
1142 0 : STREAM_BOUND_WARN(s, "put");
1143 : /* This is a logic error in the calling code: the stream is too
1144 : small
1145 : to hold the desired data! */
1146 : return -1;
1147 : }
1148 :
1149 0 : iov = &(msgh->msg_iov[0]);
1150 0 : iov->iov_base = (s->data + s->endp);
1151 0 : iov->iov_len = size;
1152 :
1153 0 : nbytes = recvmsg(fd, msgh, flags);
1154 :
1155 0 : if (nbytes > 0)
1156 0 : s->endp += nbytes;
1157 :
1158 0 : return nbytes;
1159 : }
1160 :
1161 : /* Write data to buffer. */
1162 15 : size_t stream_write(struct stream *s, const void *ptr, size_t size)
1163 : {
1164 :
1165 15 : CHECK_SIZE(s, size);
1166 :
1167 15 : STREAM_VERIFY_SANE(s);
1168 :
1169 15 : if (STREAM_WRITEABLE(s) < size) {
1170 0 : STREAM_BOUND_WARN(s, "put");
1171 : return 0;
1172 : }
1173 :
1174 15 : memcpy(s->data + s->endp, ptr, size);
1175 15 : s->endp += size;
1176 :
1177 15 : return size;
1178 : }
1179 :
1180 : /* Return current read pointer.
1181 : * DEPRECATED!
1182 : * Use stream_get_pnt_to if you must, but decoding streams properly
1183 : * is preferred
1184 : */
1185 372 : uint8_t *stream_pnt(struct stream *s)
1186 : {
1187 372 : STREAM_VERIFY_SANE(s);
1188 372 : return s->data + s->getp;
1189 : }
1190 :
1191 : /* Check does this stream empty? */
1192 77 : int stream_empty(struct stream *s)
1193 : {
1194 77 : STREAM_VERIFY_SANE(s);
1195 :
1196 77 : return (s->endp == 0);
1197 : }
1198 :
1199 : /* Reset stream. */
1200 535 : void stream_reset(struct stream *s)
1201 : {
1202 535 : STREAM_VERIFY_SANE(s);
1203 :
1204 535 : s->getp = s->endp = 0;
1205 535 : }
1206 :
1207 : /* Write stream contens to the file discriptor. */
1208 0 : int stream_flush(struct stream *s, int fd)
1209 : {
1210 0 : int nbytes;
1211 :
1212 0 : STREAM_VERIFY_SANE(s);
1213 :
1214 0 : nbytes = write(fd, s->data + s->getp, s->endp - s->getp);
1215 :
1216 0 : return nbytes;
1217 : }
1218 :
1219 0 : void stream_hexdump(const struct stream *s)
1220 : {
1221 0 : zlog_hexdump(s->data, s->endp);
1222 0 : }
1223 :
1224 : /* Stream first in first out queue. */
1225 :
1226 140 : struct stream_fifo *stream_fifo_new(void)
1227 : {
1228 140 : struct stream_fifo *new;
1229 :
1230 140 : new = XMALLOC(MTYPE_STREAM_FIFO, sizeof(struct stream_fifo));
1231 140 : stream_fifo_init(new);
1232 140 : return new;
1233 : }
1234 :
1235 174 : void stream_fifo_init(struct stream_fifo *fifo)
1236 : {
1237 174 : memset(fifo, 0, sizeof(struct stream_fifo));
1238 174 : pthread_mutex_init(&fifo->mtx, NULL);
1239 174 : }
1240 :
1241 : /* Add new stream to fifo. */
1242 778 : void stream_fifo_push(struct stream_fifo *fifo, struct stream *s)
1243 : {
1244 : #if defined DEV_BUILD
1245 : size_t max, curmax;
1246 : #endif
1247 :
1248 778 : if (fifo->tail)
1249 571 : fifo->tail->next = s;
1250 : else
1251 207 : fifo->head = s;
1252 :
1253 778 : fifo->tail = s;
1254 778 : fifo->tail->next = NULL;
1255 : #if !defined DEV_BUILD
1256 778 : atomic_fetch_add_explicit(&fifo->count, 1, memory_order_release);
1257 : #else
1258 : max = atomic_fetch_add_explicit(&fifo->count, 1, memory_order_release);
1259 : curmax = atomic_load_explicit(&fifo->max_count, memory_order_relaxed);
1260 : if (max > curmax)
1261 : atomic_store_explicit(&fifo->max_count, max,
1262 : memory_order_relaxed);
1263 : #endif
1264 778 : }
1265 :
1266 0 : void stream_fifo_push_safe(struct stream_fifo *fifo, struct stream *s)
1267 : {
1268 0 : frr_with_mutex (&fifo->mtx) {
1269 0 : stream_fifo_push(fifo, s);
1270 : }
1271 0 : }
1272 :
1273 : /* Delete first stream from fifo. */
1274 791 : struct stream *stream_fifo_pop(struct stream_fifo *fifo)
1275 : {
1276 791 : struct stream *s;
1277 :
1278 791 : s = fifo->head;
1279 :
1280 791 : if (s) {
1281 774 : fifo->head = s->next;
1282 :
1283 774 : if (fifo->head == NULL)
1284 203 : fifo->tail = NULL;
1285 :
1286 774 : atomic_fetch_sub_explicit(&fifo->count, 1,
1287 : memory_order_release);
1288 :
1289 : /* ensure stream is scrubbed of references to this fifo */
1290 774 : s->next = NULL;
1291 : }
1292 :
1293 791 : return s;
1294 : }
1295 :
1296 0 : struct stream *stream_fifo_pop_safe(struct stream_fifo *fifo)
1297 : {
1298 0 : struct stream *ret;
1299 :
1300 0 : frr_with_mutex (&fifo->mtx) {
1301 0 : ret = stream_fifo_pop(fifo);
1302 : }
1303 :
1304 0 : return ret;
1305 : }
1306 :
1307 866 : struct stream *stream_fifo_head(struct stream_fifo *fifo)
1308 : {
1309 866 : return fifo->head;
1310 : }
1311 :
1312 0 : struct stream *stream_fifo_head_safe(struct stream_fifo *fifo)
1313 : {
1314 0 : struct stream *ret;
1315 :
1316 0 : frr_with_mutex (&fifo->mtx) {
1317 0 : ret = stream_fifo_head(fifo);
1318 : }
1319 :
1320 0 : return ret;
1321 : }
1322 :
1323 202 : void stream_fifo_clean(struct stream_fifo *fifo)
1324 : {
1325 202 : struct stream *s;
1326 202 : struct stream *next;
1327 :
1328 206 : for (s = fifo->head; s; s = next) {
1329 4 : next = s->next;
1330 4 : stream_free(s);
1331 : }
1332 202 : fifo->head = fifo->tail = NULL;
1333 202 : atomic_store_explicit(&fifo->count, 0, memory_order_release);
1334 202 : }
1335 :
1336 0 : void stream_fifo_clean_safe(struct stream_fifo *fifo)
1337 : {
1338 0 : frr_with_mutex (&fifo->mtx) {
1339 0 : stream_fifo_clean(fifo);
1340 : }
1341 0 : }
1342 :
1343 22 : size_t stream_fifo_count_safe(struct stream_fifo *fifo)
1344 : {
1345 22 : return atomic_load_explicit(&fifo->count, memory_order_acquire);
1346 : }
1347 :
1348 166 : void stream_fifo_deinit(struct stream_fifo *fifo)
1349 : {
1350 166 : stream_fifo_clean(fifo);
1351 166 : pthread_mutex_destroy(&fifo->mtx);
1352 166 : }
1353 :
1354 132 : void stream_fifo_free(struct stream_fifo *fifo)
1355 : {
1356 132 : stream_fifo_deinit(fifo);
1357 132 : XFREE(MTYPE_STREAM_FIFO, fifo);
1358 132 : }
1359 :
1360 0 : void stream_pulldown(struct stream *s)
1361 : {
1362 0 : size_t rlen = STREAM_READABLE(s);
1363 :
1364 : /* No more data, so just move the pointers. */
1365 0 : if (rlen == 0) {
1366 0 : stream_reset(s);
1367 0 : return;
1368 : }
1369 :
1370 : /* Move the available data to the beginning. */
1371 0 : memmove(s->data, &s->data[s->getp], rlen);
1372 0 : s->getp = 0;
1373 0 : s->endp = rlen;
1374 : }
|