vici: Fall back to heap buffer when vararg printing on stack fails
[strongswan.git] / src / libcharon / plugins / vici / vici_builder.c
1 /*
2 * Copyright (C) 2014 Martin Willi
3 * Copyright (C) 2014 revosec AG
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "vici_builder.h"
17
18 #include <bio/bio_writer.h>
19
20 typedef struct private_vici_builder_t private_vici_builder_t;
21
22 /**
23 * Private data of an vici_builder_t object.
24 */
25 struct private_vici_builder_t {
26
27 /**
28 * Public vici_builder_t interface.
29 */
30 vici_builder_t public;
31
32 /**
33 * Writer for elements
34 */
35 bio_writer_t *writer;
36
37 /**
38 * Errors encountered
39 */
40 u_int error;
41
42 /**
43 * Section nesting level
44 */
45 u_int section;
46
47 /**
48 * In list element?
49 */
50 bool list;
51 };
52
53 METHOD(vici_builder_t, add, void,
54 private_vici_builder_t *this, vici_type_t type, ...)
55 {
56 va_list args;
57 char *name = NULL;
58 chunk_t value = chunk_empty;
59
60 va_start(args, type);
61 switch (type)
62 {
63 case VICI_SECTION_END:
64 case VICI_LIST_END:
65 case VICI_END:
66 break;
67 case VICI_LIST_START:
68 case VICI_SECTION_START:
69 name = va_arg(args, char*);
70 break;
71 case VICI_KEY_VALUE:
72 name = va_arg(args, char*);
73 value = va_arg(args, chunk_t);
74 break;
75 case VICI_LIST_ITEM:
76 value = va_arg(args, chunk_t);
77 break;
78 default:
79 va_end(args);
80 this->error++;
81 return;
82 }
83 va_end(args);
84
85 if (value.len > 0xffff)
86 {
87 DBG1(DBG_ENC, "vici value exceeds size limit (%zu > %u)",
88 value.len, 0xffff);
89 this->error++;
90 return;
91 }
92 if (!vici_verify_type(type, this->section, this->list))
93 {
94 this->error++;
95 return;
96 }
97 if (type != VICI_END)
98 {
99 this->writer->write_uint8(this->writer, type);
100 }
101 switch (type)
102 {
103 case VICI_SECTION_START:
104 this->writer->write_data8(this->writer, chunk_from_str(name));
105 this->section++;
106 break;
107 case VICI_SECTION_END:
108 this->section--;
109 break;
110 case VICI_KEY_VALUE:
111 this->writer->write_data8(this->writer, chunk_from_str(name));
112 this->writer->write_data16(this->writer, value);
113 break;
114 case VICI_LIST_START:
115 this->writer->write_data8(this->writer, chunk_from_str(name));
116 this->list = TRUE;
117 break;
118 case VICI_LIST_ITEM:
119 this->writer->write_data16(this->writer, value);
120 break;
121 case VICI_LIST_END:
122 this->list = FALSE;
123 break;
124 default:
125 this->error++;
126 break;
127 }
128 }
129
130 /**
131 * Add a list item or a key/value, if key given
132 */
133 static void vadd_kv_or_li(private_vici_builder_t *this, char *key,
134 char *fmt, va_list args)
135 {
136 u_char buf[512];
137 chunk_t value;
138 ssize_t len;
139 va_list copy;
140
141 va_copy(copy, args);
142 len = vsnprintf(buf, sizeof(buf), fmt, copy);
143 va_end(copy);
144 if (len >= sizeof(buf))
145 {
146 value = chunk_alloc(len + 1);
147 len = vsnprintf(value.ptr, value.len, fmt, args);
148 }
149 else
150 {
151 value = chunk_create(buf, len);
152 }
153
154 if (len < 0)
155 {
156 DBG1(DBG_ENC, "vici builder format print failed");
157 this->error++;
158 }
159 else
160 {
161 if (key)
162 {
163 add(this, VICI_KEY_VALUE, key, value);
164 }
165 else
166 {
167 add(this, VICI_LIST_ITEM, value);
168 }
169 }
170 if (value.ptr != buf)
171 {
172 free(value.ptr);
173 }
174 }
175
176 METHOD(vici_builder_t, vadd_kv, void,
177 private_vici_builder_t *this, char *key, char *fmt, va_list args)
178 {
179 vadd_kv_or_li(this, key, fmt, args);
180 }
181
182 METHOD(vici_builder_t, add_kv, void,
183 private_vici_builder_t *this, char *key, char *fmt, ...)
184 {
185 va_list args;
186
187 va_start(args, fmt);
188 vadd_kv(this, key, fmt, args);
189 va_end(args);
190 }
191
192 METHOD(vici_builder_t, vadd_li, void,
193 private_vici_builder_t *this, char *fmt, va_list args)
194 {
195 vadd_kv_or_li(this, NULL, fmt, args);
196 }
197
198 METHOD(vici_builder_t, add_li, void,
199 private_vici_builder_t *this, char *fmt, ...)
200 {
201 va_list args;
202
203 va_start(args, fmt);
204 vadd_li(this, fmt, args);
205 va_end(args);
206 }
207
208 METHOD(vici_builder_t, begin_section, void,
209 private_vici_builder_t *this, char *name)
210 {
211 add(this, VICI_SECTION_START, name);
212 }
213
214 METHOD(vici_builder_t, end_section, void,
215 private_vici_builder_t *this)
216 {
217 add(this, VICI_SECTION_END);
218 }
219
220 METHOD(vici_builder_t, begin_list, void,
221 private_vici_builder_t *this, char *name)
222 {
223 add(this, VICI_LIST_START, name);
224 }
225
226 METHOD(vici_builder_t, end_list, void,
227 private_vici_builder_t *this)
228 {
229 add(this, VICI_LIST_END);
230 }
231
232 METHOD(vici_builder_t, destroy, void,
233 private_vici_builder_t *this)
234 {
235 this->writer->destroy(this->writer);
236 free(this);
237 }
238
239 METHOD(vici_builder_t, finalize, vici_message_t*,
240 private_vici_builder_t *this)
241 {
242 vici_message_t *product;
243
244 if (this->error || this->section || this->list)
245 {
246 DBG1(DBG_ENC, "vici builder error: %u errors (section: %u, list %u)",
247 this->error, this->section, this->list);
248 destroy(this);
249 return NULL;
250 }
251 product = vici_message_create_from_data(
252 this->writer->extract_buf(this->writer), TRUE);
253 destroy(this);
254 return product;
255 }
256
257 /**
258 * See header
259 */
260 vici_builder_t *vici_builder_create()
261 {
262 private_vici_builder_t *this;
263
264 INIT(this,
265 .public = {
266 .add = _add,
267 .add_kv = _add_kv,
268 .vadd_kv = _vadd_kv,
269 .add_li = _add_li,
270 .vadd_li = _vadd_li,
271 .begin_section = _begin_section,
272 .end_section = _end_section,
273 .begin_list = _begin_list,
274 .end_list = _end_list,
275 .finalize = _finalize,
276 .destroy = _destroy,
277 },
278 .writer = bio_writer_create(0),
279 );
280
281 return &this->public;
282 }