1 /*
2 * Copyright (C) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include "dhcp_options.h"
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20
21 #include "securec.h"
22 #include "dhcp_logger.h"
23
24 DEFINE_DHCPLOG_DHCP_LABEL("DhcpOptions");
25
26 /* Check packet option OPTION_OVERLOAD_OPTION. */
CheckOptSoverloaded(const struct DhcpPacket * packet,int code,int maxLen,int * over,int * index)27 static bool CheckOptSoverloaded(const struct DhcpPacket *packet, int code, int maxLen, int *over, int *index)
28 {
29 if (packet == nullptr) {
30 DHCP_LOGE("CheckOptSoverloaded failed, packet == nullptr!");
31 return false;
32 }
33
34 const uint8_t *pOption = packet->options;
35 if (*index + DHCP_OPT_LEN_INDEX + pOption[*index + DHCP_OPT_LEN_INDEX] >= maxLen) {
36 DHCP_LOGW("CheckOptSoverloaded code:%{public}d,*index:%{public}d more than max bytes:%{public}d!",
37 code, *index, maxLen);
38 return false;
39 }
40 *over = pOption[*index + DHCP_OPT_DATA_INDEX + DHCP_OPT_CODE_INDEX];
41 *index += pOption[DHCP_OPT_LEN_INDEX] + DHCP_OPT_CODE_BYTES + DHCP_OPT_LEN_BYTES;
42 return true;
43 }
44
45 /* Check packet options based on the code and index. */
CheckOptionsData(const struct DhcpPacket * packet,int code,int index,int maxLen)46 static int CheckOptionsData(const struct DhcpPacket *packet, int code, int index, int maxLen)
47 {
48 if (packet == nullptr) {
49 DHCP_LOGE("CheckOptionsData failed, packet == nullptr!");
50 return DHCP_OPT_FAILED;
51 }
52
53 if (index >= maxLen - DHCP_OPT_DATA_INDEX) {
54 DHCP_LOGW("CheckOptionsData code:%{public}d,index:%{public}d more than max bytes:%{public}d!",
55 code, index, maxLen);
56 return DHCP_OPT_FAILED;
57 }
58
59 const uint8_t *pOption = packet->options;
60 if (pOption[index + DHCP_OPT_CODE_INDEX] != code) {
61 return DHCP_OPT_NONE;
62 }
63
64 if (index + DHCP_OPT_LEN_INDEX + pOption[index + DHCP_OPT_LEN_INDEX] >= maxLen) {
65 DHCP_LOGW("CheckOptionsData failed, options data too long, code:%{public}d,index:%{public}d!", code, index);
66 return DHCP_OPT_FAILED;
67 }
68
69 return DHCP_OPT_SUCCESS;
70 }
71
72 /* Obtains the data type based on the code. */
GetDhcpOptionCodeType(const uint8_t code)73 static uint8_t GetDhcpOptionCodeType(const uint8_t code)
74 {
75 if ((code <= PAD_OPTION) || (code >= END_OPTION)) {
76 DHCP_LOGE("GetDhcpOptionCodeType error, code:%{public}d is error!", code);
77 return DHCP_OPTION_DATA_INVALID;
78 }
79
80 uint8_t nDataType = DHCP_OPTION_DATA_INVALID;
81 switch (code) {
82 case DHCP_MESSAGE_TYPE_OPTION:
83 case FORCERENEW_NONCE_OPTION:
84 nDataType = DHCP_OPTION_DATA_U8;
85 break;
86 case INTERFACE_MTU_OPTION:
87 case MAXIMUM_DHCP_MESSAGE_SIZE_OPTION:
88 nDataType = DHCP_OPTION_DATA_U16;
89 break;
90 case IP_ADDRESS_LEASE_TIME_OPTION:
91 nDataType = DHCP_OPTION_DATA_U32;
92 break;
93 case SUBNET_MASK_OPTION:
94 case BROADCAST_ADDRESS_OPTION:
95 case REQUESTED_IP_ADDRESS_OPTION:
96 case SERVER_IDENTIFIER_OPTION:
97 nDataType = DHCP_OPTION_DATA_IP;
98 break;
99 case ROUTER_OPTION:
100 case DOMAIN_NAME_SERVER_OPTION:
101 case NETWORK_TIME_PROTOCOL_SERVERS_OPTION:
102 nDataType = DHCP_OPTION_DATA_IP_LIST;
103 break;
104 case HOST_NAME_OPTION:
105 case DOMAIN_NAME_OPTION:
106 case MESSAGE_OPTION:
107 nDataType = DHCP_OPTION_DATA_IP_STRING;
108 break;
109 default:
110 DHCP_LOGE("GetDhcpOptionCodeType failed, code:%{public}d is invalid!", code);
111 break;
112 }
113
114 return nDataType;
115 }
116
117 /* Obtains the data length based on the code. */
GetDhcpOptionDataLen(const uint8_t code)118 uint8_t GetDhcpOptionDataLen(const uint8_t code)
119 {
120 uint8_t nDataType = GetDhcpOptionCodeType(code);
121 if (nDataType == DHCP_OPTION_DATA_INVALID) {
122 DHCP_LOGE("GetDhcpOptionDataLen code:%{public}d error, GetDhcpOptionCodeType invalid!", code);
123 return 0;
124 }
125
126 uint8_t nDataLen = 0;
127 switch (nDataType) {
128 case DHCP_OPTION_DATA_U8:
129 nDataLen = DHCP_UINT8_BYTES;
130 break;
131 case DHCP_OPTION_DATA_U16:
132 nDataLen = DHCP_UINT16_BYTES;
133 break;
134 case DHCP_OPTION_DATA_U32:
135 nDataLen = DHCP_UINT32_BYTES;
136 break;
137 case DHCP_OPTION_DATA_IP:
138 nDataLen = DHCP_UINT32_BYTES;
139 break;
140 case DHCP_OPTION_DATA_IP_PAIR:
141 nDataLen = DHCP_UINT32_DOUBLE_BYTES;
142 break;
143 default:
144 DHCP_LOGE("GetDhcpOptionDataLen code:%{public}d failed, nDataType:%{public}d is invalid!",
145 code, nDataType);
146 break;
147 }
148
149 return nDataLen;
150 }
151
152 /* Obtains the data pointer and length from the packet based on the code. */
GetDhcpOption(const struct DhcpPacket * packet,int code,size_t * length)153 const uint8_t *GetDhcpOption(const struct DhcpPacket *packet, int code, size_t *length)
154 {
155 *length = 0;
156 if (packet == nullptr) {
157 DHCP_LOGE("GetDhcpOption failed, packet == nullptr!");
158 return nullptr;
159 }
160
161 const uint8_t *pOption = packet->options;
162 int nIndex = 0, maxLen = DHCP_OPT_SIZE, nOver = 0, nFinished = 0, nFlag = OPTION_FIELD;
163 while (nFinished == 0) {
164 int nRet = CheckOptionsData(packet, code, nIndex, maxLen);
165 if (nRet == DHCP_OPT_SUCCESS) {
166 *length = pOption[nIndex + DHCP_OPT_LEN_INDEX];
167 return pOption + nIndex + DHCP_OPT_DATA_INDEX;
168 } else if (nRet == DHCP_OPT_FAILED) {
169 return nullptr;
170 }
171
172 switch (pOption[nIndex + DHCP_OPT_CODE_INDEX]) {
173 case PAD_OPTION:
174 nIndex++;
175 break;
176 case OPTION_OVERLOAD_OPTION:
177 if (!CheckOptSoverloaded(packet, code, maxLen, &nOver, &nIndex)) {
178 return nullptr;
179 }
180 break;
181 case END_OPTION:
182 if ((nFlag == OPTION_FIELD) && (nOver & FILE_FIELD)) {
183 pOption = packet->file;
184 nIndex = 0;
185 maxLen = DHCP_BOOT_FILE_LENGTH;
186 nFlag = FILE_FIELD;
187 } else if ((nFlag == FILE_FIELD) && (nOver & SNAME_FIELD)) {
188 pOption = packet->sname;
189 nIndex = 0;
190 maxLen = DHCP_HOST_NAME_LENGTH;
191 nFlag = SNAME_FIELD;
192 } else {
193 nFinished = 1;
194 }
195 break;
196 default:
197 nIndex += DHCP_OPT_CODE_BYTES + DHCP_OPT_LEN_BYTES + pOption[nIndex + DHCP_OPT_LEN_INDEX];
198 break;
199 }
200 }
201 DHCP_LOGW("GetDhcpOption options no find code:%{public}d, nIndex:%{public}d!", code, nIndex);
202 return nullptr;
203 }
204
205 /* Obtains the uint8 data from the packet based on the code. */
GetDhcpOptionUint8(const struct DhcpPacket * packet,int code,uint8_t * data)206 bool GetDhcpOptionUint8(const struct DhcpPacket *packet, int code, uint8_t *data)
207 {
208 size_t len = 0;
209 const uint8_t *p = GetDhcpOption(packet, code, &len);
210 if (p == nullptr) {
211 DHCP_LOGW("GetDhcpOptionUint8 GetDhcpOption nullptr, code:%{public}d!", code);
212 return false;
213 }
214 if (len < static_cast<size_t>(sizeof(uint8_t))) {
215 DHCP_LOGE("GetDhcpOptionUint8 failed, len:%{public}zu less data:%{public}zu, code:%{public}d!",
216 len, sizeof(uint8_t), code);
217 return false;
218 }
219 if (memcpy_s(data, sizeof(uint8_t), p, sizeof(uint8_t)) != EOK) {
220 return false;
221 }
222 return true;
223 }
224
225 /* Obtains the uint32 data from the packet based on the code. */
GetDhcpOptionUint32(const struct DhcpPacket * packet,int code,uint32_t * data)226 bool GetDhcpOptionUint32(const struct DhcpPacket *packet, int code, uint32_t *data)
227 {
228 size_t len = 0;
229 const uint8_t *p = GetDhcpOption(packet, code, &len);
230 if (p == nullptr) {
231 DHCP_LOGW("GetDhcpOptionUint32 GetDhcpOption nullptr, code:%{public}d!", code);
232 return false;
233 }
234 uint32_t uData = 0;
235 if (len < static_cast<size_t>(sizeof(uData))) {
236 DHCP_LOGE("GetDhcpOptionUint32 failed, len:%{public}zu less uData:%{public}zu, code:%{public}d!",
237 len, sizeof(uData), code);
238 return false;
239 }
240 if (memcpy_s(&uData, sizeof(uData), p, sizeof(uData)) != EOK) {
241 return false;
242 }
243 if (uData > 0) {
244 *data = ntohl(uData);
245 }
246 return true;
247 }
248
249 /* Obtains the uint32n data from the packet based on the code. */
GetDhcpOptionUint32n(const struct DhcpPacket * packet,int code,uint32_t * data1,uint32_t * data2)250 bool GetDhcpOptionUint32n(const struct DhcpPacket *packet, int code, uint32_t *data1, uint32_t *data2)
251 {
252 size_t len = 0;
253 const uint8_t *p = GetDhcpOption(packet, code, &len);
254 if (p == nullptr) {
255 DHCP_LOGW("GetDhcpOptionUint32n GetDhcpOption nullptr, code:%{public}d!", code);
256 return false;
257 }
258 uint32_t uData = 0;
259 if ((len < static_cast<size_t>(sizeof(uData))) || (len % static_cast<size_t>(sizeof(uData)) != 0)) {
260 DHCP_LOGE("GetDhcpOptionUint32n failed, len:%{public}zu is not %{public}zu * n, code:%{public}d!",
261 len, sizeof(uData), code);
262 return false;
263 }
264 if (memcpy_s(&uData, sizeof(uData), p, sizeof(uData)) != EOK) {
265 return false;
266 }
267 if (uData > 0) {
268 *data1 = ntohl(uData);
269 }
270 if (len > static_cast<size_t>(sizeof(uData))) {
271 p += sizeof(uData);
272 uData = 0;
273 if (memcpy_s(&uData, sizeof(uData), p, sizeof(uData)) != EOK) {
274 return false;
275 }
276 if (uData > 0) {
277 *data2 = ntohl(uData);
278 }
279 }
280 return true;
281 }
282
283 /* Obtains the string data from the packet based on the code. */
GetDhcpOptionString(const struct DhcpPacket * packet,int code)284 char *GetDhcpOptionString(const struct DhcpPacket *packet, int code)
285 {
286 size_t len;
287 const uint8_t *p = GetDhcpOption(packet, code, &len);
288 if ((p == nullptr) || (*p == '\0')) {
289 DHCP_LOGW("GetDhcpOptionString GetDhcpOption nullptr, code:%{public}d!", code);
290 return nullptr;
291 }
292 if (len < static_cast<size_t>(sizeof(uint8_t))) {
293 DHCP_LOGE("GetDhcpOptionString failed, len:%{public}zu less data:%{public}zu, code:%{public}d!",
294 len, sizeof(uint8_t), code);
295 return nullptr;
296 }
297
298 char *s = (char *)malloc(sizeof(char) * (len + 1));
299 if (s) {
300 if (memcpy_s(s, len + 1, p, len) != EOK) {
301 free(s);
302 s = nullptr;
303 return nullptr;
304 }
305 s[len] = '\0';
306 }
307 return s;
308 }
309
310 /* Obtain the end index from options. */
GetEndOptionIndex(const uint8_t * pOpts)311 int GetEndOptionIndex(const uint8_t *pOpts)
312 {
313 int nIndex = 0;
314 while (pOpts[nIndex] != END_OPTION) {
315 if (pOpts[nIndex] != PAD_OPTION) {
316 nIndex += pOpts[nIndex + DHCP_OPT_LEN_INDEX] + DHCP_OPT_CODE_BYTES + DHCP_OPT_LEN_BYTES;
317 continue;
318 }
319 nIndex++;
320 }
321 return nIndex;
322 }
323
324 /* Adds a single option string to options. */
AddOptStrToOpts(uint8_t * pOpts,uint8_t * pOpt,int nOptLen)325 int AddOptStrToOpts(uint8_t *pOpts, uint8_t *pOpt, int nOptLen)
326 {
327 int optStrLen = DHCP_OPT_CODE_BYTES + DHCP_OPT_LEN_BYTES + pOpt[DHCP_OPT_LEN_INDEX];
328 if (nOptLen != optStrLen) {
329 DHCP_LOGE("AddOptStrToOpts() code:%{public}u nOptLen:%{public}d no equal optStrLen:%{public}d!",
330 pOpt[DHCP_OPT_CODE_INDEX], nOptLen, optStrLen);
331 return 0;
332 }
333
334 int nEndIndex = GetEndOptionIndex(pOpts);
335 if ((nEndIndex + nOptLen + 1) >= DHCP_OPT_SIZE) {
336 DHCP_LOGE("AddOptStrToOpts() code:%{public}u did not fit into the packet!", pOpt[DHCP_OPT_CODE_INDEX]);
337 return 0;
338 }
339
340 DHCP_LOGD("AddOptStrToOpts() adding option code %{public}u.", pOpt[DHCP_OPT_CODE_INDEX]);
341 if (memcpy_s(pOpts + nEndIndex, nOptLen + 1, pOpt, nOptLen) != EOK) {
342 return 0;
343 }
344 pOpts[nEndIndex + nOptLen] = END_OPTION;
345 return nOptLen;
346 }
347
348 /* Adds a single option value to options. */
AddOptValueToOpts(uint8_t * pOpts,uint8_t code,uint32_t value)349 int AddOptValueToOpts(uint8_t *pOpts, uint8_t code, uint32_t value)
350 {
351 uint8_t uLen = GetDhcpOptionDataLen(code);
352 if (uLen == 0) {
353 DHCP_LOGE("AddOptValueToOpts() code:%{public}d failed, GetDhcpOptionDataLen uLen:0!", code);
354 return 0;
355 }
356
357 uint32_t uValue = 0;
358 uint8_t *pUint8 = (uint8_t *)&uValue;
359 uint16_t *pUint16 = (uint16_t *)&uValue;
360 uint32_t *pUint32 = &uValue;
361 switch (uLen) {
362 case DHCP_UINT8_BYTES:
363 *pUint8 = value;
364 break;
365 case DHCP_UINT16_BYTES:
366 *pUint16 = value;
367 break;
368 case DHCP_UINT32_BYTES:
369 *pUint32 = value;
370 break;
371 default:
372 DHCP_LOGE("AddOptValueToOpts() uLen:%{public}d error, break!", uLen);
373 break;
374 }
375
376 uint8_t uOption[DHCP_OPT_CODE_BYTES + DHCP_OPT_LEN_BYTES + DHCP_UINT32_BYTES];
377 uOption[DHCP_OPT_CODE_INDEX] = code;
378 uOption[DHCP_OPT_LEN_INDEX] = uLen;
379 if (memcpy_s(uOption + DHCP_OPT_DATA_INDEX, sizeof(uint32_t), &uValue, uLen) != EOK) {
380 return 0;
381 }
382
383 int nLen = DHCP_OPT_CODE_BYTES + DHCP_OPT_LEN_BYTES + uOption[DHCP_OPT_LEN_INDEX];
384 return AddOptStrToOpts(pOpts, uOption, nLen);
385 }
386