Line data Source code
1 : #include <errno.h>
2 : #include <stdbool.h>
3 : #include <stdio.h>
4 : #include <string.h>
5 :
6 : #include "alloc-util.h"
7 : #include "dhcp-internal.h"
8 : #include "dhcp-protocol.h"
9 : #include "macro.h"
10 : #include "memory-util.h"
11 :
12 : struct option_desc {
13 : uint8_t sname[64];
14 : int snamelen;
15 : uint8_t file[128];
16 : int filelen;
17 : uint8_t options[128];
18 : int len;
19 : bool success;
20 : int filepos;
21 : int snamepos;
22 : int pos;
23 : };
24 :
25 : static bool verbose = false;
26 :
27 : static struct option_desc option_tests[] = {
28 : { {}, 0, {}, 0, { 42, 5, 65, 66, 67, 68, 69 }, 7, false, },
29 : { {}, 0, {}, 0, { 42, 5, 65, 66, 67, 68, 69, 0, 0,
30 : SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 12, true, },
31 : { {}, 0, {}, 0, { 8, 255, 70, 71, 72 }, 5, false, },
32 : { {}, 0, {}, 0, { 0x35, 0x01, 0x05, 0x36, 0x04, 0x01, 0x00, 0xa8,
33 : 0xc0, 0x33, 0x04, 0x00, 0x01, 0x51, 0x80, 0x01,
34 : 0x04, 0xff, 0xff, 0xff, 0x00, 0x03, 0x04, 0xc0,
35 : 0xa8, 0x00, 0x01, 0x06, 0x04, 0xc0, 0xa8, 0x00,
36 : 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
37 : 40, true, },
38 : { {}, 0, {}, 0, { SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_OFFER,
39 : 42, 3, 0, 0, 0 }, 8, true, },
40 : { {}, 0, {}, 0, { 42, 2, 1, 2, 44 }, 5, false, },
41 :
42 : { {}, 0,
43 : { 222, 3, 1, 2, 3, SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_NAK }, 8,
44 : { SD_DHCP_OPTION_OVERLOAD, 1, DHCP_OVERLOAD_FILE }, 3, true, },
45 :
46 : { { 1, 4, 1, 2, 3, 4, SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 9,
47 : { 222, 3, 1, 2, 3 }, 5,
48 : { SD_DHCP_OPTION_OVERLOAD, 1,
49 : DHCP_OVERLOAD_FILE|DHCP_OVERLOAD_SNAME }, 3, true, },
50 : };
51 :
52 0 : static const char *dhcp_type(int type) {
53 0 : switch(type) {
54 0 : case DHCP_DISCOVER:
55 0 : return "DHCPDISCOVER";
56 0 : case DHCP_OFFER:
57 0 : return "DHCPOFFER";
58 0 : case DHCP_REQUEST:
59 0 : return "DHCPREQUEST";
60 0 : case DHCP_DECLINE:
61 0 : return "DHCPDECLINE";
62 0 : case DHCP_ACK:
63 0 : return "DHCPACK";
64 0 : case DHCP_NAK:
65 0 : return "DHCPNAK";
66 0 : case DHCP_RELEASE:
67 0 : return "DHCPRELEASE";
68 0 : default:
69 0 : return "unknown";
70 : }
71 : }
72 :
73 1 : static void test_invalid_buffer_length(void) {
74 : DHCPMessage message;
75 :
76 1 : assert_se(dhcp_option_parse(&message, 0, NULL, NULL, NULL) == -EINVAL);
77 1 : assert_se(dhcp_option_parse(&message, sizeof(DHCPMessage) - 1, NULL, NULL, NULL) == -EINVAL);
78 1 : }
79 :
80 1 : static void test_message_init(void) {
81 1 : _cleanup_free_ DHCPMessage *message = NULL;
82 1 : size_t optlen = 4, optoffset;
83 1 : size_t len = sizeof(DHCPMessage) + optlen;
84 : uint8_t *magic;
85 :
86 1 : message = malloc0(len);
87 :
88 1 : assert_se(dhcp_message_init(message, BOOTREQUEST, 0x12345678,
89 : DHCP_DISCOVER, ARPHRD_ETHER, optlen, &optoffset) >= 0);
90 :
91 1 : assert_se(message->xid == htobe32(0x12345678));
92 1 : assert_se(message->op == BOOTREQUEST);
93 :
94 1 : magic = (uint8_t*)&message->magic;
95 :
96 1 : assert_se(magic[0] == 99);
97 1 : assert_se(magic[1] == 130);
98 1 : assert_se(magic[2] == 83);
99 1 : assert_se(magic[3] == 99);
100 :
101 1 : assert_se(dhcp_option_parse(message, len, NULL, NULL, NULL) >= 0);
102 1 : }
103 :
104 9 : static DHCPMessage *create_message(uint8_t *options, uint16_t optlen,
105 : uint8_t *file, uint8_t filelen,
106 : uint8_t *sname, uint8_t snamelen) {
107 : DHCPMessage *message;
108 9 : size_t len = sizeof(DHCPMessage) + optlen;
109 :
110 9 : message = malloc0(len);
111 9 : assert_se(message);
112 :
113 9 : memcpy_safe(&message->options, options, optlen);
114 9 : memcpy_safe(&message->file, file, filelen);
115 9 : memcpy_safe(&message->sname, sname, snamelen);
116 :
117 9 : return message;
118 : }
119 :
120 26 : static void test_ignore_opts(uint8_t *descoption, int *descpos, int *desclen) {
121 26 : assert(*descpos >= 0);
122 :
123 42 : while (*descpos < *desclen) {
124 33 : switch(descoption[*descpos]) {
125 9 : case SD_DHCP_OPTION_PAD:
126 9 : *descpos += 1;
127 9 : break;
128 :
129 7 : case SD_DHCP_OPTION_MESSAGE_TYPE:
130 : case SD_DHCP_OPTION_OVERLOAD:
131 7 : *descpos += 3;
132 7 : break;
133 :
134 17 : default:
135 17 : return;
136 : }
137 : }
138 : }
139 :
140 12 : static int test_options_cb(uint8_t code, uint8_t len, const void *option, void *userdata) {
141 12 : struct option_desc *desc = userdata;
142 12 : uint8_t *descoption = NULL;
143 12 : int *desclen = NULL, *descpos = NULL;
144 12 : uint8_t optcode = 0;
145 12 : uint8_t optlen = 0;
146 : uint8_t i;
147 :
148 12 : assert_se((!desc && !code && !len) || desc);
149 :
150 12 : if (!desc)
151 0 : return -EINVAL;
152 :
153 12 : assert_se(code != SD_DHCP_OPTION_PAD);
154 12 : assert_se(code != SD_DHCP_OPTION_END);
155 12 : assert_se(code != SD_DHCP_OPTION_MESSAGE_TYPE);
156 12 : assert_se(code != SD_DHCP_OPTION_OVERLOAD);
157 :
158 14 : while (desc->pos >= 0 || desc->filepos >= 0 || desc->snamepos >= 0) {
159 :
160 14 : if (desc->pos >= 0) {
161 11 : descoption = &desc->options[0];
162 11 : desclen = &desc->len;
163 11 : descpos = &desc->pos;
164 3 : } else if (desc->filepos >= 0) {
165 2 : descoption = &desc->file[0];
166 2 : desclen = &desc->filelen;
167 2 : descpos = &desc->filepos;
168 1 : } else if (desc->snamepos >= 0) {
169 1 : descoption = &desc->sname[0];
170 1 : desclen = &desc->snamelen;
171 1 : descpos = &desc->snamepos;
172 : }
173 :
174 14 : assert_se(descoption && desclen && descpos);
175 :
176 14 : if (*desclen)
177 14 : test_ignore_opts(descoption, descpos, desclen);
178 :
179 14 : if (*descpos < *desclen)
180 12 : break;
181 :
182 2 : if (*descpos == *desclen)
183 2 : *descpos = -1;
184 : }
185 :
186 12 : assert_se(descpos);
187 12 : assert_se(*descpos != -1);
188 :
189 12 : optcode = descoption[*descpos];
190 12 : optlen = descoption[*descpos + 1];
191 :
192 12 : if (verbose)
193 0 : printf("DHCP code %2d(%2d) len %2d(%2d) ", code, optcode,
194 : len, optlen);
195 :
196 12 : assert_se(code == optcode);
197 12 : assert_se(len == optlen);
198 :
199 57 : for (i = 0; i < len; i++) {
200 :
201 45 : if (verbose)
202 0 : printf("0x%02x(0x%02x) ", ((uint8_t*) option)[i],
203 0 : descoption[*descpos + 2 + i]);
204 :
205 45 : assert_se(((uint8_t*) option)[i] == descoption[*descpos + 2 + i]);
206 : }
207 :
208 12 : if (verbose)
209 0 : printf("\n");
210 :
211 12 : *descpos += optlen + 2;
212 :
213 12 : test_ignore_opts(descoption, descpos, desclen);
214 :
215 12 : if (desc->pos != -1 && desc->pos == desc->len)
216 4 : desc->pos = -1;
217 :
218 12 : if (desc->filepos != -1 && desc->filepos == desc->filelen)
219 2 : desc->filepos = -1;
220 :
221 12 : if (desc->snamepos != -1 && desc->snamepos == desc->snamelen)
222 1 : desc->snamepos = -1;
223 :
224 12 : return 0;
225 : }
226 :
227 9 : static void test_options(struct option_desc *desc) {
228 9 : uint8_t *options = NULL;
229 9 : uint8_t *file = NULL;
230 9 : uint8_t *sname = NULL;
231 9 : int optlen = 0;
232 9 : int filelen = 0;
233 9 : int snamelen = 0;
234 9 : int buflen = 0;
235 9 : _cleanup_free_ DHCPMessage *message = NULL;
236 : int res;
237 :
238 9 : if (desc) {
239 8 : file = &desc->file[0];
240 8 : filelen = desc->filelen;
241 8 : if (!filelen)
242 6 : desc->filepos = -1;
243 :
244 8 : sname = &desc->sname[0];
245 8 : snamelen = desc->snamelen;
246 8 : if (!snamelen)
247 7 : desc->snamepos = -1;
248 :
249 8 : options = &desc->options[0];
250 8 : optlen = desc->len;
251 8 : desc->pos = 0;
252 : }
253 9 : message = create_message(options, optlen, file, filelen,
254 : sname, snamelen);
255 :
256 9 : buflen = sizeof(DHCPMessage) + optlen;
257 :
258 9 : if (!desc) {
259 1 : assert_se((res = dhcp_option_parse(message, buflen, test_options_cb, NULL, NULL)) == -ENOMSG);
260 8 : } else if (desc->success) {
261 5 : assert_se((res = dhcp_option_parse(message, buflen, test_options_cb, desc, NULL)) >= 0);
262 5 : assert_se(desc->pos == -1 && desc->filepos == -1 && desc->snamepos == -1);
263 : } else
264 3 : assert_se((res = dhcp_option_parse(message, buflen, test_options_cb, desc, NULL)) < 0);
265 :
266 9 : if (verbose)
267 0 : printf("DHCP type %s\n", dhcp_type(res));
268 9 : }
269 :
270 : static uint8_t options[64] = {
271 : 'A', 'B', 'C', 'D',
272 : 160, 2, 0x11, 0x12,
273 : 0,
274 : 31, 8, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
275 : 0,
276 : 55, 3, 0x51, 0x52, 0x53,
277 : 17, 7, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
278 : 255
279 : };
280 :
281 1 : static void test_option_set(void) {
282 1 : _cleanup_free_ DHCPMessage *result = NULL;
283 1 : size_t offset = 0, len, pos;
284 : unsigned i;
285 :
286 1 : result = malloc0(sizeof(DHCPMessage) + 11);
287 1 : assert_se(result);
288 :
289 1 : result->options[0] = 'A';
290 1 : result->options[1] = 'B';
291 1 : result->options[2] = 'C';
292 1 : result->options[3] = 'D';
293 :
294 1 : assert_se(dhcp_option_append(result, 0, &offset, 0, SD_DHCP_OPTION_PAD,
295 : 0, NULL) == -ENOBUFS);
296 1 : assert_se(offset == 0);
297 :
298 1 : offset = 4;
299 1 : assert_se(dhcp_option_append(result, 5, &offset, 0, SD_DHCP_OPTION_PAD,
300 : 0, NULL) == -ENOBUFS);
301 1 : assert_se(offset == 4);
302 1 : assert_se(dhcp_option_append(result, 6, &offset, 0, SD_DHCP_OPTION_PAD,
303 : 0, NULL) >= 0);
304 1 : assert_se(offset == 5);
305 :
306 1 : offset = pos = 4;
307 1 : len = 11;
308 4 : while (pos < len && options[pos] != SD_DHCP_OPTION_END) {
309 3 : assert_se(dhcp_option_append(result, len, &offset, DHCP_OVERLOAD_SNAME,
310 : options[pos],
311 : options[pos + 1],
312 : &options[pos + 2]) >= 0);
313 :
314 3 : if (options[pos] == SD_DHCP_OPTION_PAD)
315 1 : pos++;
316 : else
317 2 : pos += 2 + options[pos + 1];
318 :
319 3 : if (pos < len)
320 2 : assert_se(offset == pos);
321 : }
322 :
323 10 : for (i = 0; i < 9; i++) {
324 9 : if (verbose)
325 0 : printf("%2u: 0x%02x(0x%02x) (options)\n", i, result->options[i],
326 0 : options[i]);
327 9 : assert_se(result->options[i] == options[i]);
328 : }
329 :
330 1 : if (verbose)
331 0 : printf("%2d: 0x%02x(0x%02x) (options)\n", 9, result->options[9],
332 : SD_DHCP_OPTION_END);
333 :
334 1 : assert_se(result->options[9] == SD_DHCP_OPTION_END);
335 :
336 1 : if (verbose)
337 0 : printf("%2d: 0x%02x(0x%02x) (options)\n", 10, result->options[10],
338 : SD_DHCP_OPTION_PAD);
339 :
340 1 : assert_se(result->options[10] == SD_DHCP_OPTION_PAD);
341 :
342 12 : for (i = 0; i < pos - 8; i++) {
343 11 : if (verbose)
344 0 : printf("%2u: 0x%02x(0x%02x) (sname)\n", i, result->sname[i],
345 0 : options[i + 9]);
346 11 : assert_se(result->sname[i] == options[i + 9]);
347 : }
348 :
349 1 : if (verbose)
350 0 : printf ("\n");
351 1 : }
352 :
353 1 : int main(int argc, char *argv[]) {
354 : unsigned i;
355 :
356 1 : test_invalid_buffer_length();
357 1 : test_message_init();
358 :
359 1 : test_options(NULL);
360 :
361 9 : for (i = 0; i < ELEMENTSOF(option_tests); i++)
362 8 : test_options(&option_tests[i]);
363 :
364 1 : test_option_set();
365 :
366 1 : return 0;
367 : }
|