1/*
2 * Copyright (C) 2021-2022 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
16#include "dhcp_argument.h"
17#include <map>
18#include <getopt.h>
19#include <securec.h>
20#include <stddef.h>
21#include <stdint.h>
22#include <stdio.h>
23#include <string.h>
24#include "address_utils.h"
25#include "dhcp_s_define.h"
26#include "dhcp_logger.h"
27
28DEFINE_DHCPLOG_DHCP_LABEL("DhcpArgument");
29
30static std::map<std::string, ArgumentInfo> g_argumentsTable;
31
32static int PutIpArgument(const char *argument, const char *val)
33{
34    if (!ParseIpAddr(val)) {
35        DHCP_LOGE("%s format error.", argument);
36        return RET_FAILED;
37    }
38    return PutArgument(argument, val);
39}
40
41static int PutPoolArgument(const char *argument, const char *val)
42{
43    if (!val) {
44        return 0;
45    }
46    if (strchr(val, ',') == nullptr) {
47        DHCP_LOGE("too few pool option arguments.");
48        return RET_FAILED;
49    }
50    return PutArgument(argument, val);
51}
52
53static int ShowVersion(const char *argument, const char *val)
54{
55    DHCP_LOGI("version:%s\n", DHCPD_VERSION);
56    return RET_SUCCESS;
57}
58
59static int DefaultArgument(const char *argument, const char *val)
60{
61    DHCP_LOGI("Input argument is: [%s], value is [%s]", (argument == nullptr) ? "" : argument,
62        (val == nullptr) ? "" : val);
63    return RET_SUCCESS;
64}
65
66const char *g_optionString = "i:c:d:g:s:n:P:S:Bp:o:lb:rvhD";
67
68static struct option g_longOptions[] = {
69    {"ifname", REQUIRED_ARG, 0, 'i'},
70    {"conf", REQUIRED_ARG, 0, 'c'},
71    {"dns", REQUIRED_ARG, 0, 'd'},
72    {"gateway", REQUIRED_ARG, 0, 'g'},
73    {"server", REQUIRED_ARG, 0, 's'},
74    {"netmask", REQUIRED_ARG, 0, 'n'},
75    {"pool", REQUIRED_ARG, 0, 'P'},
76    {"lease", REQUIRED_ARG, 0, 0},
77    {"renewal", REQUIRED_ARG, 0, 0},
78    {"rebinding", REQUIRED_ARG, 0, 0},
79    {"version", NO_ARG, 0, 'v'},
80    {"help", NO_ARG, 0, 'h'},
81    {0, 0, 0, 0},
82};
83
84static DhcpUsage usages[] = {
85    {&g_longOptions[NUM_ZERO], "<interface>", "network interface name.", "--ifname eth0", 1, PutArgument},
86    {&g_longOptions[NUM_ONE], "<file>", "configure file name.", "--conf /etc/conf/dhcp_server.conf", 0, PutArgument},
87    {&g_longOptions[NUM_TWO], "<dns1>[,dns2][,dns3][...]", "domain name server IP address list.", "", 0, PutArgument},
88    {&g_longOptions[NUM_THREE], "<gateway>", "gateway option.", "", 0, PutIpArgument},
89    {&g_longOptions[NUM_FOUR], "<server>", "server identifier.", "", 1, PutIpArgument},
90    {&g_longOptions[NUM_FIVE], "<netmask>", "default subnet mask.", "", 1, PutIpArgument},
91    {&g_longOptions[NUM_SIX], "<beginip>,<endip>", "pool address range.", "", 0,
92        PutPoolArgument},
93    {&g_longOptions[NUM_SEVEN], "<leaseTime>", "set lease time value, the value is in units of seconds.", "", 0,
94        PutArgument},
95    {&g_longOptions[NUM_EIGHT], "<renewalTime>", "set renewal time value, the value is in units of seconds.", "", 0,
96        PutArgument},
97    {&g_longOptions[NUM_NINE], "<rebindingTime>", "set rebinding time value, the value is in units of seconds.", "", 0,
98        PutArgument},
99    {&g_longOptions[NUM_TEN], "", "show version information.", "", 0, ShowVersion},
100    {&g_longOptions[NUM_ELEVEN], "", "show help information.", "", 0, DefaultArgument},
101    {0, "", "", ""},
102};
103
104int HasArgument(const char *argument)
105{
106    char name[ARGUMENT_NAME_SIZE] = {'\0'};
107    if (!argument) {
108        return 0;
109    }
110    size_t ssize = strlen(argument);
111    if (ssize > ARGUMENT_NAME_SIZE) {
112        ssize = ARGUMENT_NAME_SIZE;
113    }
114    if (memcpy_s(name, ARGUMENT_NAME_SIZE, argument, ssize) != EOK) {
115        DHCP_LOGE("failed to set argument name.");
116        return 0;
117    }
118    if (g_argumentsTable.empty()) {
119        return 0;
120    }
121    if (g_argumentsTable.count(name) > 0) {
122        return 1;
123    }
124    return 0;
125}
126
127static void ShowUsage(const DhcpUsage *usage)
128{
129    if (!usage || !usage->opt) {
130        return;
131    }
132    if (usage->opt->val) {
133        DHCP_LOGI("-%{public}c,--%{public}s ", (char)usage->opt->val, usage->opt->name);
134    } else {
135        DHCP_LOGI("   --%{public}s ", usage->opt->name);
136    }
137    if (usage->params[0] == '\0') {
138        DHCP_LOGI("\t\t%{public}s\n", usage->desc);
139    } else {
140        int plen = strlen(usage->params) + strlen(usage->params);
141        if (plen < USAGE_DESC_MAX_LENGTH) {
142            DHCP_LOGI("\t\t%{public}s\t\t%{public}s\n", usage->params, usage->desc);
143        } else {
144            DHCP_LOGI("\t\t%{public}s\n", usage->params);
145            DHCP_LOGI("\t\t\t%{public}s\n\n", usage->desc);
146        }
147    }
148}
149
150void PrintRequiredArguments(void)
151{
152    size_t argc = sizeof(usages) / sizeof(DhcpUsage);
153    DHCP_LOGI("required parameters:");
154    int idx = 0;
155    for (size_t i = 0; i < argc; i++) {
156        DhcpUsage usage = usages[i];
157        if (!usage.opt) {
158            break;
159        }
160        if (usage.required) {
161            if (idx == 0) {
162                DHCP_LOGI("\"%{public}s\"", usage.opt->name);
163            } else {
164                DHCP_LOGI(", \"%{public}s\"", usage.opt->name);
165            }
166            idx++;
167        }
168    }
169    DHCP_LOGI(".\n\n");
170    DHCP_LOGI("Usage: dhcp_server [options] \n");
171    DHCP_LOGI("e.g: dhcp_server -i eth0 -c /data/service/el1/public/dhcp/dhcp_server.conf \n");
172    DHCP_LOGI("     dhcp_server --help \n\n");
173}
174
175static void PrintUsage(void)
176{
177    DHCP_LOGI("Usage: dhcp_server [options] \n\n");
178
179    size_t argc = sizeof(usages) / sizeof(DhcpUsage);
180    for (size_t i = 0; i < argc; i++) {
181        DhcpUsage usage = usages[i];
182        if (!usage.opt) {
183            break;
184        }
185        ShowUsage(&usage);
186    }
187    DHCP_LOGI("\n");
188}
189
190void ShowHelp(int argc)
191{
192    if (argc == NUM_TWO) {
193        PrintUsage();
194        return;
195    }
196}
197
198int InitArguments(void)
199{
200    DHCP_LOGI("start InitArguments.");
201    g_argumentsTable.clear();
202    DHCP_LOGI("end InitArguments.");
203    return RET_SUCCESS;
204}
205
206ArgumentInfo *GetArgument(const char *name)
207{
208    char argName[ARGUMENT_NAME_SIZE] = {'\0'};
209    size_t ssize = strlen(name);
210    if (ssize > ARGUMENT_NAME_SIZE) {
211        ssize = ARGUMENT_NAME_SIZE;
212    }
213    if (memcpy_s(argName, ARGUMENT_NAME_SIZE, name, ssize) != EOK) {
214        DHCP_LOGE("failed to set argument name.");
215        return nullptr;
216    }
217    if (g_argumentsTable.count(argName) > 0) {
218        return &g_argumentsTable[argName];
219    }
220    return nullptr;
221}
222
223int PutArgument(const char *argument, const char *val)
224{
225    DHCP_LOGI("start PutArgument.");
226    if (!argument) {
227        return RET_FAILED;
228    }
229    if (!val) {
230        return RET_FAILED;
231    }
232
233    if (HasArgument(argument)) {
234        return RET_FAILED;
235    }
236
237    ArgumentInfo arg;
238    size_t ssize = strlen(argument);
239    if (ssize >= ARGUMENT_NAME_SIZE) {
240        ssize = ARGUMENT_NAME_SIZE -1;
241    }
242    size_t vlen = strlen(val);
243    if (memset_s(arg.name, ARGUMENT_NAME_SIZE, '\0', ARGUMENT_NAME_SIZE) != EOK) {
244        DHCP_LOGE("failed to reset argument name.");
245        return RET_ERROR;
246    }
247    if (memcpy_s(arg.name, ARGUMENT_NAME_SIZE, argument, ssize) != EOK) {
248        DHCP_LOGE("failed to set argument name.");
249        return RET_ERROR;
250    }
251    if (vlen >= ARGUMENT_VALUE_SIZE) {
252        DHCP_LOGE("value string too long.");
253        return RET_ERROR;
254    }
255    if (memset_s(arg.value, ARGUMENT_VALUE_SIZE, '\0', ARGUMENT_NAME_SIZE) != EOK) {
256        DHCP_LOGE("failed to reset argument value.");
257        return RET_ERROR;
258    }
259    if (memcpy_s(arg.value, ARGUMENT_VALUE_SIZE, val, vlen) != EOK) {
260        DHCP_LOGE("failed to set argument value.");
261        return RET_ERROR;
262    }
263    g_argumentsTable[std::string(arg.name)] = arg;
264    return RET_SUCCESS;
265}
266
267int FindIndex(int c)
268{
269    int size = sizeof(g_longOptions) / sizeof(g_longOptions[0]);
270    for (int i = 0; i < size; ++i) {
271        if (g_longOptions[i].val == c) {
272            return i;
273        }
274    }
275    return -1;
276}
277
278int ParseArguments(const std::string& ifName, const std::string& netMask, const std::string& ipRange,
279    const std::string& localIp)
280{
281    DHCP_LOGI("start ParseArguments.");
282    PutArgument("ifname", ifName.c_str());
283    PutIpArgument("server", localIp.c_str());
284    PutIpArgument("netmask", netMask.c_str());
285    PutPoolArgument("pool", ipRange.c_str());
286    return 0;
287}
288
289void FreeArguments(void)
290{
291    g_argumentsTable.clear();
292}
293