- documented
[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 * @private data stored in a context
33 *
34 * contains pointers and counters to store current state
35 */
36 typedef struct private_parser_context_s private_parser_context_t;
37
38 struct private_parser_context_s {
39 /**
40 * Public members
41 */
42 parser_context_t public;
43
44 /**
45 * Current bit for reading in input data
46 */
47 u_int8_t bit_pos;
48
49 /**
50 * Current byte for reading in input data
51 */
52 u_int8_t *byte_pos;
53
54 /**
55 * input data to parse
56 */
57 u_int8_t *input;
58
59 /**
60 * roof of input
61 */
62 u_int8_t *input_roof;
63
64
65 };
66
67 /**
68 * implementation of parser_context_t.destroy
69 */
70 static status_t parser_context_destroy(private_parser_context_t *this)
71 {
72 allocator_free(this);
73
74 return SUCCESS;
75 }
76
77
78 /**
79 * @brief Private data of a parser_t object
80 */
81 typedef struct private_parser_s private_parser_t;
82
83 struct private_parser_s {
84 /**
85 * Public part of a generator object
86 */
87 parser_t public;
88
89 /**
90 * list of payloads and their description
91 */
92 payload_info_t **payload_infos;
93
94 /**
95 * logger object
96 */
97 logger_t *logger;
98 };
99
100 /**
101 * implementation of parser_t.create_context
102 */
103 static private_parser_context_t *create_context(private_parser_t *this, chunk_t data)
104 {
105 private_parser_context_t *context = allocator_alloc_thing(private_parser_context_t);
106 if (this == NULL)
107 {
108 return NULL;
109 }
110
111 context->public.destroy = (status_t(*)(parser_context_t*)) parser_context_destroy;
112
113 context->input = data.ptr;
114 context->byte_pos = data.ptr;
115 context->bit_pos = 0;
116 context->input_roof = data.ptr + data.len;
117
118 return context;
119 }
120
121 /**
122 * implementation of parser_context_t.parse_payload
123 */
124 static status_t parse_payload(private_parser_t *this, payload_type_t payload_type, void **data_struct, private_parser_context_t *context)
125 {
126 payload_info_t *payload_info = NULL;
127
128 /* find payload in null terminated list*/
129 payload_info = *(this->payload_infos);
130 while (payload_info)
131 {
132 if (payload_info->payload_type == payload_type)
133 {
134 void *output;
135 int current;
136
137 /* ok, do the parsing */
138 output = allocator_alloc(payload_info->data_struct_length);
139
140 for (current = 0; current < payload_info->encoding_rules_count; current++)
141 {
142 encoding_rule_t *rule = &(payload_info->ecoding_rules[current]);
143 switch (rule->type)
144 {
145 case U_INT_4:
146 {
147 u_int8_t *output_pos = output + rule->offset;
148 if (context->byte_pos + sizeof(u_int8_t) > context->input_roof)
149 {
150 this->logger->log(this->logger, ERROR, "not enough input to parse U_INT_4");
151 allocator_free(output);
152 return PARSE_ERROR;
153 }
154 switch (context->bit_pos)
155 {
156 case 0:
157 *output_pos = *(context->byte_pos) >> 4;
158 context->bit_pos = 4;
159 break;
160 case 4:
161 *output_pos = *(context->byte_pos) & 0x0F;
162 context->bit_pos = 0;
163 context->byte_pos++;
164 break;
165 default:
166 this->logger->log(this->logger, ERROR, "found rule U_INT_4 on bitpos %d", context->bit_pos);
167 allocator_free(output);
168 return PARSE_ERROR;
169 }
170 break;
171 }
172 case U_INT_8:
173 {
174 u_int8_t *output_pos = output + rule->offset;
175 if (context->byte_pos + sizeof(u_int8_t) > context->input_roof)
176 {
177 this->logger->log(this->logger, ERROR, "not enough input to parse U_INT_8");
178 allocator_free(output);
179 return PARSE_ERROR;
180 }
181 if (context->bit_pos)
182 {
183 this->logger->log(this->logger, ERROR, "found rule U_INT_8 on bitpos %d", context->bit_pos);
184 allocator_free(output);
185 return PARSE_ERROR;
186 }
187
188 *output_pos = *(context->byte_pos);
189 context->byte_pos++;
190 break;
191 }
192 case U_INT_16:
193 {
194 u_int16_t *output_pos = output + rule->offset;
195 if (context->byte_pos + sizeof(u_int16_t) > context->input_roof)
196 {
197 this->logger->log(this->logger, ERROR, "not enough input to parse U_INT_16");
198 allocator_free(output);
199 return PARSE_ERROR;
200 }
201 if (context->bit_pos)
202 {
203 this->logger->log(this->logger, ERROR, "found rule U_INT_16 on bitpos %d", context->bit_pos);
204 allocator_free(output);
205 return PARSE_ERROR;
206 }
207 if ((int)context->byte_pos % 2)
208 {
209 this->logger->log(this->logger, ERROR, "found rule U_INT_16 on odd bytepos");
210 allocator_free(output);
211 return PARSE_ERROR;
212 }
213 *output_pos = ntohs(*((u_int16_t*)context->byte_pos));
214 context->byte_pos += 2;
215 break;
216 }
217 case U_INT_32:
218 {
219 u_int32_t *output_pos = output + rule->offset;
220 if (context->byte_pos + sizeof(u_int32_t) > context->input_roof)
221 {
222 this->logger->log(this->logger, ERROR, "not enough input to parse U_INT_32");
223 allocator_free(output);
224 return PARSE_ERROR;
225 }
226 if (context->bit_pos)
227 {
228 this->logger->log(this->logger, ERROR, "found rule U_INT_32 on bitpos %d", context->bit_pos);
229 allocator_free(output);
230 return PARSE_ERROR;
231 }
232 if ((int)context->byte_pos % 4)
233 {
234 this->logger->log(this->logger, ERROR, "found rule U_INT_32 on unaligned bytepos");
235 allocator_free(output);
236 return PARSE_ERROR;
237 }
238 *output_pos = ntohl(*((u_int32_t*)context->byte_pos));
239 context->byte_pos += 4;
240 break;
241 }
242 case U_INT_64:
243 {
244 u_int32_t *output_pos = output + rule->offset;
245 if (context->byte_pos + 2 * sizeof(u_int32_t) > context->input_roof)
246 {
247 this->logger->log(this->logger, ERROR, "not enough input to parse U_INT_64");
248 allocator_free(output);
249 return PARSE_ERROR;
250 }
251 if (context->bit_pos)
252 {
253 this->logger->log(this->logger, ERROR, "found rule U_INT_64 on bitpos %d", context->bit_pos);
254 allocator_free(output);
255 return PARSE_ERROR;
256 }
257 if ((int)context->byte_pos % 8)
258 {
259 this->logger->log(this->logger, ERROR, "found rule U_INT_64 on unaligned bytepos");
260 allocator_free(output);
261 return PARSE_ERROR;
262 }
263 /* assuming little endian host order */
264 *(output_pos + 1) = ntohl(*((u_int32_t*)context->byte_pos));
265 context->byte_pos += 4;
266 *output_pos = ntohl(*((u_int32_t*)context->byte_pos));
267 context->byte_pos += 4;
268
269 break;
270 }
271 case RESERVED_BIT:
272 {
273 if (context->byte_pos > context->input_roof)
274 {
275 this->logger->log(this->logger, ERROR, "not enough input to parse RESERVED_BIT");
276 allocator_free(output);
277 return PARSE_ERROR;
278 }
279 context->bit_pos = (context->bit_pos + 1) % 8;
280 if (context->bit_pos == 0)
281 {
282 context->byte_pos++;
283 }
284 break;
285 }
286 case RESERVED_BYTE:
287 {
288 if (context->byte_pos > context->input_roof)
289 {
290 this->logger->log(this->logger, ERROR, "not enough input to parse RESERVED_BYTE");
291 allocator_free(output);
292 return PARSE_ERROR;
293 }
294 if (context->bit_pos)
295 {
296 this->logger->log(this->logger, ERROR, "found rule RESERVED_BYTE on bitpos %d", context->bit_pos);
297 allocator_free(output);
298 return PARSE_ERROR;
299 }
300 context->byte_pos++;
301 break;
302 }
303 case FLAG:
304 {
305 bool *output_pos = output + rule->offset;
306 u_int8_t mask;
307 if (context->byte_pos > context->input_roof)
308 {
309 this->logger->log(this->logger, ERROR, "not enough input to parse FLAG");
310 allocator_free(output);
311 return PARSE_ERROR;
312 }
313 mask = 0x01 << (7 - context->bit_pos);
314 *output_pos = *context->byte_pos & mask;
315
316 if (*output_pos)
317 {
318 /* set to a "clean", comparable true */
319 *output_pos = TRUE;
320 }
321 context->bit_pos = (context->bit_pos + 1) % 8;
322 if (context->bit_pos == 0)
323 {
324 context->byte_pos++;
325 }
326 break;
327 }
328 case LENGTH:
329 {
330 u_int32_t *output_pos = output + rule->offset;
331 if (context->byte_pos + sizeof(u_int32_t) > context->input_roof)
332 {
333 this->logger->log(this->logger, ERROR, "not enough input to parse LENGTH");
334 allocator_free(output);
335 return PARSE_ERROR;
336 }
337 if (context->bit_pos)
338 {
339 this->logger->log(this->logger, ERROR, "found rule LENGTH on bitpos %d", context->bit_pos);
340 allocator_free(output);
341 return PARSE_ERROR;
342 }
343 if ((int)context->byte_pos % 4)
344 {
345 this->logger->log(this->logger, ERROR, "found rule LENGTH on unaligned bytepos");
346 allocator_free(output);
347 return PARSE_ERROR;
348 }
349 *output_pos = ntohl(*((u_int32_t*)context->byte_pos));
350 context->byte_pos += 4;
351 break;
352
353 }
354 case SPI_SIZE:
355 {
356
357 }
358 default:
359 {
360 this->logger->log(this->logger, ERROR, "parser found unknown type");
361 allocator_free(output);
362 return PARSE_ERROR;
363 }
364 }
365 }
366
367 *data_struct = output;
368 return SUCCESS;
369 }
370 payload_info++;
371 }
372
373 this->logger->log(this->logger, ERROR, "Payload not supported");
374 return NOT_SUPPORTED;
375 }
376
377 /**
378 * implementation of parser_t.destroy
379 */
380 static status_t destroy(private_parser_t *this)
381 {
382 this->logger->destroy(this->logger);
383 allocator_free(this);
384
385 return SUCCESS;
386 }
387
388 /*
389 * see header file
390 */
391 parser_t *parser_create(payload_info_t **payload_infos)
392 {
393 private_parser_t *this = allocator_alloc_thing(private_parser_t);
394
395 if (this == NULL)
396 {
397 return NULL;
398 }
399
400 this->logger = logger_create("parser", ALL);
401 if (this->logger == NULL)
402 {
403 allocator_free(this);
404 return NULL;
405 }
406 this->public.create_context = (parser_context_t*(*)(parser_t*,chunk_t)) create_context;
407 this->public.parse_payload = (status_t(*)(parser_t*,payload_type_t,void**,parser_context_t*)) parse_payload;
408 this->public.destroy = (status_t(*)(parser_t*)) destroy;
409
410 this->payload_infos = payload_infos;
411
412
413 return (parser_t*)this;
414 }