2 This file is part of systemd.
4 Copyright (C) 2013 Intel Corporation. All rights reserved.
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
25 #include "alloc-util.h"
28 #include "dhcp-internal.h"
30 static int option_append(uint8_t options
[], size_t size
, size_t *offset
,
31 uint8_t code
, size_t optlen
, const void *optval
) {
35 if (code
!= SD_DHCP_OPTION_END
)
36 /* always make sure there is space for an END option */
41 case SD_DHCP_OPTION_PAD
:
42 case SD_DHCP_OPTION_END
:
43 if (size
< *offset
+ 1)
46 options
[*offset
] = code
;
51 if (size
< *offset
+ optlen
+ 2)
54 options
[*offset
] = code
;
55 options
[*offset
+ 1] = optlen
;
57 memcpy_safe(&options
[*offset
+ 2], optval
, optlen
);
58 *offset
+= optlen
+ 2;
66 int dhcp_option_append(DHCPMessage
*message
, size_t size
, size_t *offset
,
68 uint8_t code
, size_t optlen
, const void *optval
) {
69 size_t file_offset
= 0, sname_offset
=0;
76 file
= overload
& DHCP_OVERLOAD_FILE
;
77 sname
= overload
& DHCP_OVERLOAD_SNAME
;
80 /* still space in the options array */
81 r
= option_append(message
->options
, size
, offset
, code
, optlen
, optval
);
84 else if (r
== -ENOBUFS
&& (file
|| sname
)) {
85 /* did not fit, but we have more buffers to try
86 close the options array and move the offset to its end */
87 r
= option_append(message
->options
, size
, offset
, SD_DHCP_OPTION_END
, 0, NULL
);
96 if (overload
& DHCP_OVERLOAD_FILE
) {
97 file_offset
= *offset
- size
;
99 if (file_offset
< sizeof(message
->file
)) {
100 /* still space in the 'file' array */
101 r
= option_append(message
->file
, sizeof(message
->file
), &file_offset
, code
, optlen
, optval
);
103 *offset
= size
+ file_offset
;
105 } else if (r
== -ENOBUFS
&& sname
) {
106 /* did not fit, but we have more buffers to try
107 close the file array and move the offset to its end */
108 r
= option_append(message
->options
, size
, offset
, SD_DHCP_OPTION_END
, 0, NULL
);
112 *offset
= size
+ sizeof(message
->file
);
118 if (overload
& DHCP_OVERLOAD_SNAME
) {
119 sname_offset
= *offset
- size
- (file
? sizeof(message
->file
) : 0);
121 if (sname_offset
< sizeof(message
->sname
)) {
122 /* still space in the 'sname' array */
123 r
= option_append(message
->sname
, sizeof(message
->sname
), &sname_offset
, code
, optlen
, optval
);
125 *offset
= size
+ (file
? sizeof(message
->file
) : 0) + sname_offset
;
128 /* no space, or other error, give up */
137 static int parse_options(const uint8_t options
[], size_t buflen
, uint8_t *overload
,
138 uint8_t *message_type
, char **error_message
, dhcp_option_callback_t cb
,
141 const uint8_t *option
;
144 while (offset
< buflen
) {
145 code
= options
[offset
++];
148 case SD_DHCP_OPTION_PAD
:
151 case SD_DHCP_OPTION_END
:
155 if (buflen
< offset
+ 1)
158 len
= options
[offset
++];
160 if (buflen
< offset
+ len
)
163 option
= &options
[offset
];
166 case SD_DHCP_OPTION_MESSAGE_TYPE
:
171 *message_type
= *option
;
175 case SD_DHCP_OPTION_ERROR_MESSAGE
:
180 _cleanup_free_
char *string
= NULL
;
182 /* Accept a trailing NUL byte */
183 if (memchr(option
, 0, len
- 1))
186 string
= strndup((const char *) option
, len
);
190 if (!ascii_is_valid(string
))
193 free(*error_message
);
194 *error_message
= string
;
199 case SD_DHCP_OPTION_OVERLOAD
:
210 cb(code
, len
, option
, userdata
);
224 int dhcp_option_parse(DHCPMessage
*message
, size_t len
, dhcp_option_callback_t cb
, void *userdata
, char **_error_message
) {
225 _cleanup_free_
char *error_message
= NULL
;
226 uint8_t overload
= 0;
227 uint8_t message_type
= 0;
233 if (len
< sizeof(DHCPMessage
))
236 len
-= sizeof(DHCPMessage
);
238 r
= parse_options(message
->options
, len
, &overload
, &message_type
, &error_message
, cb
, userdata
);
242 if (overload
& DHCP_OVERLOAD_FILE
) {
243 r
= parse_options(message
->file
, sizeof(message
->file
), NULL
, &message_type
, &error_message
, cb
, userdata
);
248 if (overload
& DHCP_OVERLOAD_SNAME
) {
249 r
= parse_options(message
->sname
, sizeof(message
->sname
), NULL
, &message_type
, &error_message
, cb
, userdata
);
254 if (message_type
== 0)
257 if (_error_message
&& IN_SET(message_type
, DHCP_NAK
, DHCP_DECLINE
)) {
258 *_error_message
= error_message
;
259 error_message
= NULL
;