VirtualBox

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

Last change on this file since 79812 was 79778, checked in by vboxsync, 5 years ago

Main: s/DHCPOptionEncoding_Legacy/DHCPOptionEncoding_Normal/g as 'Legacy' does not seem a good fit for the more userfriendly value encoding. bugref:9288

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 49.1 KB
Line 
1/* $Id: DHCPServerImpl.cpp 79778 2019-07-15 00:36:08Z 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.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#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 {
385 for (Data::GroupConfigIterator it = m->groupConfigs.begin(); it != m->groupConfigs.end();)
386 {
387 DHCPConfig *pCurConfig = it->second;
388 if (pCurConfig == pConfig)
389 {
390 m->groupConfigs.erase(it++); /* Post increment returns copy of original that is then erased. */
391 fFound = true;
392 }
393 else
394 ++it;
395 }
396 break;
397 }
398
399 case DHCPConfigScope_MAC:
400 case DHCPConfigScope_MachineNIC:
401 {
402 for (Data::IndividualConfigIterator it = m->individualConfigs.begin(); it != m->individualConfigs.end();)
403 {
404 DHCPConfig *pCurConfig = it->second;
405 if (pCurConfig == pConfig)
406 {
407 m->individualConfigs.erase(it++); /* Post increment returns copy of original that is then erased. */
408 fFound = true;
409 }
410 else
411 ++it;
412 }
413 break;
414 }
415
416 default:
417 AssertFailedReturn(E_FAIL);
418 }
419
420 /* Don't complain if already removed, right? */
421 if (!fFound)
422 return S_OK;
423 }
424
425 return i_doSaveSettings();
426}
427
428
429/**
430 * Internal worker that saves the settings after a modification was made.
431 *
432 * @returns COM status code.
433 *
434 * @note Caller must not hold any locks!
435 */
436HRESULT DHCPServer::i_doSaveSettings()
437{
438 // save the global settings; for that we should hold only the VirtualBox lock
439 AutoWriteLock vboxLock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
440 return m->pVirtualBox->i_saveSettings();
441}
442
443
444HRESULT DHCPServer::getNetworkName(com::Utf8Str &aName)
445{
446 /* The name is const, so no need to for locking. */
447 return aName.assignEx(m->strName);
448}
449
450
451HRESULT DHCPServer::getEnabled(BOOL *aEnabled)
452{
453 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
454 *aEnabled = m->enabled;
455 return S_OK;
456}
457
458
459HRESULT DHCPServer::setEnabled(BOOL aEnabled)
460{
461 {
462 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
463 m->enabled = aEnabled;
464 }
465 return i_doSaveSettings();
466}
467
468
469HRESULT DHCPServer::getIPAddress(com::Utf8Str &aIPAddress)
470{
471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
472 return aIPAddress.assignEx(m->IPAddress);
473}
474
475
476HRESULT DHCPServer::getNetworkMask(com::Utf8Str &aNetworkMask)
477{
478 return m->globalConfig->i_getNetworkMask(aNetworkMask);
479}
480
481
482HRESULT DHCPServer::getLowerIP(com::Utf8Str &aIPAddress)
483{
484 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
485 return aIPAddress.assignEx(m->lowerIP);
486}
487
488
489HRESULT DHCPServer::getUpperIP(com::Utf8Str &aIPAddress)
490{
491 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
492 return aIPAddress.assignEx(m->upperIP);
493}
494
495
496HRESULT DHCPServer::setConfiguration(const com::Utf8Str &aIPAddress,
497 const com::Utf8Str &aNetworkMask,
498 const com::Utf8Str &aLowerIP,
499 const com::Utf8Str &aUpperIP)
500{
501 RTNETADDRIPV4 IPAddress, NetworkMask, LowerIP, UpperIP;
502
503 int vrc = RTNetStrToIPv4Addr(aIPAddress.c_str(), &IPAddress);
504 if (RT_FAILURE(vrc))
505 return setErrorBoth(E_INVALIDARG, vrc, tr("Invalid server address: %s"), aIPAddress.c_str());
506
507 vrc = RTNetStrToIPv4Addr(aNetworkMask.c_str(), &NetworkMask);
508 if (RT_FAILURE(vrc))
509 return setErrorBoth(E_INVALIDARG, vrc, tr("Invalid netmask: %s"), aNetworkMask.c_str());
510
511 vrc = RTNetStrToIPv4Addr(aLowerIP.c_str(), &LowerIP);
512 if (RT_FAILURE(vrc))
513 return setErrorBoth(E_INVALIDARG, vrc, tr("Invalid range lower address: %s"), aLowerIP.c_str());
514
515 vrc = RTNetStrToIPv4Addr(aUpperIP.c_str(), &UpperIP);
516 if (RT_FAILURE(vrc))
517 return setErrorBoth(E_INVALIDARG, vrc, tr("Invalid range upper address: %s"), aUpperIP.c_str());
518
519 /*
520 * Insist on continuous mask. May be also accept prefix length
521 * here or address/prefix for aIPAddress?
522 */
523 vrc = RTNetMaskToPrefixIPv4(&NetworkMask, NULL);
524 if (RT_FAILURE(vrc))
525 return setErrorBoth(E_INVALIDARG, vrc, tr("Invalid netmask: %s"), aNetworkMask.c_str());
526
527 /* It's more convenient to convert to host order once: */
528 IPAddress.u = RT_N2H_U32(IPAddress.u);
529 NetworkMask.u = RT_N2H_U32(NetworkMask.u);
530 LowerIP.u = RT_N2H_U32(LowerIP.u);
531 UpperIP.u = RT_N2H_U32(UpperIP.u);
532
533 /*
534 * Addresses must be unicast and from the same network
535 */
536 if ( (IPAddress.u & UINT32_C(0xe0000000)) == UINT32_C(0xe0000000)
537 || (IPAddress.u & ~NetworkMask.u) == 0
538 || ((IPAddress.u & ~NetworkMask.u) | NetworkMask.u) == UINT32_C(0xffffffff))
539 return setError(E_INVALIDARG, tr("Invalid server address: %s (mask %s)"), aIPAddress.c_str(), aNetworkMask.c_str());
540
541 if ( (LowerIP.u & UINT32_C(0xe0000000)) == UINT32_C(0xe0000000)
542 || (LowerIP.u & NetworkMask.u) != (IPAddress.u &NetworkMask.u)
543 || (LowerIP.u & ~NetworkMask.u) == 0
544 || ((LowerIP.u & ~NetworkMask.u) | NetworkMask.u) == UINT32_C(0xffffffff))
545 return setError(E_INVALIDARG, tr("Invalid range lower address: %s (mask %s)"), aLowerIP.c_str(), aNetworkMask.c_str());
546
547 if ( (UpperIP.u & UINT32_C(0xe0000000)) == UINT32_C(0xe0000000)
548 || (UpperIP.u & NetworkMask.u) != (IPAddress.u &NetworkMask.u)
549 || (UpperIP.u & ~NetworkMask.u) == 0
550 || ((UpperIP.u & ~NetworkMask.u) | NetworkMask.u) == UINT32_C(0xffffffff))
551 return setError(E_INVALIDARG, tr("Invalid range upper address"), aUpperIP.c_str(), aNetworkMask.c_str());
552
553 /* The range should be valid ... */
554 if (LowerIP.u > UpperIP.u)
555 return setError(E_INVALIDARG, tr("Lower bound must be less or eqaul than the upper: %s vs %s"),
556 aLowerIP.c_str(), aUpperIP.c_str());
557
558 /* ... and shouldn't contain the server's address */
559 if (LowerIP.u <= IPAddress.u && IPAddress.u <= UpperIP.u)
560 return setError(E_INVALIDARG, tr("Server address within range bounds: %s in %s - %s"),
561 aIPAddress.c_str(), aLowerIP.c_str(), aUpperIP.c_str());
562
563 /*
564 * Input is valid, effect the changes.
565 */
566 HRESULT hrc;
567 {
568 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
569 m->IPAddress = aIPAddress;
570 m->lowerIP = aLowerIP;
571 m->upperIP = aUpperIP;
572 hrc = m->globalConfig->i_setNetworkMask(aNetworkMask);
573 }
574 if (SUCCEEDED(hrc))
575 hrc = i_doSaveSettings();
576 return hrc;
577}
578
579
580/**
581 * Used by the legacy 6.0 IDHCPServer::GetVmSlotOptions() and
582 * IDHCPServer::GetGlobalOptions() implementations.
583 *
584 * New interfaces have the option number and option encoding separate from the
585 * value.
586 */
587HRESULT DHCPServer::i_encode60Option(com::Utf8Str &strEncoded, DhcpOpt_T enmOption,
588 DHCPOptionEncoding_T enmEncoding, const com::Utf8Str &strValue)
589{
590 int vrc;
591 switch (enmEncoding)
592 {
593 case DHCPOptionEncoding_Normal:
594 {
595 /*
596 * This is original encoding which assumed that for each
597 * option we know its format and so we know how option
598 * "value" text is to be interpreted.
599 *
600 * "2:10800" # integer 32
601 * "6:1.2.3.4 8.8.8.8" # array of ip-address
602 */
603 vrc = strEncoded.printfNoThrow("%d:%s", (int)enmOption, strValue.c_str());
604 break;
605 }
606
607 case DHCPOptionEncoding_Hex:
608 {
609 /*
610 * This is a bypass for any option - preformatted value as
611 * hex string with no semantic involved in formatting the
612 * value for the DHCP reply.
613 *
614 * 234=68:65:6c:6c:6f:2c:20:77:6f:72:6c:64
615 */
616 vrc = strEncoded.printfNoThrow("%d=%s", (int)enmOption, strValue.c_str());
617 break;
618 }
619
620 default:
621 {
622 /*
623 * Try to be forward compatible.
624 *
625 * "254@42=i hope you know what this means"
626 */
627 vrc = strEncoded.printfNoThrow("%d@%d=%s", (int)enmOption, (int)enmEncoding, strValue.c_str());
628 break;
629 }
630 }
631 return RT_SUCCESS(vrc) ? S_OK : E_OUTOFMEMORY;
632}
633
634
635/**
636 * worker for IDHCPServer::GetGlobalOptions.
637 */
638HRESULT DHCPServer::i_getAllOptions60(DHCPConfig &aSourceConfig, std::vector<com::Utf8Str> &aValues)
639{
640 /* Get the values using the new getter: */
641 std::vector<DhcpOpt_T> Options;
642 std::vector<DHCPOptionEncoding_T> Encodings;
643 std::vector<com::Utf8Str> Values;
644 HRESULT hrc = aSourceConfig.i_getAllOptions(Options, Encodings, Values);
645 if (SUCCEEDED(hrc))
646 {
647 /* Encoding them using in the 6.0 style: */
648 size_t const cValues = Values.size();
649 aValues.resize(cValues);
650 for (size_t i = 0; i < cValues && SUCCEEDED(hrc); i++)
651 hrc = i_encode60Option(aValues[i], Options[i], Encodings[i], Values[i]);
652 }
653 return hrc;
654}
655
656
657/**
658 * Worker for legacy <=6.0 interfaces for adding options.
659 *
660 * @throws std::bad_alloc
661 */
662HRESULT DHCPServer::i_add60Option(DHCPConfig &aTargetConfig, DhcpOpt_T aOption, const com::Utf8Str &aValue)
663{
664 if (aOption != 0)
665 return aTargetConfig.i_setOption(aOption, DHCPOptionEncoding_Normal, aValue);
666
667 /*
668 * This is a kludge to sneak in option encoding information
669 * through existing API. We use option 0 and supply the real
670 * option/value in the same format that i_encode60Option() above
671 * produces for getter methods.
672 */
673 uint8_t u8Code;
674 char *pszNext;
675 int vrc = RTStrToUInt8Ex(aValue.c_str(), &pszNext, 10, &u8Code);
676 if (RT_FAILURE(vrc))
677 return setErrorBoth(E_INVALIDARG, VERR_PARSE_ERROR);
678
679 DHCPOptionEncoding_T enmEncoding;
680 switch (*pszNext)
681 {
682 case ':': /* support legacy format too */
683 {
684 enmEncoding = DHCPOptionEncoding_Normal;
685 break;
686 }
687
688 case '=':
689 {
690 enmEncoding = DHCPOptionEncoding_Hex;
691 break;
692 }
693
694 case '@':
695 {
696 uint32_t u32Enc = 0;
697 vrc = RTStrToUInt32Ex(pszNext + 1, &pszNext, 10, &u32Enc);
698 if (RT_FAILURE(vrc))
699 return setErrorBoth(E_INVALIDARG, VERR_PARSE_ERROR);
700 if (*pszNext != '=')
701 return setErrorBoth(E_INVALIDARG, VERR_PARSE_ERROR);
702 enmEncoding = (DHCPOptionEncoding_T)u32Enc;
703 break;
704 }
705
706 default:
707 return VERR_PARSE_ERROR;
708 }
709
710 return aTargetConfig.i_setOption(aOption, enmEncoding, com::Utf8Str(pszNext + 1));
711}
712
713
714HRESULT DHCPServer::addGlobalOption(DhcpOpt_T aOption, const com::Utf8Str &aValue)
715{
716 return i_add60Option(*m->globalConfig, aOption, aValue);
717}
718
719
720HRESULT DHCPServer::removeGlobalOption(DhcpOpt_T aOption)
721{
722 return m->globalConfig->i_removeOption(aOption);
723}
724
725
726HRESULT DHCPServer::removeGlobalOptions()
727{
728 return m->globalConfig->i_removeAllOptions();
729}
730
731
732HRESULT DHCPServer::getGlobalOptions(std::vector<com::Utf8Str> &aValues)
733{
734 return i_getAllOptions60(*m->globalConfig, aValues);
735}
736
737
738HRESULT DHCPServer::getVmConfigs(std::vector<com::Utf8Str> &aValues)
739{
740 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
741
742 try
743 {
744 aValues.resize(m->individualConfigs.size());
745 size_t i = 0;
746 for (Data::IndividualConfigIterator it = m->individualConfigs.begin(); it != m->individualConfigs.end(); ++it, i++)
747 if (it->second->i_getScope() != DHCPConfigScope_MAC)
748 aValues[i].printf("[%RTuuid]:%d", it->second->i_getMachineId().raw(), it->second->i_getSlot());
749 else
750 aValues[i].printf("[%RTmac]", it->second->i_getMACAddress());
751 }
752 catch (std::bad_alloc &)
753 {
754 return E_OUTOFMEMORY;
755 }
756
757 return S_OK;
758}
759
760
761/**
762 * Validates the VM name and slot, returning the machine ID.
763 *
764 * If a machine ID is given instead of a name, we won't check whether it
765 * actually exists...
766 *
767 * @returns COM status code.
768 * @param aVmName The VM name or UUID.
769 * @param aSlot The slot.
770 * @param idMachine Where to return the VM UUID.
771 */
772HRESULT DHCPServer::i_vmNameToIdAndValidateSlot(const com::Utf8Str &aVmName, LONG aSlot, com::Guid &idMachine)
773{
774 if ((ULONG)aSlot <= 32)
775 {
776 /* Is it a UUID? */
777 idMachine = aVmName;
778 if (idMachine.isValid() && !idMachine.isZero())
779 return S_OK;
780
781 /* No, find the VM and get it's UUID. */
782 ComObjPtr<Machine> ptrMachine;
783 HRESULT hrc = m->pVirtualBox->i_findMachineByName(aVmName, true /*aSetError*/, &ptrMachine);
784 if (SUCCEEDED(hrc))
785 idMachine = ptrMachine->i_getId();
786 return hrc;
787 }
788 return setError(E_INVALIDARG, tr("NIC slot number (%d) is out of range (0..32)"), aSlot);
789}
790
791
792/**
793 * Translates a VM name/id and slot to an individual configuration object.
794 *
795 * @returns COM status code.
796 * @param a_strVmName The VM name or ID.
797 * @param a_uSlot The NIC slot.
798 * @param a_fCreateIfNeeded Whether to create a new entry if not found.
799 * @param a_rPtrConfig Where to return the config object. It's
800 * implicitly referenced, so we don't be returning
801 * with any locks held.
802 *
803 * @note Caller must not be holding any locks!
804 */
805HRESULT DHCPServer::i_vmNameAndSlotToConfig(const com::Utf8Str &a_strVmName, LONG a_uSlot, bool a_fCreateIfNeeded,
806 ComObjPtr<DHCPIndividualConfig> &a_rPtrConfig)
807{
808 /*
809 * Validate the slot and normalize the name into a UUID.
810 */
811 com::Guid idMachine;
812 HRESULT hrc = i_vmNameToIdAndValidateSlot(a_strVmName, a_uSlot, idMachine);
813 if (SUCCEEDED(hrc))
814 {
815 Utf8Str strKey;
816 int vrc = strKey.printfNoThrow("%RTuuid/%u", idMachine.raw(), a_uSlot);
817 if (RT_SUCCESS(vrc))
818 {
819 /*
820 * Look it up.
821 */
822 {
823 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
824 Data::IndividualConfigIterator it = m->individualConfigs.find(strKey);
825 if (it != m->individualConfigs.end())
826 {
827 a_rPtrConfig = it->second;
828 return S_OK;
829 }
830 }
831 if (a_fCreateIfNeeded)
832 {
833 /*
834 * Create a new slot.
835 */
836 /* Instantiate the object: */
837 hrc = a_rPtrConfig.createObject();
838 if (SUCCEEDED(hrc))
839 hrc = a_rPtrConfig->initWithMachineIdAndSlot(m->pVirtualBox, this, idMachine, a_uSlot,
840 m->uIndividualMACAddressVersion - UINT32_MAX / 4);
841 if (SUCCEEDED(hrc))
842 {
843 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
844
845 /* Check for creation race: */
846 Data::IndividualConfigIterator it = m->individualConfigs.find(strKey);
847 if (it != m->individualConfigs.end())
848 {
849 a_rPtrConfig.setNull();
850 a_rPtrConfig = it->second;
851 return S_OK;
852 }
853
854 /* Add it. */
855 try
856 {
857 m->individualConfigs[strKey] = a_rPtrConfig;
858 return S_OK;
859 }
860 catch (std::bad_alloc &)
861 {
862 hrc = E_OUTOFMEMORY;
863 }
864 a_rPtrConfig.setNull();
865 }
866 }
867 else
868 hrc = VBOX_E_OBJECT_NOT_FOUND;
869 }
870 else
871 hrc = E_OUTOFMEMORY;
872 }
873 return hrc;
874}
875
876
877HRESULT DHCPServer::addVmSlotOption(const com::Utf8Str &aVmName, LONG aSlot, DhcpOpt_T aOption, const com::Utf8Str &aValue)
878{
879 ComObjPtr<DHCPIndividualConfig> ptrConfig;
880 HRESULT hrc = i_vmNameAndSlotToConfig(aVmName, aSlot, true, ptrConfig);
881 if (SUCCEEDED(hrc))
882 hrc = i_add60Option(*ptrConfig, aOption, aValue);
883 return hrc;
884}
885
886
887HRESULT DHCPServer::removeVmSlotOption(const com::Utf8Str &aVmName, LONG aSlot, DhcpOpt_T aOption)
888{
889 ComObjPtr<DHCPIndividualConfig> ptrConfig;
890 HRESULT hrc = i_vmNameAndSlotToConfig(aVmName, aSlot, false, ptrConfig);
891 if (SUCCEEDED(hrc))
892 hrc = ptrConfig->i_removeOption(aOption);
893 return hrc;
894}
895
896
897HRESULT DHCPServer::removeVmSlotOptions(const com::Utf8Str &aVmName, LONG aSlot)
898{
899 ComObjPtr<DHCPIndividualConfig> ptrConfig;
900 HRESULT hrc = i_vmNameAndSlotToConfig(aVmName, aSlot, false, ptrConfig);
901 if (SUCCEEDED(hrc))
902 hrc = ptrConfig->i_removeAllOptions();
903 return hrc;
904}
905
906
907HRESULT DHCPServer::getVmSlotOptions(const com::Utf8Str &aVmName, LONG aSlot, std::vector<com::Utf8Str> &aValues)
908{
909 ComObjPtr<DHCPIndividualConfig> ptrConfig;
910 HRESULT hrc = i_vmNameAndSlotToConfig(aVmName, aSlot, false, ptrConfig);
911 if (SUCCEEDED(hrc))
912 hrc = i_getAllOptions60(*ptrConfig, aValues);
913 else if (hrc == VBOX_E_OBJECT_NOT_FOUND)
914 {
915 aValues.resize(0);
916 hrc = S_OK;
917 }
918 return hrc;
919}
920
921
922HRESULT DHCPServer::getMacOptions(const com::Utf8Str &aMAC, std::vector<com::Utf8Str> &aOption)
923{
924 RT_NOREF(aMAC, aOption);
925 AssertFailed();
926 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"));
927}
928
929
930HRESULT DHCPServer::getEventSource(ComPtr<IEventSource> &aEventSource)
931{
932 NOREF(aEventSource);
933 ReturnComNotImplemented();
934}
935
936
937HRESULT DHCPServer::getGlobalConfig(ComPtr<IDHCPGlobalConfig> &aGlobalConfig)
938{
939 /* The global configuration is immutable, so no need to lock anything here. */
940 return m->globalConfig.queryInterfaceTo(aGlobalConfig.asOutParam());
941}
942
943
944HRESULT DHCPServer::getGroupConfigs(std::vector<ComPtr<IDHCPGroupConfig> > &aGroupConfigs)
945{
946 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
947
948 size_t const cGroupConfigs = m->groupConfigs.size();
949 try
950 {
951 aGroupConfigs.resize(cGroupConfigs);
952 }
953 catch (std::bad_alloc &)
954 {
955 return E_OUTOFMEMORY;
956 }
957
958 size_t i = 0;
959 for (Data::GroupConfigIterator it = m->groupConfigs.begin(); it != m->groupConfigs.end(); ++it, i++)
960 {
961 Assert(i < cGroupConfigs);
962 HRESULT hrc = it->second.queryInterfaceTo(aGroupConfigs[i].asOutParam());
963 if (FAILED(hrc))
964 return hrc;
965 }
966
967 return S_OK;
968}
969
970
971HRESULT DHCPServer::getIndividualConfigs(std::vector<ComPtr<IDHCPIndividualConfig> > &aIndividualConfigs)
972{
973 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
974
975 size_t const cIndividualConfigs = m->individualConfigs.size();
976 try
977 {
978 aIndividualConfigs.resize(cIndividualConfigs);
979 }
980 catch (std::bad_alloc &)
981 {
982 return E_OUTOFMEMORY;
983 }
984
985 size_t i = 0;
986 for (Data::IndividualConfigIterator it = m->individualConfigs.begin(); it != m->individualConfigs.end(); ++it, i++)
987 {
988 Assert(i < cIndividualConfigs);
989 HRESULT hrc = it->second.queryInterfaceTo(aIndividualConfigs[i].asOutParam());
990 if (FAILED(hrc))
991 return hrc;
992 }
993
994 return S_OK;
995}
996
997
998HRESULT DHCPServer::restart()
999{
1000 if (!m->dhcp.isRunning())
1001 return setErrorBoth(E_FAIL, VERR_PROCESS_NOT_FOUND, tr("not running"));
1002
1003 /*
1004 * Disabled servers will be brought down, but won't be restarted.
1005 * (see DHCPServer::start)
1006 */
1007 HRESULT hrc = stop();
1008 if (SUCCEEDED(hrc))
1009 hrc = start(m->networkName, m->trunkName, m->trunkType);
1010 return hrc;
1011}
1012
1013
1014/**
1015 * @throws std::bad_alloc
1016 */
1017HRESULT DHCPServer::i_writeDhcpdConfig(const char *pszFilename, uint32_t uMACAddressVersion) RT_NOEXCEPT
1018{
1019 /*
1020 * Produce the DHCP server configuration.
1021 */
1022 xml::Document doc;
1023 try
1024 {
1025 xml::ElementNode *pElmRoot = doc.createRootElement("DHCPServer");
1026 pElmRoot->setAttribute("networkName", m->networkName);
1027 if (m->trunkName.isNotEmpty())
1028 pElmRoot->setAttribute("trunkName", m->trunkName);
1029 pElmRoot->setAttribute("trunkType", m->trunkType);
1030 pElmRoot->setAttribute("IPAddress", m->IPAddress);
1031 pElmRoot->setAttribute("lowerIP", m->lowerIP);
1032 pElmRoot->setAttribute("upperIP", m->upperIP);
1033 pElmRoot->setAttribute("leasesFilename", m->strLeasesFilename);
1034 Utf8Str strNetworkMask;
1035 HRESULT hrc = m->globalConfig->i_getNetworkMask(strNetworkMask);
1036 if (FAILED(hrc))
1037 return hrc;
1038 pElmRoot->setAttribute("networkMask", strNetworkMask);
1039
1040 /*
1041 * Process global options
1042 */
1043 m->globalConfig->i_writeDhcpdConfig(pElmRoot->createChild("Options"));
1044
1045 /*
1046 * Groups.
1047 */
1048 for (Data::GroupConfigIterator it = m->groupConfigs.begin(); it != m->groupConfigs.end(); ++it)
1049 it->second->i_writeDhcpdConfig(pElmRoot->createChild("Group"));
1050
1051 /*
1052 * Individual NIC configurations.
1053 */
1054 for (Data::IndividualConfigIterator it = m->individualConfigs.begin(); it != m->individualConfigs.end(); ++it)
1055 if (it->second->i_isMACAddressResolved(uMACAddressVersion))
1056 it->second->i_writeDhcpdConfig(pElmRoot->createChild("Config"));
1057 else
1058 LogRelFunc(("Skipping %RTuuid/%u, no MAC address.\n", it->second->i_getMachineId().raw(), it->second->i_getSlot()));
1059 }
1060 catch (std::bad_alloc &)
1061 {
1062 return E_OUTOFMEMORY;
1063 }
1064
1065 /*
1066 * Write out the document.
1067 */
1068 try
1069 {
1070 xml::XmlFileWriter writer(doc);
1071 writer.write(pszFilename, false);
1072 }
1073 catch (...)
1074 {
1075 return E_FAIL;
1076 }
1077
1078 return S_OK;
1079}
1080
1081
1082/** @todo r=bird: why do we get the network name passed in here? it's the same
1083 * as m->strName, isn't it? */
1084HRESULT DHCPServer::start(const com::Utf8Str &aNetworkName, const com::Utf8Str &aTrunkName, const com::Utf8Str &aTrunkType)
1085{
1086 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1087
1088 /* Silently ignore attempts to run disabled servers. */
1089 if (!m->enabled)
1090 return S_OK;
1091
1092 /*
1093 * Resolve the MAC addresses. This requires us to leave the lock.
1094 */
1095 uint32_t uMACAddressVersion = m->uIndividualMACAddressVersion;
1096 if (m->individualConfigs.size() > 0)
1097 {
1098 m->uIndividualMACAddressVersion = uMACAddressVersion + 1;
1099
1100 /* Retain pointers to all the individual configuration objects so we
1101 can safely access these after releaseing the lock: */
1102 std::vector< ComObjPtr<DHCPIndividualConfig> > vecIndividualConfigs;
1103 try
1104 {
1105 vecIndividualConfigs.resize(m->individualConfigs.size());
1106 }
1107 catch (std::bad_alloc &)
1108 {
1109 return E_OUTOFMEMORY;
1110 }
1111 size_t i = 0;
1112 for (Data::IndividualConfigIterator it = m->individualConfigs.begin(); it != m->individualConfigs.end(); ++it, i++)
1113 vecIndividualConfigs[i] = it->second;
1114
1115 /* Drop the lock and resolve the MAC addresses: */
1116 alock.release();
1117
1118 i = vecIndividualConfigs.size();
1119 while (i-- > 0)
1120 vecIndividualConfigs[i]->i_resolveMACAddress(uMACAddressVersion);
1121
1122 /* Reacquire the lock */
1123 alock.acquire();
1124 if (!m->enabled)
1125 return S_OK;
1126 }
1127
1128 /*
1129 * Refuse to start a 2nd DHCP server instance for the same network.
1130 */
1131 if (m->dhcp.isRunning())
1132 return setErrorBoth(VBOX_E_OBJECT_IN_USE, VERR_PROCESS_RUNNING,
1133 tr("Cannot start DHCP server because it is already running (pid %RTproc)"), m->dhcp.getPid());
1134
1135 /*
1136 * Copy the startup parameters.
1137 */
1138 m->networkName = aNetworkName;
1139 m->trunkName = aTrunkName;
1140 m->trunkType = aTrunkType;
1141 HRESULT hrc = i_calcLeasesConfigAndLogFilenames(aNetworkName);
1142 if (SUCCEEDED(hrc))
1143 {
1144 /*
1145 * Create configuration file path and write out the configuration.
1146 */
1147 hrc = i_writeDhcpdConfig(m->strConfigFilename.c_str(), uMACAddressVersion);
1148 if (SUCCEEDED(hrc))
1149 {
1150 /*
1151 * Setup the arguments and start the DHCP server.
1152 */
1153 m->dhcp.resetArguments();
1154 int vrc = m->dhcp.addArgPair("--comment", m->networkName.c_str());
1155 if (RT_SUCCESS(vrc))
1156 vrc = m->dhcp.addArgPair("--config", m->strConfigFilename.c_str());
1157 if (RT_SUCCESS(vrc))
1158 vrc = m->dhcp.addArgPair("--log", m->networkName.c_str());
1159 /** @todo Add --log-flags, --log-group-settings, and --log-destinations with
1160 * associated IDHCPServer attributes. (Not doing it now because that'll
1161 * exhaust all reserved attribute slot in 6.0.) */
1162 if (RT_SUCCESS(vrc))
1163 {
1164 /* Start it: */
1165 vrc = m->dhcp.start(true /*aKillProcessOnStop*/);
1166 if (RT_FAILURE(vrc))
1167 hrc = setErrorVrc(vrc, tr("Failed to start DHCP server for '%s': %Rrc"), m->strName.c_str(), vrc);
1168 }
1169 else
1170 hrc = setErrorVrc(vrc, tr("Failed to assemble the command line for DHCP server '%s': %Rrc"),
1171 m->strName.c_str(), vrc);
1172 }
1173 }
1174 return hrc;
1175}
1176
1177
1178HRESULT DHCPServer::stop(void)
1179{
1180 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1181
1182 int vrc = m->dhcp.stop();
1183 if (RT_SUCCESS(vrc))
1184 return S_OK;
1185 return setErrorVrc(vrc);
1186}
1187
1188
1189HRESULT DHCPServer::findLeaseByMAC(const com::Utf8Str &aMac, LONG aType,
1190 com::Utf8Str &aAddress, com::Utf8Str &aState, LONG64 *aIssued, LONG64 *aExpire)
1191{
1192 /* Reset output before we start */
1193 *aIssued = 0;
1194 *aExpire = 0;
1195 aAddress.setNull();
1196 aState.setNull();
1197
1198 /*
1199 * Convert and check input.
1200 */
1201 RTMAC MacAddress;
1202 int vrc = RTStrConvertHexBytes(aMac.c_str(), &MacAddress, sizeof(MacAddress), RTSTRCONVERTHEXBYTES_F_SEP_COLON);
1203 if (vrc != VINF_SUCCESS)
1204 return setErrorBoth(E_INVALIDARG, vrc, tr("Invalid MAC address '%s': %Rrc"), aMac.c_str(), vrc);
1205 if (aType != 0)
1206 return setError(E_INVALIDARG, tr("flags must be zero (not %#x)"), aType);
1207
1208 /*
1209 * Make sure we've got a lease filename to work with.
1210 */
1211 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1212 if (m->strLeasesFilename.isEmpty())
1213 {
1214 HRESULT hrc = i_calcLeasesConfigAndLogFilenames(m->networkName.isEmpty() ? m->strName : m->networkName);
1215 if (FAILED(hrc))
1216 return hrc;
1217 }
1218
1219 /*
1220 * Try at least twice to read the lease database, more if busy.
1221 */
1222 uint64_t const nsStart = RTTimeNanoTS();
1223 for (uint32_t uReadAttempt = 0; ; uReadAttempt++)
1224 {
1225 /*
1226 * Try read the file.
1227 */
1228 xml::Document doc;
1229 try
1230 {
1231 xml::XmlFileParser parser;
1232 parser.read(m->strLeasesFilename.c_str(), doc);
1233 }
1234 catch (const xml::EIPRTFailure &e)
1235 {
1236 vrc = e.rc();
1237 LogThisFunc(("caught xml::EIPRTFailure: rc=%Rrc (attempt %u, msg=%s)\n", vrc, uReadAttempt, e.what()));
1238 if ( ( vrc == VERR_FILE_NOT_FOUND
1239 || vrc == VERR_OPEN_FAILED
1240 || vrc == VERR_ACCESS_DENIED
1241 || vrc == VERR_SHARING_VIOLATION
1242 || vrc == VERR_READ_ERROR /*?*/)
1243 && ( uReadAttempt == 0
1244 || ( uReadAttempt < 64
1245 && RTTimeNanoTS() - nsStart < RT_NS_1SEC / 4)) )
1246 {
1247 alock.release();
1248
1249 if (uReadAttempt > 0)
1250 RTThreadYield();
1251 RTThreadSleep(8/*ms*/);
1252
1253 alock.acquire();
1254 LogThisFunc(("Retrying...\n"));
1255 continue;
1256 }
1257 return setErrorBoth(VBOX_E_FILE_ERROR, vrc, "Reading '%s' failed: %Rrc - %s",
1258 m->strLeasesFilename.c_str(), vrc, e.what());
1259 }
1260 catch (const RTCError &e)
1261 {
1262 if (e.what())
1263 return setError(VBOX_E_FILE_ERROR, "Reading '%s' failed: %s", m->strLeasesFilename.c_str(), e.what());
1264 return setError(VBOX_E_FILE_ERROR, "Reading '%s' failed: RTCError", m->strLeasesFilename.c_str());
1265 }
1266 catch (std::bad_alloc &)
1267 {
1268 return E_OUTOFMEMORY;
1269 }
1270 catch (...)
1271 {
1272 AssertFailed();
1273 return setError(VBOX_E_FILE_ERROR, tr("Reading '%s' failed: Unexpected exception"), m->strLeasesFilename.c_str());
1274 }
1275
1276 /*
1277 * Look for that mac address.
1278 */
1279 xml::ElementNode *pElmRoot = doc.getRootElement();
1280 if (pElmRoot && pElmRoot->nameEquals("Leases"))
1281 {
1282 xml::NodesLoop it(*pElmRoot);
1283 const xml::ElementNode *pElmLease;
1284 while ((pElmLease = it.forAllNodes()) != NULL)
1285 if (pElmLease->nameEquals("Lease"))
1286 {
1287 const char *pszCurMacAddress = pElmLease->findAttributeValue("mac");
1288 RTMAC CurMacAddress;
1289 if ( pszCurMacAddress
1290 && RT_SUCCESS(RTNetStrToMacAddr(pszCurMacAddress, &CurMacAddress))
1291 && memcmp(&CurMacAddress, &MacAddress, sizeof(MacAddress)) == 0)
1292 {
1293 /*
1294 * Found it!
1295 */
1296 xml::ElementNode const *pElmTime = pElmLease->findChildElement("Time");
1297 int64_t secIssued = 0;
1298 uint32_t cSecsToLive = 0;
1299 if (pElmTime)
1300 {
1301 pElmTime->getAttributeValue("issued", &secIssued);
1302 pElmTime->getAttributeValue("expiration", &cSecsToLive);
1303 *aIssued = secIssued;
1304 *aExpire = secIssued + cSecsToLive;
1305 }
1306 try
1307 {
1308 aAddress = pElmLease->findChildElementAttributeValue("Address", "value");
1309 aState = pElmLease->findAttributeValue("state");
1310 }
1311 catch (std::bad_alloc &)
1312 {
1313 return E_OUTOFMEMORY;
1314 }
1315
1316 /* Check if the lease has expired in the mean time. */
1317 HRESULT hrc = S_OK;
1318 RTTIMESPEC Now;
1319 if ( (aState.equals("acked") || aState.equals("offered") || aState.isEmpty())
1320 && secIssued + cSecsToLive < RTTimeSpecGetSeconds(RTTimeNow(&Now)))
1321 hrc = aState.assignNoThrow("expired");
1322 return hrc;
1323 }
1324 }
1325 }
1326 break;
1327 }
1328
1329 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("Could not find a lease for %RTmac"), &MacAddress);
1330}
1331
1332
1333HRESULT DHCPServer::getConfig(DHCPConfigScope_T aScope, const com::Utf8Str &aName, ULONG aSlot, BOOL aMayAdd,
1334 ComPtr<IDHCPConfig> &aConfig)
1335{
1336 if (aSlot != 0 && aScope != DHCPConfigScope_MachineNIC)
1337 return setError(E_INVALIDARG, tr("The 'slot' argument must be zero for all but the MachineNIC scope!"));
1338
1339 switch (aScope)
1340 {
1341 case DHCPConfigScope_Global:
1342 if (aName.isNotEmpty())
1343 return setError(E_INVALIDARG, tr("The name must be empty or NULL for the Global scope!"));
1344
1345 /* No locking required here. */
1346 return m->globalConfig.queryInterfaceTo(aConfig.asOutParam());
1347
1348 case DHCPConfigScope_Group:
1349 {
1350 if (aName.isEmpty())
1351 return setError(E_INVALIDARG, tr("A group must have a name!"));
1352 if (aName.length() > _1K)
1353 return setError(E_INVALIDARG, tr("Name too long! %zu bytes"), aName.length());
1354
1355 /* Look up the group: */
1356 {
1357 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1358 Data::GroupConfigIterator it = m->groupConfigs.find(aName);
1359 if (it != m->groupConfigs.end())
1360 return it->second.queryInterfaceTo(aConfig.asOutParam());
1361 }
1362 /* Create a new group if we can. */
1363 if (!aMayAdd)
1364 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("Found no configuration for group %s"), aName.c_str());
1365 ComObjPtr<DHCPGroupConfig> ptrGroupConfig;
1366 HRESULT hrc = ptrGroupConfig.createObject();
1367 if (SUCCEEDED(hrc))
1368 hrc = ptrGroupConfig->initWithDefaults(m->pVirtualBox, this, aName);
1369 if (SUCCEEDED(hrc))
1370 {
1371 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1372
1373 /* Check for insertion race: */
1374 Data::GroupConfigIterator it = m->groupConfigs.find(aName);
1375 if (it != m->groupConfigs.end())
1376 return it->second.queryInterfaceTo(aConfig.asOutParam()); /* creation race*/
1377
1378 /* Try insert it: */
1379 try
1380 {
1381 m->groupConfigs[aName] = ptrGroupConfig;
1382 }
1383 catch (std::bad_alloc &)
1384 {
1385 return E_OUTOFMEMORY;
1386 }
1387 return ptrGroupConfig.queryInterfaceTo(aConfig.asOutParam());
1388 }
1389 return hrc;
1390 }
1391
1392 case DHCPConfigScope_MachineNIC:
1393 {
1394 ComObjPtr<DHCPIndividualConfig> ptrIndividualConfig;
1395 HRESULT hrc = i_vmNameAndSlotToConfig(aName, aSlot, aMayAdd != FALSE, ptrIndividualConfig);
1396 if (SUCCEEDED(hrc))
1397 hrc = ptrIndividualConfig.queryInterfaceTo(aConfig.asOutParam());
1398 return hrc;
1399 }
1400
1401 case DHCPConfigScope_MAC:
1402 {
1403 /* Check and Normalize the MAC address into a key: */
1404 RTMAC MACAddress;
1405 int vrc = RTNetStrToMacAddr(aName.c_str(), &MACAddress);
1406 if (RT_SUCCESS(vrc))
1407 {
1408 Utf8Str strKey;
1409 vrc = strKey.printfNoThrow("%RTmac", &MACAddress);
1410 if (RT_SUCCESS(vrc))
1411 {
1412 /* Look up the MAC address: */
1413 {
1414 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1415 Data::IndividualConfigIterator it = m->individualConfigs.find(strKey);
1416 if (it != m->individualConfigs.end())
1417 return it->second.queryInterfaceTo(aConfig.asOutParam());
1418 }
1419 if (aMayAdd)
1420 {
1421 /* Create a new individiual configuration: */
1422 ComObjPtr<DHCPIndividualConfig> ptrIndividualConfig;
1423 HRESULT hrc = ptrIndividualConfig.createObject();
1424 if (SUCCEEDED(hrc))
1425 hrc = ptrIndividualConfig->initWithMACAddress(m->pVirtualBox, this, &MACAddress);
1426 if (SUCCEEDED(hrc))
1427 {
1428 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1429
1430 /* Check for insertion race: */
1431 Data::IndividualConfigIterator it = m->individualConfigs.find(strKey);
1432 if (it != m->individualConfigs.end())
1433 return it->second.queryInterfaceTo(aConfig.asOutParam()); /* creation race*/
1434
1435 /* Try insert it: */
1436 try
1437 {
1438 m->individualConfigs[strKey] = ptrIndividualConfig;
1439 }
1440 catch (std::bad_alloc &)
1441 {
1442 return E_OUTOFMEMORY;
1443 }
1444 return ptrIndividualConfig.queryInterfaceTo(aConfig.asOutParam());
1445 }
1446 }
1447 else
1448 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("Found no configuration for MAC address %s"), strKey.c_str());
1449 }
1450 return E_OUTOFMEMORY;
1451 }
1452 return setErrorBoth(E_INVALIDARG, vrc, tr("Invalid MAC address: %s"), aName.c_str());
1453 }
1454
1455 default:
1456 return E_FAIL;
1457 }
1458}
1459
1460
1461/**
1462 * Calculates and updates the value of strLeasesFilename given @a aNetwork.
1463 */
1464HRESULT DHCPServer::i_calcLeasesConfigAndLogFilenames(const com::Utf8Str &aNetwork) RT_NOEXCEPT
1465{
1466 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1467
1468 /* The lease file must be the same as we used the last time, so careful when changing this code. */
1469 int vrc = m->strLeasesFilename.assignNoThrow(m->pVirtualBox->i_homeDir());
1470 if (RT_SUCCESS(vrc))
1471 vrc = RTPathAppendCxx(m->strLeasesFilename, aNetwork);
1472 if (RT_SUCCESS(vrc))
1473 {
1474 RTPathPurgeFilename(RTPathFilename(m->strLeasesFilename.mutableRaw()), RTPATH_STR_F_STYLE_HOST);
1475
1476 /* The configuration file: */
1477 vrc = m->strConfigFilename.assignNoThrow(m->strLeasesFilename);
1478 if (RT_SUCCESS(vrc))
1479 vrc = m->strConfigFilename.appendNoThrow("-Dhcpd.config");
1480
1481
1482 /* The log file: */
1483 if (RT_SUCCESS(vrc))
1484 {
1485 vrc = m->strLogFilename.assignNoThrow(m->strLeasesFilename);
1486 if (RT_SUCCESS(vrc))
1487 vrc = m->strLogFilename.appendNoThrow("-Dhcpd.log");
1488
1489 /* Finally, complete the leases file: */
1490 if (RT_SUCCESS(vrc))
1491 {
1492 vrc = m->strLeasesFilename.appendNoThrow("-Dhcpd.leases");
1493 if (RT_SUCCESS(vrc))
1494 {
1495 RTPathPurgeFilename(RTPathFilename(m->strLeasesFilename.mutableRaw()), RTPATH_STR_F_STYLE_HOST);
1496 m->strLeasesFilename.jolt();
1497 return S_OK;
1498 }
1499 }
1500 }
1501 }
1502 return setErrorBoth(E_FAIL, vrc, tr("Failed to construct leases, config and log filenames: %Rrc"), vrc);
1503}
1504
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