VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxManage/VBoxManageDHCPServer.cpp

Last change on this file was 106061, checked in by vboxsync, 4 weeks ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 56.0 KB
Line 
1/* $Id: VBoxManageDHCPServer.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * VBoxManage - Implementation of dhcpserver command.
4 */
5
6/*
7 * Copyright (C) 2006-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#include <VBox/com/com.h>
33#include <VBox/com/array.h>
34#include <VBox/com/ErrorInfo.h>
35#include <VBox/com/errorprint.h>
36#include <VBox/com/VirtualBox.h>
37
38#include <iprt/cidr.h>
39#include <iprt/param.h>
40#include <iprt/path.h>
41#include <iprt/stream.h>
42#include <iprt/string.h>
43#include <iprt/net.h>
44#include <iprt/getopt.h>
45#include <iprt/ctype.h>
46
47#include <VBox/log.h>
48
49#include "VBoxManage.h"
50
51#include <vector>
52#include <map>
53
54using namespace com;
55
56DECLARE_TRANSLATION_CONTEXT(DHCPServer);
57
58
59/*********************************************************************************************************************************
60* Defined Constants And Macros *
61*********************************************************************************************************************************/
62#define DHCPD_CMD_COMMON_OPT_NETWORK 999 /**< The --network / --netname option number. */
63#define DHCPD_CMD_COMMON_OPT_INTERFACE 998 /**< The --interface / --ifname option number. */
64/** Common option definitions. */
65#define DHCPD_CMD_COMMON_OPTION_DEFS() \
66 { "--network", DHCPD_CMD_COMMON_OPT_NETWORK, RTGETOPT_REQ_STRING }, \
67 { "--netname", DHCPD_CMD_COMMON_OPT_NETWORK, RTGETOPT_REQ_STRING }, /* legacy */ \
68 { "--interface", DHCPD_CMD_COMMON_OPT_INTERFACE, RTGETOPT_REQ_STRING }, \
69 { "--ifname", DHCPD_CMD_COMMON_OPT_INTERFACE, RTGETOPT_REQ_STRING } /* legacy */
70
71/** Handles common options in the typical option parsing switch. */
72#define DHCPD_CMD_COMMON_OPTION_CASES(a_pCtx, a_ch, a_pValueUnion) \
73 case DHCPD_CMD_COMMON_OPT_NETWORK: \
74 if ((a_pCtx)->pszInterface != NULL) \
75 return errorSyntax(DHCPServer::tr("Either --network or --interface, not both")); \
76 (a_pCtx)->pszNetwork = ValueUnion.psz; \
77 break; \
78 case DHCPD_CMD_COMMON_OPT_INTERFACE: \
79 if ((a_pCtx)->pszNetwork != NULL) \
80 return errorSyntax(DHCPServer::tr("Either --interface or --network, not both")); \
81 (a_pCtx)->pszInterface = ValueUnion.psz; \
82 break
83
84
85/*********************************************************************************************************************************
86* Structures and Typedefs *
87*********************************************************************************************************************************/
88/** Pointer to a dhcpserver command context. */
89typedef struct DHCPDCMDCTX *PDHCPDCMDCTX;
90
91/**
92 * Definition of a dhcpserver command, with handler and various flags.
93 */
94typedef struct DHCPDCMDDEF
95{
96 /** The command name. */
97 const char *pszName;
98
99 /**
100 * Actual command handler callback.
101 *
102 * @param pCtx Pointer to command context to use.
103 */
104 DECLR3CALLBACKMEMBER(RTEXITCODE, pfnHandler, (PDHCPDCMDCTX pCtx, int argc, char **argv));
105
106 /** The sub-command scope flags. */
107 uint64_t fSubcommandScope;
108} DHCPDCMDDEF;
109/** Pointer to a const dhcpserver command definition. */
110typedef DHCPDCMDDEF const *PCDHCPDCMDDEF;
111
112/**
113 * dhcpserver command context (mainly for carrying common options and such).
114 */
115typedef struct DHCPDCMDCTX
116{
117 /** The handler arguments from the main() function. */
118 HandlerArg *pArg;
119 /** Pointer to the command definition. */
120 PCDHCPDCMDDEF pCmdDef;
121 /** The network name. */
122 const char *pszNetwork;
123 /** The (trunk) interface name. */
124 const char *pszInterface;
125} DHCPDCMDCTX;
126
127typedef std::pair<DHCPOption_T, Utf8Str> DhcpOptSpec;
128typedef std::vector<DhcpOptSpec> DhcpOpts;
129typedef DhcpOpts::iterator DhcpOptIterator;
130
131typedef std::vector<DHCPOption_T> DhcpOptIds;
132typedef DhcpOptIds::iterator DhcpOptIdIterator;
133
134struct VmNameSlotKey
135{
136 const Utf8Str VmName;
137 uint8_t u8Slot;
138
139 VmNameSlotKey(const Utf8Str &aVmName, uint8_t aSlot)
140 : VmName(aVmName)
141 , u8Slot(aSlot)
142 {}
143
144 bool operator<(const VmNameSlotKey& that) const
145 {
146 if (VmName == that.VmName)
147 return u8Slot < that.u8Slot;
148 return VmName < that.VmName;
149 }
150};
151
152typedef std::map<VmNameSlotKey, DhcpOpts> VmSlot2OptionsM;
153typedef VmSlot2OptionsM::iterator VmSlot2OptionsIterator;
154typedef VmSlot2OptionsM::value_type VmSlot2OptionsPair;
155
156typedef std::map<VmNameSlotKey, DhcpOptIds> VmSlot2OptionIdsM;
157typedef VmSlot2OptionIdsM::iterator VmSlot2OptionIdsIterator;
158
159
160
161/**
162 * Helper that find the DHCP server instance.
163 *
164 * @returns The DHCP server instance. NULL if failed (complaining done).
165 * @param pCtx The DHCP server command context.
166 */
167static ComPtr<IDHCPServer> dhcpdFindServer(PDHCPDCMDCTX pCtx)
168{
169 ComPtr<IDHCPServer> ptrRet;
170 if (pCtx->pszNetwork || pCtx->pszInterface)
171 {
172 Assert(pCtx->pszNetwork == NULL || pCtx->pszInterface == NULL);
173
174 /*
175 * We need a network name to find the DHCP server. So, if interface is
176 * given we have to look it up.
177 */
178 HRESULT hrc;
179 Bstr bstrNetName(pCtx->pszNetwork);
180 if (!pCtx->pszNetwork)
181 {
182 ComPtr<IHost> ptrIHost;
183 CHECK_ERROR2_RET(hrc, pCtx->pArg->virtualBox, COMGETTER(Host)(ptrIHost.asOutParam()), ptrRet);
184
185 Bstr bstrInterface(pCtx->pszInterface);
186 ComPtr<IHostNetworkInterface> ptrIHostIf;
187 CHECK_ERROR2(hrc, ptrIHost, FindHostNetworkInterfaceByName(bstrInterface.raw(), ptrIHostIf.asOutParam()));
188 if (FAILED(hrc))
189 {
190 errorArgument(DHCPServer::tr("Failed to locate host-only interface '%s'"), pCtx->pszInterface);
191 return ptrRet;
192 }
193
194 CHECK_ERROR2_RET(hrc, ptrIHostIf, COMGETTER(NetworkName)(bstrNetName.asOutParam()), ptrRet);
195 }
196
197 /*
198 * Now, try locate the server
199 */
200 hrc = pCtx->pArg->virtualBox->FindDHCPServerByNetworkName(bstrNetName.raw(), ptrRet.asOutParam());
201 if (SUCCEEDED(hrc))
202 return ptrRet;
203 if (pCtx->pszNetwork)
204 errorArgument(DHCPServer::tr("Failed to find DHCP server for network '%s'"), pCtx->pszNetwork);
205 else
206 errorArgument(DHCPServer::tr("Failed to find DHCP server for host-only interface '%s' (network '%ls')"),
207 pCtx->pszInterface, bstrNetName.raw());
208 }
209 else
210 errorSyntax(DHCPServer::tr("You need to specify either --network or --interface to identify the DHCP server"));
211 return ptrRet;
212}
213
214
215/**
216 * Helper class for dhcpdHandleAddAndModify
217 */
218class DHCPCmdScope
219{
220 DHCPConfigScope_T m_enmScope;
221 const char *m_pszName;
222 uint8_t m_uSlot;
223 ComPtr<IDHCPConfig> m_ptrConfig;
224 ComPtr<IDHCPGlobalConfig> m_ptrGlobalConfig;
225 ComPtr<IDHCPGroupConfig> m_ptrGroupConfig;
226 ComPtr<IDHCPIndividualConfig> m_ptrIndividualConfig;
227
228public:
229 DHCPCmdScope()
230 : m_enmScope(DHCPConfigScope_Global)
231 , m_pszName(NULL)
232 , m_uSlot(0)
233 {
234 }
235
236 void setGlobal()
237 {
238 m_enmScope = DHCPConfigScope_Global;
239 m_pszName = NULL;
240 m_uSlot = 0;
241 resetPointers();
242 }
243
244 void setGroup(const char *pszGroup)
245 {
246 m_enmScope = DHCPConfigScope_Group;
247 m_pszName = pszGroup;
248 m_uSlot = 0;
249 resetPointers();
250 }
251
252 void setMachineNIC(const char *pszMachine)
253 {
254 m_enmScope = DHCPConfigScope_MachineNIC;
255 m_pszName = pszMachine;
256 m_uSlot = 0;
257 resetPointers();
258 }
259
260 void setMachineSlot(uint8_t uSlot)
261 {
262 Assert(m_enmScope == DHCPConfigScope_MachineNIC);
263 m_uSlot = uSlot;
264 resetPointers();
265 }
266
267 void setMACAddress(const char *pszMACAddress)
268 {
269 m_enmScope = DHCPConfigScope_MAC;
270 m_pszName = pszMACAddress;
271 m_uSlot = 0;
272 resetPointers();
273 }
274
275 ComPtr<IDHCPConfig> &getConfig(ComPtr<IDHCPServer> const &ptrDHCPServer)
276 {
277 if (m_ptrConfig.isNull())
278 {
279 CHECK_ERROR2I_STMT(ptrDHCPServer, GetConfig(m_enmScope, Bstr(m_pszName).raw(), m_uSlot, TRUE /*mayAdd*/,
280 m_ptrConfig.asOutParam()), m_ptrConfig.setNull());
281 }
282 return m_ptrConfig;
283 }
284
285 ComPtr<IDHCPIndividualConfig> &getIndividual(ComPtr<IDHCPServer> const &ptrDHCPServer)
286 {
287 getConfig(ptrDHCPServer);
288 if (m_ptrIndividualConfig.isNull() && m_ptrConfig.isNotNull())
289 {
290 HRESULT hrc = m_ptrConfig.queryInterfaceTo(m_ptrIndividualConfig.asOutParam());
291 if (FAILED(hrc))
292 {
293 com::GlueHandleComError(m_ptrConfig, "queryInterface", hrc, __FILE__, __LINE__);
294 m_ptrIndividualConfig.setNull();
295 }
296 }
297 return m_ptrIndividualConfig;
298 }
299
300 ComPtr<IDHCPGroupConfig> &getGroup(ComPtr<IDHCPServer> const &ptrDHCPServer)
301 {
302 getConfig(ptrDHCPServer);
303 if (m_ptrGroupConfig.isNull() && m_ptrConfig.isNotNull())
304 {
305 HRESULT hrc = m_ptrConfig.queryInterfaceTo(m_ptrGroupConfig.asOutParam());
306 if (FAILED(hrc))
307 {
308 com::GlueHandleComError(m_ptrConfig, "queryInterface", hrc, __FILE__, __LINE__);
309 m_ptrGroupConfig.setNull();
310 }
311 }
312 return m_ptrGroupConfig;
313 }
314
315 DHCPConfigScope_T getScope() const { return m_enmScope; }
316
317private:
318 void resetPointers()
319 {
320 m_ptrConfig.setNull();
321 m_ptrGlobalConfig.setNull();
322 m_ptrIndividualConfig.setNull();
323 m_ptrGroupConfig.setNull();
324 }
325};
326
327enum
328{
329 DHCP_ADDMOD = 1000,
330 DHCP_ADDMOD_FORCE_OPTION,
331 DHCP_ADDMOD_UNFORCE_OPTION,
332 DHCP_ADDMOD_SUPPRESS_OPTION,
333 DHCP_ADDMOD_UNSUPPRESS_OPTION,
334 DHCP_ADDMOD_ZAP_OPTIONS,
335 DHCP_ADDMOD_INCL_MAC,
336 DHCP_ADDMOD_EXCL_MAC,
337 DHCP_ADDMOD_DEL_MAC,
338 DHCP_ADDMOD_INCL_MAC_WILD,
339 DHCP_ADDMOD_EXCL_MAC_WILD,
340 DHCP_ADDMOD_DEL_MAC_WILD,
341 DHCP_ADDMOD_INCL_VENDOR,
342 DHCP_ADDMOD_EXCL_VENDOR,
343 DHCP_ADDMOD_DEL_VENDOR,
344 DHCP_ADDMOD_INCL_VENDOR_WILD,
345 DHCP_ADDMOD_EXCL_VENDOR_WILD,
346 DHCP_ADDMOD_DEL_VENDOR_WILD,
347 DHCP_ADDMOD_INCL_USER,
348 DHCP_ADDMOD_EXCL_USER,
349 DHCP_ADDMOD_DEL_USER,
350 DHCP_ADDMOD_INCL_USER_WILD,
351 DHCP_ADDMOD_EXCL_USER_WILD,
352 DHCP_ADDMOD_DEL_USER_WILD,
353 DHCP_ADDMOD_ZAP_CONDITIONS
354};
355
356/**
357 * Handles the 'add' and 'modify' subcommands.
358 */
359static DECLCALLBACK(RTEXITCODE) dhcpdHandleAddAndModify(PDHCPDCMDCTX pCtx, int argc, char **argv)
360{
361 static const RTGETOPTDEF s_aOptions[] =
362 {
363 DHCPD_CMD_COMMON_OPTION_DEFS(),
364 { "--server-ip", 'a', RTGETOPT_REQ_STRING },
365 { "--ip", 'a', RTGETOPT_REQ_STRING }, // deprecated
366 { "-ip", 'a', RTGETOPT_REQ_STRING }, // deprecated
367 { "--netmask", 'm', RTGETOPT_REQ_STRING },
368 { "-netmask", 'm', RTGETOPT_REQ_STRING }, // deprecated
369 { "--lower-ip", 'l', RTGETOPT_REQ_STRING },
370 { "--lowerip", 'l', RTGETOPT_REQ_STRING },
371 { "-lowerip", 'l', RTGETOPT_REQ_STRING }, // deprecated
372 { "--upper-ip", 'u', RTGETOPT_REQ_STRING },
373 { "--upperip", 'u', RTGETOPT_REQ_STRING },
374 { "-upperip", 'u', RTGETOPT_REQ_STRING }, // deprecated
375 { "--enable", 'e', RTGETOPT_REQ_NOTHING },
376 { "-enable", 'e', RTGETOPT_REQ_NOTHING }, // deprecated
377 { "--disable", 'd', RTGETOPT_REQ_NOTHING },
378 { "-disable", 'd', RTGETOPT_REQ_NOTHING }, // deprecated
379 { "--global", 'g', RTGETOPT_REQ_NOTHING },
380 { "--group", 'G', RTGETOPT_REQ_STRING },
381 { "--mac-address", 'E', RTGETOPT_REQ_MACADDR },
382 { "--vm", 'M', RTGETOPT_REQ_STRING },
383 { "--nic", 'n', RTGETOPT_REQ_UINT8 },
384 { "--set-opt", 's', RTGETOPT_REQ_UINT8 },
385 { "--set-opt-hex", 'x', RTGETOPT_REQ_UINT8 },
386 { "--del-opt", 'D', RTGETOPT_REQ_UINT8 },
387 { "--force-opt", DHCP_ADDMOD_FORCE_OPTION, RTGETOPT_REQ_UINT8 },
388 { "--unforce-opt", DHCP_ADDMOD_UNFORCE_OPTION, RTGETOPT_REQ_UINT8 },
389 { "--suppress-opt", DHCP_ADDMOD_SUPPRESS_OPTION, RTGETOPT_REQ_UINT8 },
390 { "--unsuppress-opt", DHCP_ADDMOD_UNSUPPRESS_OPTION, RTGETOPT_REQ_UINT8 },
391 { "--zap-options", DHCP_ADDMOD_ZAP_OPTIONS, RTGETOPT_REQ_NOTHING },
392 { "--min-lease-time", 'q' , RTGETOPT_REQ_UINT32 },
393 { "--default-lease-time", 'L' , RTGETOPT_REQ_UINT32 },
394 { "--max-lease-time", 'Q' , RTGETOPT_REQ_UINT32 },
395 { "--remove-config", 'R', RTGETOPT_REQ_NOTHING },
396 { "--fixed-address", 'f', RTGETOPT_REQ_STRING },
397 /* group conditions: */
398 { "--incl-mac", DHCP_ADDMOD_INCL_MAC, RTGETOPT_REQ_STRING },
399 { "--excl-mac", DHCP_ADDMOD_EXCL_MAC, RTGETOPT_REQ_STRING },
400 { "--del-mac", DHCP_ADDMOD_DEL_MAC, RTGETOPT_REQ_STRING },
401 { "--incl-mac-wild", DHCP_ADDMOD_INCL_MAC_WILD, RTGETOPT_REQ_STRING },
402 { "--excl-mac-wild", DHCP_ADDMOD_EXCL_MAC_WILD, RTGETOPT_REQ_STRING },
403 { "--del-mac-wild", DHCP_ADDMOD_DEL_MAC_WILD, RTGETOPT_REQ_STRING },
404 { "--incl-vendor", DHCP_ADDMOD_INCL_VENDOR, RTGETOPT_REQ_STRING },
405 { "--excl-vendor", DHCP_ADDMOD_EXCL_VENDOR, RTGETOPT_REQ_STRING },
406 { "--del-vendor", DHCP_ADDMOD_DEL_VENDOR, RTGETOPT_REQ_STRING },
407 { "--incl-vendor-wild", DHCP_ADDMOD_INCL_VENDOR_WILD, RTGETOPT_REQ_STRING },
408 { "--excl-vendor-wild", DHCP_ADDMOD_EXCL_VENDOR_WILD, RTGETOPT_REQ_STRING },
409 { "--del-vendor-wild", DHCP_ADDMOD_DEL_VENDOR_WILD, RTGETOPT_REQ_STRING },
410 { "--incl-user", DHCP_ADDMOD_INCL_USER, RTGETOPT_REQ_STRING },
411 { "--excl-user", DHCP_ADDMOD_EXCL_USER, RTGETOPT_REQ_STRING },
412 { "--del-user", DHCP_ADDMOD_DEL_USER, RTGETOPT_REQ_STRING },
413 { "--incl-user-wild", DHCP_ADDMOD_INCL_USER_WILD, RTGETOPT_REQ_STRING },
414 { "--excl-user-wild", DHCP_ADDMOD_EXCL_USER_WILD, RTGETOPT_REQ_STRING },
415 { "--del-user-wild", DHCP_ADDMOD_DEL_USER_WILD, RTGETOPT_REQ_STRING },
416 { "--zap-conditions", DHCP_ADDMOD_ZAP_CONDITIONS, RTGETOPT_REQ_NOTHING },
417 /* obsolete, to be removed: */
418 { "--id", 'i', RTGETOPT_REQ_UINT8 }, // obsolete, backwards compatibility only.
419 { "--value", 'p', RTGETOPT_REQ_STRING }, // obsolete, backwards compatibility only.
420 { "--remove", 'r', RTGETOPT_REQ_NOTHING }, // obsolete, backwards compatibility only.
421 { "--options", 'o', RTGETOPT_REQ_NOTHING }, // obsolete legacy, ignored
422
423 };
424
425 /*
426 * Parse the arguments in two passes:
427 *
428 * 1. Validate the command line and establish the IDHCPServer settings.
429 * 2. Execute the various IDHCPConfig settings changes.
430 *
431 * This is considered simpler than duplicating the command line instructions
432 * into elaborate structures and executing these.
433 */
434 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
435 ComPtr<IDHCPServer> ptrDHCPServer;
436 for (size_t iPass = 0; iPass < 2; iPass++)
437 {
438 const char *pszServerIp = NULL;
439 const char *pszNetmask = NULL;
440 const char *pszLowerIp = NULL;
441 const char *pszUpperIp = NULL;
442 int fEnabled = -1;
443
444 DHCPCmdScope Scope;
445 char szMACAddress[32];
446
447 bool fNeedValueOrRemove = false; /* Only used with --id; remove in 6.1+ */
448 uint8_t u8OptId = 0; /* Only used too keep --id for following --value/--remove. remove in 6.1+ */
449
450 RTGETOPTSTATE GetState;
451 int vrc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
452 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
453
454 RTGETOPTUNION ValueUnion;
455 while ((vrc = RTGetOpt(&GetState, &ValueUnion)))
456 {
457 switch (vrc)
458 {
459 DHCPD_CMD_COMMON_OPTION_CASES(pCtx, vrc, &ValueUnion);
460 case 'a': // --server-ip
461 pszServerIp = ValueUnion.psz;
462 break;
463 case 'm': // --netmask
464 pszNetmask = ValueUnion.psz;
465 break;
466 case 'l': // --lower-ip
467 pszLowerIp = ValueUnion.psz;
468 break;
469 case 'u': // --upper-ip
470 pszUpperIp = ValueUnion.psz;
471 break;
472 case 'e': // --enable
473 fEnabled = 1;
474 break;
475 case 'd': // --disable
476 fEnabled = 0;
477 break;
478
479 /*
480 * Configuration selection:
481 */
482 case 'g': // --global Sets the option scope to 'global'.
483 if (fNeedValueOrRemove)
484 return errorSyntax(DHCPServer::tr("Incomplete option sequence preseeding '--global'"));
485 Scope.setGlobal();
486 break;
487
488 case 'G': // --group
489 if (fNeedValueOrRemove)
490 return errorSyntax(DHCPServer::tr("Incomplete option sequence preseeding '--group'"));
491 if (!*ValueUnion.psz)
492 return errorSyntax(DHCPServer::tr("Group name cannot be empty"));
493 Scope.setGroup(ValueUnion.psz);
494 break;
495
496 case 'E': // --mac-address
497 if (fNeedValueOrRemove)
498 return errorSyntax(DHCPServer::tr("Incomplete option sequence preseeding '--mac-address'"));
499 RTStrPrintf(szMACAddress, sizeof(szMACAddress), "%RTmac", &ValueUnion.MacAddr);
500 Scope.setMACAddress(szMACAddress);
501 break;
502
503 case 'M': // --vm Sets the option scope to ValueUnion.psz + 0.
504 if (fNeedValueOrRemove)
505 return errorSyntax(DHCPServer::tr("Incomplete option sequence preseeding '--vm'"));
506 Scope.setMachineNIC(ValueUnion.psz);
507 break;
508
509 case 'n': // --nic Sets the option scope to pszVmName + (ValueUnion.u8 - 1).
510 if (Scope.getScope() != DHCPConfigScope_MachineNIC)
511 return errorSyntax(DHCPServer::tr("--nic option requires a --vm preceeding selecting the VM it should apply to"));
512 if (fNeedValueOrRemove)
513 return errorSyntax(DHCPServer::tr("Incomplete option sequence preseeding '--nic=%u"), ValueUnion.u8);
514 if (ValueUnion.u8 < 1)
515 return errorSyntax(DHCPServer::tr("invalid NIC number: %u"), ValueUnion.u8);
516 Scope.setMachineSlot(ValueUnion.u8 - 1);
517 break;
518
519 /*
520 * Modify configuration:
521 */
522 case 's': // --set-opt num stringvalue
523 {
524 uint8_t const idAddOpt = ValueUnion.u8;
525 vrc = RTGetOptFetchValue(&GetState, &ValueUnion, RTGETOPT_REQ_STRING);
526 if (RT_FAILURE(vrc))
527 return errorFetchValue(1, "--set-opt", vrc, &ValueUnion);
528 if (iPass == 1)
529 {
530 ComPtr<IDHCPConfig> &ptrConfig = Scope.getConfig(ptrDHCPServer);
531 if (ptrConfig.isNull())
532 return RTEXITCODE_FAILURE;
533 CHECK_ERROR2I_STMT(ptrConfig, SetOption((DHCPOption_T)idAddOpt, DHCPOptionEncoding_Normal,
534 Bstr(ValueUnion.psz).raw()), rcExit = RTEXITCODE_FAILURE);
535 }
536 break;
537 }
538
539 case 'x': // --set-opt-hex num hex-string
540 {
541 uint8_t const idAddOpt = ValueUnion.u8;
542 vrc = RTGetOptFetchValue(&GetState, &ValueUnion, RTGETOPT_REQ_STRING);
543 if (RT_FAILURE(vrc))
544 return errorFetchValue(1, "--set-opt-hex", vrc, &ValueUnion);
545 uint8_t abBuf[256];
546 size_t cbRet;
547 vrc = RTStrConvertHexBytesEx(ValueUnion.psz, abBuf, sizeof(abBuf), RTSTRCONVERTHEXBYTES_F_SEP_COLON,
548 NULL, &cbRet);
549 if (RT_FAILURE(vrc))
550 return errorArgument(DHCPServer::tr("Malformed hex string given to --set-opt-hex %u: %s\n"),
551 idAddOpt, ValueUnion.psz);
552 if (iPass == 1)
553 {
554 ComPtr<IDHCPConfig> &ptrConfig = Scope.getConfig(ptrDHCPServer);
555 if (ptrConfig.isNull())
556 return RTEXITCODE_FAILURE;
557 CHECK_ERROR2I_STMT(ptrConfig, SetOption((DHCPOption_T)idAddOpt, DHCPOptionEncoding_Hex,
558 Bstr(ValueUnion.psz).raw()), rcExit = RTEXITCODE_FAILURE);
559 }
560 break;
561 }
562
563 case 'D': // --del-opt num
564 if (pCtx->pCmdDef->fSubcommandScope == HELP_SCOPE_DHCPSERVER_ADD)
565 return errorSyntax(DHCPServer::tr("--del-opt does not apply to the 'add' subcommand"));
566 if (iPass == 1)
567 {
568 ComPtr<IDHCPConfig> &ptrConfig = Scope.getConfig(ptrDHCPServer);
569 if (ptrConfig.isNull())
570 return RTEXITCODE_FAILURE;
571 CHECK_ERROR2I_STMT(ptrConfig, RemoveOption((DHCPOption_T)ValueUnion.u8), rcExit = RTEXITCODE_FAILURE);
572 }
573 break;
574
575 case DHCP_ADDMOD_UNFORCE_OPTION: // --unforce-opt
576 if (pCtx->pCmdDef->fSubcommandScope == HELP_SCOPE_DHCPSERVER_ADD)
577 return errorSyntax(DHCPServer::tr("--unforce-opt does not apply to the 'add' subcommand"));
578 RT_FALL_THROUGH();
579 case DHCP_ADDMOD_UNSUPPRESS_OPTION: // --unsuppress-opt
580 if (pCtx->pCmdDef->fSubcommandScope == HELP_SCOPE_DHCPSERVER_ADD)
581 return errorSyntax(DHCPServer::tr("--unsuppress-opt does not apply to the 'add' subcommand"));
582 RT_FALL_THROUGH();
583 case DHCP_ADDMOD_FORCE_OPTION: // --force-opt
584 case DHCP_ADDMOD_SUPPRESS_OPTION: // --suppress-opt
585 if (iPass == 1)
586 {
587 DHCPOption_T const enmOption = (DHCPOption_T)ValueUnion.u8;
588 bool const fForced = vrc == DHCP_ADDMOD_FORCE_OPTION || vrc == DHCP_ADDMOD_UNFORCE_OPTION;
589
590 /* Get the current option list: */
591 ComPtr<IDHCPConfig> &ptrConfig = Scope.getConfig(ptrDHCPServer);
592 if (ptrConfig.isNull())
593 return RTEXITCODE_FAILURE;
594 com::SafeArray<DHCPOption_T> Options;
595 if (fForced)
596 CHECK_ERROR2I_STMT(ptrConfig, COMGETTER(ForcedOptions)(ComSafeArrayAsOutParam(Options)),
597 rcExit = RTEXITCODE_FAILURE; break);
598 else
599 CHECK_ERROR2I_STMT(ptrConfig, COMGETTER(SuppressedOptions)(ComSafeArrayAsOutParam(Options)),
600 rcExit = RTEXITCODE_FAILURE; break);
601 if (vrc == DHCP_ADDMOD_FORCE_OPTION || vrc == DHCP_ADDMOD_SUPPRESS_OPTION)
602 {
603 /* Add if not present. */
604 size_t iSrc;
605 for (iSrc = 0; iSrc < Options.size(); iSrc++)
606 if (Options[iSrc] == enmOption)
607 break;
608 if (iSrc < Options.size())
609 break; /* already present */
610 Options.push_back(enmOption);
611 }
612 else
613 {
614 /* Remove */
615 size_t iDst = 0;
616 for (size_t iSrc = 0; iSrc < Options.size(); iSrc++)
617 {
618 DHCPOption_T enmCurOpt = Options[iSrc];
619 if (enmCurOpt != enmOption)
620 Options[iDst++] = enmCurOpt;
621 }
622 if (iDst == Options.size())
623 break; /* Not found. */
624 Options.resize(iDst);
625 }
626
627 /* Update the option list: */
628 if (fForced)
629 CHECK_ERROR2I_STMT(ptrConfig, COMSETTER(ForcedOptions)(ComSafeArrayAsInParam(Options)),
630 rcExit = RTEXITCODE_FAILURE);
631 else
632 CHECK_ERROR2I_STMT(ptrConfig, COMSETTER(SuppressedOptions)(ComSafeArrayAsInParam(Options)),
633 rcExit = RTEXITCODE_FAILURE);
634 }
635 break;
636
637 case DHCP_ADDMOD_ZAP_OPTIONS:
638 if (pCtx->pCmdDef->fSubcommandScope == HELP_SCOPE_DHCPSERVER_ADD)
639 return errorSyntax(DHCPServer::tr("--zap-options does not apply to the 'add' subcommand"));
640 if (iPass == 1)
641 {
642 ComPtr<IDHCPConfig> &ptrConfig = Scope.getConfig(ptrDHCPServer);
643 if (ptrConfig.isNull())
644 return RTEXITCODE_FAILURE;
645 CHECK_ERROR2I_STMT(ptrConfig, RemoveAllOptions(), rcExit = RTEXITCODE_FAILURE);
646 }
647 break;
648
649 case 'q': // --min-lease-time
650 if (iPass == 1)
651 {
652 ComPtr<IDHCPConfig> &ptrConfig = Scope.getConfig(ptrDHCPServer);
653 if (ptrConfig.isNull())
654 return RTEXITCODE_FAILURE;
655 CHECK_ERROR2I_STMT(ptrConfig, COMSETTER(MinLeaseTime)(ValueUnion.u32), rcExit = RTEXITCODE_FAILURE);
656 }
657 break;
658
659 case 'L': // --default-lease-time
660 if (iPass == 1)
661 {
662 ComPtr<IDHCPConfig> &ptrConfig = Scope.getConfig(ptrDHCPServer);
663 if (ptrConfig.isNull())
664 return RTEXITCODE_FAILURE;
665 CHECK_ERROR2I_STMT(ptrConfig, COMSETTER(DefaultLeaseTime)(ValueUnion.u32), rcExit = RTEXITCODE_FAILURE);
666 }
667 break;
668
669 case 'Q': // --max-lease-time
670 if (iPass == 1)
671 {
672 ComPtr<IDHCPConfig> &ptrConfig = Scope.getConfig(ptrDHCPServer);
673 if (ptrConfig.isNull())
674 return RTEXITCODE_FAILURE;
675 CHECK_ERROR2I_STMT(ptrConfig, COMSETTER(MaxLeaseTime)(ValueUnion.u32), rcExit = RTEXITCODE_FAILURE);
676 }
677 break;
678
679 case 'R': // --remove-config
680 if (pCtx->pCmdDef->fSubcommandScope == HELP_SCOPE_DHCPSERVER_ADD)
681 return errorSyntax(DHCPServer::tr("--remove-config does not apply to the 'add' subcommand"));
682 if (Scope.getScope() == DHCPConfigScope_Global)
683 return errorSyntax(DHCPServer::tr("--remove-config cannot be applied to the global config"));
684 if (iPass == 1)
685 {
686 ComPtr<IDHCPConfig> &ptrConfig = Scope.getConfig(ptrDHCPServer);
687 if (ptrConfig.isNull())
688 return RTEXITCODE_FAILURE;
689 CHECK_ERROR2I_STMT(ptrConfig, Remove(), rcExit = RTEXITCODE_FAILURE);
690 }
691 Scope.setGlobal();
692 break;
693
694 case 'f': // --fixed-address
695 if (Scope.getScope() != DHCPConfigScope_MachineNIC && Scope.getScope() != DHCPConfigScope_MAC)
696 return errorSyntax(DHCPServer::tr("--fixed-address can only be applied to a VM NIC or an MAC address"));
697 if (iPass == 1)
698 {
699 ComPtr<IDHCPIndividualConfig> &ptrIndividualConfig = Scope.getIndividual(ptrDHCPServer);
700 if (ptrIndividualConfig.isNull())
701 return RTEXITCODE_FAILURE;
702 CHECK_ERROR2I_STMT(ptrIndividualConfig, COMSETTER(FixedAddress)(Bstr(ValueUnion.psz).raw()),
703 rcExit = RTEXITCODE_FAILURE);
704 }
705 break;
706
707 /*
708 * Group conditions:
709 */
710 case DHCP_ADDMOD_INCL_MAC:
711 case DHCP_ADDMOD_EXCL_MAC:
712 case DHCP_ADDMOD_DEL_MAC:
713 case DHCP_ADDMOD_INCL_MAC_WILD:
714 case DHCP_ADDMOD_EXCL_MAC_WILD:
715 case DHCP_ADDMOD_DEL_MAC_WILD:
716 case DHCP_ADDMOD_INCL_VENDOR:
717 case DHCP_ADDMOD_EXCL_VENDOR:
718 case DHCP_ADDMOD_DEL_VENDOR:
719 case DHCP_ADDMOD_INCL_VENDOR_WILD:
720 case DHCP_ADDMOD_EXCL_VENDOR_WILD:
721 case DHCP_ADDMOD_DEL_VENDOR_WILD:
722 case DHCP_ADDMOD_INCL_USER:
723 case DHCP_ADDMOD_EXCL_USER:
724 case DHCP_ADDMOD_DEL_USER:
725 case DHCP_ADDMOD_INCL_USER_WILD:
726 case DHCP_ADDMOD_EXCL_USER_WILD:
727 case DHCP_ADDMOD_DEL_USER_WILD:
728 {
729 if (Scope.getScope() != DHCPConfigScope_Group)
730 return errorSyntax(DHCPServer::tr("A group must be selected to perform condition alterations."));
731 if (!*ValueUnion.psz)
732 return errorSyntax(DHCPServer::tr("Condition value cannot be empty")); /* or can it? */
733 if (iPass != 1)
734 break;
735
736 DHCPGroupConditionType_T enmType;
737 switch (vrc)
738 {
739 case DHCP_ADDMOD_INCL_MAC: case DHCP_ADDMOD_EXCL_MAC: case DHCP_ADDMOD_DEL_MAC:
740 enmType = DHCPGroupConditionType_MAC;
741 break;
742 case DHCP_ADDMOD_INCL_MAC_WILD: case DHCP_ADDMOD_EXCL_MAC_WILD: case DHCP_ADDMOD_DEL_MAC_WILD:
743 enmType = DHCPGroupConditionType_MACWildcard;
744 break;
745 case DHCP_ADDMOD_INCL_VENDOR: case DHCP_ADDMOD_EXCL_VENDOR: case DHCP_ADDMOD_DEL_VENDOR:
746 enmType = DHCPGroupConditionType_vendorClassID;
747 break;
748 case DHCP_ADDMOD_INCL_VENDOR_WILD: case DHCP_ADDMOD_EXCL_VENDOR_WILD: case DHCP_ADDMOD_DEL_VENDOR_WILD:
749 enmType = DHCPGroupConditionType_vendorClassIDWildcard;
750 break;
751 case DHCP_ADDMOD_INCL_USER: case DHCP_ADDMOD_EXCL_USER: case DHCP_ADDMOD_DEL_USER:
752 enmType = DHCPGroupConditionType_userClassID;
753 break;
754 case DHCP_ADDMOD_INCL_USER_WILD: case DHCP_ADDMOD_EXCL_USER_WILD: case DHCP_ADDMOD_DEL_USER_WILD:
755 enmType = DHCPGroupConditionType_userClassIDWildcard;
756 break;
757 default:
758 AssertFailedReturn(RTEXITCODE_FAILURE);
759 }
760
761 int fInclusive;
762 switch (vrc)
763 {
764 case DHCP_ADDMOD_DEL_MAC:
765 case DHCP_ADDMOD_DEL_MAC_WILD:
766 case DHCP_ADDMOD_DEL_USER:
767 case DHCP_ADDMOD_DEL_USER_WILD:
768 case DHCP_ADDMOD_DEL_VENDOR:
769 case DHCP_ADDMOD_DEL_VENDOR_WILD:
770 fInclusive = -1;
771 break;
772 case DHCP_ADDMOD_EXCL_MAC:
773 case DHCP_ADDMOD_EXCL_MAC_WILD:
774 case DHCP_ADDMOD_EXCL_USER:
775 case DHCP_ADDMOD_EXCL_USER_WILD:
776 case DHCP_ADDMOD_EXCL_VENDOR:
777 case DHCP_ADDMOD_EXCL_VENDOR_WILD:
778 fInclusive = 0;
779 break;
780 case DHCP_ADDMOD_INCL_MAC:
781 case DHCP_ADDMOD_INCL_MAC_WILD:
782 case DHCP_ADDMOD_INCL_USER:
783 case DHCP_ADDMOD_INCL_USER_WILD:
784 case DHCP_ADDMOD_INCL_VENDOR:
785 case DHCP_ADDMOD_INCL_VENDOR_WILD:
786 fInclusive = 1;
787 break;
788 default:
789 AssertFailedReturn(RTEXITCODE_FAILURE);
790 }
791
792 ComPtr<IDHCPGroupConfig> &ptrGroupConfig = Scope.getGroup(ptrDHCPServer);
793 if (ptrGroupConfig.isNull())
794 return RTEXITCODE_FAILURE;
795 if (fInclusive >= 0)
796 {
797 ComPtr<IDHCPGroupCondition> ptrCondition;
798 CHECK_ERROR2I_STMT(ptrGroupConfig, AddCondition((BOOL)fInclusive, enmType, Bstr(ValueUnion.psz).raw(),
799 ptrCondition.asOutParam()), rcExit = RTEXITCODE_FAILURE);
800 }
801 else
802 {
803 com::SafeIfaceArray<IDHCPGroupCondition> Conditions;
804 CHECK_ERROR2I_STMT(ptrGroupConfig, COMGETTER(Conditions)(ComSafeArrayAsOutParam(Conditions)),
805 rcExit = RTEXITCODE_FAILURE; break);
806 bool fFound = false;
807 for (size_t iCond = 0; iCond < Conditions.size(); iCond++)
808 {
809 DHCPGroupConditionType_T enmCurType = DHCPGroupConditionType_MAC;
810 CHECK_ERROR2I_STMT(Conditions[iCond], COMGETTER(Type)(&enmCurType),
811 rcExit = RTEXITCODE_FAILURE; continue);
812 if (enmCurType == enmType)
813 {
814 Bstr bstrValue;
815 CHECK_ERROR2I_STMT(Conditions[iCond], COMGETTER(Value)(bstrValue.asOutParam()),
816 rcExit = RTEXITCODE_FAILURE; continue);
817 if (RTUtf16CmpUtf8(bstrValue.raw(), ValueUnion.psz) == 0)
818 {
819 CHECK_ERROR2I_STMT(Conditions[iCond], Remove(), rcExit = RTEXITCODE_FAILURE);
820 fFound = true;
821 }
822 }
823 }
824 if (!fFound)
825 rcExit = RTMsgErrorExitFailure(DHCPServer::tr("Could not find any condition of type %d with value '%s' to delete"),
826 enmType, ValueUnion.psz);
827 }
828 break;
829 }
830
831 case DHCP_ADDMOD_ZAP_CONDITIONS:
832 if (Scope.getScope() != DHCPConfigScope_Group)
833 return errorSyntax(DHCPServer::tr("--zap-conditions can only be with a group selected"));
834 if (iPass == 1)
835 {
836 ComPtr<IDHCPGroupConfig> &ptrGroupConfig = Scope.getGroup(ptrDHCPServer);
837 if (ptrGroupConfig.isNull())
838 return RTEXITCODE_FAILURE;
839 CHECK_ERROR2I_STMT(ptrGroupConfig, RemoveAllConditions(), rcExit = RTEXITCODE_FAILURE);
840 }
841 break;
842
843 /*
844 * For backwards compatibility. Remove in 6.1 or later.
845 */
846
847 case 'o': // --options - obsolete, ignored.
848 break;
849
850 case 'i': // --id
851 if (fNeedValueOrRemove)
852 return errorSyntax(DHCPServer::tr("Incomplete option sequence preseeding '--id=%u"), ValueUnion.u8);
853 u8OptId = ValueUnion.u8;
854 fNeedValueOrRemove = true;
855 break;
856
857 case 'p': // --value
858 if (!fNeedValueOrRemove)
859 return errorSyntax(DHCPServer::tr("--value without --id=dhcp-opt-no"));
860 if (iPass == 1)
861 {
862 ComPtr<IDHCPConfig> &ptrConfig = Scope.getConfig(ptrDHCPServer);
863 if (ptrConfig.isNull())
864 return RTEXITCODE_FAILURE;
865 CHECK_ERROR2I_STMT(ptrConfig, SetOption((DHCPOption_T)u8OptId, DHCPOptionEncoding_Normal,
866 Bstr(ValueUnion.psz).raw()), rcExit = RTEXITCODE_FAILURE);
867 }
868 fNeedValueOrRemove = false;
869 break;
870
871 case 'r': // --remove
872 if (pCtx->pCmdDef->fSubcommandScope == HELP_SCOPE_DHCPSERVER_ADD)
873 return errorSyntax(DHCPServer::tr("--remove does not apply to the 'add' subcommand"));
874 if (!fNeedValueOrRemove)
875 return errorSyntax(DHCPServer::tr("--remove without --id=dhcp-opt-no"));
876
877 if (iPass == 1)
878 {
879 ComPtr<IDHCPConfig> &ptrConfig = Scope.getConfig(ptrDHCPServer);
880 if (ptrConfig.isNull())
881 return RTEXITCODE_FAILURE;
882 CHECK_ERROR2I_STMT(ptrConfig, RemoveOption((DHCPOption_T)u8OptId), rcExit = RTEXITCODE_FAILURE);
883 }
884 fNeedValueOrRemove = false;
885 break;
886
887 default:
888 return errorGetOpt(vrc, &ValueUnion);
889 }
890 }
891
892 if (iPass != 0)
893 break;
894
895 /*
896 * Ensure we've got mandatory options and supply defaults
897 * where needed (modify case)
898 */
899 if (!pCtx->pszNetwork && !pCtx->pszInterface)
900 return errorSyntax(DHCPServer::tr("You need to specify either --network or --interface to identify the DHCP server"));
901
902 if (pCtx->pCmdDef->fSubcommandScope == HELP_SCOPE_DHCPSERVER_ADD)
903 {
904 if (!pszServerIp)
905 rcExit = errorSyntax(DHCPServer::tr("Missing required option: --ip"));
906 if (!pszNetmask)
907 rcExit = errorSyntax(DHCPServer::tr("Missing required option: --netmask"));
908 if (!pszLowerIp)
909 rcExit = errorSyntax(DHCPServer::tr("Missing required option: --lowerip"));
910 if (!pszUpperIp)
911 rcExit = errorSyntax(DHCPServer::tr("Missing required option: --upperip"));
912 if (rcExit != RTEXITCODE_SUCCESS)
913 return rcExit;
914 }
915
916 /*
917 * Find or create the server.
918 */
919 HRESULT hrc;
920 Bstr NetName;
921 if (!pCtx->pszNetwork)
922 {
923 ComPtr<IHost> host;
924 CHECK_ERROR(pCtx->pArg->virtualBox, COMGETTER(Host)(host.asOutParam()));
925
926 ComPtr<IHostNetworkInterface> hif;
927 CHECK_ERROR(host, FindHostNetworkInterfaceByName(Bstr(pCtx->pszInterface).mutableRaw(), hif.asOutParam()));
928 if (FAILED(hrc))
929 return errorArgument(DHCPServer::tr("Could not find interface '%s'"), pCtx->pszInterface);
930
931 CHECK_ERROR(hif, COMGETTER(NetworkName) (NetName.asOutParam()));
932 if (FAILED(hrc))
933 return errorArgument(DHCPServer::tr("Could not get network name for the interface '%s'"), pCtx->pszInterface);
934 }
935 else
936 {
937 NetName = Bstr(pCtx->pszNetwork);
938 }
939
940 hrc = pCtx->pArg->virtualBox->FindDHCPServerByNetworkName(NetName.mutableRaw(), ptrDHCPServer.asOutParam());
941 if (pCtx->pCmdDef->fSubcommandScope == HELP_SCOPE_DHCPSERVER_ADD)
942 {
943 if (SUCCEEDED(hrc))
944 return errorArgument(DHCPServer::tr("DHCP server already exists"));
945
946 CHECK_ERROR(pCtx->pArg->virtualBox, CreateDHCPServer(NetName.mutableRaw(), ptrDHCPServer.asOutParam()));
947 if (FAILED(hrc))
948 return errorArgument(DHCPServer::tr("Failed to create the DHCP server"));
949 }
950 else if (FAILED(hrc))
951 return errorArgument(DHCPServer::tr("DHCP server does not exist"));
952
953 /*
954 * Apply IDHCPServer settings:
955 */
956 if (pszServerIp || pszNetmask || pszLowerIp || pszUpperIp)
957 {
958 Bstr bstrServerIp(pszServerIp);
959 Bstr bstrNetmask(pszNetmask);
960 Bstr bstrLowerIp(pszLowerIp);
961 Bstr bstrUpperIp(pszUpperIp);
962
963 if (!pszServerIp)
964 {
965 CHECK_ERROR2_RET(hrc, ptrDHCPServer, COMGETTER(IPAddress)(bstrServerIp.asOutParam()), RTEXITCODE_FAILURE);
966 }
967 if (!pszNetmask)
968 {
969 CHECK_ERROR2_RET(hrc, ptrDHCPServer, COMGETTER(NetworkMask)(bstrNetmask.asOutParam()), RTEXITCODE_FAILURE);
970 }
971 if (!pszLowerIp)
972 {
973 CHECK_ERROR2_RET(hrc, ptrDHCPServer, COMGETTER(LowerIP)(bstrLowerIp.asOutParam()), RTEXITCODE_FAILURE);
974 }
975 if (!pszUpperIp)
976 {
977 CHECK_ERROR2_RET(hrc, ptrDHCPServer, COMGETTER(UpperIP)(bstrUpperIp.asOutParam()), RTEXITCODE_FAILURE);
978 }
979
980 CHECK_ERROR2_STMT(hrc, ptrDHCPServer, SetConfiguration(bstrServerIp.raw(), bstrNetmask.raw(),
981 bstrLowerIp.raw(), bstrUpperIp.raw()),
982 rcExit = errorArgument(DHCPServer::tr("Failed to set configuration (%ls, %ls, %ls, %ls)"), bstrServerIp.raw(),
983 bstrNetmask.raw(), bstrLowerIp.raw(), bstrUpperIp.raw()));
984 }
985
986 if (fEnabled >= 0)
987 {
988 CHECK_ERROR2_STMT(hrc, ptrDHCPServer, COMSETTER(Enabled)((BOOL)fEnabled), rcExit = RTEXITCODE_FAILURE);
989 }
990 }
991
992 return rcExit;
993}
994
995
996/**
997 * Handles the 'remove' subcommand.
998 */
999static DECLCALLBACK(RTEXITCODE) dhcpdHandleRemove(PDHCPDCMDCTX pCtx, int argc, char **argv)
1000{
1001 /*
1002 * Parse the command line.
1003 */
1004 static const RTGETOPTDEF s_aOptions[] =
1005 {
1006 DHCPD_CMD_COMMON_OPTION_DEFS(),
1007 };
1008
1009 RTGETOPTSTATE GetState;
1010 int vrc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
1011 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
1012
1013 RTGETOPTUNION ValueUnion;
1014 while ((vrc = RTGetOpt(&GetState, &ValueUnion)))
1015 {
1016 switch (vrc)
1017 {
1018 DHCPD_CMD_COMMON_OPTION_CASES(pCtx, vrc, &ValueUnion);
1019 default:
1020 return errorGetOpt(vrc, &ValueUnion);
1021 }
1022 }
1023
1024 /*
1025 * Locate the server and perform the requested operation.
1026 */
1027 ComPtr<IDHCPServer> ptrDHCPServer = dhcpdFindServer(pCtx);
1028 if (ptrDHCPServer.isNotNull())
1029 {
1030 HRESULT hrc;
1031 CHECK_ERROR2(hrc, pCtx->pArg->virtualBox, RemoveDHCPServer(ptrDHCPServer));
1032 if (SUCCEEDED(hrc))
1033 return RTEXITCODE_SUCCESS;
1034 errorArgument(DHCPServer::tr("Failed to remove server"));
1035 }
1036 return RTEXITCODE_FAILURE;
1037}
1038
1039
1040/**
1041 * Handles the 'start' subcommand.
1042 */
1043static DECLCALLBACK(RTEXITCODE) dhcpdHandleStart(PDHCPDCMDCTX pCtx, int argc, char **argv)
1044{
1045 /*
1046 * Parse the command line.
1047 */
1048 static const RTGETOPTDEF s_aOptions[] =
1049 {
1050 DHCPD_CMD_COMMON_OPTION_DEFS(),
1051 };
1052
1053 RTGETOPTSTATE GetState;
1054 int vrc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
1055 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
1056
1057 RTGETOPTUNION ValueUnion;
1058 while ((vrc = RTGetOpt(&GetState, &ValueUnion)))
1059 {
1060 switch (vrc)
1061 {
1062 DHCPD_CMD_COMMON_OPTION_CASES(pCtx, vrc, &ValueUnion);
1063 default:
1064 return errorGetOpt(vrc, &ValueUnion);
1065 }
1066 }
1067
1068 /*
1069 * Locate the server.
1070 */
1071 ComPtr<IDHCPServer> ptrDHCPServer = dhcpdFindServer(pCtx);
1072 if (ptrDHCPServer.isNotNull())
1073 {
1074 /*
1075 * We have to figure out the trunk name and type here, which is silly to
1076 * leave to the API client as it's a pain to get right. But here we go...
1077 */
1078 static char const s_szHostOnlyPrefix[] = "HostInterfaceNetworking-";
1079 bool fHostOnly = true;
1080 Bstr strTrunkName;
1081 if (pCtx->pszInterface)
1082 strTrunkName = pCtx->pszInterface;
1083 else if (RTStrStartsWith(pCtx->pszNetwork, s_szHostOnlyPrefix))
1084 strTrunkName = &pCtx->pszNetwork[sizeof(s_szHostOnlyPrefix) - 1];
1085 else
1086 fHostOnly = false;
1087
1088 Bstr strTrunkType;
1089 if (fHostOnly)
1090#if defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
1091 strTrunkType = "netadp";
1092#else /* lazy implementations: */
1093 strTrunkType = "netflt";
1094#endif
1095 else
1096 strTrunkType = "whatever";
1097
1098 HRESULT hrc = ptrDHCPServer->Start(strTrunkName.raw(), strTrunkType.raw());
1099 if (SUCCEEDED(hrc))
1100 return RTEXITCODE_SUCCESS;
1101 errorArgument(DHCPServer::tr("Failed to start the server"));
1102 GlueHandleComErrorNoCtx(ptrDHCPServer, hrc);
1103 }
1104 return RTEXITCODE_FAILURE;
1105}
1106
1107
1108/**
1109 * Handles the 'restart' subcommand.
1110 */
1111static DECLCALLBACK(RTEXITCODE) dhcpdHandleRestart(PDHCPDCMDCTX pCtx, int argc, char **argv)
1112{
1113 /*
1114 * Parse the command line.
1115 */
1116 static const RTGETOPTDEF s_aOptions[] =
1117 {
1118 DHCPD_CMD_COMMON_OPTION_DEFS(),
1119 };
1120
1121 RTGETOPTSTATE GetState;
1122 int vrc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
1123 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
1124
1125 RTGETOPTUNION ValueUnion;
1126 while ((vrc = RTGetOpt(&GetState, &ValueUnion)))
1127 {
1128 switch (vrc)
1129 {
1130 DHCPD_CMD_COMMON_OPTION_CASES(pCtx, vrc, &ValueUnion);
1131 default:
1132 return errorGetOpt(vrc, &ValueUnion);
1133 }
1134 }
1135
1136 /*
1137 * Locate the server and perform the requested operation.
1138 */
1139 ComPtr<IDHCPServer> ptrDHCPServer = dhcpdFindServer(pCtx);
1140 if (ptrDHCPServer.isNotNull())
1141 {
1142 HRESULT hrc = ptrDHCPServer->Restart();
1143 if (SUCCEEDED(hrc))
1144 return RTEXITCODE_SUCCESS;
1145 errorArgument(DHCPServer::tr("Failed to restart the server"));
1146 GlueHandleComErrorNoCtx(ptrDHCPServer, hrc);
1147 }
1148 return RTEXITCODE_FAILURE;
1149}
1150
1151
1152/**
1153 * Handles the 'stop' subcommand.
1154 */
1155static DECLCALLBACK(RTEXITCODE) dhcpdHandleStop(PDHCPDCMDCTX pCtx, int argc, char **argv)
1156{
1157 /*
1158 * Parse the command line.
1159 */
1160 static const RTGETOPTDEF s_aOptions[] =
1161 {
1162 DHCPD_CMD_COMMON_OPTION_DEFS(),
1163 };
1164
1165 RTGETOPTSTATE GetState;
1166 int vrc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
1167 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
1168
1169 RTGETOPTUNION ValueUnion;
1170 while ((vrc = RTGetOpt(&GetState, &ValueUnion)))
1171 {
1172 switch (vrc)
1173 {
1174 DHCPD_CMD_COMMON_OPTION_CASES(pCtx, vrc, &ValueUnion);
1175 default:
1176 return errorGetOpt(vrc, &ValueUnion);
1177 }
1178 }
1179
1180 /*
1181 * Locate the server and perform the requested operation.
1182 */
1183 ComPtr<IDHCPServer> ptrDHCPServer = dhcpdFindServer(pCtx);
1184 if (ptrDHCPServer.isNotNull())
1185 {
1186 HRESULT hrc = ptrDHCPServer->Stop();
1187 if (SUCCEEDED(hrc))
1188 return RTEXITCODE_SUCCESS;
1189 errorArgument(DHCPServer::tr("Failed to stop the server"));
1190 GlueHandleComErrorNoCtx(ptrDHCPServer, hrc);
1191 }
1192 return RTEXITCODE_FAILURE;
1193}
1194
1195
1196/**
1197 * Handles the 'findlease' subcommand.
1198 */
1199static DECLCALLBACK(RTEXITCODE) dhcpdHandleFindLease(PDHCPDCMDCTX pCtx, int argc, char **argv)
1200{
1201 /*
1202 * Parse the command line.
1203 */
1204 static const RTGETOPTDEF s_aOptions[] =
1205 {
1206 DHCPD_CMD_COMMON_OPTION_DEFS(),
1207 { "--mac-address", 'm', RTGETOPT_REQ_MACADDR },
1208
1209 };
1210
1211 bool fHaveMacAddress = false;
1212 RTMAC MacAddress = { { 0, 0, 0, 0, 0, 0 } };
1213
1214 RTGETOPTSTATE GetState;
1215 int vrc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
1216 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
1217
1218 RTGETOPTUNION ValueUnion;
1219 while ((vrc = RTGetOpt(&GetState, &ValueUnion)))
1220 {
1221 switch (vrc)
1222 {
1223 DHCPD_CMD_COMMON_OPTION_CASES(pCtx, vrc, &ValueUnion);
1224
1225 case 'm': // --mac-address
1226 fHaveMacAddress = true;
1227 MacAddress = ValueUnion.MacAddr;
1228 break;
1229
1230 default:
1231 return errorGetOpt(vrc, &ValueUnion);
1232 }
1233 }
1234
1235 if (!fHaveMacAddress)
1236 return errorSyntax(DHCPServer::tr("You need to specify a MAC address too look for"));
1237
1238 /*
1239 * Locate the server and perform the requested operation.
1240 */
1241 ComPtr<IDHCPServer> ptrDHCPServer = dhcpdFindServer(pCtx);
1242 if (ptrDHCPServer.isNull())
1243 return RTEXITCODE_FAILURE;
1244
1245 char szMac[32];
1246 RTStrPrintf(szMac, sizeof(szMac), "%RTmac", &MacAddress);
1247 Bstr bstrAddress;
1248 Bstr bstrState;
1249 LONG64 secIssued = 0;
1250 LONG64 secExpire = 0;
1251 HRESULT hrc;
1252 CHECK_ERROR2(hrc, ptrDHCPServer, FindLeaseByMAC(Bstr(szMac).raw(), 0 /*type*/,
1253 bstrAddress.asOutParam(), bstrState.asOutParam(), &secIssued, &secExpire));
1254 if (SUCCEEDED(hrc))
1255 {
1256 RTTIMESPEC TimeSpec;
1257 int64_t cSecLeftToLive = secExpire - RTTimeSpecGetSeconds(RTTimeNow(&TimeSpec));
1258 RTTIME Time;
1259 char szIssued[RTTIME_STR_LEN];
1260 RTTimeToStringEx(RTTimeExplode(&Time, RTTimeSpecSetSeconds(&TimeSpec, secIssued)), szIssued, sizeof(szIssued), 0);
1261 char szExpire[RTTIME_STR_LEN];
1262 RTTimeToStringEx(RTTimeExplode(&Time, RTTimeSpecSetSeconds(&TimeSpec, secExpire)), szExpire, sizeof(szExpire), 0);
1263
1264 RTPrintf(DHCPServer::tr("IP Address: %ls\n"
1265 "MAC Address: %RTmac\n"
1266 "State: %ls\n"
1267 "Issued: %s (%RU64)\n"
1268 "Expire: %s (%RU64)\n"
1269 "TTL: %RU64 sec, currently %RU64 sec left\n"),
1270 bstrAddress.raw(),
1271 &MacAddress,
1272 bstrState.raw(),
1273 szIssued, secIssued,
1274 szExpire, secExpire,
1275 secExpire >= secIssued ? secExpire - secIssued : 0, cSecLeftToLive > 0 ? cSecLeftToLive : 0);
1276 return RTEXITCODE_SUCCESS;
1277 }
1278 return RTEXITCODE_FAILURE;
1279}
1280
1281
1282/**
1283 * Handles the 'dhcpserver' command.
1284 */
1285RTEXITCODE handleDHCPServer(HandlerArg *pArg)
1286{
1287 /*
1288 * Command definitions.
1289 */
1290 static const DHCPDCMDDEF s_aCmdDefs[] =
1291 {
1292 { "add", dhcpdHandleAddAndModify, HELP_SCOPE_DHCPSERVER_ADD },
1293 { "modify", dhcpdHandleAddAndModify, HELP_SCOPE_DHCPSERVER_MODIFY },
1294 { "remove", dhcpdHandleRemove, HELP_SCOPE_DHCPSERVER_REMOVE },
1295 { "start", dhcpdHandleStart, HELP_SCOPE_DHCPSERVER_START },
1296 { "restart", dhcpdHandleRestart, HELP_SCOPE_DHCPSERVER_RESTART },
1297 { "stop", dhcpdHandleStop, HELP_SCOPE_DHCPSERVER_STOP },
1298 { "findlease", dhcpdHandleFindLease, HELP_SCOPE_DHCPSERVER_FINDLEASE },
1299 };
1300
1301 /*
1302 * VBoxManage dhcpserver [common-options] subcommand ...
1303 */
1304 DHCPDCMDCTX CmdCtx;
1305 CmdCtx.pArg = pArg;
1306 CmdCtx.pCmdDef = NULL;
1307 CmdCtx.pszInterface = NULL;
1308 CmdCtx.pszNetwork = NULL;
1309
1310 static const RTGETOPTDEF s_CommonOptions[] = { DHCPD_CMD_COMMON_OPTION_DEFS() };
1311 RTGETOPTSTATE GetState;
1312 int vrc = RTGetOptInit(&GetState, pArg->argc, pArg->argv, s_CommonOptions, RT_ELEMENTS(s_CommonOptions), 0,
1313 0 /* No sorting! */);
1314 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
1315
1316 RTGETOPTUNION ValueUnion;
1317 while ((vrc = RTGetOpt(&GetState, &ValueUnion)) != 0)
1318 {
1319 switch (vrc)
1320 {
1321 DHCPD_CMD_COMMON_OPTION_CASES(&CmdCtx, vrc, &ValueUnion);
1322
1323 case VINF_GETOPT_NOT_OPTION:
1324 {
1325 const char *pszCmd = ValueUnion.psz;
1326 uint32_t iCmd;
1327 for (iCmd = 0; iCmd < RT_ELEMENTS(s_aCmdDefs); iCmd++)
1328 if (strcmp(s_aCmdDefs[iCmd].pszName, pszCmd) == 0)
1329 {
1330 CmdCtx.pCmdDef = &s_aCmdDefs[iCmd];
1331 setCurrentSubcommand(s_aCmdDefs[iCmd].fSubcommandScope);
1332 return s_aCmdDefs[iCmd].pfnHandler(&CmdCtx, pArg->argc - GetState.iNext + 1,
1333 &pArg->argv[GetState.iNext - 1]);
1334 }
1335 return errorUnknownSubcommand(pszCmd);
1336 }
1337
1338 default:
1339 return errorGetOpt(vrc, &ValueUnion);
1340 }
1341 }
1342 return errorNoSubcommand();
1343}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette