1518678f8Sopenharmony_ci/*
2518678f8Sopenharmony_ci * Copyright (C) 2021-2022 Huawei Device Co., Ltd.
3518678f8Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
4518678f8Sopenharmony_ci * you may not use this file except in compliance with the License.
5518678f8Sopenharmony_ci * You may obtain a copy of the License at
6518678f8Sopenharmony_ci *
7518678f8Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
8518678f8Sopenharmony_ci *
9518678f8Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
10518678f8Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
11518678f8Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12518678f8Sopenharmony_ci * See the License for the specific language governing permissions and
13518678f8Sopenharmony_ci * limitations under the License.
14518678f8Sopenharmony_ci */
15518678f8Sopenharmony_ci
16518678f8Sopenharmony_ci#include "dhcp_option.h"
17518678f8Sopenharmony_ci#include <stdint.h>
18518678f8Sopenharmony_ci#include <stdlib.h>
19518678f8Sopenharmony_ci#include "dhcp_s_define.h"
20518678f8Sopenharmony_ci#include "dhcp_logger.h"
21518678f8Sopenharmony_ci#include "securec.h"
22518678f8Sopenharmony_ci
23518678f8Sopenharmony_ciDEFINE_DHCPLOG_DHCP_LABEL("DhcpServerOption");
24518678f8Sopenharmony_ci
25518678f8Sopenharmony_ciPDhcpOptionNode CreateOptionNode(PDhcpOption opt)
26518678f8Sopenharmony_ci{
27518678f8Sopenharmony_ci    if (!opt) {
28518678f8Sopenharmony_ci        DHCP_LOGE("input parameter is null.");
29518678f8Sopenharmony_ci        return nullptr;
30518678f8Sopenharmony_ci    }
31518678f8Sopenharmony_ci    DhcpOptionNode *pNode = (DhcpOptionNode *)calloc(1, sizeof(DhcpOptionNode));
32518678f8Sopenharmony_ci    if (pNode == nullptr) {
33518678f8Sopenharmony_ci        DHCP_LOGE("failed to create dhcp option node!");
34518678f8Sopenharmony_ci        return nullptr;
35518678f8Sopenharmony_ci    }
36518678f8Sopenharmony_ci    pNode->option.code = opt->code;
37518678f8Sopenharmony_ci    pNode->option.length = opt->length;
38518678f8Sopenharmony_ci    if (memcpy_s(pNode->option.data, sizeof(pNode->option.data), opt->data, opt->length) != EOK) {
39518678f8Sopenharmony_ci        DHCP_LOGE("create option node failed when memcpy opt data!");
40518678f8Sopenharmony_ci        free(pNode);
41518678f8Sopenharmony_ci        pNode = nullptr;
42518678f8Sopenharmony_ci        return nullptr;
43518678f8Sopenharmony_ci    }
44518678f8Sopenharmony_ci    pNode->previous = pNode->next = 0;
45518678f8Sopenharmony_ci    return pNode;
46518678f8Sopenharmony_ci}
47518678f8Sopenharmony_ci
48518678f8Sopenharmony_ciint HasInitialized(PDhcpOptionList pOptions)
49518678f8Sopenharmony_ci{
50518678f8Sopenharmony_ci    if (!pOptions) {
51518678f8Sopenharmony_ci        DHCP_LOGE("option list pointer is null.");
52518678f8Sopenharmony_ci        return 0;
53518678f8Sopenharmony_ci    }
54518678f8Sopenharmony_ci    if (pOptions->first != nullptr) {
55518678f8Sopenharmony_ci        return 1;
56518678f8Sopenharmony_ci    }
57518678f8Sopenharmony_ci    return 0;
58518678f8Sopenharmony_ci}
59518678f8Sopenharmony_ci
60518678f8Sopenharmony_ciint InitOptionList(PDhcpOptionList pOptions)
61518678f8Sopenharmony_ci{
62518678f8Sopenharmony_ci    DHCP_LOGI("start %{public}s %{public}d ", __func__, __LINE__);
63518678f8Sopenharmony_ci    if (!pOptions) {
64518678f8Sopenharmony_ci        return RET_ERROR;
65518678f8Sopenharmony_ci    }
66518678f8Sopenharmony_ci    if (pOptions->first != nullptr && pOptions->first == pOptions->last) {
67518678f8Sopenharmony_ci        DHCP_LOGE(" start %{public}s %{public}d   over  return success", __func__, __LINE__);
68518678f8Sopenharmony_ci        return RET_SUCCESS;
69518678f8Sopenharmony_ci    }
70518678f8Sopenharmony_ci
71518678f8Sopenharmony_ci    DhcpOptionNode *pNode = (DhcpOptionNode *)calloc(1, sizeof(DhcpOptionNode));
72518678f8Sopenharmony_ci    if (!pNode) {
73518678f8Sopenharmony_ci        DHCP_LOGE("failed to create dhcp option node!");
74518678f8Sopenharmony_ci        return 1;
75518678f8Sopenharmony_ci    }
76518678f8Sopenharmony_ci
77518678f8Sopenharmony_ci    pOptions->size = 0;
78518678f8Sopenharmony_ci    pOptions->first = pOptions->last = pNode;
79518678f8Sopenharmony_ci    pOptions->first->previous = nullptr;
80518678f8Sopenharmony_ci    pOptions->last->next = nullptr;
81518678f8Sopenharmony_ci    DHCP_LOGI("start %{public}s  %{public}d success ", __func__, __LINE__);
82518678f8Sopenharmony_ci    return RET_SUCCESS;
83518678f8Sopenharmony_ci}
84518678f8Sopenharmony_ci
85518678f8Sopenharmony_ciint PushBackOption(PDhcpOptionList pOptions, PDhcpOption pOption)
86518678f8Sopenharmony_ci{
87518678f8Sopenharmony_ci    if (!pOptions) {
88518678f8Sopenharmony_ci        DHCP_LOGE("option list pointer is null.");
89518678f8Sopenharmony_ci        return RET_ERROR;
90518678f8Sopenharmony_ci    }
91518678f8Sopenharmony_ci    if (!pOption) {
92518678f8Sopenharmony_ci        DHCP_LOGE("option pointer is null.");
93518678f8Sopenharmony_ci        return RET_ERROR;
94518678f8Sopenharmony_ci    }
95518678f8Sopenharmony_ci    if (pOptions->first == nullptr) {
96518678f8Sopenharmony_ci        DHCP_LOGE("option list not initialized");
97518678f8Sopenharmony_ci        return RET_SUCCESS;
98518678f8Sopenharmony_ci    }
99518678f8Sopenharmony_ci    DhcpOptionNode *pNode = CreateOptionNode(pOption);
100518678f8Sopenharmony_ci    if (!pNode) {
101518678f8Sopenharmony_ci        DHCP_LOGE("failed to create option node.");
102518678f8Sopenharmony_ci        return 1;
103518678f8Sopenharmony_ci    }
104518678f8Sopenharmony_ci    pNode->previous = pOptions->last;
105518678f8Sopenharmony_ci    pOptions->last->next = pNode;
106518678f8Sopenharmony_ci    pOptions->last = pNode;
107518678f8Sopenharmony_ci    pOptions->size++;
108518678f8Sopenharmony_ci
109518678f8Sopenharmony_ci    return RET_SUCCESS;
110518678f8Sopenharmony_ci}
111518678f8Sopenharmony_ci
112518678f8Sopenharmony_ciint PushFrontOption(PDhcpOptionList pOptions, PDhcpOption pOption)
113518678f8Sopenharmony_ci{
114518678f8Sopenharmony_ci    if (!pOptions) {
115518678f8Sopenharmony_ci        DHCP_LOGE("option list pointer is null.");
116518678f8Sopenharmony_ci        return RET_ERROR;
117518678f8Sopenharmony_ci    }
118518678f8Sopenharmony_ci    if (!pOption) {
119518678f8Sopenharmony_ci        DHCP_LOGE("option pointer is null.");
120518678f8Sopenharmony_ci        return RET_ERROR;
121518678f8Sopenharmony_ci    }
122518678f8Sopenharmony_ci    PDhcpOptionNode pNode = CreateOptionNode(pOption);
123518678f8Sopenharmony_ci    if (!pNode) {
124518678f8Sopenharmony_ci        return RET_FAILED;
125518678f8Sopenharmony_ci    }
126518678f8Sopenharmony_ci
127518678f8Sopenharmony_ci    if (pOptions->first == pOptions->last) {
128518678f8Sopenharmony_ci        pNode->previous = pOptions->first;
129518678f8Sopenharmony_ci        pOptions->first->next = pNode;
130518678f8Sopenharmony_ci        pOptions->last = pNode;
131518678f8Sopenharmony_ci    } else {
132518678f8Sopenharmony_ci        pNode->next = pOptions->first->next;
133518678f8Sopenharmony_ci        pNode->next->previous = pNode;
134518678f8Sopenharmony_ci        pNode->previous = pOptions->first;
135518678f8Sopenharmony_ci        pOptions->first->next = pNode;
136518678f8Sopenharmony_ci    }
137518678f8Sopenharmony_ci    pOptions->size++;
138518678f8Sopenharmony_ci
139518678f8Sopenharmony_ci    return RET_SUCCESS;
140518678f8Sopenharmony_ci}
141518678f8Sopenharmony_ci
142518678f8Sopenharmony_ciint RemoveOption(PDhcpOptionList pOptions, uint8_t code)
143518678f8Sopenharmony_ci{
144518678f8Sopenharmony_ci    if (pOptions == nullptr) {
145518678f8Sopenharmony_ci        return RET_ERROR;
146518678f8Sopenharmony_ci    }
147518678f8Sopenharmony_ci    if (pOptions->size == 0) {
148518678f8Sopenharmony_ci        return RET_FAILED;
149518678f8Sopenharmony_ci    }
150518678f8Sopenharmony_ci    DhcpOptionNode *pNode = GetOptionNode(pOptions, code);
151518678f8Sopenharmony_ci    if (pNode == nullptr) {
152518678f8Sopenharmony_ci        return RET_FAILED;
153518678f8Sopenharmony_ci    }
154518678f8Sopenharmony_ci    if (pNode == pOptions->last) {
155518678f8Sopenharmony_ci        pOptions->last = pNode->previous;
156518678f8Sopenharmony_ci        pOptions->last->next = nullptr;
157518678f8Sopenharmony_ci    } else {
158518678f8Sopenharmony_ci        pNode->next->previous = pNode->previous;
159518678f8Sopenharmony_ci        pNode->previous->next = pNode->next;
160518678f8Sopenharmony_ci    }
161518678f8Sopenharmony_ci    pOptions->size--;
162518678f8Sopenharmony_ci    free(pNode);
163518678f8Sopenharmony_ci    pNode = nullptr;
164518678f8Sopenharmony_ci    return RET_SUCCESS;
165518678f8Sopenharmony_ci}
166518678f8Sopenharmony_ci
167518678f8Sopenharmony_ciPDhcpOptionNode GetOptionNode(PDhcpOptionList pOptions, uint8_t code)
168518678f8Sopenharmony_ci{
169518678f8Sopenharmony_ci    if (pOptions->first == nullptr) {
170518678f8Sopenharmony_ci        return nullptr;
171518678f8Sopenharmony_ci    }
172518678f8Sopenharmony_ci    PDhcpOptionNode pNode = pOptions->first->next;
173518678f8Sopenharmony_ci    while (pNode != nullptr && pNode->option.code != code) {
174518678f8Sopenharmony_ci        pNode = pNode->next;
175518678f8Sopenharmony_ci    }
176518678f8Sopenharmony_ci    return pNode;
177518678f8Sopenharmony_ci}
178518678f8Sopenharmony_ci
179518678f8Sopenharmony_ciPDhcpOption GetOption(PDhcpOptionList pOptions, uint8_t code)
180518678f8Sopenharmony_ci{
181518678f8Sopenharmony_ci    PDhcpOptionNode pNode = GetOptionNode(pOptions, code);
182518678f8Sopenharmony_ci    if (pNode) {
183518678f8Sopenharmony_ci        return &pNode->option;
184518678f8Sopenharmony_ci    }
185518678f8Sopenharmony_ci    return nullptr;
186518678f8Sopenharmony_ci}
187518678f8Sopenharmony_ci
188518678f8Sopenharmony_civoid ClearOptions(PDhcpOptionList pOptions)
189518678f8Sopenharmony_ci{
190518678f8Sopenharmony_ci    if (pOptions == nullptr || pOptions->size == 0) {
191518678f8Sopenharmony_ci        return;
192518678f8Sopenharmony_ci    }
193518678f8Sopenharmony_ci    DhcpOptionNode *pNode = pOptions->first->next;
194518678f8Sopenharmony_ci    while (pNode != nullptr) {
195518678f8Sopenharmony_ci        if (pNode == pOptions->last) {
196518678f8Sopenharmony_ci            pOptions->last = pOptions->first;
197518678f8Sopenharmony_ci            pOptions->last->next = nullptr;
198518678f8Sopenharmony_ci        } else {
199518678f8Sopenharmony_ci            pNode->next->previous = pNode->previous;
200518678f8Sopenharmony_ci            pNode->previous->next = pNode->next;
201518678f8Sopenharmony_ci        }
202518678f8Sopenharmony_ci        free(pNode);
203518678f8Sopenharmony_ci        pNode = pOptions->first->next;
204518678f8Sopenharmony_ci    }
205518678f8Sopenharmony_ci    pNode = pOptions->first;
206518678f8Sopenharmony_ci    pOptions->size = 0;
207518678f8Sopenharmony_ci    pOptions->first = pOptions->last = pNode;
208518678f8Sopenharmony_ci    pOptions->first->previous = nullptr;
209518678f8Sopenharmony_ci    pOptions->last->next = nullptr;
210518678f8Sopenharmony_ci}
211518678f8Sopenharmony_ci
212518678f8Sopenharmony_civoid FreeOptionList(PDhcpOptionList pOptions)
213518678f8Sopenharmony_ci{
214518678f8Sopenharmony_ci    if (pOptions == nullptr) {
215518678f8Sopenharmony_ci        return;
216518678f8Sopenharmony_ci    }
217518678f8Sopenharmony_ci    if (pOptions->first == nullptr) {
218518678f8Sopenharmony_ci        return;
219518678f8Sopenharmony_ci    }
220518678f8Sopenharmony_ci    DhcpOptionNode *pNode = pOptions->first->next;
221518678f8Sopenharmony_ci    while (pNode != nullptr) {
222518678f8Sopenharmony_ci        if (pNode == pOptions->last) {
223518678f8Sopenharmony_ci            pOptions->last = pOptions->first;
224518678f8Sopenharmony_ci            pOptions->last->next = nullptr;
225518678f8Sopenharmony_ci        } else {
226518678f8Sopenharmony_ci            pNode->next->previous = pNode->previous;
227518678f8Sopenharmony_ci            pNode->previous->next = pNode->next;
228518678f8Sopenharmony_ci        }
229518678f8Sopenharmony_ci        free(pNode);
230518678f8Sopenharmony_ci        pNode = pOptions->first->next;
231518678f8Sopenharmony_ci    }
232518678f8Sopenharmony_ci    pOptions->size = 0;
233518678f8Sopenharmony_ci    free(pOptions->first);
234518678f8Sopenharmony_ci    pOptions->first = pOptions->last = nullptr;
235518678f8Sopenharmony_ci    return;
236518678f8Sopenharmony_ci}
237518678f8Sopenharmony_ci
238518678f8Sopenharmony_ciint FillOption(PDhcpOption pOption, const char *data, size_t len)
239518678f8Sopenharmony_ci{
240518678f8Sopenharmony_ci    if (!pOption) {
241518678f8Sopenharmony_ci        return RET_ERROR;
242518678f8Sopenharmony_ci    }
243518678f8Sopenharmony_ci    if (!data) {
244518678f8Sopenharmony_ci        return RET_FAILED;
245518678f8Sopenharmony_ci    }
246518678f8Sopenharmony_ci    size_t flen = len;
247518678f8Sopenharmony_ci    if (flen > (DHCP_OPTION_SIZE - 1)) {
248518678f8Sopenharmony_ci        flen = DHCP_OPTION_SIZE - 1;
249518678f8Sopenharmony_ci    }
250518678f8Sopenharmony_ci    if (memcpy_s(pOption->data, sizeof(pOption->data) - 1, data, flen) != EOK) {
251518678f8Sopenharmony_ci        return RET_ERROR;
252518678f8Sopenharmony_ci    }
253518678f8Sopenharmony_ci    pOption->length = flen;
254518678f8Sopenharmony_ci    return RET_SUCCESS;
255518678f8Sopenharmony_ci}
256518678f8Sopenharmony_ci
257518678f8Sopenharmony_ciint FillU32Option(PDhcpOption pOption, uint32_t u32)
258518678f8Sopenharmony_ci{
259518678f8Sopenharmony_ci    if (!pOption) {
260518678f8Sopenharmony_ci        return RET_ERROR;
261518678f8Sopenharmony_ci    }
262518678f8Sopenharmony_ci    if (memcpy_s(pOption->data, sizeof(pOption->data), &u32, sizeof(uint32_t)) != EOK) {
263518678f8Sopenharmony_ci        return RET_ERROR;
264518678f8Sopenharmony_ci    }
265518678f8Sopenharmony_ci    pOption->length = sizeof(uint32_t);
266518678f8Sopenharmony_ci    return RET_SUCCESS;
267518678f8Sopenharmony_ci}
268518678f8Sopenharmony_ci
269518678f8Sopenharmony_ciint FillOptionData(PDhcpOption pOption, const uint8_t *data, size_t len)
270518678f8Sopenharmony_ci{
271518678f8Sopenharmony_ci    size_t flen = len;
272518678f8Sopenharmony_ci    if (!pOption) {
273518678f8Sopenharmony_ci        return RET_ERROR;
274518678f8Sopenharmony_ci    }
275518678f8Sopenharmony_ci    if (!data) {
276518678f8Sopenharmony_ci        return RET_FAILED;
277518678f8Sopenharmony_ci    }
278518678f8Sopenharmony_ci    if (flen > (DHCP_OPTION_SIZE)) {
279518678f8Sopenharmony_ci        flen = DHCP_OPTION_SIZE;
280518678f8Sopenharmony_ci    }
281518678f8Sopenharmony_ci    if (memcpy_s(pOption->data, sizeof(pOption->data), data, flen) != EOK) {
282518678f8Sopenharmony_ci        return RET_ERROR;
283518678f8Sopenharmony_ci    }
284518678f8Sopenharmony_ci    pOption->length = flen;
285518678f8Sopenharmony_ci    return RET_SUCCESS;
286518678f8Sopenharmony_ci}
287518678f8Sopenharmony_ci
288518678f8Sopenharmony_ciint AppendAddressOption(PDhcpOption pOption, uint32_t address)
289518678f8Sopenharmony_ci{
290518678f8Sopenharmony_ci    if (!pOption) {
291518678f8Sopenharmony_ci        return RET_ERROR;
292518678f8Sopenharmony_ci    }
293518678f8Sopenharmony_ci    uint8_t addrLen = pOption->length;
294518678f8Sopenharmony_ci    uint8_t *pData = pOption->data;
295518678f8Sopenharmony_ci    int spaceSize = sizeof(pOption->data) - addrLen;
296518678f8Sopenharmony_ci    if (spaceSize < DHCP_ADDRESS_LENGTH) {
297518678f8Sopenharmony_ci        DHCP_LOGE("failed to append address, not enough space for option data.");
298518678f8Sopenharmony_ci        return RET_ERROR;
299518678f8Sopenharmony_ci    }
300518678f8Sopenharmony_ci    if ((int)addrLen > 0) {
301518678f8Sopenharmony_ci        pData += addrLen;
302518678f8Sopenharmony_ci    }
303518678f8Sopenharmony_ci    if (memcpy_s(pData, spaceSize, &address, DHCP_ADDRESS_LENGTH) != EOK) {
304518678f8Sopenharmony_ci        return RET_ERROR;
305518678f8Sopenharmony_ci    }
306518678f8Sopenharmony_ci    pOption->length += DHCP_ADDRESS_LENGTH;
307518678f8Sopenharmony_ci    return RET_SUCCESS;
308518678f8Sopenharmony_ci}
309