VirtualBox

source: vbox/trunk/src/VBox/NetworkServices/Dhcpd/Config.cpp@ 79824

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

Dhcpd: Build fix for newer compilers. bugref:9288

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 43.7 KB
Line 
1/* $Id: Config.cpp 79824 2019-07-16 20:30:59Z vboxsync $ */
2/** @file
3 * DHCP server - server configuration
4 */
5
6/*
7 * Copyright (C) 2017-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#include "DhcpdInternal.h"
23
24#include <iprt/ctype.h>
25#include <iprt/net.h> /* NB: must come before getopt.h */
26#include <iprt/getopt.h>
27#include <iprt/path.h>
28#include <iprt/message.h>
29#include <iprt/string.h>
30#include <iprt/uuid.h>
31#include <iprt/cpp/path.h>
32
33#include <VBox/com/com.h> /* For log initialization. */
34
35#include "Config.h"
36
37
38/*********************************************************************************************************************************
39* Global Variables *
40*********************************************************************************************************************************/
41/*static*/ bool Config::g_fInitializedLog = false;
42/*static*/ uint32_t GroupConfig::s_uGroupNo = 0;
43
44
45/**
46 * Configuration file exception.
47 */
48class ConfigFileError
49 : public RTCError
50{
51public:
52#if 0 /* This just confuses the compiler. */
53 ConfigFileError(const char *a_pszMessage)
54 : RTCError(a_pszMessage)
55 {}
56#endif
57
58 explicit ConfigFileError(xml::Node const *pNode, const char *a_pszMsgFmt, ...)
59 : RTCError((char *)NULL)
60 {
61
62 i_buildPath(pNode);
63 m_strMsg.append(": ");
64
65 va_list va;
66 va_start(va, a_pszMsgFmt);
67 m_strMsg.appendPrintfV(a_pszMsgFmt, va);
68 va_end(va);
69 }
70
71
72 ConfigFileError(const char *a_pszMsgFmt, ...)
73 : RTCError((char *)NULL)
74 {
75 va_list va;
76 va_start(va, a_pszMsgFmt);
77 m_strMsg.printfV(a_pszMsgFmt, va);
78 va_end(va);
79 }
80
81 ConfigFileError(const RTCString &a_rstrMessage)
82 : RTCError(a_rstrMessage)
83 {}
84
85private:
86 void i_buildPath(xml::Node const *pNode)
87 {
88 if (pNode)
89 {
90 i_buildPath(pNode->getParent());
91 m_strMsg.append('/');
92 m_strMsg.append(pNode->getName());
93 if (pNode->isElement() && pNode->getParent())
94 {
95 xml::ElementNode const *pElm = (xml::ElementNode const *)pNode;
96 for (xml::Node const *pAttrib = pElm->getFirstAttribute(); pAttrib != NULL;
97 pAttrib = pAttrib->getNextSibiling())
98 if (pAttrib->isAttribute())
99 {
100 m_strMsg.append("[@");
101 m_strMsg.append(pAttrib->getName());
102 m_strMsg.append('=');
103 m_strMsg.append(pAttrib->getValue());
104 m_strMsg.append(']');
105 }
106 }
107 }
108 }
109
110};
111
112
113/**
114 * Private default constructor, external users use factor methods.
115 */
116Config::Config()
117 : m_strHome()
118 , m_strNetwork()
119 , m_strTrunk()
120 , m_enmTrunkType(kIntNetTrunkType_Invalid)
121 , m_MacAddress()
122 , m_IPv4Address()
123 , m_IPv4Netmask()
124 , m_IPv4PoolFirst()
125 , m_IPv4PoolLast()
126 , m_GlobalConfig()
127 , m_GroupConfigs()
128 , m_HostConfigs()
129{
130}
131
132
133/**
134 * Initializes the object.
135 *
136 * @returns IPRT status code.
137 */
138int Config::i_init() RT_NOEXCEPT
139{
140 return i_homeInit();
141}
142
143
144/**
145 * Initializes the m_strHome member with the path to ~/.VirtualBox or equivalent.
146 *
147 * @returns IPRT status code.
148 * @todo Too many init functions?
149 */
150int Config::i_homeInit() RT_NOEXCEPT
151{
152 char szHome[RTPATH_MAX];
153 int rc = com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome), false);
154 if (RT_SUCCESS(rc))
155 rc = m_strHome.assignNoThrow(szHome);
156 else
157 DHCP_LOG_MSG_ERROR(("unable to locate the VirtualBox home directory: %Rrc\n", rc));
158 return rc;
159}
160
161
162/**
163 * Internal worker for the public factory methods that creates an instance and
164 * calls i_init() on it.
165 *
166 * @returns Config instance on success, NULL on failure.
167 */
168/*static*/ Config *Config::i_createInstanceAndCallInit() RT_NOEXCEPT
169{
170 Config *pConfig;
171 try
172 {
173 pConfig = new Config();
174 }
175 catch (std::bad_alloc &)
176 {
177 return NULL;
178 }
179
180 int rc = pConfig->i_init();
181 if (RT_SUCCESS(rc))
182 return pConfig;
183 delete pConfig;
184 return NULL;
185}
186
187
188/**
189 * Worker for i_complete() that initializes the release log of the process.
190 *
191 * Requires network name to be known as the log file name depends on
192 * it. Alternatively, consider passing the log file name via the
193 * command line?
194 *
195 * @note This is only used when no --log parameter was given.
196 */
197int Config::i_logInit() RT_NOEXCEPT
198{
199 if (g_fInitializedLog)
200 return VINF_SUCCESS;
201
202 if (m_strHome.isEmpty() || m_strNetwork.isEmpty())
203 return VERR_PATH_ZERO_LENGTH;
204
205 /* default log file name */
206 char szLogFile[RTPATH_MAX];
207 ssize_t cch = RTStrPrintf2(szLogFile, sizeof(szLogFile),
208 "%s%c%s-Dhcpd.log",
209 m_strHome.c_str(), RTPATH_DELIMITER, m_strNetwork.c_str());
210 if (cch > 0)
211 {
212 RTPathPurgeFilename(RTPathFilename(szLogFile), RTPATH_STR_F_STYLE_HOST);
213 return i_logInitWithFilename(szLogFile);
214 }
215 return VERR_BUFFER_OVERFLOW;
216}
217
218
219/**
220 * Worker for i_logInit and for handling --log on the command line.
221 *
222 * @returns IPRT status code.
223 * @param pszFilename The log filename.
224 */
225/*static*/ int Config::i_logInitWithFilename(const char *pszFilename) RT_NOEXCEPT
226{
227 AssertReturn(!g_fInitializedLog, VERR_WRONG_ORDER);
228
229 int rc = com::VBoxLogRelCreate("DHCP Server",
230 pszFilename,
231 RTLOGFLAGS_PREFIX_TIME_PROG,
232 "all net_dhcpd.e.l",
233 "VBOXDHCP_RELEASE_LOG",
234 RTLOGDEST_FILE
235#ifdef DEBUG
236 | RTLOGDEST_STDERR
237#endif
238 ,
239 32768 /* cMaxEntriesPerGroup */,
240 5 /* cHistory */,
241 RT_SEC_1DAY /* uHistoryFileTime */,
242 _32M /* uHistoryFileSize */,
243 NULL /* pErrInfo */);
244 if (RT_SUCCESS(rc))
245 g_fInitializedLog = true;
246 else
247 RTMsgError("Log initialization failed: %Rrc, log file '%s'", rc, pszFilename);
248 return rc;
249
250}
251
252
253/**
254 * Post process and validate the configuration after it has been loaded.
255 */
256int Config::i_complete() RT_NOEXCEPT
257{
258 if (m_strNetwork.isEmpty())
259 {
260 LogRel(("network name is not specified\n"));
261 return false;
262 }
263
264 i_logInit();
265
266 /** @todo the MAC address is always generated, no XML config option for it ... */
267 bool fMACGenerated = false;
268 if ( m_MacAddress.au16[0] == 0
269 && m_MacAddress.au16[1] == 0
270 && m_MacAddress.au16[2] == 0)
271 {
272 RTUUID Uuid;
273 int rc = RTUuidCreate(&Uuid);
274 AssertRCReturn(rc, rc);
275
276 m_MacAddress.au8[0] = 0x08;
277 m_MacAddress.au8[1] = 0x00;
278 m_MacAddress.au8[2] = 0x27;
279 m_MacAddress.au8[3] = Uuid.Gen.au8Node[3];
280 m_MacAddress.au8[4] = Uuid.Gen.au8Node[4];
281 m_MacAddress.au8[5] = Uuid.Gen.au8Node[5];
282
283 LogRel(("MAC address is not specified: will use generated MAC %RTmac\n", &m_MacAddress));
284 fMACGenerated = true;
285 }
286
287 /* unicast MAC address */
288 if (m_MacAddress.au8[0] & 0x01)
289 {
290 LogRel(("MAC address is not unicast: %RTmac\n", &m_MacAddress));
291 return VERR_GENERAL_FAILURE;
292 }
293
294 if (!fMACGenerated)
295 LogRel(("MAC address %RTmac\n", &m_MacAddress));
296
297 return VINF_SUCCESS;
298}
299
300
301/**
302 * Parses the command line and loads the configuration.
303 *
304 * @returns The configuration, NULL if we ran into some fatal problem.
305 * @param argc The argc from main().
306 * @param argv The argv from main().
307 */
308Config *Config::create(int argc, char **argv) RT_NOEXCEPT
309{
310 /*
311 * Parse the command line.
312 */
313 static const RTGETOPTDEF s_aOptions[] =
314 {
315 { "--comment", '#', RTGETOPT_REQ_STRING },
316 { "--config", 'c', RTGETOPT_REQ_STRING },
317 { "--log", 'l', RTGETOPT_REQ_STRING },
318 { "--log-destinations", 'd', RTGETOPT_REQ_STRING },
319 { "--log-flags", 'f', RTGETOPT_REQ_STRING },
320 { "--log-group-settings", 'g', RTGETOPT_REQ_STRING },
321 { "--relaxed", 'r', RTGETOPT_REQ_NOTHING },
322 { "--strict", 's', RTGETOPT_REQ_NOTHING },
323 };
324
325 RTGETOPTSTATE State;
326 int rc = RTGetOptInit(&State, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
327 AssertRCReturn(rc, NULL);
328
329 const char *pszLogFile = NULL;
330 const char *pszLogGroupSettings = NULL;
331 const char *pszLogDestinations = NULL;
332 const char *pszLogFlags = NULL;
333 const char *pszConfig = NULL;
334 const char *pszComment = NULL;
335 bool fStrict = true;
336
337 for (;;)
338 {
339 RTGETOPTUNION ValueUnion;
340 rc = RTGetOpt(&State, &ValueUnion);
341 if (rc == 0) /* done */
342 break;
343
344 switch (rc)
345 {
346 case 'c': /* --config */
347 pszConfig = ValueUnion.psz;
348 break;
349
350 case 'l':
351 pszLogFile = ValueUnion.psz;
352 break;
353
354 case 'd':
355 pszLogDestinations = ValueUnion.psz;
356 break;
357
358 case 'f':
359 pszLogFlags = ValueUnion.psz;
360 break;
361
362 case 'g':
363 pszLogGroupSettings = ValueUnion.psz;
364 break;
365
366 case 'r':
367 fStrict = false;
368 break;
369
370 case 's':
371 fStrict = true;
372 break;
373
374 case '#': /* --comment */
375 /* The sole purpose of this option is to allow identification of DHCP
376 * server instances in the process list. We ignore the required string
377 * argument of this option. */
378 pszComment = ValueUnion.psz;
379 break;
380
381 default:
382 RTGetOptPrintError(rc, &ValueUnion);
383 return NULL;
384 }
385 }
386
387 if (!pszConfig)
388 {
389 RTMsgError("No configuration file specified (--config file)!\n");
390 return NULL;
391 }
392
393 /*
394 * Init the log if a log file was specified.
395 */
396 if (pszLogFile)
397 {
398 rc = i_logInitWithFilename(pszLogFile);
399 if (RT_FAILURE(rc))
400 RTMsgError("Failed to initialize log file '%s': %Rrc", pszLogFile, rc);
401
402 if (pszLogDestinations)
403 RTLogDestinations(RTLogRelGetDefaultInstance(), pszLogDestinations);
404 if (pszLogFlags)
405 RTLogFlags(RTLogRelGetDefaultInstance(), pszLogFlags);
406 if (pszLogGroupSettings)
407 RTLogGroupSettings(RTLogRelGetDefaultInstance(), pszLogGroupSettings);
408
409 LogRel(("--config: %s\n", pszComment));
410 if (pszComment)
411 LogRel(("--comment: %s\n", pszComment));
412 }
413
414 /*
415 * Read the log file.
416 */
417 RTMsgInfo("reading config from '%s'...\n", pszConfig);
418 std::unique_ptr<Config> ptrConfig;
419 ptrConfig.reset(Config::i_read(pszConfig, fStrict));
420 if (ptrConfig.get() != NULL)
421 {
422 rc = ptrConfig->i_complete();
423 if (RT_SUCCESS(rc))
424 return ptrConfig.release();
425 }
426 return NULL;
427}
428
429
430/**
431 *
432 * @note The release log has is not operational when this method is called.
433 */
434Config *Config::i_read(const char *pszFileName, bool fStrict) RT_NOEXCEPT
435{
436 if (pszFileName == NULL || pszFileName[0] == '\0')
437 {
438 DHCP_LOG_MSG_ERROR(("Config::i_read: Empty configuration filename\n"));
439 return NULL;
440 }
441
442 xml::Document doc;
443 try
444 {
445 xml::XmlFileParser parser;
446 parser.read(pszFileName, doc);
447 }
448 catch (const xml::EIPRTFailure &e)
449 {
450 DHCP_LOG_MSG_ERROR(("Config::i_read: %s\n", e.what()));
451 return NULL;
452 }
453 catch (const RTCError &e)
454 {
455 DHCP_LOG_MSG_ERROR(("Config::i_read: %s\n", e.what()));
456 return NULL;
457 }
458 catch (...)
459 {
460 DHCP_LOG_MSG_ERROR(("Config::i_read: Unknown exception while reading and parsing '%s'\n", pszFileName));
461 return NULL;
462 }
463
464 std::unique_ptr<Config> config(i_createInstanceAndCallInit());
465 AssertReturn(config.get() != NULL, NULL);
466
467 try
468 {
469 config->i_parseConfig(doc.getRootElement(), fStrict);
470 }
471 catch (const RTCError &e)
472 {
473 DHCP_LOG_MSG_ERROR(("Config::i_read: %s\n", e.what()));
474 return NULL;
475 }
476 catch (std::bad_alloc &)
477 {
478 DHCP_LOG_MSG_ERROR(("Config::i_read: std::bad_alloc\n"));
479 return NULL;
480 }
481 catch (...)
482 {
483 DHCP_LOG_MSG_ERROR(("Config::i_read: Unexpected exception\n"));
484 return NULL;
485 }
486
487 return config.release();
488}
489
490
491/**
492 * Helper for retrieving a IPv4 attribute.
493 *
494 * @param pElm The element to get the attribute from.
495 * @param pszAttrName The name of the attribute
496 * @param pAddr Where to return the address.
497 * @throws ConfigFileError
498 */
499static void getIPv4AddrAttribute(const xml::ElementNode *pElm, const char *pszAttrName, PRTNETADDRIPV4 pAddr)
500{
501 const char *pszAttrValue;
502 if (pElm->getAttributeValue(pszAttrName, &pszAttrValue))
503 {
504 int rc = RTNetStrToIPv4Addr(pszAttrValue, pAddr);
505 if (RT_SUCCESS(rc))
506 return;
507 throw ConfigFileError(pElm, "Attribute %s is not a valid IPv4 address: '%s' -> %Rrc", pszAttrName, pszAttrValue, rc);
508 }
509 throw ConfigFileError(pElm, "Required %s attribute missing", pszAttrName);
510}
511
512
513/**
514 * Helper for retrieving a MAC address attribute.
515 *
516 * @param pElm The element to get the attribute from.
517 * @param pszAttrName The name of the attribute
518 * @param pMacAddr Where to return the MAC address.
519 * @throws ConfigFileError
520 */
521static void getMacAddressAttribute(const xml::ElementNode *pElm, const char *pszAttrName, PRTMAC pMacAddr)
522{
523 const char *pszAttrValue;
524 if (pElm->getAttributeValue(pszAttrName, &pszAttrValue))
525 {
526 int rc = RTNetStrToMacAddr(pszAttrValue, pMacAddr);
527 if (RT_SUCCESS(rc) && rc != VWRN_TRAILING_CHARS)
528 return;
529 throw ConfigFileError(pElm, "attribute %s is not a valid MAC address: '%s' -> %Rrc", pszAttrName, pszAttrValue, rc);
530 }
531 throw ConfigFileError(pElm, "Required %s attribute missing", pszAttrName);
532}
533
534
535/**
536 * Internal worker for i_read() that parses the root element and everything
537 * below it.
538 *
539 * @param pElmRoot The root element.
540 * @param fStrict Set if we're in strict mode, clear if we just
541 * want to get on with it if we can.
542 * @throws std::bad_alloc, ConfigFileError
543 */
544void Config::i_parseConfig(const xml::ElementNode *pElmRoot, bool fStrict)
545{
546 /*
547 * Check the root element and call i_parseServer to do real work.
548 */
549 if (pElmRoot == NULL)
550 throw ConfigFileError("Empty config file");
551
552 /** @todo XXX: NAMESPACE API IS COMPLETELY BROKEN, SO IGNORE IT FOR NOW */
553
554 if (!pElmRoot->nameEquals("DHCPServer"))
555 throw ConfigFileError("Unexpected root element '%s'", pElmRoot->getName());
556
557 i_parseServer(pElmRoot, fStrict);
558
559#if 0 /** @todo convert to LogRel2 stuff */
560 // XXX: debug
561 for (optmap_t::const_iterator it = m_GlobalOptions.begin(); it != m_GlobalOptions.end(); ++it) {
562 std::shared_ptr<DhcpOption> opt(it->second);
563
564 octets_t data;
565 opt->encode(data);
566
567 bool space = false;
568 for (octets_t::const_iterator itData = data.begin(); itData != data.end(); ++itData) {
569 uint8_t c = *itData;
570 if (space)
571 std::cout << " ";
572 else
573 space = true;
574 std::cout << (int)c;
575 }
576 std::cout << std::endl;
577 }
578#endif
579}
580
581
582/**
583 * Internal worker for parsing the elements under /DHCPServer/.
584 *
585 * @param pElmServer The DHCPServer element.
586 * @param fStrict Set if we're in strict mode, clear if we just
587 * want to get on with it if we can.
588 * @throws std::bad_alloc, ConfigFileError
589 */
590void Config::i_parseServer(const xml::ElementNode *pElmServer, bool fStrict)
591{
592 /*
593 * <DHCPServer> attributes
594 */
595 if (!pElmServer->getAttributeValue("networkName", m_strNetwork))
596 throw ConfigFileError("DHCPServer/@networkName missing");
597 if (m_strNetwork.isEmpty())
598 throw ConfigFileError("DHCPServer/@networkName is empty");
599
600 const char *pszTrunkType;
601 if (!pElmServer->getAttributeValue("trunkType", &pszTrunkType))
602 throw ConfigFileError("DHCPServer/@trunkType missing");
603 if (strcmp(pszTrunkType, "none") == 0)
604 m_enmTrunkType = kIntNetTrunkType_None;
605 else if (strcmp(pszTrunkType, "whatever") == 0)
606 m_enmTrunkType = kIntNetTrunkType_WhateverNone;
607 else if (strcmp(pszTrunkType, "netflt") == 0)
608 m_enmTrunkType = kIntNetTrunkType_NetFlt;
609 else if (strcmp(pszTrunkType, "netadp") == 0)
610 m_enmTrunkType = kIntNetTrunkType_NetAdp;
611 else
612 throw ConfigFileError("Invalid DHCPServer/@trunkType value: %s", pszTrunkType);
613
614 if ( m_enmTrunkType == kIntNetTrunkType_NetFlt
615 || m_enmTrunkType == kIntNetTrunkType_NetAdp)
616 {
617 if (!pElmServer->getAttributeValue("trunkName", &m_strTrunk))
618 throw ConfigFileError("DHCPServer/@trunkName missing");
619 }
620 else
621 m_strTrunk = "";
622
623 m_strLeasesFilename = pElmServer->findAttributeValue("leasesFilename"); /* optional */
624 if (m_strLeasesFilename.isEmpty())
625 {
626 int rc = m_strLeasesFilename.assignNoThrow(getHome());
627 if (RT_SUCCESS(rc))
628 rc = RTPathAppendCxx(m_strLeasesFilename, m_strNetwork);
629 if (RT_SUCCESS(rc))
630 rc = m_strLeasesFilename.appendNoThrow("-Dhcpd.leases");
631 if (RT_FAILURE(rc))
632 throw ConfigFileError("Unexpected error constructing default m_strLeasesFilename value: %Rrc", rc);
633 RTPathPurgeFilename(RTPathFilename(m_strLeasesFilename.mutableRaw()), RTPATH_STR_F_STYLE_HOST);
634 m_strLeasesFilename.jolt();
635 }
636
637 /*
638 * Addresses and mask.
639 */
640 ::getIPv4AddrAttribute(pElmServer, "IPAddress", &m_IPv4Address);
641 ::getIPv4AddrAttribute(pElmServer, "networkMask", &m_IPv4Netmask);
642 ::getIPv4AddrAttribute(pElmServer, "lowerIP", &m_IPv4PoolFirst);
643 ::getIPv4AddrAttribute(pElmServer, "upperIP", &m_IPv4PoolLast);
644
645 /* unicast IP address */
646 if ((m_IPv4Address.au8[0] & 0xe0) == 0xe0)
647 throw ConfigFileError("DHCP server IP address is not unicast: %RTnaipv4", m_IPv4Address.u);
648
649 /* valid netmask */
650 int cPrefixBits;
651 int rc = RTNetMaskToPrefixIPv4(&m_IPv4Netmask, &cPrefixBits);
652 if (RT_FAILURE(rc) || cPrefixBits == 0)
653 throw ConfigFileError("IP mask is not valid: %RTnaipv4", m_IPv4Netmask.u);
654
655 /* first IP is from the same network */
656 if ((m_IPv4PoolFirst.u & m_IPv4Netmask.u) != (m_IPv4Address.u & m_IPv4Netmask.u))
657 throw ConfigFileError("first pool address is outside the network %RTnaipv4/%d: %RTnaipv4",
658 (m_IPv4Address.u & m_IPv4Netmask.u), cPrefixBits, m_IPv4PoolFirst.u);
659
660 /* last IP is from the same network */
661 if ((m_IPv4PoolLast.u & m_IPv4Netmask.u) != (m_IPv4Address.u & m_IPv4Netmask.u))
662 throw ConfigFileError("last pool address is outside the network %RTnaipv4/%d: %RTnaipv4\n",
663 (m_IPv4Address.u & m_IPv4Netmask.u), cPrefixBits, m_IPv4PoolLast.u);
664
665 /* the pool is valid */
666 if (RT_N2H_U32(m_IPv4PoolLast.u) < RT_N2H_U32(m_IPv4PoolFirst.u))
667 throw ConfigFileError("pool range is invalid: %RTnaipv4 - %RTnaipv4", m_IPv4PoolFirst.u, m_IPv4PoolLast.u);
668 LogRel(("IP address: %RTnaipv4/%d\n", m_IPv4Address.u, cPrefixBits));
669 LogRel(("Address pool: %RTnaipv4 - %RTnaipv4\n", m_IPv4PoolFirst.u, m_IPv4PoolLast.u));
670
671 /*
672 * <DHCPServer> children
673 */
674 xml::NodesLoop it(*pElmServer);
675 const xml::ElementNode *pElmChild;
676 while ((pElmChild = it.forAllNodes()) != NULL)
677 {
678 /* Global options: */
679 if (pElmChild->nameEquals("Options"))
680 m_GlobalConfig.initFromXml(pElmChild, fStrict, this);
681 /* Group w/ options: */
682 else if (pElmChild->nameEquals("Group"))
683 {
684 std::unique_ptr<GroupConfig> ptrGroup(new GroupConfig());
685 ptrGroup->initFromXml(pElmChild, fStrict, this);
686 if (m_GroupConfigs.find(ptrGroup->getGroupName()) == m_GroupConfigs.end())
687 {
688 m_GroupConfigs[ptrGroup->getGroupName()] = ptrGroup.get();
689 ptrGroup.release();
690 }
691 else if (!fStrict)
692 LogRelFunc(("Ignoring duplicate group name: %s", ptrGroup->getGroupName().c_str()));
693 else
694 throw ConfigFileError("Duplicate group name: %s", ptrGroup->getGroupName().c_str());
695 }
696 /*
697 * MAC address and per VM NIC configurations:
698 */
699 else if (pElmChild->nameEquals("Config"))
700 {
701 std::unique_ptr<HostConfig> ptrHost(new HostConfig());
702 ptrHost->initFromXml(pElmChild, fStrict, this);
703 if (m_HostConfigs.find(ptrHost->getMACAddress()) == m_HostConfigs.end())
704 {
705 m_HostConfigs[ptrHost->getMACAddress()] = ptrHost.get();
706 ptrHost.release();
707 }
708 else if (!fStrict)
709 LogRelFunc(("Ignorining duplicate MAC address (Config): %RTmac", &ptrHost->getMACAddress()));
710 else
711 throw ConfigFileError("Duplicate MAC address (Config): %RTmac", &ptrHost->getMACAddress());
712 }
713 else if (!fStrict)
714 LogRel(("Ignoring unexpected DHCPServer child: %s\n", pElmChild->getName()));
715 else
716 throw ConfigFileError("Unexpected DHCPServer child <%s>'", pElmChild->getName());
717 }
718}
719
720
721/**
722 * Internal worker for parsing \<Option\> elements found under
723 * /DHCPServer/Options/, /DHCPServer/Group/ and /DHCPServer/Config/.
724 *
725 * @param pElmOption An \<Option\> element.
726 * @throws std::bad_alloc, ConfigFileError
727 */
728void ConfigLevelBase::i_parseOption(const xml::ElementNode *pElmOption)
729{
730 /* The 'name' attribute: */
731 const char *pszName;
732 if (!pElmOption->getAttributeValue("name", &pszName))
733 throw ConfigFileError(pElmOption, "missing option name");
734
735 uint8_t u8Opt;
736 int rc = RTStrToUInt8Full(pszName, 10, &u8Opt);
737 if (rc != VINF_SUCCESS) /* no warnings either */
738 throw ConfigFileError(pElmOption, "Bad option name '%s': %Rrc", pszName, rc);
739
740 /* The opional 'encoding' attribute: */
741 uint32_t u32Enc = 0; /* XXX: DHCPOptionEncoding_Normal */
742 const char *pszEncoding;
743 if (pElmOption->getAttributeValue("encoding", &pszEncoding))
744 {
745 rc = RTStrToUInt32Full(pszEncoding, 10, &u32Enc);
746 if (rc != VINF_SUCCESS) /* no warnings either */
747 throw ConfigFileError(pElmOption, "Bad option encoding '%s': %Rrc", pszEncoding, rc);
748
749 switch (u32Enc)
750 {
751 case 0: /* XXX: DHCPOptionEncoding_Normal */
752 case 1: /* XXX: DHCPOptionEncoding_Hex */
753 break;
754 default:
755 throw ConfigFileError(pElmOption, "Unknown encoding '%s'", pszEncoding);
756 }
757 }
758
759 /* The 'value' attribute. May be omitted for OptNoValue options like rapid commit. */
760 const char *pszValue;
761 if (!pElmOption->getAttributeValue("value", &pszValue))
762 pszValue = "";
763
764 /** @todo XXX: TODO: encoding, handle hex */
765 DhcpOption *opt = DhcpOption::parse(u8Opt, u32Enc, pszValue);
766 if (opt == NULL)
767 throw ConfigFileError(pElmOption, "Bad option '%s' (encoding %u): '%s' ", pszName, u32Enc, pszValue ? pszValue : "");
768
769 /* Add it to the map: */
770 m_Options << opt;
771}
772
773
774/**
775 * Final children parser, handling only \<Option\> and barfing at anything else.
776 *
777 * @param pElmChild The child element to handle.
778 * @param fStrict Set if we're in strict mode, clear if we just
779 * want to get on with it if we can.
780 * @param pConfig The configuration object.
781 * @throws std::bad_alloc, ConfigFileError
782 */
783void ConfigLevelBase::i_parseChild(const xml::ElementNode *pElmChild, bool fStrict, Config const *pConfig)
784{
785 if (pElmChild->nameEquals("Option"))
786 {
787 try
788 {
789 i_parseOption(pElmChild);
790 }
791 catch (ConfigFileError &rXcpt)
792 {
793 if (fStrict)
794 throw rXcpt;
795 LogRelFunc(("Ignoring option: %s\n", rXcpt.what()));
796 }
797 }
798 else if (!fStrict)
799 {
800 ConfigFileError Dummy(pElmChild->getParent(), "Unexpected child '%s'", pElmChild->getName());
801 LogRelFunc(("%s\n", Dummy.what()));
802 }
803 else
804 throw ConfigFileError(pElmChild->getParent(), "Unexpected child '%s'", pElmChild->getName());
805 RT_NOREF(pConfig);
806}
807
808
809/**
810 * Base class initialization taking a /DHCPServer/Options, /DHCPServer/Group or
811 * /DHCPServer/Config element as input and handling common attributes as well as
812 * any \<Option\> children.
813 *
814 * @param pElmConfig The configuration element to parse.
815 * @param fStrict Set if we're in strict mode, clear if we just
816 * want to get on with it if we can.
817 * @param pConfig The configuration object.
818 * @throws std::bad_alloc, ConfigFileError
819 */
820void ConfigLevelBase::initFromXml(const xml::ElementNode *pElmConfig, bool fStrict, Config const *pConfig)
821{
822 /*
823 * Common attributes:
824 */
825 if (!pElmConfig->getAttributeValue("secMinLeaseTime", &m_secMinLeaseTime))
826 m_secMinLeaseTime = 0;
827 if (!pElmConfig->getAttributeValue("secDefaultLeaseTime", &m_secDefaultLeaseTime))
828 m_secDefaultLeaseTime = 0;
829 if (!pElmConfig->getAttributeValue("secMaxLeaseTime", &m_secMaxLeaseTime))
830 m_secMaxLeaseTime = 0;
831
832 /* Swap min and max if max is smaller: */
833 if (m_secMaxLeaseTime < m_secMinLeaseTime && m_secMinLeaseTime && m_secMaxLeaseTime)
834 {
835 LogRel(("Swapping min/max lease times: %u <-> %u\n", m_secMinLeaseTime, m_secMaxLeaseTime));
836 uint32_t uTmp = m_secMaxLeaseTime;
837 m_secMaxLeaseTime = m_secMinLeaseTime;
838 m_secMinLeaseTime = uTmp;
839 }
840
841 /*
842 * Parse children.
843 */
844 xml::NodesLoop it(*pElmConfig);
845 const xml::ElementNode *pElmChild;
846 while ((pElmChild = it.forAllNodes()) != NULL)
847 i_parseChild(pElmChild, fStrict, pConfig);
848}
849
850
851/**
852 * Internal worker for parsing the elements under /DHCPServer/Options/.
853 *
854 * @param pElmOptions The <Options> element.
855 * @param fStrict Set if we're in strict mode, clear if we just
856 * want to get on with it if we can.
857 * @param pConfig The configuration object.
858 * @throws std::bad_alloc, ConfigFileError
859 */
860void GlobalConfig::initFromXml(const xml::ElementNode *pElmOptions, bool fStrict, Config const *pConfig)
861{
862 ConfigLevelBase::initFromXml(pElmOptions, fStrict, pConfig);
863
864 /*
865 * Resolve defaults here in the global config so we don't have to do this
866 * in Db::allocateBinding() for every lease request.
867 */
868 if (m_secMaxLeaseTime == 0 && m_secDefaultLeaseTime == 0 && m_secMinLeaseTime == 0)
869 {
870 m_secMinLeaseTime = 300; /* 5 min */
871 m_secDefaultLeaseTime = 600; /* 10 min */
872 m_secMaxLeaseTime = 12 * RT_SEC_1HOUR; /* 12 hours */
873 }
874 else
875 {
876 if (m_secDefaultLeaseTime == 0)
877 {
878 if (m_secMaxLeaseTime != 0)
879 m_secDefaultLeaseTime = RT_MIN(RT_MAX(m_secMinLeaseTime, 600), m_secMaxLeaseTime);
880 else
881 {
882 m_secDefaultLeaseTime = RT_MAX(m_secMinLeaseTime, 600);
883 m_secMaxLeaseTime = RT_MAX(m_secDefaultLeaseTime, 12 * RT_SEC_1HOUR);
884 }
885 }
886 if (m_secMaxLeaseTime == 0)
887 m_secMaxLeaseTime = RT_MAX(RT_MAX(m_secMinLeaseTime, m_secDefaultLeaseTime), 12 * RT_SEC_1HOUR);
888 if (m_secMinLeaseTime == 0)
889 m_secMinLeaseTime = RT_MIN(300, m_secDefaultLeaseTime);
890 }
891
892}
893
894
895/**
896 * Overrides base class to handle the condition elements under \<Group\>.
897 *
898 * @param pElmChild The child element.
899 * @param fStrict Set if we're in strict mode, clear if we just
900 * want to get on with it if we can.
901 * @param pConfig The configuration object.
902 * @throws std::bad_alloc, ConfigFileError
903 */
904void GroupConfig::i_parseChild(const xml::ElementNode *pElmChild, bool fStrict, Config const *pConfig)
905{
906 /*
907 * Match the condition
908 */
909 std::unique_ptr<GroupCondition> ptrCondition;
910 if (pElmChild->nameEquals("ConditionMAC"))
911 ptrCondition.reset(new GroupConditionMAC());
912 else if (pElmChild->nameEquals("ConditionMACWildcard"))
913 ptrCondition.reset(new GroupConditionMACWildcard());
914 else if (pElmChild->nameEquals("ConditionVendorClassID"))
915 ptrCondition.reset(new GroupConditionVendorClassID());
916 else if (pElmChild->nameEquals("ConditionVendorClassIDWildcard"))
917 ptrCondition.reset(new GroupConditionVendorClassIDWildcard());
918 else if (pElmChild->nameEquals("ConditionUserClassID"))
919 ptrCondition.reset(new GroupConditionUserClassID());
920 else if (pElmChild->nameEquals("ConditionUserClassIDWildcard"))
921 ptrCondition.reset(new GroupConditionUserClassIDWildcard());
922 else
923 {
924 /*
925 * Not a condition, pass it on to the base class.
926 */
927 ConfigLevelBase::i_parseChild(pElmChild, fStrict, pConfig);
928 return;
929 }
930
931 /*
932 * Get the attributes and initialize the condition.
933 */
934 bool fInclusive;
935 if (!pElmChild->getAttributeValue("inclusive", fInclusive))
936 fInclusive = true;
937 const char *pszValue = pElmChild->findAttributeValue("value");
938 if (pszValue && *pszValue)
939 {
940 int rc = ptrCondition->initCondition(pszValue, fInclusive);
941 if (RT_SUCCESS(rc))
942 {
943 /*
944 * Add it to the appropriate vector.
945 */
946 if (fInclusive)
947 m_Inclusive.push_back(ptrCondition.release());
948 else
949 m_Exclusive.push_back(ptrCondition.release());
950 }
951 else
952 {
953 ConfigFileError Xcpt(pElmChild, "initCondition failed with %Rrc for '%s' and %RTbool", rc, pszValue, fInclusive);
954 if (!fStrict)
955 LogRelFunc(("%s, ignoring condition\n", Xcpt.what()));
956 else
957 throw ConfigFileError(Xcpt);
958 }
959 }
960 else
961 {
962 ConfigFileError Xcpt(pElmChild, "condition value is empty or missing (inclusive=%RTbool)", fInclusive);
963 if (fStrict)
964 throw Xcpt;
965 LogRelFunc(("%s, ignoring condition\n", Xcpt.what()));
966 }
967}
968
969
970/**
971 * Internal worker for parsing the elements under /DHCPServer/Group/.
972 *
973 * @param pElmGroup The \<Group\> element.
974 * @param fStrict Set if we're in strict mode, clear if we just
975 * want to get on with it if we can.
976 * @param pConfig The configuration object.
977 * @throws std::bad_alloc, ConfigFileError
978 */
979void GroupConfig::initFromXml(const xml::ElementNode *pElmGroup, bool fStrict, Config const *pConfig)
980{
981 /*
982 * Attributes:
983 */
984 if (!pElmGroup->getAttributeValue("name", m_strName) || m_strName.isEmpty())
985 {
986 if (fStrict)
987 throw ConfigFileError(pElmGroup, "Group as no name or the name is empty");
988 m_strName.printf("Group#%u", s_uGroupNo++);
989 }
990
991 /*
992 * Do common initialization (including children).
993 */
994 ConfigLevelBase::initFromXml(pElmGroup, fStrict, pConfig);
995}
996
997
998/**
999 * Internal worker for parsing the elements under /DHCPServer/Config/.
1000 *
1001 * VM Config entries are generated automatically from VirtualBox.xml
1002 * with the MAC fetched from the VM config. The client id is nowhere
1003 * in the picture there, so VM config is indexed with plain RTMAC, not
1004 * ClientId (also see getOptions below).
1005 *
1006 * @param pElmConfig The \<Config\> element.
1007 * @param fStrict Set if we're in strict mode, clear if we just
1008 * want to get on with it if we can.
1009 * @param pConfig The configuration object (for netmask).
1010 * @throws std::bad_alloc, ConfigFileError
1011 */
1012void HostConfig::initFromXml(const xml::ElementNode *pElmConfig, bool fStrict, Config const *pConfig)
1013{
1014 /*
1015 * Attributes:
1016 */
1017 /* The MAC address: */
1018 ::getMacAddressAttribute(pElmConfig, "MACAddress", &m_MACAddress);
1019
1020 /* Name - optional: */
1021 if (!pElmConfig->getAttributeValue("name", m_strName))
1022 m_strName.printf("MAC:%RTmac", m_MACAddress);
1023
1024 /* Fixed IP address assignment - optional: */
1025 const char *pszFixedAddress = pElmConfig->findAttributeValue("fixedAddress");
1026 if (!pszFixedAddress || *RTStrStripL(pszFixedAddress) == '\0')
1027 m_fHaveFixedAddress = false;
1028 else
1029 {
1030 ::getIPv4AddrAttribute(pElmConfig, "fixedAddress", &m_FixedAddress);
1031 if (pConfig->isInIPv4Network(m_FixedAddress))
1032 m_fHaveFixedAddress = true;
1033 else
1034 {
1035 ConfigFileError Xcpt(pElmConfig, "fixedAddress '%s' is not the DHCP network", pszFixedAddress);
1036 if (fStrict)
1037 throw Xcpt;
1038 LogRelFunc(("%s - ignoring the fixed address assignment\n", Xcpt.what()));
1039 m_fHaveFixedAddress = false;
1040 }
1041 }
1042
1043 /*
1044 * Do common initialization.
1045 */
1046 ConfigLevelBase::initFromXml(pElmConfig, fStrict, pConfig);
1047}
1048
1049
1050/**
1051 * Assembles a list of hosts with fixed address assignments.
1052 *
1053 * @returns IPRT status code.
1054 * @param a_rRetConfigs Where to return the configurations.
1055 */
1056int Config::getFixedAddressConfigs(HostConfigVec &a_rRetConfigs) const
1057{
1058 for (HostConfigMap::const_iterator it = m_HostConfigs.begin(); it != m_HostConfigs.end(); ++it)
1059 {
1060 HostConfig const *pHostConfig = it->second;
1061 if (pHostConfig->haveFixedAddress())
1062 try
1063 {
1064 a_rRetConfigs.push_back(pHostConfig);
1065 }
1066 catch (std::bad_alloc &)
1067 {
1068 return VERR_NO_MEMORY;
1069 }
1070 }
1071 return VINF_SUCCESS;
1072}
1073
1074
1075/**
1076 * Assembles a priorities vector of configurations for the client.
1077 *
1078 * @returns a_rRetConfigs for convenience.
1079 * @param a_rRetConfigs Where to return the configurations.
1080 * @param a_ridClient The client ID.
1081 * @param a_ridVendorClass The vendor class ID if present.
1082 * @param a_ridUserClass The user class ID if present
1083 */
1084Config::ConfigVec &Config::getConfigsForClient(Config::ConfigVec &a_rRetConfigs, const ClientId &a_ridClient,
1085 const OptVendorClassId &a_ridVendorClass,
1086 const OptUserClassId &a_ridUserClass) const
1087{
1088 /* Host specific config first: */
1089 HostConfigMap::const_iterator itHost = m_HostConfigs.find(a_ridClient.mac());
1090 if (itHost != m_HostConfigs.end())
1091 a_rRetConfigs.push_back(itHost->second);
1092
1093 /* Groups: */
1094 for (GroupConfigMap::const_iterator itGrp = m_GroupConfigs.begin(); itGrp != m_GroupConfigs.end(); ++itGrp)
1095 if (itGrp->second->match(a_ridClient, a_ridVendorClass, a_ridUserClass))
1096 a_rRetConfigs.push_back(itGrp->second);
1097
1098 /* Global: */
1099 a_rRetConfigs.push_back(&m_GlobalConfig);
1100
1101 return a_rRetConfigs;
1102}
1103
1104
1105/**
1106 * Method used by DHCPD to assemble a list of options for the client.
1107 *
1108 * @returns a_rRetOpts for convenience
1109 * @param a_rRetOpts Where to put the requested options.
1110 * @param a_rReqOpts The requested options.
1111 * @param a_rConfigs Relevant configurations returned by
1112 * Config::getConfigsForClient().
1113 *
1114 * @throws std::bad_alloc
1115 */
1116optmap_t &Config::getOptionsForClient(optmap_t &a_rRetOpts, const OptParameterRequest &a_rReqOpts, ConfigVec &a_rConfigs) const
1117{
1118 /*
1119 * Always supply the subnet:
1120 */
1121 a_rRetOpts << new OptSubnetMask(m_IPv4Netmask);
1122
1123 /** @todo If a_rReqOpts is not present, provide the sum of all options in
1124 * a_rConfigs like ics says it does. */
1125 /** @todo Look thru a_rConfigs for forced options, maybe we do it by using
1126 * DHCP option 55, and merging these into a_rReqOpts. */
1127 /** @todo Have a way to mute options, i.e. break out of the inner search
1128 * loop below. Maybe using 'Suppress' encoding? */
1129
1130 /*
1131 * Try provide the requested options:
1132 */
1133 const OptParameterRequest::value_t &reqValue = a_rReqOpts.value();
1134 for (octets_t::const_iterator itOptReq = reqValue.begin(); itOptReq != reqValue.end(); ++itOptReq)
1135 {
1136 uint8_t bOptReq = *itOptReq;
1137 LogRel2((">>> requested option %d (%#x)\n", bOptReq, bOptReq));
1138
1139 if (bOptReq != OptSubnetMask::optcode)
1140 {
1141 bool fFound = false;
1142 for (size_t i = 0; i < a_rConfigs.size(); i++)
1143 {
1144 optmap_t::const_iterator itFound;
1145 if (a_rConfigs[i]->findOption(bOptReq, itFound)) /* crap interface */
1146 {
1147 LogRel2(("... found in %s (type %s)\n", a_rConfigs[i]->getName(), a_rConfigs[i]->getType()));
1148 a_rRetOpts << itFound->second;
1149 fFound = true;
1150 break;
1151 }
1152 }
1153 if (!fFound)
1154 LogRel3(("... not found\n"));
1155 }
1156 else
1157 LogRel2(("... always supplied\n"));
1158 }
1159
1160
1161#if 0 /* bird disabled this as it looks dubious and testing only. */
1162 /** @todo XXX: testing ... */
1163 if (vmopts != NULL)
1164 {
1165 for (optmap_t::const_iterator it = vmopts->begin(); it != vmopts->end(); ++it)
1166 {
1167 std::shared_ptr<DhcpOption> opt(it->second);
1168 if (a_rRetOpts.count(opt->optcode()) == 0 && opt->optcode() > 127)
1169 {
1170 a_rRetOpts << opt;
1171 LogRel2(("... forcing VM option %d (%#x)\n", opt->optcode(), opt->optcode()));
1172 }
1173 }
1174 }
1175
1176 for (optmap_t::const_iterator it = m_GlobalOptions.begin(); it != m_GlobalOptions.end(); ++it)
1177 {
1178 std::shared_ptr<DhcpOption> opt(it->second);
1179 if (a_rRetOpts.count(opt->optcode()) == 0 && opt->optcode() > 127)
1180 {
1181 a_rRetOpts << opt;
1182 LogRel2(("... forcing global option %d (%#x)", opt->optcode(), opt->optcode()));
1183 }
1184 }
1185#endif
1186
1187 return a_rRetOpts;
1188}
1189
1190
1191
1192/*********************************************************************************************************************************
1193* Group Condition Matching *
1194*********************************************************************************************************************************/
1195
1196bool GroupConfig::match(const ClientId &a_ridClient, const OptVendorClassId &a_ridVendorClass,
1197 const OptUserClassId &a_ridUserClass) const
1198{
1199 /*
1200 * Check the inclusive ones first, only one need to match.
1201 */
1202 for (GroupConditionVec::const_iterator itIncl = m_Inclusive.begin(); itIncl != m_Inclusive.end(); ++itIncl)
1203 if ((*itIncl)->match(a_ridClient, a_ridVendorClass, a_ridUserClass))
1204 {
1205 /*
1206 * Now make sure it isn't excluded by any of the exclusion condition.
1207 */
1208 for (GroupConditionVec::const_iterator itExcl = m_Exclusive.begin(); itExcl != m_Exclusive.end(); ++itExcl)
1209 if ((*itIncl)->match(a_ridClient, a_ridVendorClass, a_ridUserClass))
1210 return false;
1211 return true;
1212 }
1213
1214 return false;
1215}
1216
1217
1218int GroupCondition::initCondition(const char *a_pszValue, bool a_fInclusive)
1219{
1220 m_fInclusive = a_fInclusive;
1221 return m_strValue.assignNoThrow(a_pszValue);
1222}
1223
1224
1225bool GroupCondition::matchClassId(bool a_fPresent, const std::vector<uint8_t> &a_rBytes, bool fWildcard) const RT_NOEXCEPT
1226{
1227 if (a_fPresent)
1228 {
1229 size_t const cbBytes = a_rBytes.size();
1230 if (cbBytes > 0)
1231 {
1232 if (a_rBytes[cbBytes - 1] == '\0')
1233 {
1234 uint8_t const *pb = &a_rBytes.front();
1235 if (!fWildcard)
1236 return m_strValue.equals((const char *)pb);
1237 return RTStrSimplePatternMatch(m_strValue.c_str(), (const char *)pb);
1238 }
1239
1240 if (cbBytes <= 255)
1241 {
1242 char szTmp[256];
1243 memcpy(szTmp, &a_rBytes.front(), cbBytes);
1244 szTmp[cbBytes] = '\0';
1245 if (!fWildcard)
1246 return m_strValue.equals(szTmp);
1247 return RTStrSimplePatternMatch(m_strValue.c_str(), szTmp);
1248 }
1249 }
1250 }
1251 return false;
1252
1253}
1254
1255
1256int GroupConditionMAC::initCondition(const char *a_pszValue, bool a_fInclusive)
1257{
1258 int vrc = RTNetStrToMacAddr(a_pszValue, &m_MACAddress);
1259 if (RT_SUCCESS(vrc))
1260 return GroupCondition::initCondition(a_pszValue, a_fInclusive);
1261 return vrc;
1262}
1263
1264
1265bool GroupConditionMAC::match(const ClientId &a_ridClient, const OptVendorClassId &a_ridVendorClass,
1266 const OptUserClassId &a_ridUserClass) const RT_NOEXCEPT
1267{
1268 RT_NOREF(a_ridVendorClass, a_ridUserClass);
1269 return a_ridClient.mac() == m_MACAddress;
1270}
1271
1272
1273bool GroupConditionMACWildcard::match(const ClientId &a_ridClient, const OptVendorClassId &a_ridVendorClass,
1274 const OptUserClassId &a_ridUserClass) const RT_NOEXCEPT
1275{
1276 RT_NOREF(a_ridVendorClass, a_ridUserClass);
1277 char szTmp[32];
1278 RTStrPrintf(szTmp, sizeof(szTmp), "%RTmac", &a_ridClient.mac());
1279 return RTStrSimplePatternMatch(m_strValue.c_str(), szTmp);
1280}
1281
1282
1283bool GroupConditionVendorClassID::match(const ClientId &a_ridClient, const OptVendorClassId &a_ridVendorClass,
1284 const OptUserClassId &a_ridUserClass) const RT_NOEXCEPT
1285{
1286 RT_NOREF(a_ridClient, a_ridUserClass);
1287 return matchClassId(a_ridVendorClass.present(), a_ridVendorClass.value());
1288}
1289
1290
1291bool GroupConditionVendorClassIDWildcard::match(const ClientId &a_ridClient, const OptVendorClassId &a_ridVendorClass,
1292 const OptUserClassId &a_ridUserClass) const RT_NOEXCEPT
1293{
1294 RT_NOREF(a_ridClient, a_ridUserClass);
1295 return matchClassId(a_ridVendorClass.present(), a_ridVendorClass.value(), true /*fWildcard*/);
1296}
1297
1298
1299bool GroupConditionUserClassID::match(const ClientId &a_ridClient, const OptVendorClassId &a_ridVendorClass,
1300 const OptUserClassId &a_ridUserClass) const RT_NOEXCEPT
1301{
1302 RT_NOREF(a_ridClient, a_ridVendorClass);
1303 return matchClassId(a_ridVendorClass.present(), a_ridUserClass.value());
1304}
1305
1306
1307bool GroupConditionUserClassIDWildcard::match(const ClientId &a_ridClient, const OptVendorClassId &a_ridVendorClass,
1308 const OptUserClassId &a_ridUserClass) const RT_NOEXCEPT
1309{
1310 RT_NOREF(a_ridClient, a_ridVendorClass);
1311 return matchClassId(a_ridVendorClass.present(), a_ridUserClass.value(), true /*fWildcard*/);
1312}
1313
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