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