1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright (C) 2013 Intel Corporation. All rights reserved.
13 #include "alloc-util.h"
16 #include "dhcp-internal.h"
18 static int option_append(uint8_t options
[], size_t size
, size_t *offset
,
19 uint8_t code
, size_t optlen
, const void *optval
) {
23 if (code
!= SD_DHCP_OPTION_END
)
24 /* always make sure there is space for an END option */
29 case SD_DHCP_OPTION_PAD
:
30 case SD_DHCP_OPTION_END
:
31 if (size
< *offset
+ 1)
34 options
[*offset
] = code
;
39 if (size
< *offset
+ optlen
+ 2)
42 options
[*offset
] = code
;
43 options
[*offset
+ 1] = optlen
;
45 memcpy_safe(&options
[*offset
+ 2], optval
, optlen
);
46 *offset
+= optlen
+ 2;
54 int dhcp_option_append(DHCPMessage
*message
, size_t size
, size_t *offset
,
56 uint8_t code
, size_t optlen
, const void *optval
) {
57 size_t file_offset
= 0, sname_offset
=0;
64 file
= overload
& DHCP_OVERLOAD_FILE
;
65 sname
= overload
& DHCP_OVERLOAD_SNAME
;
68 /* still space in the options array */
69 r
= option_append(message
->options
, size
, offset
, code
, optlen
, optval
);
72 else if (r
== -ENOBUFS
&& (file
|| sname
)) {
73 /* did not fit, but we have more buffers to try
74 close the options array and move the offset to its end */
75 r
= option_append(message
->options
, size
, offset
, SD_DHCP_OPTION_END
, 0, NULL
);
84 if (overload
& DHCP_OVERLOAD_FILE
) {
85 file_offset
= *offset
- size
;
87 if (file_offset
< sizeof(message
->file
)) {
88 /* still space in the 'file' array */
89 r
= option_append(message
->file
, sizeof(message
->file
), &file_offset
, code
, optlen
, optval
);
91 *offset
= size
+ file_offset
;
93 } else if (r
== -ENOBUFS
&& sname
) {
94 /* did not fit, but we have more buffers to try
95 close the file array and move the offset to its end */
96 r
= option_append(message
->options
, size
, offset
, SD_DHCP_OPTION_END
, 0, NULL
);
100 *offset
= size
+ sizeof(message
->file
);
106 if (overload
& DHCP_OVERLOAD_SNAME
) {
107 sname_offset
= *offset
- size
- (file
? sizeof(message
->file
) : 0);
109 if (sname_offset
< sizeof(message
->sname
)) {
110 /* still space in the 'sname' array */
111 r
= option_append(message
->sname
, sizeof(message
->sname
), &sname_offset
, code
, optlen
, optval
);
113 *offset
= size
+ (file
? sizeof(message
->file
) : 0) + sname_offset
;
116 /* no space, or other error, give up */
125 static int parse_options(const uint8_t options
[], size_t buflen
, uint8_t *overload
,
126 uint8_t *message_type
, char **error_message
, dhcp_option_callback_t cb
,
129 const uint8_t *option
;
132 while (offset
< buflen
) {
133 code
= options
[offset
++];
136 case SD_DHCP_OPTION_PAD
:
139 case SD_DHCP_OPTION_END
:
143 if (buflen
< offset
+ 1)
146 len
= options
[offset
++];
148 if (buflen
< offset
+ len
)
151 option
= &options
[offset
];
154 case SD_DHCP_OPTION_MESSAGE_TYPE
:
159 *message_type
= *option
;
163 case SD_DHCP_OPTION_ERROR_MESSAGE
:
168 _cleanup_free_
char *string
= NULL
;
170 /* Accept a trailing NUL byte */
171 if (memchr(option
, 0, len
- 1))
174 string
= strndup((const char *) option
, len
);
178 if (!ascii_is_valid(string
))
181 free_and_replace(*error_message
, string
);
185 case SD_DHCP_OPTION_OVERLOAD
:
196 cb(code
, len
, option
, userdata
);
210 int dhcp_option_parse(DHCPMessage
*message
, size_t len
, dhcp_option_callback_t cb
, void *userdata
, char **_error_message
) {
211 _cleanup_free_
char *error_message
= NULL
;
212 uint8_t overload
= 0;
213 uint8_t message_type
= 0;
219 if (len
< sizeof(DHCPMessage
))
222 len
-= sizeof(DHCPMessage
);
224 r
= parse_options(message
->options
, len
, &overload
, &message_type
, &error_message
, cb
, userdata
);
228 if (overload
& DHCP_OVERLOAD_FILE
) {
229 r
= parse_options(message
->file
, sizeof(message
->file
), NULL
, &message_type
, &error_message
, cb
, userdata
);
234 if (overload
& DHCP_OVERLOAD_SNAME
) {
235 r
= parse_options(message
->sname
, sizeof(message
->sname
), NULL
, &message_type
, &error_message
, cb
, userdata
);
240 if (message_type
== 0)
243 if (_error_message
&& IN_SET(message_type
, DHCP_NAK
, DHCP_DECLINE
))
244 *_error_message
= TAKE_PTR(error_message
);