VirtualBox

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

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

Main/DHCPServer,Dhcpd,VBoxManage: Added --log option to the DHCP server so we can start logging early. Added log rotation and limits. Put the config file next to the log and leases file. Validate DHCP options by reusing the parser code from the server, adding a bunch more DHCP options to the parser. Removed legacy and hardcoded configuration options from the dhcp server, it's all config file now. Fixed a bug in the option parsing of the VBoxManage dhcpserver add/modify commands. bugref:9288

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

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