chiark / gitweb /
df717af06d512f7d0c7d5391ab934b774783301c
[elogind.git] / src / libsystemd-dhcp / test-dhcp-option.c
1
2 #include <stdio.h>
3 #include <stdbool.h>
4 #include <errno.h>
5 #include <string.h>
6 #include <assert.h>
7
8 #include "util.h"
9 #include "macro.h"
10
11 #include "dhcp-protocol.h"
12 #include "dhcp-internal.h"
13
14 struct option_desc {
15         uint8_t sname[64];
16         int snamelen;
17         uint8_t file[128];
18         int filelen;
19         uint8_t options[128];
20         int len;
21         bool success;
22         int filepos;
23         int snamepos;
24         int pos;
25 };
26
27 static bool verbose = false;
28
29 static struct option_desc option_tests[] = {
30         { {}, 0, {}, 0, { 42, 5, 65, 66, 67, 68, 69 }, 7, false, },
31         { {}, 0, {}, 0, { 42, 5, 65, 66, 67, 68, 69, 0, 0,
32                           DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 12, true, },
33         { {}, 0, {}, 0, { 8, 255, 70, 71, 72 }, 5, false, },
34         { {}, 0, {}, 0, { 0x35, 0x01, 0x05, 0x36, 0x04, 0x01, 0x00, 0xa8,
35                           0xc0, 0x33, 0x04, 0x00, 0x01, 0x51, 0x80, 0x01,
36                           0x04, 0xff, 0xff, 0xff, 0x00, 0x03, 0x04, 0xc0,
37                           0xa8, 0x00, 0x01, 0x06, 0x04, 0xc0, 0xa8, 0x00,
38                           0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
39           40, true, },
40         { {}, 0, {}, 0, { DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_OFFER,
41                           42, 3, 0, 0, 0 }, 8, true, },
42         { {}, 0, {}, 0, { 42, 2, 1, 2, 44 }, 5, false, },
43
44         { {}, 0,
45           { 222, 3, 1, 2, 3, DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_NAK }, 8,
46           { DHCP_OPTION_OVERLOAD, 1, DHCP_OVERLOAD_FILE }, 3, true, },
47
48         { { 1, 4, 1, 2, 3, 4, DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 9,
49           { 222, 3, 1, 2, 3 }, 5,
50           { DHCP_OPTION_OVERLOAD, 1,
51             DHCP_OVERLOAD_FILE|DHCP_OVERLOAD_SNAME }, 3, true, },
52 };
53
54 static const char *dhcp_type(int type)
55 {
56         switch(type) {
57         case DHCP_DISCOVER:
58                 return "DHCPDISCOVER";
59         case DHCP_OFFER:
60                 return "DHCPOFFER";
61         case DHCP_REQUEST:
62                 return "DHCPREQUEST";
63         case DHCP_DECLINE:
64                 return "DHCPDECLINE";
65         case DHCP_ACK:
66                 return "DHCPACK";
67         case DHCP_NAK:
68                 return "DHCPNAK";
69         case DHCP_RELEASE:
70                 return "DHCPRELEASE";
71         default:
72                 return "unknown";
73         }
74 }
75
76 static void test_invalid_buffer_length(void)
77 {
78         DHCPMessage message;
79
80         assert(dhcp_option_parse(&message, 0, NULL, NULL) == -EINVAL);
81         assert(dhcp_option_parse(&message, sizeof(DHCPMessage), NULL, NULL)
82                == -EINVAL);
83 }
84
85 static void test_cookie(void)
86 {
87         _cleanup_free_ DHCPMessage *message;
88         size_t len = sizeof(DHCPMessage) + 4;
89         uint8_t *opt;
90
91         message = malloc0(len);
92
93         opt = (uint8_t *)(message + 1);
94         opt[0] = 0xff;
95
96         assert(dhcp_option_parse(message, len, NULL, NULL) == -EINVAL);
97
98         opt[0] = 99;
99         opt[1] = 130;
100         opt[2] = 83;
101         opt[3] = 99;
102
103         assert(dhcp_option_parse(message, len, NULL, NULL) == -ENOMSG);
104 }
105
106 static DHCPMessage *create_message(uint8_t *options, uint16_t optlen,
107                 uint8_t *file, uint8_t filelen,
108                 uint8_t *sname, uint8_t snamelen)
109 {
110         DHCPMessage *message;
111         size_t len = sizeof(DHCPMessage) + 4 + optlen;
112         uint8_t *opt;
113
114         message = malloc0(len);
115         opt = (uint8_t *)(message + 1);
116
117         opt[0] = 99;
118         opt[1] = 130;
119         opt[2] = 83;
120         opt[3] = 99;
121
122         if (options && optlen)
123                 memcpy(&opt[4], options, optlen);
124
125         if (file && filelen <= 128)
126                 memcpy(&message->file, file, filelen);
127
128         if (sname && snamelen <= 64)
129                 memcpy(&message->sname, sname, snamelen);
130
131         return message;
132 }
133
134 static void test_ignore_opts(uint8_t *descoption, int *descpos, int *desclen)
135 {
136         while (*descpos < *desclen) {
137                 switch(descoption[*descpos]) {
138                 case DHCP_OPTION_PAD:
139                         *descpos += 1;
140                         break;
141
142                 case DHCP_OPTION_MESSAGE_TYPE:
143                 case DHCP_OPTION_OVERLOAD:
144                         *descpos += 3;
145                         break;
146
147                 default:
148                         return;
149                 }
150         }
151 }
152
153 static int test_options_cb(uint8_t code, uint8_t len, const uint8_t *option,
154                            void *user_data)
155 {
156         struct option_desc *desc = user_data;
157         uint8_t *descoption = NULL;
158         int *desclen = NULL, *descpos = NULL;
159         uint8_t optcode = 0;
160         uint8_t optlen = 0;
161         uint8_t i;
162
163         assert((!desc && !code && !len) || desc);
164
165         if (!desc)
166                 return -EINVAL;
167
168         assert(code != DHCP_OPTION_PAD);
169         assert(code != DHCP_OPTION_END);
170         assert(code != DHCP_OPTION_MESSAGE_TYPE);
171         assert(code != DHCP_OPTION_OVERLOAD);
172
173         while (desc->pos >= 0 || desc->filepos >= 0 || desc->snamepos >= 0) {
174
175                 if (desc->pos >= 0) {
176                         descoption = &desc->options[0];
177                         desclen = &desc->len;
178                         descpos = &desc->pos;
179                 } else if (desc->filepos >= 0) {
180                         descoption = &desc->file[0];
181                         desclen = &desc->filelen;
182                         descpos = &desc->filepos;
183                 } else if (desc->snamepos >= 0) {
184                         descoption = &desc->sname[0];
185                         desclen = &desc->snamelen;
186                         descpos = &desc->snamepos;
187                 }
188
189                 assert(descoption && desclen && descpos);
190
191                 if (*desclen)
192                         test_ignore_opts(descoption, descpos, desclen);
193
194                 if (*descpos < *desclen)
195                         break;
196
197                 if (*descpos == *desclen)
198                         *descpos = -1;
199         }
200
201         assert(*descpos != -1);
202
203         optcode = descoption[*descpos];
204         optlen = descoption[*descpos + 1];
205
206         if (verbose)
207                 printf("DHCP code %2d(%2d) len %2d(%2d) ", code, optcode,
208                                 len, optlen);
209
210         assert(code == optcode);
211         assert(len == optlen);
212
213         for (i = 0; i < len; i++) {
214
215                 if (verbose)
216                         printf("0x%02x(0x%02x) ", option[i],
217                                         descoption[*descpos + 2 + i]);
218
219                 assert(option[i] == descoption[*descpos + 2 + i]);
220         }
221
222         if (verbose)
223                 printf("\n");
224
225         *descpos += optlen + 2;
226
227         test_ignore_opts(descoption, descpos, desclen);
228
229         if (desc->pos != -1 && desc->pos == desc->len)
230                 desc->pos = -1;
231
232         if (desc->filepos != -1 && desc->filepos == desc->filelen)
233                 desc->filepos = -1;
234
235         if (desc->snamepos != -1 && desc->snamepos == desc->snamelen)
236                 desc->snamepos = -1;
237
238         return 0;
239 }
240
241 static void test_options(struct option_desc *desc)
242 {
243         uint8_t *options = NULL;
244         uint8_t *file = NULL;
245         uint8_t *sname = NULL;
246         int optlen = 0;
247         int filelen = 0;
248         int snamelen = 0;
249         int buflen = 0;
250         _cleanup_free_ DHCPMessage *message;
251         int res;
252
253         if (desc) {
254                 file = &desc->file[0];
255                 filelen = desc->filelen;
256                 if (!filelen)
257                         desc->filepos = -1;
258
259                 sname = &desc->sname[0];
260                 snamelen = desc->snamelen;
261                 if (!snamelen)
262                         desc->snamepos = -1;
263
264                 options = &desc->options[0];
265                 optlen = desc->len;
266                 desc->pos = 0;
267         }
268         message = create_message(options, optlen, file, filelen,
269                         sname, snamelen);
270
271         buflen = sizeof(DHCPMessage) + 4 + optlen;
272
273         if (!desc) {
274                 assert((res = dhcp_option_parse(message, buflen,
275                                                 test_options_cb,
276                                                 NULL)) == -ENOMSG);
277         } else if (desc->success) {
278                 assert((res = dhcp_option_parse(message, buflen,
279                                                 test_options_cb,
280                                                 desc)) >= 0);
281                 assert(desc->pos == -1 && desc->filepos == -1 &&
282                                 desc->snamepos == -1);
283         } else
284                 assert((res = dhcp_option_parse(message, buflen,
285                                                 test_options_cb,
286                                                 desc)) < 0);
287
288         if (verbose)
289                 printf("DHCP type %s\n", dhcp_type(res));
290 }
291
292 int main(int argc, char *argv[])
293 {
294         unsigned int i;
295
296         test_invalid_buffer_length();
297         test_cookie();
298
299         test_options(NULL);
300
301         for (i = 0; i < ELEMENTSOF(option_tests); i++)
302                 test_options(&option_tests[i]);
303
304         return 0;
305 }