]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/dhcp-option.c
hwdb: Add accelerometer orientation quirk for the PoV TAB-P1006W-232-3G
[thirdparty/systemd.git] / src / libsystemd-network / dhcp-option.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
524cf458 2/***
810adae9 3 Copyright © 2013 Intel Corporation. All rights reserved.
524cf458
PF
4***/
5
524cf458 6#include <errno.h>
cf0fbc49 7#include <stdint.h>
524cf458 8#include <stdio.h>
cf0fbc49 9#include <string.h>
524cf458 10
f693e9b3
TG
11#include "alloc-util.h"
12#include "utf8.h"
af1c0de0 13#include "strv.h"
f693e9b3 14
524cf458
PF
15#include "dhcp-internal.h"
16
04b28be1
TG
17static int option_append(uint8_t options[], size_t size, size_t *offset,
18 uint8_t code, size_t optlen, const void *optval) {
20b958bf
TG
19 assert(options);
20 assert(offset);
524cf458 21
22805d92 22 if (code != SD_DHCP_OPTION_END)
2688ef60 23 /* always make sure there is space for an END option */
313cefa1 24 size--;
2688ef60 25
524cf458
PF
26 switch (code) {
27
22805d92
BG
28 case SD_DHCP_OPTION_PAD:
29 case SD_DHCP_OPTION_END:
04b28be1 30 if (size < *offset + 1)
524cf458
PF
31 return -ENOBUFS;
32
20b958bf
TG
33 options[*offset] = code;
34 *offset += 1;
524cf458
PF
35 break;
36
af1c0de0
SS
37 case SD_DHCP_OPTION_USER_CLASS: {
38 size_t len = 0;
39 char **s;
40
41 STRV_FOREACH(s, (char **) optval)
42 len += strlen(*s) + 1;
43
44 if (size < *offset + len + 2)
45 return -ENOBUFS;
46
47 options[*offset] = code;
48 options[*offset + 1] = len;
49 *offset += 2;
50
51 STRV_FOREACH(s, (char **) optval) {
52 len = strlen(*s);
53
54 if (len > 255)
55 return -ENAMETOOLONG;
56
57 options[*offset] = len;
58
59 memcpy_safe(&options[*offset + 1], *s, len);
60 *offset += len + 1;
61 }
62
63 break;
64 }
524cf458 65 default:
04b28be1 66 if (size < *offset + optlen + 2)
524cf458
PF
67 return -ENOBUFS;
68
20b958bf
TG
69 options[*offset] = code;
70 options[*offset + 1] = optlen;
04b28be1 71
75f32f04 72 memcpy_safe(&options[*offset + 2], optval, optlen);
20b958bf 73 *offset += optlen + 2;
524cf458
PF
74
75 break;
76 }
77
78 return 0;
79}
80
04b28be1
TG
81int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset,
82 uint8_t overload,
83 uint8_t code, size_t optlen, const void *optval) {
84 size_t file_offset = 0, sname_offset =0;
85 bool file, sname;
86 int r;
87
88 assert(message);
89 assert(offset);
90
91 file = overload & DHCP_OVERLOAD_FILE;
92 sname = overload & DHCP_OVERLOAD_SNAME;
93
94 if (*offset < size) {
95 /* still space in the options array */
96 r = option_append(message->options, size, offset, code, optlen, optval);
97 if (r >= 0)
98 return 0;
99 else if (r == -ENOBUFS && (file || sname)) {
100 /* did not fit, but we have more buffers to try
101 close the options array and move the offset to its end */
22805d92 102 r = option_append(message->options, size, offset, SD_DHCP_OPTION_END, 0, NULL);
04b28be1
TG
103 if (r < 0)
104 return r;
105
106 *offset = size;
107 } else
108 return r;
109 }
110
111 if (overload & DHCP_OVERLOAD_FILE) {
112 file_offset = *offset - size;
113
114 if (file_offset < sizeof(message->file)) {
115 /* still space in the 'file' array */
116 r = option_append(message->file, sizeof(message->file), &file_offset, code, optlen, optval);
117 if (r >= 0) {
118 *offset = size + file_offset;
119 return 0;
120 } else if (r == -ENOBUFS && sname) {
121 /* did not fit, but we have more buffers to try
122 close the file array and move the offset to its end */
22805d92 123 r = option_append(message->options, size, offset, SD_DHCP_OPTION_END, 0, NULL);
04b28be1
TG
124 if (r < 0)
125 return r;
126
127 *offset = size + sizeof(message->file);
128 } else
129 return r;
130 }
131 }
132
133 if (overload & DHCP_OVERLOAD_SNAME) {
134 sname_offset = *offset - size - (file ? sizeof(message->file) : 0);
135
136 if (sname_offset < sizeof(message->sname)) {
137 /* still space in the 'sname' array */
138 r = option_append(message->sname, sizeof(message->sname), &sname_offset, code, optlen, optval);
139 if (r >= 0) {
140 *offset = size + (file ? sizeof(message->file) : 0) + sname_offset;
141 return 0;
142 } else {
143 /* no space, or other error, give up */
144 return r;
145 }
146 }
147 }
148
149 return -ENOBUFS;
150}
151
32008a96 152static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overload,
ccf86354 153 uint8_t *message_type, char **error_message, dhcp_option_callback_t cb,
89ca10c6 154 void *userdata) {
32008a96 155 uint8_t code, len;
f693e9b3 156 const uint8_t *option;
32008a96 157 size_t offset = 0;
524cf458 158
32008a96 159 while (offset < buflen) {
f693e9b3 160 code = options[offset ++];
32008a96 161
f693e9b3 162 switch (code) {
22805d92 163 case SD_DHCP_OPTION_PAD:
f693e9b3 164 continue;
524cf458 165
22805d92 166 case SD_DHCP_OPTION_END:
524cf458 167 return 0;
f693e9b3 168 }
524cf458 169
f693e9b3
TG
170 if (buflen < offset + 1)
171 return -ENOBUFS;
172
173 len = options[offset ++];
524cf458 174
f693e9b3
TG
175 if (buflen < offset + len)
176 return -EINVAL;
177
178 option = &options[offset];
179
180 switch (code) {
22805d92 181 case SD_DHCP_OPTION_MESSAGE_TYPE:
32008a96 182 if (len != 1)
524cf458
PF
183 return -EINVAL;
184
185 if (message_type)
f693e9b3 186 *message_type = *option;
524cf458
PF
187
188 break;
189
22805d92 190 case SD_DHCP_OPTION_ERROR_MESSAGE:
f693e9b3 191 if (len == 0)
524cf458
PF
192 return -EINVAL;
193
f693e9b3
TG
194 if (error_message) {
195 _cleanup_free_ char *string = NULL;
524cf458 196
f693e9b3
TG
197 /* Accept a trailing NUL byte */
198 if (memchr(option, 0, len - 1))
199 return -EINVAL;
524cf458 200
f693e9b3
TG
201 string = strndup((const char *) option, len);
202 if (!string)
203 return -ENOMEM;
524cf458 204
f693e9b3
TG
205 if (!ascii_is_valid(string))
206 return -EINVAL;
524cf458 207
f9ecfd3b 208 free_and_replace(*error_message, string);
f693e9b3 209 }
524cf458 210
f693e9b3 211 break;
22805d92 212 case SD_DHCP_OPTION_OVERLOAD:
f693e9b3 213 if (len != 1)
524cf458 214 return -EINVAL;
524cf458 215
f693e9b3
TG
216 if (overload)
217 *overload = *option;
524cf458 218
f693e9b3
TG
219 break;
220
221 default:
222 if (cb)
223 cb(code, len, option, userdata);
524cf458
PF
224
225 break;
226 }
f693e9b3
TG
227
228 offset += len;
524cf458
PF
229 }
230
32008a96 231 if (offset < buflen)
524cf458
PF
232 return -EINVAL;
233
234 return 0;
235}
236
ccf86354 237int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_callback_t cb, void *userdata, char **_error_message) {
f693e9b3 238 _cleanup_free_ char *error_message = NULL;
524cf458
PF
239 uint8_t overload = 0;
240 uint8_t message_type = 0;
32008a96 241 int r;
524cf458
PF
242
243 if (!message)
244 return -EINVAL;
245
3b7ca119 246 if (len < sizeof(DHCPMessage))
524cf458
PF
247 return -EINVAL;
248
3b7ca119 249 len -= sizeof(DHCPMessage);
524cf458 250
f693e9b3 251 r = parse_options(message->options, len, &overload, &message_type, &error_message, cb, userdata);
32008a96
TG
252 if (r < 0)
253 return r;
524cf458
PF
254
255 if (overload & DHCP_OVERLOAD_FILE) {
f693e9b3 256 r = parse_options(message->file, sizeof(message->file), NULL, &message_type, &error_message, cb, userdata);
32008a96
TG
257 if (r < 0)
258 return r;
524cf458
PF
259 }
260
261 if (overload & DHCP_OVERLOAD_SNAME) {
f693e9b3 262 r = parse_options(message->sname, sizeof(message->sname), NULL, &message_type, &error_message, cb, userdata);
32008a96
TG
263 if (r < 0)
264 return r;
524cf458
PF
265 }
266
f693e9b3
TG
267 if (message_type == 0)
268 return -ENOMSG;
269
ae2a15bc
LP
270 if (_error_message && IN_SET(message_type, DHCP_NAK, DHCP_DECLINE))
271 *_error_message = TAKE_PTR(error_message);
524cf458 272
f693e9b3 273 return message_type;
524cf458 274}