xref: /netbsd-src/external/mpl/dhcp/bind/dist/lib/isc/buffer.c (revision 4afad4b7fa6d4a0d3dedf41d1587a7250710ae54)
1 /*	$NetBSD: buffer.c,v 1.1 2024/02/18 20:57:48 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * SPDX-License-Identifier: MPL-2.0
7  *
8  * This Source Code Form is subject to the terms of the Mozilla Public
9  * License, v. 2.0. If a copy of the MPL was not distributed with this
10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11  *
12  * See the COPYRIGHT file distributed with this work for additional
13  * information regarding copyright ownership.
14  */
15 
16 /*! \file */
17 
18 #include <inttypes.h>
19 #include <stdarg.h>
20 #include <stdbool.h>
21 
22 #include <isc/buffer.h>
23 #include <isc/mem.h>
24 #include <isc/print.h>
25 #include <isc/region.h>
26 #include <isc/string.h>
27 #include <isc/util.h>
28 
29 void
isc__buffer_init(isc_buffer_t * b,void * base,unsigned int length)30 isc__buffer_init(isc_buffer_t *b, void *base, unsigned int length) {
31 	/*
32 	 * Make 'b' refer to the 'length'-byte region starting at 'base'.
33 	 * XXXDCL see the comment in buffer.h about base being const.
34 	 */
35 	ISC__BUFFER_INIT(b, base, length);
36 }
37 
38 void
isc__buffer_initnull(isc_buffer_t * b)39 isc__buffer_initnull(isc_buffer_t *b) {
40 	/*
41 	 * Initialize a new buffer which has no backing store.  This can
42 	 * later be grown as needed and swapped in place.
43 	 */
44 	ISC__BUFFER_INIT(b, NULL, 0);
45 }
46 
47 void
isc_buffer_reinit(isc_buffer_t * b,void * base,unsigned int length)48 isc_buffer_reinit(isc_buffer_t *b, void *base, unsigned int length) {
49 	/*
50 	 * Re-initialize the buffer enough to reconfigure the base of the
51 	 * buffer.  We will swap in the new buffer, after copying any
52 	 * data we contain into the new buffer and adjusting all of our
53 	 * internal pointers.
54 	 *
55 	 * The buffer must not be smaller than the length of the original
56 	 * buffer.
57 	 */
58 	REQUIRE(b->length <= length);
59 	REQUIRE(base != NULL);
60 	REQUIRE(!b->autore);
61 
62 	if (b->length > 0U) {
63 		(void)memmove(base, b->base, b->length);
64 	}
65 
66 	b->base = base;
67 	b->length = length;
68 }
69 
70 void
isc__buffer_invalidate(isc_buffer_t * b)71 isc__buffer_invalidate(isc_buffer_t *b) {
72 	/*
73 	 * Make 'b' an invalid buffer.
74 	 */
75 	ISC__BUFFER_INVALIDATE(b);
76 }
77 
78 void
isc_buffer_setautorealloc(isc_buffer_t * b,bool enable)79 isc_buffer_setautorealloc(isc_buffer_t *b, bool enable) {
80 	REQUIRE(ISC_BUFFER_VALID(b));
81 	REQUIRE(b->mctx != NULL);
82 	b->autore = enable;
83 }
84 
85 void
isc__buffer_region(isc_buffer_t * b,isc_region_t * r)86 isc__buffer_region(isc_buffer_t *b, isc_region_t *r) {
87 	/*
88 	 * Make 'r' refer to the region of 'b'.
89 	 */
90 	ISC__BUFFER_REGION(b, r);
91 }
92 
93 void
isc__buffer_usedregion(const isc_buffer_t * b,isc_region_t * r)94 isc__buffer_usedregion(const isc_buffer_t *b, isc_region_t *r) {
95 	/*
96 	 * Make 'r' refer to the used region of 'b'.
97 	 */
98 	ISC__BUFFER_USEDREGION(b, r);
99 }
100 
101 void
isc__buffer_availableregion(isc_buffer_t * b,isc_region_t * r)102 isc__buffer_availableregion(isc_buffer_t *b, isc_region_t *r) {
103 	/*
104 	 * Make 'r' refer to the available region of 'b'.
105 	 */
106 	ISC__BUFFER_AVAILABLEREGION(b, r);
107 }
108 
109 void
isc__buffer_add(isc_buffer_t * b,unsigned int n)110 isc__buffer_add(isc_buffer_t *b, unsigned int n) {
111 	/*
112 	 * Increase the 'used' region of 'b' by 'n' bytes.
113 	 */
114 	ISC__BUFFER_ADD(b, n);
115 }
116 
117 void
isc__buffer_subtract(isc_buffer_t * b,unsigned int n)118 isc__buffer_subtract(isc_buffer_t *b, unsigned int n) {
119 	/*
120 	 * Decrease the 'used' region of 'b' by 'n' bytes.
121 	 */
122 	ISC__BUFFER_SUBTRACT(b, n);
123 }
124 
125 void
isc__buffer_clear(isc_buffer_t * b)126 isc__buffer_clear(isc_buffer_t *b) {
127 	/*
128 	 * Make the used region empty.
129 	 */
130 	ISC__BUFFER_CLEAR(b);
131 }
132 
133 void
isc__buffer_consumedregion(isc_buffer_t * b,isc_region_t * r)134 isc__buffer_consumedregion(isc_buffer_t *b, isc_region_t *r) {
135 	/*
136 	 * Make 'r' refer to the consumed region of 'b'.
137 	 */
138 	ISC__BUFFER_CONSUMEDREGION(b, r);
139 }
140 
141 void
isc__buffer_remainingregion(isc_buffer_t * b,isc_region_t * r)142 isc__buffer_remainingregion(isc_buffer_t *b, isc_region_t *r) {
143 	/*
144 	 * Make 'r' refer to the remaining region of 'b'.
145 	 */
146 	ISC__BUFFER_REMAININGREGION(b, r);
147 }
148 
149 void
isc__buffer_activeregion(isc_buffer_t * b,isc_region_t * r)150 isc__buffer_activeregion(isc_buffer_t *b, isc_region_t *r) {
151 	/*
152 	 * Make 'r' refer to the active region of 'b'.
153 	 */
154 	ISC__BUFFER_ACTIVEREGION(b, r);
155 }
156 
157 void
isc__buffer_setactive(isc_buffer_t * b,unsigned int n)158 isc__buffer_setactive(isc_buffer_t *b, unsigned int n) {
159 	/*
160 	 * Sets the end of the active region 'n' bytes after current.
161 	 */
162 	ISC__BUFFER_SETACTIVE(b, n);
163 }
164 
165 void
isc__buffer_first(isc_buffer_t * b)166 isc__buffer_first(isc_buffer_t *b) {
167 	/*
168 	 * Make the consumed region empty.
169 	 */
170 	ISC__BUFFER_FIRST(b);
171 }
172 
173 void
isc__buffer_forward(isc_buffer_t * b,unsigned int n)174 isc__buffer_forward(isc_buffer_t *b, unsigned int n) {
175 	/*
176 	 * Increase the 'consumed' region of 'b' by 'n' bytes.
177 	 */
178 	ISC__BUFFER_FORWARD(b, n);
179 }
180 
181 void
isc__buffer_back(isc_buffer_t * b,unsigned int n)182 isc__buffer_back(isc_buffer_t *b, unsigned int n) {
183 	/*
184 	 * Decrease the 'consumed' region of 'b' by 'n' bytes.
185 	 */
186 	ISC__BUFFER_BACK(b, n);
187 }
188 
189 void
isc_buffer_compact(isc_buffer_t * b)190 isc_buffer_compact(isc_buffer_t *b) {
191 	unsigned int length;
192 	void *src;
193 
194 	/*
195 	 * Compact the used region by moving the remaining region so it occurs
196 	 * at the start of the buffer.  The used region is shrunk by the size
197 	 * of the consumed region, and the consumed region is then made empty.
198 	 */
199 
200 	REQUIRE(ISC_BUFFER_VALID(b));
201 
202 	src = isc_buffer_current(b);
203 	length = isc_buffer_remaininglength(b);
204 	if (length > 0U) {
205 		(void)memmove(b->base, src, (size_t)length);
206 	}
207 
208 	if (b->active > b->current) {
209 		b->active -= b->current;
210 	} else {
211 		b->active = 0;
212 	}
213 	b->current = 0;
214 	b->used = length;
215 }
216 
217 uint8_t
isc_buffer_getuint8(isc_buffer_t * b)218 isc_buffer_getuint8(isc_buffer_t *b) {
219 	unsigned char *cp;
220 	uint8_t result;
221 
222 	/*
223 	 * Read an unsigned 8-bit integer from 'b' and return it.
224 	 */
225 
226 	REQUIRE(ISC_BUFFER_VALID(b));
227 	REQUIRE(b->used - b->current >= 1);
228 
229 	cp = isc_buffer_current(b);
230 	b->current += 1;
231 	result = ((uint8_t)(cp[0]));
232 
233 	return (result);
234 }
235 
236 void
isc__buffer_putuint8(isc_buffer_t * b,uint8_t val)237 isc__buffer_putuint8(isc_buffer_t *b, uint8_t val) {
238 	ISC__BUFFER_PUTUINT8(b, val);
239 }
240 
241 uint16_t
isc_buffer_getuint16(isc_buffer_t * b)242 isc_buffer_getuint16(isc_buffer_t *b) {
243 	unsigned char *cp;
244 	uint16_t result;
245 
246 	/*
247 	 * Read an unsigned 16-bit integer in network byte order from 'b',
248 	 * convert it to host byte order, and return it.
249 	 */
250 
251 	REQUIRE(ISC_BUFFER_VALID(b));
252 	REQUIRE(b->used - b->current >= 2);
253 
254 	cp = isc_buffer_current(b);
255 	b->current += 2;
256 	result = ((unsigned int)(cp[0])) << 8;
257 	result |= ((unsigned int)(cp[1]));
258 
259 	return (result);
260 }
261 
262 void
isc__buffer_putuint16(isc_buffer_t * b,uint16_t val)263 isc__buffer_putuint16(isc_buffer_t *b, uint16_t val) {
264 	ISC__BUFFER_PUTUINT16(b, val);
265 }
266 
267 void
isc__buffer_putuint24(isc_buffer_t * b,uint32_t val)268 isc__buffer_putuint24(isc_buffer_t *b, uint32_t val) {
269 	ISC__BUFFER_PUTUINT24(b, val);
270 }
271 
272 uint32_t
isc_buffer_getuint32(isc_buffer_t * b)273 isc_buffer_getuint32(isc_buffer_t *b) {
274 	unsigned char *cp;
275 	uint32_t result;
276 
277 	/*
278 	 * Read an unsigned 32-bit integer in network byte order from 'b',
279 	 * convert it to host byte order, and return it.
280 	 */
281 
282 	REQUIRE(ISC_BUFFER_VALID(b));
283 	REQUIRE(b->used - b->current >= 4);
284 
285 	cp = isc_buffer_current(b);
286 	b->current += 4;
287 	result = ((unsigned int)(cp[0])) << 24;
288 	result |= ((unsigned int)(cp[1])) << 16;
289 	result |= ((unsigned int)(cp[2])) << 8;
290 	result |= ((unsigned int)(cp[3]));
291 
292 	return (result);
293 }
294 
295 void
isc__buffer_putuint32(isc_buffer_t * b,uint32_t val)296 isc__buffer_putuint32(isc_buffer_t *b, uint32_t val) {
297 	ISC__BUFFER_PUTUINT32(b, val);
298 }
299 
300 uint64_t
isc_buffer_getuint48(isc_buffer_t * b)301 isc_buffer_getuint48(isc_buffer_t *b) {
302 	unsigned char *cp;
303 	uint64_t result;
304 
305 	/*
306 	 * Read an unsigned 48-bit integer in network byte order from 'b',
307 	 * convert it to host byte order, and return it.
308 	 */
309 
310 	REQUIRE(ISC_BUFFER_VALID(b));
311 	REQUIRE(b->used - b->current >= 6);
312 
313 	cp = isc_buffer_current(b);
314 	b->current += 6;
315 	result = ((int64_t)(cp[0])) << 40;
316 	result |= ((int64_t)(cp[1])) << 32;
317 	result |= ((int64_t)(cp[2])) << 24;
318 	result |= ((int64_t)(cp[3])) << 16;
319 	result |= ((int64_t)(cp[4])) << 8;
320 	result |= ((int64_t)(cp[5]));
321 
322 	return (result);
323 }
324 
325 void
isc__buffer_putuint48(isc_buffer_t * b,uint64_t val)326 isc__buffer_putuint48(isc_buffer_t *b, uint64_t val) {
327 	isc_result_t result;
328 	uint16_t valhi;
329 	uint32_t vallo;
330 
331 	REQUIRE(ISC_BUFFER_VALID(b));
332 	if (ISC_UNLIKELY(b->autore)) {
333 		result = isc_buffer_reserve(&b, 6);
334 		REQUIRE(result == ISC_R_SUCCESS);
335 	}
336 	REQUIRE(isc_buffer_availablelength(b) >= 6);
337 
338 	valhi = (uint16_t)(val >> 32);
339 	vallo = (uint32_t)(val & 0xFFFFFFFF);
340 	ISC__BUFFER_PUTUINT16(b, valhi);
341 	ISC__BUFFER_PUTUINT32(b, vallo);
342 }
343 
344 void
isc__buffer_putmem(isc_buffer_t * b,const unsigned char * base,unsigned int length)345 isc__buffer_putmem(isc_buffer_t *b, const unsigned char *base,
346 		   unsigned int length) {
347 	ISC__BUFFER_PUTMEM(b, base, length);
348 }
349 
350 void
isc__buffer_putstr(isc_buffer_t * b,const char * source)351 isc__buffer_putstr(isc_buffer_t *b, const char *source) {
352 	ISC__BUFFER_PUTSTR(b, source);
353 }
354 
355 void
isc_buffer_putdecint(isc_buffer_t * b,int64_t v)356 isc_buffer_putdecint(isc_buffer_t *b, int64_t v) {
357 	unsigned int l = 0;
358 	unsigned char *cp;
359 	char buf[21];
360 	isc_result_t result;
361 
362 	REQUIRE(ISC_BUFFER_VALID(b));
363 
364 	/* xxxwpk do it more low-level way ? */
365 	l = snprintf(buf, 21, "%" PRId64, v);
366 	RUNTIME_CHECK(l <= 21);
367 	if (ISC_UNLIKELY(b->autore)) {
368 		result = isc_buffer_reserve(&b, l);
369 		REQUIRE(result == ISC_R_SUCCESS);
370 	}
371 	REQUIRE(isc_buffer_availablelength(b) >= l);
372 
373 	cp = isc_buffer_used(b);
374 	memmove(cp, buf, l);
375 	b->used += l;
376 }
377 
378 isc_result_t
isc_buffer_dup(isc_mem_t * mctx,isc_buffer_t ** dstp,const isc_buffer_t * src)379 isc_buffer_dup(isc_mem_t *mctx, isc_buffer_t **dstp, const isc_buffer_t *src) {
380 	isc_buffer_t *dst = NULL;
381 	isc_region_t region;
382 	isc_result_t result;
383 
384 	REQUIRE(dstp != NULL && *dstp == NULL);
385 	REQUIRE(ISC_BUFFER_VALID(src));
386 
387 	isc_buffer_usedregion(src, &region);
388 
389 	isc_buffer_allocate(mctx, &dst, region.length);
390 
391 	result = isc_buffer_copyregion(dst, &region);
392 	RUNTIME_CHECK(result == ISC_R_SUCCESS); /* NOSPACE is impossible */
393 	*dstp = dst;
394 	return (ISC_R_SUCCESS);
395 }
396 
397 isc_result_t
isc_buffer_copyregion(isc_buffer_t * b,const isc_region_t * r)398 isc_buffer_copyregion(isc_buffer_t *b, const isc_region_t *r) {
399 	isc_result_t result;
400 
401 	REQUIRE(ISC_BUFFER_VALID(b));
402 	REQUIRE(r != NULL);
403 
404 	if (ISC_UNLIKELY(b->autore)) {
405 		result = isc_buffer_reserve(&b, r->length);
406 		if (result != ISC_R_SUCCESS) {
407 			return (result);
408 		}
409 	}
410 
411 	if (r->length > isc_buffer_availablelength(b)) {
412 		return (ISC_R_NOSPACE);
413 	}
414 
415 	if (r->length > 0U) {
416 		memmove(isc_buffer_used(b), r->base, r->length);
417 		b->used += r->length;
418 	}
419 
420 	return (ISC_R_SUCCESS);
421 }
422 
423 void
isc_buffer_allocate(isc_mem_t * mctx,isc_buffer_t ** dynbuffer,unsigned int length)424 isc_buffer_allocate(isc_mem_t *mctx, isc_buffer_t **dynbuffer,
425 		    unsigned int length) {
426 	REQUIRE(dynbuffer != NULL && *dynbuffer == NULL);
427 
428 	isc_buffer_t *dbuf = isc_mem_get(mctx, sizeof(isc_buffer_t));
429 	unsigned char *bdata = isc_mem_get(mctx, length);
430 
431 	isc_buffer_init(dbuf, bdata, length);
432 
433 	ENSURE(ISC_BUFFER_VALID(dbuf));
434 
435 	dbuf->mctx = mctx;
436 
437 	*dynbuffer = dbuf;
438 }
439 
440 isc_result_t
isc_buffer_reserve(isc_buffer_t ** dynbuffer,unsigned int size)441 isc_buffer_reserve(isc_buffer_t **dynbuffer, unsigned int size) {
442 	unsigned char *bdata;
443 	uint64_t len;
444 
445 	REQUIRE(dynbuffer != NULL);
446 	REQUIRE(ISC_BUFFER_VALID(*dynbuffer));
447 
448 	len = (*dynbuffer)->length;
449 	if ((len - (*dynbuffer)->used) >= size) {
450 		return (ISC_R_SUCCESS);
451 	}
452 
453 	if ((*dynbuffer)->mctx == NULL) {
454 		return (ISC_R_NOSPACE);
455 	}
456 
457 	/* Round to nearest buffer size increment */
458 	len = size + (*dynbuffer)->used;
459 	len = (len + ISC_BUFFER_INCR - 1 - ((len - 1) % ISC_BUFFER_INCR));
460 
461 	/* Cap at UINT_MAX */
462 	if (len > UINT_MAX) {
463 		len = UINT_MAX;
464 	}
465 
466 	if ((len - (*dynbuffer)->used) < size) {
467 		return (ISC_R_NOMEMORY);
468 	}
469 
470 	/*
471 	 * XXXMUKS: This is far more expensive than plain realloc() as
472 	 * it doesn't remap pages, but does ordinary copy. So is
473 	 * isc_mem_reallocate(), which has additional issues.
474 	 */
475 	bdata = isc_mem_get((*dynbuffer)->mctx, (unsigned int)len);
476 
477 	memmove(bdata, (*dynbuffer)->base, (*dynbuffer)->length);
478 	isc_mem_put((*dynbuffer)->mctx, (*dynbuffer)->base,
479 		    (*dynbuffer)->length);
480 
481 	(*dynbuffer)->base = bdata;
482 	(*dynbuffer)->length = (unsigned int)len;
483 
484 	return (ISC_R_SUCCESS);
485 }
486 
487 void
isc_buffer_free(isc_buffer_t ** dynbuffer)488 isc_buffer_free(isc_buffer_t **dynbuffer) {
489 	isc_buffer_t *dbuf;
490 	isc_mem_t *mctx;
491 
492 	REQUIRE(dynbuffer != NULL);
493 	REQUIRE(ISC_BUFFER_VALID(*dynbuffer));
494 	REQUIRE((*dynbuffer)->mctx != NULL);
495 
496 	dbuf = *dynbuffer;
497 	*dynbuffer = NULL; /* destroy external reference */
498 	mctx = dbuf->mctx;
499 	dbuf->mctx = NULL;
500 
501 	isc_mem_put(mctx, dbuf->base, dbuf->length);
502 	isc_buffer_invalidate(dbuf);
503 	isc_mem_put(mctx, dbuf, sizeof(isc_buffer_t));
504 }
505 
506 isc_result_t
isc_buffer_printf(isc_buffer_t * b,const char * format,...)507 isc_buffer_printf(isc_buffer_t *b, const char *format, ...) {
508 	va_list ap;
509 	int n;
510 	isc_result_t result;
511 
512 	REQUIRE(ISC_BUFFER_VALID(b));
513 
514 	va_start(ap, format);
515 	n = vsnprintf(NULL, 0, format, ap);
516 	va_end(ap);
517 
518 	if (n < 0) {
519 		return (ISC_R_FAILURE);
520 	}
521 
522 	if (ISC_UNLIKELY(b->autore)) {
523 		result = isc_buffer_reserve(&b, n + 1);
524 		if (result != ISC_R_SUCCESS) {
525 			return (result);
526 		}
527 	}
528 
529 	if (isc_buffer_availablelength(b) < (unsigned int)n + 1) {
530 		return (ISC_R_NOSPACE);
531 	}
532 
533 	va_start(ap, format);
534 	n = vsnprintf(isc_buffer_used(b), n + 1, format, ap);
535 	va_end(ap);
536 
537 	if (n < 0) {
538 		return (ISC_R_FAILURE);
539 	}
540 
541 	b->used += n;
542 
543 	return (ISC_R_SUCCESS);
544 }
545