VirtualBox

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

Last change on this file since 94887 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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