VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/DHCPServerImpl.cpp@ 79771

Last change on this file since 79771 was 79771, checked in by vboxsync, 6 years ago

Main/DHCP*,VBoxManage: Some adjustments to the DHCP*Config interfaces and reworked the VBoxMAnage dhcpserver add/modify commands to support groups, fixed address assignments, setting lease times, removing group and individaul configurations, ++. todo: update man page. bugref:9288

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 48.9 KB
Line 
1/* $Id: DHCPServerImpl.cpp 79771 2019-07-14 21:10:58Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. 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#define LOG_GROUP LOG_GROUP_MAIN_DHCPSERVER
23#include "DHCPServerImpl.h"
24#include "LoggingNew.h"
25
26#include <iprt/asm.h>
27#include <iprt/err.h>
28#include <iprt/file.h>
29#include <iprt/net.h>
30#include <iprt/path.h>
31#include <iprt/cpp/path.h>
32#include <iprt/cpp/utils.h>
33#include <iprt/cpp/xml.h>
34
35#include <VBox/com/array.h>
36#include <VBox/settings.h>
37
38#include "AutoCaller.h"
39#include "DHCPConfigImpl.h"
40#include "MachineImpl.h"
41#include "NetworkServiceRunner.h"
42#include "VirtualBoxImpl.h"
43
44
45/*********************************************************************************************************************************
46* Defined Constants And Macros *
47*********************************************************************************************************************************/
48#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
49# define DHCP_EXECUTABLE_NAME "VBoxNetDHCP.exe"
50#else
51# define DHCP_EXECUTABLE_NAME "VBoxNetDHCP"
52#endif
53
54
55/**
56 * DHCP server specialization of NetworkServiceRunner.
57 *
58 * Just defines the executable name and adds option constants.
59 */
60class DHCPServerRunner : public NetworkServiceRunner
61{
62public:
63 DHCPServerRunner() : NetworkServiceRunner(DHCP_EXECUTABLE_NAME)
64 {}
65 virtual ~DHCPServerRunner()
66 {}
67};
68
69
70/**
71 * Hidden private data of the DHCPServer class.
72 */
73struct DHCPServer::Data
74{
75 Data()
76 : pVirtualBox(NULL)
77 , strName()
78 , enabled(FALSE)
79 , uIndividualMACAddressVersion(1)
80 {
81 }
82
83 /** weak VirtualBox parent */
84 VirtualBox * const pVirtualBox;
85 /** The DHCP server name (network).
86 * @todo Kind of duplicated by networkName, if I understand it correctly. */
87 Utf8Str const strName;
88
89 Utf8Str IPAddress;
90 Utf8Str lowerIP;
91 Utf8Str upperIP;
92
93 BOOL enabled;
94 DHCPServerRunner dhcp;
95
96 com::Utf8Str strLeasesFilename;
97 com::Utf8Str strConfigFilename;
98 com::Utf8Str strLogFilename;
99
100 com::Utf8Str networkName;
101 com::Utf8Str trunkName;
102 com::Utf8Str trunkType;
103
104 /** Global configuration. */
105 ComObjPtr<DHCPGlobalConfig> globalConfig;
106
107 /** Group configuration indexed by name. */
108 std::map<com::Utf8Str, ComObjPtr<DHCPGroupConfig> > groupConfigs;
109 /** Iterator for groupConfigs. */
110 typedef std::map<com::Utf8Str, ComObjPtr<DHCPGroupConfig> >::iterator GroupConfigIterator;
111
112 /** Individual (host) configuration indexed by MAC address or VM UUID. */
113 std::map<com::Utf8Str, ComObjPtr<DHCPIndividualConfig> > individualConfigs;
114 /** Iterator for individualConfigs. */
115 typedef std::map<com::Utf8Str, ComObjPtr<DHCPIndividualConfig> >::iterator IndividualConfigIterator;
116
117 /** Part of a lock-avoidance hack to resolve the VM ID + slot into MAC
118 * addresses before writing out the Dhcpd configuration file. */
119 uint32_t uIndividualMACAddressVersion;
120};
121
122
123// constructor / destructor
124/////////////////////////////////////////////////////////////////////////////
125
126
127DHCPServer::DHCPServer()
128 : m(NULL)
129{
130 m = new DHCPServer::Data();
131}
132
133
134DHCPServer::~DHCPServer()
135{
136 if (m)
137 {
138 delete m;
139 m = NULL;
140 }
141}
142
143
144HRESULT DHCPServer::FinalConstruct()
145{
146 return BaseFinalConstruct();
147}
148
149
150void DHCPServer::FinalRelease()
151{
152 uninit();
153 BaseFinalRelease();
154}
155
156
157void DHCPServer::uninit()
158{
159 /* Enclose the state transition Ready->InUninit->NotReady */
160 AutoUninitSpan autoUninitSpan(this);
161 if (autoUninitSpan.uninitDone())
162 return;
163
164 if (m->dhcp.isRunning())
165 stop();
166
167 unconst(m->pVirtualBox) = NULL;
168}
169
170
171HRESULT DHCPServer::init(VirtualBox *aVirtualBox, const Utf8Str &aName)
172{
173 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
174
175 AutoInitSpan autoInitSpan(this);
176 AssertReturn(autoInitSpan.isOk(), E_FAIL);
177
178 /* share VirtualBox weakly (parent remains NULL so far) */
179 unconst(m->pVirtualBox) = aVirtualBox;
180
181 unconst(m->strName) = aName;
182 m->IPAddress = "0.0.0.0";
183 m->lowerIP = "0.0.0.0";
184 m->upperIP = "0.0.0.0";
185 m->enabled = FALSE;
186
187 /* Global configuration: */
188 HRESULT hrc = m->globalConfig.createObject();
189 if (SUCCEEDED(hrc))
190 hrc = m->globalConfig->initWithDefaults(aVirtualBox, this);
191
192 Assert(m->groupConfigs.size() == 0);
193 Assert(m->individualConfigs.size() == 0);
194
195 /* Confirm a successful initialization or not: */
196 if (SUCCEEDED(hrc))
197 autoInitSpan.setSucceeded();
198 else
199 autoInitSpan.setFailed(hrc);
200 return hrc;
201}
202
203
204HRESULT DHCPServer::init(VirtualBox *aVirtualBox, const settings::DHCPServer &rData)
205{
206 /* Enclose the state transition NotReady->InInit->Ready */
207 AutoInitSpan autoInitSpan(this);
208 AssertReturn(autoInitSpan.isOk(), E_FAIL);
209
210 /* share VirtualBox weakly (parent remains NULL so far) */
211 unconst(m->pVirtualBox) = aVirtualBox;
212
213 unconst(m->strName) = rData.strNetworkName;
214 m->IPAddress = rData.strIPAddress;
215 m->enabled = rData.fEnabled;
216 m->lowerIP = rData.strIPLower;
217 m->upperIP = rData.strIPUpper;
218
219 /*
220 * Global configuration:
221 */
222 HRESULT hrc = m->globalConfig.createObject();
223 if (SUCCEEDED(hrc))
224 hrc = m->globalConfig->initWithSettings(aVirtualBox, this, rData.globalConfig);
225
226 /*
227 * Group configurations:
228 */
229 Assert(m->groupConfigs.size() == 0);
230 for (settings::DHCPGroupConfigVec::const_iterator it = rData.vecGroupConfigs.begin();
231 it != rData.vecGroupConfigs.end() && SUCCEEDED(hrc); ++it)
232 {
233 ComObjPtr<DHCPGroupConfig> ptrGroupConfig;
234 hrc = ptrGroupConfig.createObject();
235 if (SUCCEEDED(hrc))
236 hrc = ptrGroupConfig->initWithSettings(aVirtualBox, this, *it);
237 if (SUCCEEDED(hrc))
238 {
239 try
240 {
241 m->groupConfigs[it->strName] = ptrGroupConfig;
242 }
243 catch (std::bad_alloc &)
244 {
245 return E_OUTOFMEMORY;
246 }
247 }
248 }
249
250 /*
251 * Individual configuration:
252 */
253 Assert(m->individualConfigs.size() == 0);
254 for (settings::DHCPIndividualConfigMap::const_iterator it = rData.mapIndividualConfigs.begin();
255 it != rData.mapIndividualConfigs.end() && SUCCEEDED(hrc); ++it)
256 {
257 ComObjPtr<DHCPIndividualConfig> ptrIndiCfg;
258 com::Utf8Str strKey;
259 if (!it->second.strVMName.isNotEmpty())
260 {
261 RTMAC MACAddress;
262 int vrc = RTNetStrToMacAddr(it->second.strMACAddress.c_str(), &MACAddress);
263 if (RT_FAILURE(vrc))
264 {
265 LogRel(("Ignoring invalid MAC address for individual DHCP config: '%s' - %Rrc\n", it->second.strMACAddress.c_str(), vrc));
266 continue;
267 }
268
269 vrc = strKey.printfNoThrow("%RTmac", &MACAddress);
270 AssertRCReturn(vrc, E_OUTOFMEMORY);
271
272 hrc = ptrIndiCfg.createObject();
273 if (SUCCEEDED(hrc))
274 hrc = ptrIndiCfg->initWithSettingsAndMACAddress(aVirtualBox, this, it->second, &MACAddress);
275 }
276 else
277 {
278 /* This ASSUMES that we're being called after the machines have been
279 loaded so we can resolve VM names into UUID for old settings. */
280 com::Guid idMachine;
281 hrc = i_vmNameToIdAndValidateSlot(it->second.strVMName, it->second.uSlot, idMachine);
282 if (SUCCEEDED(hrc))
283 {
284 hrc = ptrIndiCfg.createObject();
285 if (SUCCEEDED(hrc))
286 hrc = ptrIndiCfg->initWithSettingsAndMachineIdAndSlot(aVirtualBox, this, it->second,
287 idMachine, it->second.uSlot,
288 m->uIndividualMACAddressVersion - UINT32_MAX / 4);
289 }
290 }
291 if (SUCCEEDED(hrc))
292 {
293 try
294 {
295 m->individualConfigs[strKey] = ptrIndiCfg;
296 }
297 catch (std::bad_alloc &)
298 {
299 return E_OUTOFMEMORY;
300 }
301 }
302 }
303
304 /* Confirm a successful initialization or not: */
305 if (SUCCEEDED(hrc))
306 autoInitSpan.setSucceeded();
307 else
308 autoInitSpan.setFailed(hrc);
309 return hrc;
310}
311
312
313/**
314 * Called by VirtualBox to save our settings.
315 */
316HRESULT DHCPServer::i_saveSettings(settings::DHCPServer &rData)
317{
318 AutoCaller autoCaller(this);
319 if (FAILED(autoCaller.rc())) return autoCaller.rc();
320
321 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
322
323 rData.strNetworkName = m->strName;
324 rData.strIPAddress = m->IPAddress;
325 rData.fEnabled = m->enabled != FALSE;
326 rData.strIPLower = m->lowerIP;
327 rData.strIPUpper = m->upperIP;
328
329 /* Global configuration: */
330 HRESULT hrc = m->globalConfig->i_saveSettings(rData.globalConfig);
331
332 /* Group configuration: */
333 size_t const cGroupConfigs = m->groupConfigs.size();
334 try
335 {
336 rData.vecGroupConfigs.resize(cGroupConfigs);
337 }
338 catch (std::bad_alloc &)
339 {
340 return E_OUTOFMEMORY;
341 }
342 size_t i = 0;
343 for (Data::GroupConfigIterator it = m->groupConfigs.begin(); it != m->groupConfigs.end() && SUCCEEDED(hrc); ++it, i++)
344 {
345 try
346 {
347 rData.vecGroupConfigs[i] = settings::DHCPGroupConfig();
348 }
349 catch (std::bad_alloc &)
350 {
351 return E_OUTOFMEMORY;
352 }
353 hrc = it->second->i_saveSettings(rData.vecGroupConfigs[i]);
354 }
355
356 /* Individual configuration: */
357 for (Data::IndividualConfigIterator it = m->individualConfigs.begin();
358 it != m->individualConfigs.end() && SUCCEEDED(hrc); ++it)
359 {
360 try
361 {
362 rData.mapIndividualConfigs[it->first] = settings::DHCPIndividualConfig();
363 }
364 catch (std::bad_alloc &)
365 {
366 return E_OUTOFMEMORY;
367 }
368 hrc = it->second->i_saveSettings(rData.mapIndividualConfigs[it->first]);
369 }
370
371 return hrc;
372}
373
374
375HRESULT DHCPServer::i_removeConfig(DHCPConfig *pConfig, DHCPConfigScope_T enmScope)
376{
377 {
378 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
379
380 bool fFound = false;
381 switch (enmScope)
382 {
383 case DHCPConfigScope_Group:
384 for (Data::GroupConfigIterator it = m->groupConfigs.begin(); it != m->groupConfigs.end();)
385 {
386 DHCPConfig *pCurConfig = it->second;
387 if (pCurConfig == pConfig)
388 {
389 fFound = true;
390 it = m->groupConfigs.erase(it);
391 }
392 else
393 ++it;
394 }
395 break;
396
397 case DHCPConfigScope_MAC:
398 case DHCPConfigScope_MachineNIC:
399 for (Data::IndividualConfigIterator it = m->individualConfigs.begin(); it != m->individualConfigs.end();)
400 {
401 DHCPConfig *pCurConfig = it->second;
402 if (pCurConfig == pConfig)
403 {
404 fFound = true;
405 it = m->individualConfigs.erase(it);
406 }
407 else
408 ++it;
409 }
410 break;
411
412 default:
413 AssertFailedReturn(E_FAIL);
414 }
415
416 /* Don't complain if already removed, right? */
417 if (!fFound)
418 return S_OK;
419 }
420
421 return i_doSaveSettings();
422}
423
424
425/**
426 * Internal worker that saves the settings after a modification was made.
427 *
428 * @returns COM status code.
429 *
430 * @note Caller must not hold any locks!
431 */
432HRESULT DHCPServer::i_doSaveSettings()
433{
434 // save the global settings; for that we should hold only the VirtualBox lock
435 AutoWriteLock vboxLock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
436 return m->pVirtualBox->i_saveSettings();
437}
438
439
440HRESULT DHCPServer::getNetworkName(com::Utf8Str &aName)
441{
442 /* The name is const, so no need to for locking. */
443 return aName.assignEx(m->strName);
444}
445
446
447HRESULT DHCPServer::getEnabled(BOOL *aEnabled)
448{
449 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
450 *aEnabled = m->enabled;
451 return S_OK;
452}
453
454
455HRESULT DHCPServer::setEnabled(BOOL aEnabled)
456{
457 {
458 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
459 m->enabled = aEnabled;
460 }
461 return i_doSaveSettings();
462}
463
464
465HRESULT DHCPServer::getIPAddress(com::Utf8Str &aIPAddress)
466{
467 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
468 return aIPAddress.assignEx(m->IPAddress);
469}
470
471
472HRESULT DHCPServer::getNetworkMask(com::Utf8Str &aNetworkMask)
473{
474 return m->globalConfig->i_getNetworkMask(aNetworkMask);
475}
476
477
478HRESULT DHCPServer::getLowerIP(com::Utf8Str &aIPAddress)
479{
480 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
481 return aIPAddress.assignEx(m->lowerIP);
482}
483
484
485HRESULT DHCPServer::getUpperIP(com::Utf8Str &aIPAddress)
486{
487 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
488 return aIPAddress.assignEx(m->upperIP);
489}
490
491
492HRESULT DHCPServer::setConfiguration(const com::Utf8Str &aIPAddress,
493 const com::Utf8Str &aNetworkMask,
494 const com::Utf8Str &aLowerIP,
495 const com::Utf8Str &aUpperIP)
496{
497 RTNETADDRIPV4 IPAddress, NetworkMask, LowerIP, UpperIP;
498
499 int vrc = RTNetStrToIPv4Addr(aIPAddress.c_str(), &IPAddress);
500 if (RT_FAILURE(vrc))
501 return setErrorBoth(E_INVALIDARG, vrc, tr("Invalid server address: %s"), aIPAddress.c_str());
502
503 vrc = RTNetStrToIPv4Addr(aNetworkMask.c_str(), &NetworkMask);
504 if (RT_FAILURE(vrc))
505 return setErrorBoth(E_INVALIDARG, vrc, tr("Invalid netmask: %s"), aNetworkMask.c_str());
506
507 vrc = RTNetStrToIPv4Addr(aLowerIP.c_str(), &LowerIP);
508 if (RT_FAILURE(vrc))
509 return setErrorBoth(E_INVALIDARG, vrc, tr("Invalid range lower address: %s"), aLowerIP.c_str());
510
511 vrc = RTNetStrToIPv4Addr(aUpperIP.c_str(), &UpperIP);
512 if (RT_FAILURE(vrc))
513 return setErrorBoth(E_INVALIDARG, vrc, tr("Invalid range upper address: %s"), aUpperIP.c_str());
514
515 /*
516 * Insist on continuous mask. May be also accept prefix length
517 * here or address/prefix for aIPAddress?
518 */
519 vrc = RTNetMaskToPrefixIPv4(&NetworkMask, NULL);
520 if (RT_FAILURE(vrc))
521 return setErrorBoth(E_INVALIDARG, vrc, tr("Invalid netmask: %s"), aNetworkMask.c_str());
522
523 /* It's more convenient to convert to host order once: */
524 IPAddress.u = RT_N2H_U32(IPAddress.u);
525 NetworkMask.u = RT_N2H_U32(NetworkMask.u);
526 LowerIP.u = RT_N2H_U32(LowerIP.u);
527 UpperIP.u = RT_N2H_U32(UpperIP.u);
528
529 /*
530 * Addresses must be unicast and from the same network
531 */
532 if ( (IPAddress.u & UINT32_C(0xe0000000)) == UINT32_C(0xe0000000)
533 || (IPAddress.u & ~NetworkMask.u) == 0
534 || ((IPAddress.u & ~NetworkMask.u) | NetworkMask.u) == UINT32_C(0xffffffff))
535 return setError(E_INVALIDARG, tr("Invalid server address: %s (mask %s)"), aIPAddress.c_str(), aNetworkMask.c_str());
536
537 if ( (LowerIP.u & UINT32_C(0xe0000000)) == UINT32_C(0xe0000000)
538 || (LowerIP.u & NetworkMask.u) != (IPAddress.u &NetworkMask.u)
539 || (LowerIP.u & ~NetworkMask.u) == 0
540 || ((LowerIP.u & ~NetworkMask.u) | NetworkMask.u) == UINT32_C(0xffffffff))
541 return setError(E_INVALIDARG, tr("Invalid range lower address: %s (mask %s)"), aLowerIP.c_str(), aNetworkMask.c_str());
542
543 if ( (UpperIP.u & UINT32_C(0xe0000000)) == UINT32_C(0xe0000000)
544 || (UpperIP.u & NetworkMask.u) != (IPAddress.u &NetworkMask.u)
545 || (UpperIP.u & ~NetworkMask.u) == 0
546 || ((UpperIP.u & ~NetworkMask.u) | NetworkMask.u) == UINT32_C(0xffffffff))
547 return setError(E_INVALIDARG, tr("Invalid range upper address"), aUpperIP.c_str(), aNetworkMask.c_str());
548
549 /* The range should be valid ... */
550 if (LowerIP.u > UpperIP.u)
551 return setError(E_INVALIDARG, tr("Lower bound must be less or eqaul than the upper: %s vs %s"),
552 aLowerIP.c_str(), aUpperIP.c_str());
553
554 /* ... and shouldn't contain the server's address */
555 if (LowerIP.u <= IPAddress.u && IPAddress.u <= UpperIP.u)
556 return setError(E_INVALIDARG, tr("Server address within range bounds: %s in %s - %s"),
557 aIPAddress.c_str(), aLowerIP.c_str(), aUpperIP.c_str());
558
559 /*
560 * Input is valid, effect the changes.
561 */
562 HRESULT hrc;
563 {
564 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
565 m->IPAddress = aIPAddress;
566 m->lowerIP = aLowerIP;
567 m->upperIP = aUpperIP;
568 hrc = m->globalConfig->i_setNetworkMask(aNetworkMask);
569 }
570 if (SUCCEEDED(hrc))
571 hrc = i_doSaveSettings();
572 return hrc;
573}
574
575
576/**
577 * Used by the legacy 6.0 IDHCPServer::GetVmSlotOptions() and
578 * IDHCPServer::GetGlobalOptions() implementations.
579 *
580 * New interfaces have the option number and option encoding separate from the
581 * value.
582 */
583HRESULT DHCPServer::i_encode60Option(com::Utf8Str &strEncoded, DhcpOpt_T enmOption,
584 DHCPOptionEncoding_T enmEncoding, const com::Utf8Str &strValue)
585{
586 int vrc;
587 switch (enmEncoding)
588 {
589 case DHCPOptionEncoding_Legacy:
590 {
591 /*
592 * This is original encoding which assumed that for each
593 * option we know its format and so we know how option
594 * "value" text is to be interpreted.
595 *
596 * "2:10800" # integer 32
597 * "6:1.2.3.4 8.8.8.8" # array of ip-address
598 */
599 vrc = strEncoded.printfNoThrow("%d:%s", (int)enmOption, strValue.c_str());
600 break;
601 }
602
603 case DHCPOptionEncoding_Hex:
604 {
605 /*
606 * This is a bypass for any option - preformatted value as
607 * hex string with no semantic involved in formatting the
608 * value for the DHCP reply.
609 *
610 * 234=68:65:6c:6c:6f:2c:20:77:6f:72:6c:64
611 */
612 vrc = strEncoded.printfNoThrow("%d=%s", (int)enmOption, strValue.c_str());
613 break;
614 }
615
616 default:
617 {
618 /*
619 * Try to be forward compatible.
620 *
621 * "254@42=i hope you know what this means"
622 */
623 vrc = strEncoded.printfNoThrow("%d@%d=%s", (int)enmOption, (int)enmEncoding, strValue.c_str());
624 break;
625 }
626 }
627 return RT_SUCCESS(vrc) ? S_OK : E_OUTOFMEMORY;
628}
629
630
631/**
632 * worker for IDHCPServer::GetGlobalOptions.
633 */
634HRESULT DHCPServer::i_getAllOptions60(DHCPConfig &aSourceConfig, std::vector<com::Utf8Str> &aValues)
635{
636 /* Get the values using the new getter: */
637 std::vector<DhcpOpt_T> Options;
638 std::vector<DHCPOptionEncoding_T> Encodings;
639 std::vector<com::Utf8Str> Values;
640 HRESULT hrc = aSourceConfig.i_getAllOptions(Options, Encodings, Values);
641 if (SUCCEEDED(hrc))
642 {
643 /* Encoding them using in the 6.0 style: */
644 size_t const cValues = Values.size();
645 aValues.resize(cValues);
646 for (size_t i = 0; i < cValues && SUCCEEDED(hrc); i++)
647 hrc = i_encode60Option(aValues[i], Options[i], Encodings[i], Values[i]);
648 }
649 return hrc;
650}
651
652
653/**
654 * Worker for legacy <=6.0 interfaces for adding options.
655 *
656 * @throws std::bad_alloc
657 */
658HRESULT DHCPServer::i_add60Option(DHCPConfig &aTargetConfig, DhcpOpt_T aOption, const com::Utf8Str &aValue)
659{
660 if (aOption != 0)
661 return aTargetConfig.i_setOption(aOption, DHCPOptionEncoding_Legacy, aValue);
662
663 /*
664 * This is a kludge to sneak in option encoding information
665 * through existing API. We use option 0 and supply the real
666 * option/value in the same format that i_encode60Option() above
667 * produces for getter methods.
668 */
669 uint8_t u8Code;
670 char *pszNext;
671 int vrc = RTStrToUInt8Ex(aValue.c_str(), &pszNext, 10, &u8Code);
672 if (RT_FAILURE(vrc))
673 return setErrorBoth(E_INVALIDARG, VERR_PARSE_ERROR);
674
675 DHCPOptionEncoding_T enmEncoding;
676 switch (*pszNext)
677 {
678 case ':': /* support legacy format too */
679 {
680 enmEncoding = DHCPOptionEncoding_Legacy;
681 break;
682 }
683
684 case '=':
685 {
686 enmEncoding = DHCPOptionEncoding_Hex;
687 break;
688 }
689
690 case '@':
691 {
692 uint32_t u32Enc = 0;
693 vrc = RTStrToUInt32Ex(pszNext + 1, &pszNext, 10, &u32Enc);
694 if (RT_FAILURE(vrc))
695 return setErrorBoth(E_INVALIDARG, VERR_PARSE_ERROR);
696 if (*pszNext != '=')
697 return setErrorBoth(E_INVALIDARG, VERR_PARSE_ERROR);
698 enmEncoding = (DHCPOptionEncoding_T)u32Enc;
699 break;
700 }
701
702 default:
703 return VERR_PARSE_ERROR;
704 }
705
706 return aTargetConfig.i_setOption(aOption, enmEncoding, com::Utf8Str(pszNext + 1));
707}
708
709
710HRESULT DHCPServer::addGlobalOption(DhcpOpt_T aOption, const com::Utf8Str &aValue)
711{
712 return i_add60Option(*m->globalConfig, aOption, aValue);
713}
714
715
716HRESULT DHCPServer::removeGlobalOption(DhcpOpt_T aOption)
717{
718 return m->globalConfig->i_removeOption(aOption);
719}
720
721
722HRESULT DHCPServer::removeGlobalOptions()
723{
724 return m->globalConfig->i_removeAllOptions();
725}
726
727
728HRESULT DHCPServer::getGlobalOptions(std::vector<com::Utf8Str> &aValues)
729{
730 return i_getAllOptions60(*m->globalConfig, aValues);
731}
732
733
734HRESULT DHCPServer::getVmConfigs(std::vector<com::Utf8Str> &aValues)
735{
736 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
737
738 try
739 {
740 aValues.resize(m->individualConfigs.size());
741 size_t i = 0;
742 for (Data::IndividualConfigIterator it = m->individualConfigs.begin(); it != m->individualConfigs.end(); ++it, i++)
743 if (it->second->i_getScope() != DHCPConfigScope_MAC)
744 aValues[i].printf("[%RTuuid]:%d", it->second->i_getMachineId().raw(), it->second->i_getSlot());
745 else
746 aValues[i].printf("[%RTmac]", it->second->i_getMACAddress());
747 }
748 catch (std::bad_alloc &)
749 {
750 return E_OUTOFMEMORY;
751 }
752
753 return S_OK;
754}
755
756
757/**
758 * Validates the VM name and slot, returning the machine ID.
759 *
760 * If a machine ID is given instead of a name, we won't check whether it
761 * actually exists...
762 *
763 * @returns COM status code.
764 * @param aVmName The VM name or UUID.
765 * @param aSlot The slot.
766 * @param idMachine Where to return the VM UUID.
767 */
768HRESULT DHCPServer::i_vmNameToIdAndValidateSlot(const com::Utf8Str &aVmName, LONG aSlot, com::Guid &idMachine)
769{
770 if ((ULONG)aSlot <= 32)
771 {
772 /* Is it a UUID? */
773 idMachine = aVmName;
774 if (idMachine.isValid() && !idMachine.isZero())
775 return S_OK;
776
777 /* No, find the VM and get it's UUID. */
778 ComObjPtr<Machine> ptrMachine;
779 HRESULT hrc = m->pVirtualBox->i_findMachineByName(aVmName, true /*aSetError*/, &ptrMachine);
780 if (SUCCEEDED(hrc))
781 idMachine = ptrMachine->i_getId();
782 return hrc;
783 }
784 return setError(E_INVALIDARG, tr("NIC slot number (%d) is out of range (0..32)"), aSlot);
785}
786
787
788/**
789 * Translates a VM name/id and slot to an individual configuration object.
790 *
791 * @returns COM status code.
792 * @param a_strVmName The VM name or ID.
793 * @param a_uSlot The NIC slot.
794 * @param a_fCreateIfNeeded Whether to create a new entry if not found.
795 * @param a_rPtrConfig Where to return the config object. It's
796 * implicitly referenced, so we don't be returning
797 * with any locks held.
798 *
799 * @note Caller must not be holding any locks!
800 */
801HRESULT DHCPServer::i_vmNameAndSlotToConfig(const com::Utf8Str &a_strVmName, LONG a_uSlot, bool a_fCreateIfNeeded,
802 ComObjPtr<DHCPIndividualConfig> &a_rPtrConfig)
803{
804 /*
805 * Validate the slot and normalize the name into a UUID.
806 */
807 com::Guid idMachine;
808 HRESULT hrc = i_vmNameToIdAndValidateSlot(a_strVmName, a_uSlot, idMachine);
809 if (SUCCEEDED(hrc))
810 {
811 Utf8Str strKey;
812 int vrc = strKey.printfNoThrow("%RTuuid/%u", idMachine.raw(), a_uSlot);
813 if (RT_SUCCESS(vrc))
814 {
815 /*
816 * Look it up.
817 */
818 {
819 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
820 Data::IndividualConfigIterator it = m->individualConfigs.find(strKey);
821 if (it != m->individualConfigs.end())
822 {
823 a_rPtrConfig = it->second;
824 return S_OK;
825 }
826 }
827 if (a_fCreateIfNeeded)
828 {
829 /*
830 * Create a new slot.
831 */
832 /* Instantiate the object: */
833 hrc = a_rPtrConfig.createObject();
834 if (SUCCEEDED(hrc))
835 hrc = a_rPtrConfig->initWithMachineIdAndSlot(m->pVirtualBox, this, idMachine, a_uSlot,
836 m->uIndividualMACAddressVersion - UINT32_MAX / 4);
837 if (SUCCEEDED(hrc))
838 {
839 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
840
841 /* Check for creation race: */
842 Data::IndividualConfigIterator it = m->individualConfigs.find(strKey);
843 if (it != m->individualConfigs.end())
844 {
845 a_rPtrConfig.setNull();
846 a_rPtrConfig = it->second;
847 return S_OK;
848 }
849
850 /* Add it. */
851 try
852 {
853 m->individualConfigs[strKey] = a_rPtrConfig;
854 return S_OK;
855 }
856 catch (std::bad_alloc &)
857 {
858 hrc = E_OUTOFMEMORY;
859 }
860 a_rPtrConfig.setNull();
861 }
862 }
863 else
864 hrc = VBOX_E_OBJECT_NOT_FOUND;
865 }
866 else
867 hrc = E_OUTOFMEMORY;
868 }
869 return hrc;
870}
871
872
873HRESULT DHCPServer::addVmSlotOption(const com::Utf8Str &aVmName, LONG aSlot, DhcpOpt_T aOption, const com::Utf8Str &aValue)
874{
875 ComObjPtr<DHCPIndividualConfig> ptrConfig;
876 HRESULT hrc = i_vmNameAndSlotToConfig(aVmName, aSlot, true, ptrConfig);
877 if (SUCCEEDED(hrc))
878 hrc = i_add60Option(*ptrConfig, aOption, aValue);
879 return hrc;
880}
881
882
883HRESULT DHCPServer::removeVmSlotOption(const com::Utf8Str &aVmName, LONG aSlot, DhcpOpt_T aOption)
884{
885 ComObjPtr<DHCPIndividualConfig> ptrConfig;
886 HRESULT hrc = i_vmNameAndSlotToConfig(aVmName, aSlot, false, ptrConfig);
887 if (SUCCEEDED(hrc))
888 hrc = ptrConfig->i_removeOption(aOption);
889 return hrc;
890}
891
892
893HRESULT DHCPServer::removeVmSlotOptions(const com::Utf8Str &aVmName, LONG aSlot)
894{
895 ComObjPtr<DHCPIndividualConfig> ptrConfig;
896 HRESULT hrc = i_vmNameAndSlotToConfig(aVmName, aSlot, false, ptrConfig);
897 if (SUCCEEDED(hrc))
898 hrc = ptrConfig->i_removeAllOptions();
899 return hrc;
900}
901
902
903HRESULT DHCPServer::getVmSlotOptions(const com::Utf8Str &aVmName, LONG aSlot, std::vector<com::Utf8Str> &aValues)
904{
905 ComObjPtr<DHCPIndividualConfig> ptrConfig;
906 HRESULT hrc = i_vmNameAndSlotToConfig(aVmName, aSlot, false, ptrConfig);
907 if (SUCCEEDED(hrc))
908 hrc = i_getAllOptions60(*ptrConfig, aValues);
909 else if (hrc == VBOX_E_OBJECT_NOT_FOUND)
910 {
911 aValues.resize(0);
912 hrc = S_OK;
913 }
914 return hrc;
915}
916
917
918HRESULT DHCPServer::getMacOptions(const com::Utf8Str &aMAC, std::vector<com::Utf8Str> &aOption)
919{
920 RT_NOREF(aMAC, aOption);
921 AssertFailed();
922 return setError(E_NOTIMPL, tr("The GetMacOptions method has been discontinued as it was only supposed to be used by the DHCP server and it does not need it any more. sorry"));
923}
924
925
926HRESULT DHCPServer::getEventSource(ComPtr<IEventSource> &aEventSource)
927{
928 NOREF(aEventSource);
929 ReturnComNotImplemented();
930}
931
932
933HRESULT DHCPServer::getGlobalConfig(ComPtr<IDHCPGlobalConfig> &aGlobalConfig)
934{
935 /* The global configuration is immutable, so no need to lock anything here. */
936 return m->globalConfig.queryInterfaceTo(aGlobalConfig.asOutParam());
937}
938
939
940HRESULT DHCPServer::getGroupConfigs(std::vector<ComPtr<IDHCPGroupConfig> > &aGroupConfigs)
941{
942 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
943
944 size_t const cGroupConfigs = m->groupConfigs.size();
945 try
946 {
947 aGroupConfigs.resize(cGroupConfigs);
948 }
949 catch (std::bad_alloc &)
950 {
951 return E_OUTOFMEMORY;
952 }
953
954 size_t i = 0;
955 for (Data::GroupConfigIterator it = m->groupConfigs.begin(); it != m->groupConfigs.end(); ++it, i++)
956 {
957 Assert(i < cGroupConfigs);
958 HRESULT hrc = it->second.queryInterfaceTo(aGroupConfigs[i].asOutParam());
959 if (FAILED(hrc))
960 return hrc;
961 }
962
963 return S_OK;
964}
965
966
967HRESULT DHCPServer::getIndividualConfigs(std::vector<ComPtr<IDHCPIndividualConfig> > &aIndividualConfigs)
968{
969 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
970
971 size_t const cIndividualConfigs = m->individualConfigs.size();
972 try
973 {
974 aIndividualConfigs.resize(cIndividualConfigs);
975 }
976 catch (std::bad_alloc &)
977 {
978 return E_OUTOFMEMORY;
979 }
980
981 size_t i = 0;
982 for (Data::IndividualConfigIterator it = m->individualConfigs.begin(); it != m->individualConfigs.end(); ++it, i++)
983 {
984 Assert(i < cIndividualConfigs);
985 HRESULT hrc = it->second.queryInterfaceTo(aIndividualConfigs[i].asOutParam());
986 if (FAILED(hrc))
987 return hrc;
988 }
989
990 return S_OK;
991}
992
993
994HRESULT DHCPServer::restart()
995{
996 if (!m->dhcp.isRunning())
997 return setErrorBoth(E_FAIL, VERR_PROCESS_NOT_FOUND, tr("not running"));
998
999 /*
1000 * Disabled servers will be brought down, but won't be restarted.
1001 * (see DHCPServer::start)
1002 */
1003 HRESULT hrc = stop();
1004 if (SUCCEEDED(hrc))
1005 hrc = start(m->networkName, m->trunkName, m->trunkType);
1006 return hrc;
1007}
1008
1009
1010/**
1011 * @throws std::bad_alloc
1012 */
1013HRESULT DHCPServer::i_writeDhcpdConfig(const char *pszFilename, uint32_t uMACAddressVersion) RT_NOEXCEPT
1014{
1015 /*
1016 * Produce the DHCP server configuration.
1017 */
1018 xml::Document doc;
1019 try
1020 {
1021 xml::ElementNode *pElmRoot = doc.createRootElement("DHCPServer");
1022 pElmRoot->setAttribute("networkName", m->networkName);
1023 if (m->trunkName.isNotEmpty())
1024 pElmRoot->setAttribute("trunkName", m->trunkName);
1025 pElmRoot->setAttribute("trunkType", m->trunkType);
1026 pElmRoot->setAttribute("IPAddress", m->IPAddress);
1027 pElmRoot->setAttribute("lowerIP", m->lowerIP);
1028 pElmRoot->setAttribute("upperIP", m->upperIP);
1029 pElmRoot->setAttribute("leasesFilename", m->strLeasesFilename);
1030 Utf8Str strNetworkMask;
1031 HRESULT hrc = m->globalConfig->i_getNetworkMask(strNetworkMask);
1032 if (FAILED(hrc))
1033 return hrc;
1034 pElmRoot->setAttribute("networkMask", strNetworkMask);
1035
1036 /*
1037 * Process global options
1038 */
1039 m->globalConfig->i_writeDhcpdConfig(pElmRoot->createChild("Options"));
1040
1041 /*
1042 * Groups.
1043 */
1044 for (Data::GroupConfigIterator it = m->groupConfigs.begin(); it != m->groupConfigs.end(); ++it)
1045 it->second->i_writeDhcpdConfig(pElmRoot->createChild("Group"));
1046
1047 /*
1048 * Individual NIC configurations.
1049 */
1050 for (Data::IndividualConfigIterator it = m->individualConfigs.begin(); it != m->individualConfigs.end(); ++it)
1051 if (it->second->i_isMACAddressResolved(uMACAddressVersion))
1052 it->second->i_writeDhcpdConfig(pElmRoot->createChild("Config"));
1053 else
1054 LogRelFunc(("Skipping %RTuuid/%u, no MAC address.\n", it->second->i_getMachineId().raw(), it->second->i_getSlot()));
1055 }
1056 catch (std::bad_alloc &)
1057 {
1058 return E_OUTOFMEMORY;
1059 }
1060
1061 /*
1062 * Write out the document.
1063 */
1064 try
1065 {
1066 xml::XmlFileWriter writer(doc);
1067 writer.write(pszFilename, false);
1068 }
1069 catch (...)
1070 {
1071 return E_FAIL;
1072 }
1073
1074 return S_OK;
1075}
1076
1077
1078/** @todo r=bird: why do we get the network name passed in here? it's the same
1079 * as m->strName, isn't it? */
1080HRESULT DHCPServer::start(const com::Utf8Str &aNetworkName, const com::Utf8Str &aTrunkName, const com::Utf8Str &aTrunkType)
1081{
1082 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1083
1084 /* Silently ignore attempts to run disabled servers. */
1085 if (!m->enabled)
1086 return S_OK;
1087
1088 /*
1089 * Resolve the MAC addresses. This requires us to leave the lock.
1090 */
1091 uint32_t uMACAddressVersion = m->uIndividualMACAddressVersion;
1092 if (m->individualConfigs.size() > 0)
1093 {
1094 m->uIndividualMACAddressVersion = uMACAddressVersion + 1;
1095
1096 /* Retain pointers to all the individual configuration objects so we
1097 can safely access these after releaseing the lock: */
1098 std::vector< ComObjPtr<DHCPIndividualConfig> > vecIndividualConfigs;
1099 try
1100 {
1101 vecIndividualConfigs.resize(m->individualConfigs.size());
1102 }
1103 catch (std::bad_alloc &)
1104 {
1105 return E_OUTOFMEMORY;
1106 }
1107 size_t i = 0;
1108 for (Data::IndividualConfigIterator it = m->individualConfigs.begin(); it != m->individualConfigs.end(); ++it, i++)
1109 vecIndividualConfigs[i] = it->second;
1110
1111 /* Drop the lock and resolve the MAC addresses: */
1112 alock.release();
1113
1114 i = vecIndividualConfigs.size();
1115 while (i-- > 0)
1116 vecIndividualConfigs[i]->i_resolveMACAddress(uMACAddressVersion);
1117
1118 /* Reacquire the lock */
1119 alock.acquire();
1120 if (!m->enabled)
1121 return S_OK;
1122 }
1123
1124 /*
1125 * Refuse to start a 2nd DHCP server instance for the same network.
1126 */
1127 if (m->dhcp.isRunning())
1128 return setErrorBoth(VBOX_E_OBJECT_IN_USE, VERR_PROCESS_RUNNING,
1129 tr("Cannot start DHCP server because it is already running (pid %RTproc)"), m->dhcp.getPid());
1130
1131 /*
1132 * Copy the startup parameters.
1133 */
1134 m->networkName = aNetworkName;
1135 m->trunkName = aTrunkName;
1136 m->trunkType = aTrunkType;
1137 HRESULT hrc = i_calcLeasesConfigAndLogFilenames(aNetworkName);
1138 if (SUCCEEDED(hrc))
1139 {
1140 /*
1141 * Create configuration file path and write out the configuration.
1142 */
1143 hrc = i_writeDhcpdConfig(m->strConfigFilename.c_str(), uMACAddressVersion);
1144 if (SUCCEEDED(hrc))
1145 {
1146 /*
1147 * Setup the arguments and start the DHCP server.
1148 */
1149 m->dhcp.resetArguments();
1150 int vrc = m->dhcp.addArgPair("--comment", m->networkName.c_str());
1151 if (RT_SUCCESS(vrc))
1152 vrc = m->dhcp.addArgPair("--config", m->strConfigFilename.c_str());
1153 if (RT_SUCCESS(vrc))
1154 vrc = m->dhcp.addArgPair("--log", m->networkName.c_str());
1155 /** @todo Add --log-flags, --log-group-settings, and --log-destinations with
1156 * associated IDHCPServer attributes. (Not doing it now because that'll
1157 * exhaust all reserved attribute slot in 6.0.) */
1158 if (RT_SUCCESS(vrc))
1159 {
1160 /* Start it: */
1161 vrc = m->dhcp.start(true /*aKillProcessOnStop*/);
1162 if (RT_FAILURE(vrc))
1163 hrc = setErrorVrc(vrc, tr("Failed to start DHCP server for '%s': %Rrc"), m->strName.c_str(), vrc);
1164 }
1165 else
1166 hrc = setErrorVrc(vrc, tr("Failed to assemble the command line for DHCP server '%s': %Rrc"),
1167 m->strName.c_str(), vrc);
1168 }
1169 }
1170 return hrc;
1171}
1172
1173
1174HRESULT DHCPServer::stop(void)
1175{
1176 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1177
1178 int vrc = m->dhcp.stop();
1179 if (RT_SUCCESS(vrc))
1180 return S_OK;
1181 return setErrorVrc(vrc);
1182}
1183
1184
1185HRESULT DHCPServer::findLeaseByMAC(const com::Utf8Str &aMac, LONG aType,
1186 com::Utf8Str &aAddress, com::Utf8Str &aState, LONG64 *aIssued, LONG64 *aExpire)
1187{
1188 /* Reset output before we start */
1189 *aIssued = 0;
1190 *aExpire = 0;
1191 aAddress.setNull();
1192 aState.setNull();
1193
1194 /*
1195 * Convert and check input.
1196 */
1197 RTMAC MacAddress;
1198 int vrc = RTStrConvertHexBytes(aMac.c_str(), &MacAddress, sizeof(MacAddress), RTSTRCONVERTHEXBYTES_F_SEP_COLON);
1199 if (vrc != VINF_SUCCESS)
1200 return setErrorBoth(E_INVALIDARG, vrc, tr("Invalid MAC address '%s': %Rrc"), aMac.c_str(), vrc);
1201 if (aType != 0)
1202 return setError(E_INVALIDARG, tr("flags must be zero (not %#x)"), aType);
1203
1204 /*
1205 * Make sure we've got a lease filename to work with.
1206 */
1207 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1208 if (m->strLeasesFilename.isEmpty())
1209 {
1210 HRESULT hrc = i_calcLeasesConfigAndLogFilenames(m->networkName.isEmpty() ? m->strName : m->networkName);
1211 if (FAILED(hrc))
1212 return hrc;
1213 }
1214
1215 /*
1216 * Try at least twice to read the lease database, more if busy.
1217 */
1218 uint64_t const nsStart = RTTimeNanoTS();
1219 for (uint32_t uReadAttempt = 0; ; uReadAttempt++)
1220 {
1221 /*
1222 * Try read the file.
1223 */
1224 xml::Document doc;
1225 try
1226 {
1227 xml::XmlFileParser parser;
1228 parser.read(m->strLeasesFilename.c_str(), doc);
1229 }
1230 catch (const xml::EIPRTFailure &e)
1231 {
1232 vrc = e.rc();
1233 LogThisFunc(("caught xml::EIPRTFailure: rc=%Rrc (attempt %u, msg=%s)\n", vrc, uReadAttempt, e.what()));
1234 if ( ( vrc == VERR_FILE_NOT_FOUND
1235 || vrc == VERR_OPEN_FAILED
1236 || vrc == VERR_ACCESS_DENIED
1237 || vrc == VERR_SHARING_VIOLATION
1238 || vrc == VERR_READ_ERROR /*?*/)
1239 && ( uReadAttempt == 0
1240 || ( uReadAttempt < 64
1241 && RTTimeNanoTS() - nsStart < RT_NS_1SEC / 4)) )
1242 {
1243 alock.release();
1244
1245 if (uReadAttempt > 0)
1246 RTThreadYield();
1247 RTThreadSleep(8/*ms*/);
1248
1249 alock.acquire();
1250 LogThisFunc(("Retrying...\n"));
1251 continue;
1252 }
1253 return setErrorBoth(VBOX_E_FILE_ERROR, vrc, "Reading '%s' failed: %Rrc - %s",
1254 m->strLeasesFilename.c_str(), vrc, e.what());
1255 }
1256 catch (const RTCError &e)
1257 {
1258 if (e.what())
1259 return setError(VBOX_E_FILE_ERROR, "Reading '%s' failed: %s", m->strLeasesFilename.c_str(), e.what());
1260 return setError(VBOX_E_FILE_ERROR, "Reading '%s' failed: RTCError", m->strLeasesFilename.c_str());
1261 }
1262 catch (std::bad_alloc &)
1263 {
1264 return E_OUTOFMEMORY;
1265 }
1266 catch (...)
1267 {
1268 AssertFailed();
1269 return setError(VBOX_E_FILE_ERROR, tr("Reading '%s' failed: Unexpected exception"), m->strLeasesFilename.c_str());
1270 }
1271
1272 /*
1273 * Look for that mac address.
1274 */
1275 xml::ElementNode *pElmRoot = doc.getRootElement();
1276 if (pElmRoot && pElmRoot->nameEquals("Leases"))
1277 {
1278 xml::NodesLoop it(*pElmRoot);
1279 const xml::ElementNode *pElmLease;
1280 while ((pElmLease = it.forAllNodes()) != NULL)
1281 if (pElmLease->nameEquals("Lease"))
1282 {
1283 const char *pszCurMacAddress = pElmLease->findAttributeValue("mac");
1284 RTMAC CurMacAddress;
1285 if ( pszCurMacAddress
1286 && RT_SUCCESS(RTNetStrToMacAddr(pszCurMacAddress, &CurMacAddress))
1287 && memcmp(&CurMacAddress, &MacAddress, sizeof(MacAddress)) == 0)
1288 {
1289 /*
1290 * Found it!
1291 */
1292 xml::ElementNode const *pElmTime = pElmLease->findChildElement("Time");
1293 int64_t secIssued = 0;
1294 uint32_t cSecsToLive = 0;
1295 if (pElmTime)
1296 {
1297 pElmTime->getAttributeValue("issued", &secIssued);
1298 pElmTime->getAttributeValue("expiration", &cSecsToLive);
1299 *aIssued = secIssued;
1300 *aExpire = secIssued + cSecsToLive;
1301 }
1302 try
1303 {
1304 aAddress = pElmLease->findChildElementAttributeValue("Address", "value");
1305 aState = pElmLease->findAttributeValue("state");
1306 }
1307 catch (std::bad_alloc &)
1308 {
1309 return E_OUTOFMEMORY;
1310 }
1311
1312 /* Check if the lease has expired in the mean time. */
1313 HRESULT hrc = S_OK;
1314 RTTIMESPEC Now;
1315 if ( (aState.equals("acked") || aState.equals("offered") || aState.isEmpty())
1316 && secIssued + cSecsToLive < RTTimeSpecGetSeconds(RTTimeNow(&Now)))
1317 hrc = aState.assignNoThrow("expired");
1318 return hrc;
1319 }
1320 }
1321 }
1322 break;
1323 }
1324
1325 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("Could not find a lease for %RTmac"), &MacAddress);
1326}
1327
1328
1329HRESULT DHCPServer::getConfig(DHCPConfigScope_T aScope, const com::Utf8Str &aName, ULONG aSlot, BOOL aMayAdd,
1330 ComPtr<IDHCPConfig> &aConfig)
1331{
1332 if (aSlot != 0 && aScope != DHCPConfigScope_MachineNIC)
1333 return setError(E_INVALIDARG, tr("The 'slot' argument must be zero for all but the MachineNIC scope!"));
1334
1335 switch (aScope)
1336 {
1337 case DHCPConfigScope_Global:
1338 if (aName.isNotEmpty())
1339 return setError(E_INVALIDARG, tr("The name must be empty or NULL for the Global scope!"));
1340
1341 /* No locking required here. */
1342 return m->globalConfig.queryInterfaceTo(aConfig.asOutParam());
1343
1344 case DHCPConfigScope_Group:
1345 {
1346 if (aName.isEmpty())
1347 return setError(E_INVALIDARG, tr("A group must have a name!"));
1348 if (aName.length() > _1K)
1349 return setError(E_INVALIDARG, tr("Name too long! %zu bytes"), aName.length());
1350
1351 /* Look up the group: */
1352 {
1353 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1354 Data::GroupConfigIterator it = m->groupConfigs.find(aName);
1355 if (it != m->groupConfigs.end())
1356 return it->second.queryInterfaceTo(aConfig.asOutParam());
1357 }
1358 /* Create a new group if we can. */
1359 if (!aMayAdd)
1360 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("Found no configuration for group %s"), aName.c_str());
1361 ComObjPtr<DHCPGroupConfig> ptrGroupConfig;
1362 HRESULT hrc = ptrGroupConfig.createObject();
1363 if (SUCCEEDED(hrc))
1364 hrc = ptrGroupConfig->initWithDefaults(m->pVirtualBox, this, aName);
1365 if (SUCCEEDED(hrc))
1366 {
1367 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1368
1369 /* Check for insertion race: */
1370 Data::GroupConfigIterator it = m->groupConfigs.find(aName);
1371 if (it != m->groupConfigs.end())
1372 return it->second.queryInterfaceTo(aConfig.asOutParam()); /* creation race*/
1373
1374 /* Try insert it: */
1375 try
1376 {
1377 m->groupConfigs[aName] = ptrGroupConfig;
1378 }
1379 catch (std::bad_alloc &)
1380 {
1381 return E_OUTOFMEMORY;
1382 }
1383 return ptrGroupConfig.queryInterfaceTo(aConfig.asOutParam());
1384 }
1385 return hrc;
1386 }
1387
1388 case DHCPConfigScope_MachineNIC:
1389 {
1390 ComObjPtr<DHCPIndividualConfig> ptrIndividualConfig;
1391 HRESULT hrc = i_vmNameAndSlotToConfig(aName, aSlot, aMayAdd != FALSE, ptrIndividualConfig);
1392 if (SUCCEEDED(hrc))
1393 hrc = ptrIndividualConfig.queryInterfaceTo(aConfig.asOutParam());
1394 return hrc;
1395 }
1396
1397 case DHCPConfigScope_MAC:
1398 {
1399 /* Check and Normalize the MAC address into a key: */
1400 RTMAC MACAddress;
1401 int vrc = RTNetStrToMacAddr(aName.c_str(), &MACAddress);
1402 if (RT_SUCCESS(vrc))
1403 {
1404 Utf8Str strKey;
1405 vrc = strKey.printfNoThrow("%RTmac", &MACAddress);
1406 if (RT_SUCCESS(vrc))
1407 {
1408 /* Look up the MAC address: */
1409 {
1410 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1411 Data::IndividualConfigIterator it = m->individualConfigs.find(strKey);
1412 if (it != m->individualConfigs.end())
1413 return it->second.queryInterfaceTo(aConfig.asOutParam());
1414 }
1415 if (aMayAdd)
1416 {
1417 /* Create a new individiual configuration: */
1418 ComObjPtr<DHCPIndividualConfig> ptrIndividualConfig;
1419 HRESULT hrc = ptrIndividualConfig.createObject();
1420 if (SUCCEEDED(hrc))
1421 hrc = ptrIndividualConfig->initWithMACAddress(m->pVirtualBox, this, &MACAddress);
1422 if (SUCCEEDED(hrc))
1423 {
1424 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1425
1426 /* Check for insertion race: */
1427 Data::IndividualConfigIterator it = m->individualConfigs.find(strKey);
1428 if (it != m->individualConfigs.end())
1429 return it->second.queryInterfaceTo(aConfig.asOutParam()); /* creation race*/
1430
1431 /* Try insert it: */
1432 try
1433 {
1434 m->individualConfigs[strKey] = ptrIndividualConfig;
1435 }
1436 catch (std::bad_alloc &)
1437 {
1438 return E_OUTOFMEMORY;
1439 }
1440 return ptrIndividualConfig.queryInterfaceTo(aConfig.asOutParam());
1441 }
1442 }
1443 else
1444 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("Found no configuration for MAC address %s"), strKey.c_str());
1445 }
1446 return E_OUTOFMEMORY;
1447 }
1448 return setErrorBoth(E_INVALIDARG, vrc, tr("Invalid MAC address: %s"), aName.c_str());
1449 }
1450
1451 default:
1452 return E_FAIL;
1453 }
1454}
1455
1456
1457/**
1458 * Calculates and updates the value of strLeasesFilename given @a aNetwork.
1459 */
1460HRESULT DHCPServer::i_calcLeasesConfigAndLogFilenames(const com::Utf8Str &aNetwork) RT_NOEXCEPT
1461{
1462 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1463
1464 /* The lease file must be the same as we used the last time, so careful when changing this code. */
1465 int vrc = m->strLeasesFilename.assignNoThrow(m->pVirtualBox->i_homeDir());
1466 if (RT_SUCCESS(vrc))
1467 vrc = RTPathAppendCxx(m->strLeasesFilename, aNetwork);
1468 if (RT_SUCCESS(vrc))
1469 {
1470 RTPathPurgeFilename(RTPathFilename(m->strLeasesFilename.mutableRaw()), RTPATH_STR_F_STYLE_HOST);
1471
1472 /* The configuration file: */
1473 vrc = m->strConfigFilename.assignNoThrow(m->strLeasesFilename);
1474 if (RT_SUCCESS(vrc))
1475 vrc = m->strConfigFilename.appendNoThrow("-Dhcpd.config");
1476
1477
1478 /* The log file: */
1479 if (RT_SUCCESS(vrc))
1480 {
1481 vrc = m->strLogFilename.assignNoThrow(m->strLeasesFilename);
1482 if (RT_SUCCESS(vrc))
1483 vrc = m->strLogFilename.appendNoThrow("-Dhcpd.log");
1484
1485 /* Finally, complete the leases file: */
1486 if (RT_SUCCESS(vrc))
1487 {
1488 vrc = m->strLeasesFilename.appendNoThrow("-Dhcpd.leases");
1489 if (RT_SUCCESS(vrc))
1490 {
1491 RTPathPurgeFilename(RTPathFilename(m->strLeasesFilename.mutableRaw()), RTPATH_STR_F_STYLE_HOST);
1492 m->strLeasesFilename.jolt();
1493 return S_OK;
1494 }
1495 }
1496 }
1497 }
1498 return setErrorBoth(E_FAIL, vrc, tr("Failed to construct leases, config and log filenames: %Rrc"), vrc);
1499}
1500
Note: See TracBrowser for help on using the repository browser.

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