0e01259aff68416cc2c58441b1226713f4d217f2
[strongswan.git] / Source / charon / parser.c
1 /**
2 * @file parser.c
3 *
4 * @brief Generic parser class used to parse IKEv2-Header and Payload
5 *
6 */
7
8 /*
9 * Copyright (C) 2005 Jan Hutter, Martin Willi
10 * Hochschule fuer Technik Rapperswil
11 *
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2 of the License, or (at your
15 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * for more details.
21 */
22
23 #include <stdlib.h>
24 #include <arpa/inet.h>
25
26 #include "allocator.h"
27 #include "types.h"
28 #include "parser.h"
29 #include "logger.h"
30
31
32 typedef struct private_parser_context_s private_parser_context_t;
33
34 struct private_parser_context_s {
35 /**
36 * Public members
37 */
38 parser_context_t public;
39
40 /**
41 * Current bit for reading in input data
42 */
43 u_int8_t bit_pos;
44
45 /**
46 * Current byte for reading in input data
47 */
48 u_int8_t *byte_pos;
49
50 /**
51 * input data to parse
52 */
53 u_int8_t *input;
54
55 /**
56 * roof of input
57 */
58 u_int8_t *input_roof;
59
60
61 };
62
63 static status_t parser_context_destroy(private_parser_context_t *this)
64 {
65 allocator_free(this);
66
67 return SUCCESS;
68 }
69
70 static private_parser_context_t *parser_context_create(chunk_t input)
71 {
72 private_parser_context_t *this = allocator_alloc_thing(private_parser_context_t);
73 if (this == NULL)
74 {
75 return NULL;
76 }
77
78 this->public.destroy = (status_t(*)(parser_context_t*)) parser_context_destroy;
79
80 this->input = input.ptr;
81 this->byte_pos = input.ptr;
82 this->bit_pos = 0;
83 this->input_roof = input.ptr + input.len;
84
85 return this;
86 }
87
88
89
90 /**
91 * Private data of a parser_t object
92 */
93 typedef struct private_parser_s private_parser_t;
94
95 struct private_parser_s {
96 /**
97 * Public part of a generator object
98 */
99 parser_t public;
100
101 /**
102 * list of payloads and their description
103 */
104 payload_info_t **payload_infos;
105
106 /**
107 * logger object
108 */
109 logger_t *logger;
110
111
112 };
113
114 static private_parser_context_t *create_context(private_parser_t *this, chunk_t data)
115 {
116 private_parser_context_t *context = parser_context_create(data);
117
118 return context;
119 }
120
121 static status_t parse_payload(private_parser_t *this, private_parser_context_t *context, payload_type_t payload_type, void **data_struct)
122 {
123 payload_info_t *payload_info = NULL;
124
125 /* find payload in null terminated list*/
126 payload_info = *(this->payload_infos);
127 while (payload_info)
128 {
129 if (payload_info->payload_type == payload_type)
130 {
131 void *output;
132 int current;
133
134 /* ok, do the parsing */
135 output = allocator_alloc(payload_info->data_struct_length);
136
137 for (current = 0; current < payload_info->encoding_rules_count; current++)
138 {
139 encoding_rule_t *rule = &(payload_info->ecoding_rules[current]);
140 switch (rule->type)
141 {
142 case U_INT_4:
143 {
144 u_int8_t *output_pos = output + rule->offset;
145 if (context->byte_pos + sizeof(u_int8_t) > context->input_roof)
146 {
147 this->logger->log(this->logger, ERROR, "not enough input to parse U_INT_4");
148 allocator_free(output);
149 return PARSE_ERROR;
150 }
151 switch (context->bit_pos)
152 {
153 case 0:
154 *output_pos = *(context->byte_pos) >> 4;
155 context->bit_pos = 4;
156 break;
157 case 4:
158 *output_pos = *(context->byte_pos) & 0x0F;
159 context->bit_pos = 0;
160 context->byte_pos++;
161 break;
162 default:
163 this->logger->log(this->logger, ERROR, "found rule U_INT_4 on bitpos %d", context->bit_pos);
164 allocator_free(output);
165 return PARSE_ERROR;
166 }
167 break;
168 }
169 case U_INT_8:
170 {
171 u_int8_t *output_pos = output + rule->offset;
172 if (context->byte_pos + sizeof(u_int8_t) > context->input_roof)
173 {
174 this->logger->log(this->logger, ERROR, "not enough input to parse U_INT_8");
175 allocator_free(output);
176 return PARSE_ERROR;
177 }
178 if (context->bit_pos)
179 {
180 this->logger->log(this->logger, ERROR, "found rule U_INT_8 on bitpos %d", context->bit_pos);
181 allocator_free(output);
182 return PARSE_ERROR;
183 }
184
185 *output_pos = *(context->byte_pos);
186 context->byte_pos++;
187 break;
188 }
189 case U_INT_16:
190 {
191 u_int16_t *output_pos = output + rule->offset;
192 if (context->byte_pos + sizeof(u_int16_t) > context->input_roof)
193 {
194 this->logger->log(this->logger, ERROR, "not enough input to parse U_INT_16");
195 allocator_free(output);
196 return PARSE_ERROR;
197 }
198 if (context->bit_pos)
199 {
200 this->logger->log(this->logger, ERROR, "found rule U_INT_16 on bitpos %d", context->bit_pos);
201 allocator_free(output);
202 return PARSE_ERROR;
203 }
204 if ((int)context->byte_pos % 2)
205 {
206 this->logger->log(this->logger, ERROR, "found rule U_INT_16 on odd bytepos");
207 allocator_free(output);
208 return PARSE_ERROR;
209 }
210 *output_pos = ntohs(*((u_int16_t*)context->byte_pos));
211 context->byte_pos += 2;
212 break;
213 }
214 case U_INT_32:
215 {
216 u_int32_t *output_pos = output + rule->offset;
217 if (context->byte_pos + sizeof(u_int32_t) > context->input_roof)
218 {
219 this->logger->log(this->logger, ERROR, "not enough input to parse U_INT_32");
220 allocator_free(output);
221 return PARSE_ERROR;
222 }
223 if (context->bit_pos)
224 {
225 this->logger->log(this->logger, ERROR, "found rule U_INT_32 on bitpos %d", context->bit_pos);
226 allocator_free(output);
227 return PARSE_ERROR;
228 }
229 if ((int)context->byte_pos % 4)
230 {
231 this->logger->log(this->logger, ERROR, "found rule U_INT_32 on unaligned bytepos");
232 allocator_free(output);
233 return PARSE_ERROR;
234 }
235 *output_pos = ntohl(*((u_int32_t*)context->byte_pos));
236 context->byte_pos += 4;
237 break;
238 }
239 case U_INT_64:
240 {
241 u_int32_t *output_pos = output + rule->offset;
242 if (context->byte_pos + 2 * sizeof(u_int32_t) > context->input_roof)
243 {
244 this->logger->log(this->logger, ERROR, "not enough input to parse U_INT_64");
245 allocator_free(output);
246 return PARSE_ERROR;
247 }
248 if (context->bit_pos)
249 {
250 this->logger->log(this->logger, ERROR, "found rule U_INT_64 on bitpos %d", context->bit_pos);
251 allocator_free(output);
252 return PARSE_ERROR;
253 }
254 if ((int)context->byte_pos % 8)
255 {
256 this->logger->log(this->logger, ERROR, "found rule U_INT_64 on unaligned bytepos");
257 allocator_free(output);
258 return PARSE_ERROR;
259 }
260 /* assuming little endian host order */
261 *(output_pos + 1) = ntohl(*((u_int32_t*)context->byte_pos));
262 context->byte_pos += 4;
263 *output_pos = ntohl(*((u_int32_t*)context->byte_pos));
264 context->byte_pos += 4;
265
266 break;
267 }
268 case RESERVED_BIT:
269 {
270 if (context->byte_pos > context->input_roof)
271 {
272 this->logger->log(this->logger, ERROR, "not enough input to parse RESERVED_BIT");
273 allocator_free(output);
274 return PARSE_ERROR;
275 }
276 context->bit_pos = (context->bit_pos + 1) % 8;
277 if (context->bit_pos == 0)
278 {
279 context->byte_pos++;
280 }
281 break;
282 }
283 case RESERVED_BYTE:
284 {
285 if (context->byte_pos > context->input_roof)
286 {
287 this->logger->log(this->logger, ERROR, "not enough input to parse RESERVED_BYTE");
288 allocator_free(output);
289 return PARSE_ERROR;
290 }
291 if (context->bit_pos)
292 {
293 this->logger->log(this->logger, ERROR, "found rule RESERVED_BYTE on bitpos %d", context->bit_pos);
294 allocator_free(output);
295 return PARSE_ERROR;
296 }
297 context->byte_pos++;
298 break;
299 }
300 case FLAG:
301 {
302 bool *output_pos = output + rule->offset;
303 u_int8_t mask;
304 if (context->byte_pos > context->input_roof)
305 {
306 this->logger->log(this->logger, ERROR, "not enough input to parse FLAG");
307 allocator_free(output);
308 return PARSE_ERROR;
309 }
310 mask = 0x01 << (7 - context->bit_pos);
311 *output_pos = *context->byte_pos & mask;
312
313 if (*output_pos)
314 {
315 /* set to a "clean", comparable true */
316 *output_pos = TRUE;
317 }
318 context->bit_pos = (context->bit_pos + 1) % 8;
319 if (context->bit_pos == 0)
320 {
321 context->byte_pos++;
322 }
323 break;
324 }
325 case LENGTH:
326 {
327 u_int32_t *output_pos = output + rule->offset;
328 if (context->byte_pos + sizeof(u_int32_t) > context->input_roof)
329 {
330 this->logger->log(this->logger, ERROR, "not enough input to parse LENGTH");
331 allocator_free(output);
332 return PARSE_ERROR;
333 }
334 if (context->bit_pos)
335 {
336 this->logger->log(this->logger, ERROR, "found rule LENGTH on bitpos %d", context->bit_pos);
337 allocator_free(output);
338 return PARSE_ERROR;
339 }
340 if ((int)context->byte_pos % 4)
341 {
342 this->logger->log(this->logger, ERROR, "found rule LENGTH on unaligned bytepos");
343 allocator_free(output);
344 return PARSE_ERROR;
345 }
346 *output_pos = ntohl(*((u_int32_t*)context->byte_pos));
347 context->byte_pos += 4;
348 break;
349
350 }
351 case SPI_SIZE:
352 {
353
354 }
355 default:
356 {
357 this->logger->log(this->logger, ERROR, "parser found unknown type");
358 allocator_free(output);
359 return PARSE_ERROR;
360 }
361 }
362 }
363
364 *data_struct = output;
365 return SUCCESS;
366 }
367 payload_info++;
368 }
369
370 this->logger->log(this->logger, ERROR, "Payload not supported");
371 return NOT_SUPPORTED;
372 }
373
374 static status_t destroy(private_parser_t *this)
375 {
376 this->logger->destroy(this->logger);
377 allocator_free(this);
378
379 return SUCCESS;
380 }
381
382 parser_t *parser_create(payload_info_t **payload_infos)
383 {
384 private_parser_t *this = allocator_alloc_thing(private_parser_t);
385
386 if (this == NULL)
387 {
388 return NULL;
389 }
390
391 this->logger = logger_create("parser", ALL);
392 if (this->logger == NULL)
393 {
394 allocator_free(this);
395 return NULL;
396 }
397 this->public.create_context = (parser_context_t*(*)(parser_t*,chunk_t)) create_context;
398 this->public.parse_payload = (status_t(*)(parser_t*,parser_context_t*,payload_type_t,void**)) parse_payload;
399 this->public.destroy = (status_t(*)(parser_t*)) destroy;
400
401 this->payload_infos = payload_infos;
402
403
404 return (parser_t*)this;
405 }