1/*
2 * Dynamic data buffer
3 * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
4 *
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
7 */
8
9#include "includes.h"
10
11#include "common.h"
12#include "trace.h"
13#include "wpabuf.h"
14
15#ifdef WPA_TRACE
16#define WPABUF_MAGIC 0x51a974e3
17
18struct wpabuf_trace {
19	unsigned int magic;
20};
21
22static struct wpabuf_trace * wpabuf_get_trace(const struct wpabuf *buf)
23{
24	return (struct wpabuf_trace *)
25		((const u8 *) buf - sizeof(struct wpabuf_trace));
26}
27#endif /* WPA_TRACE */
28
29
30static void wpabuf_overflow(const struct wpabuf *buf, size_t len)
31{
32#ifdef WPA_TRACE
33	struct wpabuf_trace *trace = wpabuf_get_trace(buf);
34	if (trace->magic != WPABUF_MAGIC) {
35		wpa_printf(MSG_ERROR, "wpabuf: invalid magic %x",
36			   trace->magic);
37	}
38#endif /* WPA_TRACE */
39	wpa_printf(MSG_ERROR, "wpabuf %p (size=%lu used=%lu) overflow len=%lu",
40		   buf, (unsigned long) buf->size, (unsigned long) buf->used,
41		   (unsigned long) len);
42	wpa_trace_show("wpabuf overflow");
43	abort();
44}
45
46
47int wpabuf_resize(struct wpabuf **_buf, size_t add_len)
48{
49	struct wpabuf *buf = *_buf;
50#ifdef WPA_TRACE
51	struct wpabuf_trace *trace;
52#endif /* WPA_TRACE */
53
54	if (buf == NULL) {
55		*_buf = wpabuf_alloc(add_len);
56		return *_buf == NULL ? -1 : 0;
57	}
58
59#ifdef WPA_TRACE
60	trace = wpabuf_get_trace(buf);
61	if (trace->magic != WPABUF_MAGIC) {
62		wpa_printf(MSG_ERROR, "wpabuf: invalid magic %x",
63			   trace->magic);
64		wpa_trace_show("wpabuf_resize invalid magic");
65		abort();
66	}
67#endif /* WPA_TRACE */
68
69	if (buf->used + add_len > buf->size) {
70		unsigned char *nbuf;
71		if (buf->ext_data) {
72			nbuf = os_realloc(buf->ext_data, buf->used + add_len);
73			if (nbuf == NULL)
74				return -1;
75			os_memset(nbuf + buf->used, 0, add_len);
76			buf->ext_data = nbuf;
77		} else {
78#ifdef WPA_TRACE
79			nbuf = os_realloc(trace, sizeof(struct wpabuf_trace) +
80					  sizeof(struct wpabuf) +
81					  buf->used + add_len);
82			if (nbuf == NULL)
83				return -1;
84			trace = (struct wpabuf_trace *) nbuf;
85			buf = (struct wpabuf *) (trace + 1);
86			os_memset(nbuf + sizeof(struct wpabuf_trace) +
87				  sizeof(struct wpabuf) + buf->used, 0,
88				  add_len);
89#else /* WPA_TRACE */
90			nbuf = os_realloc(buf, sizeof(struct wpabuf) +
91					  buf->used + add_len);
92			if (nbuf == NULL)
93				return -1;
94			buf = (struct wpabuf *) nbuf;
95			os_memset(nbuf + sizeof(struct wpabuf) + buf->used, 0,
96				  add_len);
97#endif /* WPA_TRACE */
98			*_buf = buf;
99		}
100		buf->size = buf->used + add_len;
101	}
102
103	return 0;
104}
105
106
107/**
108 * wpabuf_alloc - Allocate a wpabuf of the given size
109 * @len: Length for the allocated buffer
110 * Returns: Buffer to the allocated wpabuf or %NULL on failure
111 */
112struct wpabuf * wpabuf_alloc(size_t len)
113{
114#ifdef WPA_TRACE
115	struct wpabuf_trace *trace = os_zalloc(sizeof(struct wpabuf_trace) +
116					       sizeof(struct wpabuf) + len);
117	struct wpabuf *buf;
118	if (trace == NULL)
119		return NULL;
120	trace->magic = WPABUF_MAGIC;
121	buf = (struct wpabuf *) (trace + 1);
122#else /* WPA_TRACE */
123	struct wpabuf *buf = os_zalloc(sizeof(struct wpabuf) + len);
124	if (buf == NULL)
125		return NULL;
126#endif /* WPA_TRACE */
127
128	buf->size = len;
129	return buf;
130}
131
132
133struct wpabuf * wpabuf_alloc_ext_data(u8 *data, size_t len)
134{
135#ifdef WPA_TRACE
136	struct wpabuf_trace *trace = os_zalloc(sizeof(struct wpabuf_trace) +
137					       sizeof(struct wpabuf));
138	struct wpabuf *buf;
139	if (trace == NULL)
140		return NULL;
141	trace->magic = WPABUF_MAGIC;
142	buf = (struct wpabuf *) (trace + 1);
143#else /* WPA_TRACE */
144	struct wpabuf *buf = os_zalloc(sizeof(struct wpabuf));
145	if (buf == NULL)
146		return NULL;
147#endif /* WPA_TRACE */
148
149	buf->size = len;
150	buf->used = len;
151	buf->ext_data = data;
152
153	return buf;
154}
155
156
157struct wpabuf * wpabuf_alloc_copy(const void *data, size_t len)
158{
159	struct wpabuf *buf = wpabuf_alloc(len);
160	if (buf)
161		wpabuf_put_data(buf, data, len);
162	return buf;
163}
164
165
166struct wpabuf * wpabuf_dup(const struct wpabuf *src)
167{
168	struct wpabuf *buf = wpabuf_alloc(wpabuf_len(src));
169	if (buf)
170		wpabuf_put_data(buf, wpabuf_head(src), wpabuf_len(src));
171	return buf;
172}
173
174
175/**
176 * wpabuf_free - Free a wpabuf
177 * @buf: wpabuf buffer
178 */
179void wpabuf_free(struct wpabuf *buf)
180{
181#ifdef WPA_TRACE
182	struct wpabuf_trace *trace;
183	if (buf == NULL)
184		return;
185	trace = wpabuf_get_trace(buf);
186	if (trace->magic != WPABUF_MAGIC) {
187		wpa_printf(MSG_ERROR, "wpabuf_free: invalid magic %x",
188			   trace->magic);
189		wpa_trace_show("wpabuf_free magic mismatch");
190		abort();
191	}
192	os_free(buf->ext_data);
193	os_free(trace);
194#else /* WPA_TRACE */
195	if (buf == NULL)
196		return;
197	os_free(buf->ext_data);
198	os_free(buf);
199#endif /* WPA_TRACE */
200}
201
202
203void * wpabuf_put(struct wpabuf *buf, size_t len)
204{
205	void *tmp = wpabuf_mhead_u8(buf) + wpabuf_len(buf);
206	buf->used += len;
207	if (buf->used > buf->size) {
208		wpabuf_overflow(buf, len);
209	}
210	return tmp;
211}
212
213
214/**
215 * wpabuf_concat - Concatenate two buffers into a newly allocated one
216 * @a: First buffer
217 * @b: Second buffer
218 * Returns: wpabuf with concatenated a + b data or %NULL on failure
219 *
220 * Both buffers a and b will be freed regardless of the return value. Input
221 * buffers can be %NULL which is interpreted as an empty buffer.
222 */
223struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b)
224{
225	struct wpabuf *n = NULL;
226	size_t len = 0;
227
228	if (b == NULL)
229		return a;
230
231	if (a)
232		len += wpabuf_len(a);
233	if (b)
234		len += wpabuf_len(b);
235
236	n = wpabuf_alloc(len);
237	if (n) {
238		if (a)
239			wpabuf_put_buf(n, a);
240		if (b)
241			wpabuf_put_buf(n, b);
242	}
243
244	wpabuf_free(a);
245	wpabuf_free(b);
246
247	return n;
248}
249
250
251/**
252 * wpabuf_zeropad - Pad buffer with 0x00 octets (prefix) to specified length
253 * @buf: Buffer to be padded
254 * @len: Length for the padded buffer
255 * Returns: wpabuf padded to len octets or %NULL on failure
256 *
257 * If buf is longer than len octets or of same size, it will be returned as-is.
258 * Otherwise a new buffer is allocated and prefixed with 0x00 octets followed
259 * by the source data. The source buffer will be freed on error, i.e., caller
260 * will only be responsible on freeing the returned buffer. If buf is %NULL,
261 * %NULL will be returned.
262 */
263struct wpabuf * wpabuf_zeropad(struct wpabuf *buf, size_t len)
264{
265	struct wpabuf *ret;
266	size_t blen;
267
268	if (buf == NULL)
269		return NULL;
270
271	blen = wpabuf_len(buf);
272	if (blen >= len)
273		return buf;
274
275	ret = wpabuf_alloc(len);
276	if (ret) {
277		os_memset(wpabuf_put(ret, len - blen), 0, len - blen);
278		wpabuf_put_buf(ret, buf);
279	}
280	wpabuf_free(buf);
281
282	return ret;
283}
284
285
286void wpabuf_printf(struct wpabuf *buf, char *fmt, ...)
287{
288	va_list ap;
289	void *tmp = wpabuf_mhead_u8(buf) + wpabuf_len(buf);
290	int res;
291
292	va_start(ap, fmt);
293	res = vsnprintf(tmp, buf->size - buf->used, fmt, ap);
294	va_end(ap);
295	if (res < 0 || (size_t) res >= buf->size - buf->used)
296		wpabuf_overflow(buf, res);
297	buf->used += res;
298}
299