VirtualBox

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

Last change on this file since 81394 was 79866, checked in by vboxsync, 5 years ago

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