VirtualBox

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

Last change on this file since 79832 was 79823, checked in by vboxsync, 5 years ago

Main/DHCPServer: It's okay to put the DHCP server IP in the dynamic range now, it's treated like a fixed allocation. bugref:9288

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