VirtualBox

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

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

Main: bugref:1909: Added translation marks around messages of VBoxManage output

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