VirtualBox

source: vbox/trunk/src/VBox/NetworkServices/Dhcpd/Db.cpp@ 79529

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

Dhcpd: Went over the Db and IPv4Pool code adding comments and and such. bugref:9288

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 24.2 KB
Line 
1/* $Id: Db.cpp 79529 2019-07-04 18:17:50Z vboxsync $ */
2/** @file
3 * DHCP server - address database
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#include <iprt/errcore.h>
24
25#include "Db.h"
26
27
28/*********************************************************************************************************************************
29* Global Variables *
30*********************************************************************************************************************************/
31/** Indicates whether has been called successfully yet. */
32bool Binding::g_fFormatRegistered = false;
33
34
35/**
36 * Registers the ClientId format type callback ("%R[binding]").
37 */
38void Binding::registerFormat()
39{
40 if (!g_fFormatRegistered)
41 {
42 int rc = RTStrFormatTypeRegister("binding", rtStrFormat, NULL);
43 AssertRC(rc);
44 g_fFormatRegistered = true;
45 }
46}
47
48
49/**
50 * @callback_method_impl{FNRTSTRFORMATTYPE, Formats ClientId via "%R[binding]".}
51 */
52DECLCALLBACK(size_t)
53Binding::rtStrFormat(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput,
54 const char *pszType, void const *pvValue,
55 int cchWidth, int cchPrecision, unsigned fFlags,
56 void *pvUser)
57{
58
59 AssertReturn(strcmp(pszType, "binding") == 0, 0);
60 RT_NOREF(pszType);
61
62 RT_NOREF(cchWidth, cchPrecision, fFlags);
63 RT_NOREF(pvUser);
64
65 const Binding *b = static_cast<const Binding *>(pvValue);
66 if (b == NULL)
67 return pfnOutput(pvArgOutput, RT_STR_TUPLE("<NULL>"));
68
69 size_t cb = RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%RTnaipv4", b->m_addr.u);
70 if (b->m_state == Binding::FREE)
71 cb += pfnOutput(pvArgOutput, RT_STR_TUPLE(" free"));
72 else
73 {
74 cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, " to %R[id], %s, valid from ", &b->m_id, b->stateName());
75
76 Timestamp tsIssued = b->issued();
77 cb += tsIssued.strFormatHelper(pfnOutput, pvArgOutput);
78
79 cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, " for %ds until ", b->leaseTime());
80
81 Timestamp tsValid = b->issued();
82 tsValid.addSeconds(b->leaseTime());
83 cb += tsValid.strFormatHelper(pfnOutput, pvArgOutput);
84 }
85
86 return cb;
87}
88
89const char *Binding::stateName() const
90{
91 switch (m_state)
92 {
93 case FREE:
94 return "free";
95 case RELEASED:
96 return "released";
97 case EXPIRED:
98 return "expired";
99 case OFFERED:
100 return "offered";
101 case ACKED:
102 return "acked";
103 default:
104 AssertMsgFailed(("%d\n", m_state));
105 return "released";
106 }
107}
108
109
110Binding &Binding::setState(const char *pszStateName)
111{
112 if (strcmp(pszStateName, "free") == 0)
113 m_state = Binding::FREE;
114 else if (strcmp(pszStateName, "released") == 0)
115 m_state = Binding::RELEASED;
116 else if (strcmp(pszStateName, "expired") == 0)
117 m_state = Binding::EXPIRED;
118 else if (strcmp(pszStateName, "offered") == 0)
119 m_state = Binding::OFFERED;
120 else if (strcmp(pszStateName, "acked") == 0)
121 m_state = Binding::ACKED;
122 else
123 {
124 AssertMsgFailed(("%d\n", m_state));
125 m_state = Binding::RELEASED;
126 }
127
128 return *this;
129}
130
131
132/**
133 * Expires the binding if it's past the specified deadline.
134 *
135 * @returns False if already expired, released or freed, otherwise true (i.e.
136 * does not indicate whether action was taken or not).
137 * @param tsDeadline The expiry deadline to use.
138 */
139bool Binding::expire(Timestamp tsDeadline)
140{
141 if (m_state <= Binding::EXPIRED)
142 return false;
143
144 Timestamp tsExpire = m_issued;
145 tsExpire.addSeconds(m_secLease);
146
147 if (tsExpire < tsDeadline)
148 {
149 if (m_state == Binding::OFFERED)
150 setState(Binding::FREE);
151 else
152 setState(Binding::EXPIRED);
153 }
154 return true;
155}
156
157
158/**
159 * Serializes the binding to XML for the lease database.
160 *
161 * @throw std::bad_alloc
162 */
163void Binding::toXML(xml::ElementNode *pElmParent) const
164{
165 /*
166 * Lease
167 */
168 xml::ElementNode *pElmLease = pElmParent->createChild("Lease");
169
170 pElmLease->setAttribute("mac", RTCStringFmt("%RTmac", &m_id.mac()));
171 if (m_id.id().present())
172 {
173 /* I'd prefer RTSTRPRINTHEXBYTES_F_SEP_COLON but there's no decoder */
174 size_t cbStrId = m_id.id().value().size() * 2 + 1;
175 char *pszId = new char[cbStrId];
176 int rc = RTStrPrintHexBytes(pszId, cbStrId,
177 &m_id.id().value().front(), m_id.id().value().size(),
178 0);
179 AssertRC(rc);
180 pElmLease->setAttribute("id", pszId);
181 delete[] pszId;
182 }
183
184 /* unused but we need it to keep the old code happy */
185 pElmLease->setAttribute("network", "0.0.0.0");
186 pElmLease->setAttribute("state", stateName());
187
188 /*
189 * Lease/Address
190 */
191 xml::ElementNode *pElmAddr = pElmLease->createChild("Address");
192 pElmAddr->setAttribute("value", RTCStringFmt("%RTnaipv4", m_addr.u));
193
194 /*
195 * Lease/Time
196 */
197 xml::ElementNode *pElmTime = pElmLease->createChild("Time");
198 pElmTime->setAttribute("issued", m_issued.getAbsSeconds());
199 pElmTime->setAttribute("expiration", m_secLease);
200}
201
202
203/**
204 * Deserializes the binding from the XML lease database.
205 *
206 * @param pElmLease The "Lease" element.
207 * @return Pointer to the resulting binding, NULL on failure.
208 * @throw std::bad_alloc
209 */
210Binding *Binding::fromXML(const xml::ElementNode *pElmLease)
211{
212 /* Lease/@network seems to always have bogus value, ignore it. */
213
214 /*
215 * Lease/@mac
216 */
217 RTCString strMac;
218 bool fHasMac = pElmLease->getAttributeValue("mac", &strMac);
219 if (!fHasMac)
220 return NULL;
221
222 RTMAC mac;
223 int rc = RTNetStrToMacAddr(strMac.c_str(), &mac);
224 if (RT_FAILURE(rc))
225 return NULL;
226
227 OptClientId id;
228 RTCString strId;
229 bool fHasId = pElmLease->getAttributeValue("id", &strId);
230 if (fHasId)
231 {
232 /*
233 * Decode from "de:ad:be:ef".
234 * XXX: RTStrConvertHexBytes() doesn't grok colons
235 */
236 size_t cbBytes = strId.length() / 2;
237 uint8_t *pBytes = new uint8_t[cbBytes];
238 rc = RTStrConvertHexBytes(strId.c_str(), pBytes, cbBytes, 0);
239 if (RT_SUCCESS(rc))
240 {
241 std::vector<uint8_t> rawopt(pBytes, pBytes + cbBytes);
242 id = OptClientId(rawopt);
243 }
244 delete[] pBytes;
245 }
246
247 /*
248 * Lease/@state - not present in old leases file. We will try to
249 * infer from lease time below.
250 */
251 RTCString strState;
252 bool fHasState = pElmLease->getAttributeValue("state", &strState);
253
254 /*
255 * Lease/Address
256 */
257 const xml::ElementNode *ndAddress = pElmLease->findChildElement("Address");
258 if (ndAddress == NULL)
259 return NULL;
260
261 /*
262 * Lease/Address/@value
263 */
264 RTCString strAddress;
265 bool fHasValue = ndAddress->getAttributeValue("value", &strAddress);
266 if (!fHasValue)
267 return NULL;
268
269 RTNETADDRIPV4 addr;
270 rc = RTNetStrToIPv4Addr(strAddress.c_str(), &addr);
271 if (RT_FAILURE(rc))
272 return NULL;
273
274 /*
275 * Lease/Time
276 */
277 const xml::ElementNode *ndTime = pElmLease->findChildElement("Time");
278 if (ndTime == NULL)
279 return NULL;
280
281 /*
282 * Lease/Time/@issued
283 */
284 int64_t issued;
285 bool fHasIssued = ndTime->getAttributeValue("issued", &issued);
286 if (!fHasIssued)
287 return NULL;
288
289 /*
290 * Lease/Time/@expiration
291 */
292 uint32_t duration;
293 bool fHasExpiration = ndTime->getAttributeValue("expiration", &duration);
294 if (!fHasExpiration)
295 return NULL;
296
297 std::unique_ptr<Binding> b(new Binding(addr));
298 b->m_id = ClientId(mac, id);
299
300 if (fHasState)
301 {
302 b->m_issued = Timestamp::absSeconds(issued);
303 b->m_secLease = duration;
304 b->setState(strState.c_str());
305 }
306 else
307 { /* XXX: old code wrote timestamps instead of absolute time. */
308 /* pretend that lease has just ended */
309 Timestamp fakeIssued = Timestamp::now();
310 fakeIssued.subSeconds(duration);
311 b->m_issued = fakeIssued;
312 b->m_secLease = duration;
313 b->m_state = Binding::EXPIRED;
314 }
315
316 return b.release();
317}
318
319
320
321/*********************************************************************************************************************************
322* Class Db Implementation *
323*********************************************************************************************************************************/
324
325Db::Db()
326 : m_pConfig(NULL)
327{
328}
329
330
331Db::~Db()
332{
333 /** @todo free bindings */
334}
335
336
337int Db::init(const Config *pConfig)
338{
339 Binding::registerFormat();
340
341 m_pConfig = pConfig;
342
343 m_pool.init(pConfig->getIPv4PoolFirst(),
344 pConfig->getIPv4PoolLast());
345
346 return VINF_SUCCESS;
347}
348
349
350/**
351 * Expire old binding (leases).
352 */
353void Db::expire()
354{
355 const Timestamp now = Timestamp::now();
356 for (bindings_t::iterator it = m_bindings.begin(); it != m_bindings.end(); ++it)
357 {
358 Binding *b = *it;
359 b->expire(now);
360 }
361}
362
363
364/**
365 * Internal worker that creates a binding for the given client, allocating new
366 * IPv4 address for it.
367 *
368 * @returns Pointer to the binding.
369 * @param id The client ID.
370 */
371Binding *Db::createBinding(const ClientId &id)
372{
373 Binding *pBinding = NULL;
374 RTNETADDRIPV4 addr = m_pool.allocate();
375 if (addr.u != 0)
376 {
377 try
378 {
379 pBinding = new Binding(addr, id);
380 m_bindings.push_front(pBinding);
381 }
382 catch (std::bad_alloc &)
383 {
384 if (pBinding)
385 delete pBinding;
386 /** @todo free address (no pool method for that) */
387 }
388 }
389 return pBinding;
390}
391
392
393/**
394 * Internal worker that creates a binding to the specified IPv4 address for the
395 * given client.
396 *
397 * @returns Pointer to the binding.
398 * NULL if the address is in use or we ran out of memory.
399 * @param addr The IPv4 address.
400 * @param id The client.
401 */
402Binding *Db::createBinding(RTNETADDRIPV4 addr, const ClientId &id)
403{
404 bool fAvailable = m_pool.allocate(addr);
405 if (!fAvailable)
406 {
407 /** @todo
408 * XXX: this should not happen. If the address is from the
409 * pool, which we have verified before, then either it's in
410 * the free pool or there's an binding (possibly free) for it.
411 */
412 return NULL;
413 }
414
415 Binding *b = new Binding(addr, id);
416 m_bindings.push_front(b);
417 return b;
418}
419
420
421/**
422 * Internal worker that allocates an IPv4 address for the given client, taking
423 * the preferred address (@a addr) into account when possible and if non-zero.
424 */
425Binding *Db::allocateAddress(const ClientId &id, RTNETADDRIPV4 addr)
426{
427 Assert(addr.u == 0 || addressBelongs(addr));
428
429 if (addr.u != 0)
430 LogDHCP(("> allocateAddress %RTnaipv4 to client %R[id]\n", addr.u, &id));
431 else
432 LogDHCP(("> allocateAddress to client %R[id]\n", &id));
433
434 /*
435 * Allocate existing address if client has one. Ignore requested
436 * address in that case. While here, look for free addresses and
437 * addresses that can be reused.
438 */
439 Binding *addrBinding = NULL;
440 Binding *freeBinding = NULL;
441 Binding *reuseBinding = NULL;
442 const Timestamp now = Timestamp::now();
443 for (bindings_t::iterator it = m_bindings.begin(); it != m_bindings.end(); ++it)
444 {
445 Binding *b = *it;
446 b->expire(now);
447
448 /*
449 * We've already seen this client, give it its old binding.
450 */
451 if (b->m_id == id)
452 {
453 LogDHCP(("> ... found existing binding %R[binding]\n", b));
454 return b;
455 }
456
457 if (addr.u != 0 && b->m_addr.u == addr.u)
458 {
459 Assert(addrBinding == NULL);
460 addrBinding = b;
461 LogDHCP(("> .... noted existing binding %R[binding]\n", addrBinding));
462 }
463
464 /* if we haven't found a free binding yet, keep looking */
465 if (freeBinding == NULL)
466 {
467 if (b->m_state == Binding::FREE)
468 {
469 freeBinding = b;
470 LogDHCP(("> .... noted free binding %R[binding]\n", freeBinding));
471 continue;
472 }
473
474 /* still no free binding, can this one be reused? */
475 if (b->m_state == Binding::RELEASED)
476 {
477 if ( reuseBinding == NULL
478 /* released binding is better than an expired one */
479 || reuseBinding->m_state == Binding::EXPIRED)
480 {
481 reuseBinding = b;
482 LogDHCP(("> .... noted released binding %R[binding]\n", reuseBinding));
483 }
484 }
485 else if (b->m_state == Binding::EXPIRED)
486 {
487 if ( reuseBinding == NULL
488 /* long expired binding is bettern than a recent one */
489 /* || (reuseBinding->m_state == Binding::EXPIRED && b->olderThan(reuseBinding)) */)
490 {
491 reuseBinding = b;
492 LogDHCP(("> .... noted expired binding %R[binding]\n", reuseBinding));
493 }
494 }
495 }
496 }
497
498 /*
499 * Allocate requested address if we can.
500 */
501 if (addr.u != 0)
502 {
503 if (addrBinding == NULL)
504 {
505 addrBinding = createBinding(addr, id);
506 Assert(addrBinding != NULL);
507 LogDHCP(("> .... creating new binding for this address %R[binding]\n", addrBinding));
508 return addrBinding;
509 }
510
511 if (addrBinding->m_state <= Binding::EXPIRED) /* not in use */
512 {
513 LogDHCP(("> .... reusing %s binding for this address\n", addrBinding->stateName()));
514 addrBinding->giveTo(id);
515 return addrBinding;
516 }
517 LogDHCP(("> .... cannot reuse %s binding for this address\n", addrBinding->stateName()));
518 }
519
520 /*
521 * Allocate new (or reuse).
522 */
523 Binding *idBinding = NULL;
524 if (freeBinding != NULL)
525 {
526 idBinding = freeBinding;
527 LogDHCP(("> .... reusing free binding\n"));
528 }
529 else
530 {
531 idBinding = createBinding();
532 if (idBinding != NULL)
533 LogDHCP(("> .... creating new binding\n"));
534 else
535 {
536 idBinding = reuseBinding;
537 if (idBinding != NULL)
538 LogDHCP(("> .... reusing %s binding %R[binding]\n", reuseBinding->stateName(), reuseBinding));
539 else
540 {
541 LogDHCP(("> .... failed to allocate binding\n"));
542 return NULL;
543 }
544 }
545 }
546
547 idBinding->giveTo(id);
548 LogDHCP(("> .... allocated %R[binding]\n", idBinding));
549
550 return idBinding;
551}
552
553
554
555/**
556 * Called by DHCPD to allocate a binding for the specified request.
557 *
558 * @returns Pointer to the binding, NULL on failure.
559 * @param req The DHCP request being served.
560 */
561Binding *Db::allocateBinding(const DhcpClientMessage &req)
562{
563 /** @todo XXX: handle fixed address assignments */
564
565 /*
566 * Get and validate the requested address (if present).
567 */
568 OptRequestedAddress reqAddr(req);
569 if (reqAddr.present() && !addressBelongs(reqAddr.value()))
570 {
571 if (req.messageType() == RTNET_DHCP_MT_DISCOVER)
572 {
573 LogDHCP(("DISCOVER: ignoring invalid requested address\n"));
574 reqAddr = OptRequestedAddress();
575 }
576 else
577 {
578 LogDHCP(("rejecting invalid requested address\n"));
579 return NULL;
580 }
581 }
582
583 /*
584 * Allocate the address.
585 */
586 const ClientId &id(req.clientId());
587
588 Binding *b = allocateAddress(id, reqAddr.value());
589 if (b != NULL)
590 {
591 Assert(b->id() == id);
592
593 /** @todo
594 * XXX: handle requests for specific lease time!
595 * XXX: old lease might not have expired yet?
596 * Make lease time configurable.
597 */
598 // OptLeaseTime reqLeaseTime(req);
599 b->setLeaseTime(1200);
600 }
601 return b;
602}
603
604
605/**
606 * Internal worker used by loadLease().
607 *
608 * @returns IPRT status code.
609 * @param newb .
610 */
611int Db::addBinding(Binding *pNewBinding)
612{
613 /*
614 * Validate the binding against the range and existing bindings.
615 */
616 if (!addressBelongs(pNewBinding->m_addr))
617 {
618 LogDHCP(("Binding for out of range address %RTnaipv4 ignored\n", pNewBinding->m_addr.u));
619 return VERR_OUT_OF_RANGE;
620 }
621
622 for (bindings_t::iterator it = m_bindings.begin(); it != m_bindings.end(); ++it)
623 {
624 Binding *b = *it;
625
626 if (pNewBinding->m_addr.u == b->m_addr.u)
627 {
628 LogDHCP(("> ADD: %R[binding]\n", pNewBinding));
629 LogDHCP(("> .... duplicate ip: %R[binding]\n", b));
630 return VERR_DUPLICATE;
631 }
632
633 if (pNewBinding->m_id == b->m_id)
634 {
635 LogDHCP(("> ADD: %R[binding]\n", pNewBinding));
636 LogDHCP(("> .... duplicate id: %R[binding]\n", b));
637 return VERR_DUPLICATE;
638 }
639 }
640
641 /*
642 * Allocate the address and add the binding to the list.
643 */
644 AssertLogRelMsgReturn(m_pool.allocate(pNewBinding->m_addr),
645 ("> ADD: failed to claim IP %R[binding]\n", pNewBinding),
646 VERR_INTERNAL_ERROR);
647 try
648 {
649 m_bindings.push_back(pNewBinding);
650 }
651 catch (std::bad_alloc &)
652 {
653 return VERR_NO_MEMORY;
654 }
655 return VINF_SUCCESS;
656}
657
658
659/**
660 * Called by DHCP to cancel an offset.
661 *
662 * @param req The DHCP request.
663 */
664void Db::cancelOffer(const DhcpClientMessage &req)
665{
666 const OptRequestedAddress reqAddr(req);
667 if (!reqAddr.present())
668 return;
669
670 const RTNETADDRIPV4 addr = reqAddr.value();
671 const ClientId &id(req.clientId());
672
673 for (bindings_t::iterator it = m_bindings.begin(); it != m_bindings.end(); ++it)
674 {
675 Binding *b = *it;
676
677 if (b->addr().u == addr.u && b->id() == id)
678 {
679 if (b->state() == Binding::OFFERED)
680 {
681 LogRel2(("Db::cancelOffer: cancelling %R[binding]\n", b));
682 b->setLeaseTime(0);
683 b->setState(Binding::RELEASED);
684 }
685 else
686 LogRel2(("Db::cancelOffer: not offered state: %R[binding]\n", b));
687 return;
688 }
689 }
690 LogRel2(("Db::cancelOffer: not found (%RTnaipv4, %R[id])\n", addr.u, &id));
691}
692
693
694/**
695 * Called by DHCP to cancel an offset.
696 *
697 * @param req The DHCP request.
698 * @returns true if found and released, otherwise false.
699 */
700bool Db::releaseBinding(const DhcpClientMessage &req)
701{
702 const RTNETADDRIPV4 addr = req.ciaddr();
703 const ClientId &id(req.clientId());
704
705 for (bindings_t::iterator it = m_bindings.begin(); it != m_bindings.end(); ++it)
706 {
707 Binding *b = *it;
708
709 if (b->addr().u == addr.u && b->id() == id)
710 {
711 LogRel2(("Db::releaseBinding: releasing %R[binding]\n", b));
712 b->setState(Binding::RELEASED);
713 return true;
714 }
715 }
716
717 LogRel2(("Db::releaseBinding: not found (%RTnaipv4, %R[id])\n", addr.u, &id));
718 return false;
719}
720
721
722/**
723 * Called by DHCPD to write out the lease database to @a strFilename.
724 *
725 * @returns IPRT status code.
726 * @param strFilename The file to write it to.
727 */
728int Db::writeLeases(const RTCString &strFilename) const
729{
730 LogDHCP(("writing leases to %s\n", strFilename.c_str()));
731
732 /*
733 * Create the document and root element.
734 */
735 xml::Document doc;
736 try
737 {
738 xml::ElementNode *pElmRoot = doc.createRootElement("Leases");
739 pElmRoot->setAttribute("version", "1.0");
740
741 /*
742 * Add the leases.
743 */
744 for (bindings_t::const_iterator it = m_bindings.begin(); it != m_bindings.end(); ++it)
745 {
746 const Binding *b = *it;
747 b->toXML(pElmRoot);
748 }
749 }
750 catch (std::bad_alloc &)
751 {
752 return VERR_NO_MEMORY;
753 }
754
755 /*
756 * Write the document to the specified file in a safe manner (written to temporary
757 * file, renamed to destination on success)
758 */
759 try
760 {
761 xml::XmlFileWriter writer(doc);
762 writer.write(strFilename.c_str(), true /*fSafe*/);
763 }
764 catch (const xml::EIPRTFailure &e)
765 {
766 LogDHCP(("%s\n", e.what()));
767 return e.rc();
768 }
769 catch (const RTCError &e)
770 {
771 LogDHCP(("%s\n", e.what()));
772 return VERR_GENERAL_FAILURE;
773 }
774 catch (...)
775 {
776 LogDHCP(("Unknown exception while writing '%s'\n", strFilename.c_str()));
777 return VERR_UNEXPECTED_EXCEPTION;
778 }
779
780 return VINF_SUCCESS;
781}
782
783
784/**
785 * Called by DHCPD to load the lease database to @a strFilename.
786 *
787 * @note Does not clear the database state before doing the load.
788 *
789 * @returns IPRT status code.
790 * @param strFilename The file to load it from.
791 */
792int Db::loadLeases(const RTCString &strFilename)
793{
794 LogDHCP(("loading leases from %s\n", strFilename.c_str()));
795
796 /*
797 * Load the file into an XML document.
798 */
799 xml::Document doc;
800 try
801 {
802 xml::XmlFileParser parser;
803 parser.read(strFilename.c_str(), doc);
804 }
805 catch (const xml::EIPRTFailure &e)
806 {
807 LogDHCP(("%s\n", e.what()));
808 return e.rc();
809 }
810 catch (const RTCError &e)
811 {
812 LogDHCP(("%s\n", e.what()));
813 return VERR_GENERAL_FAILURE;
814 }
815 catch (...)
816 {
817 LogDHCP(("Unknown exception while reading and parsing '%s'\n", strFilename.c_str()));
818 return VERR_UNEXPECTED_EXCEPTION;
819 }
820
821 /*
822 * Check that the root element is "Leases" and process its children.
823 */
824 xml::ElementNode *pElmRoot = doc.getRootElement();
825 if (!pElmRoot)
826 {
827 LogDHCP(("No root element in '%s'\n", strFilename.c_str()));
828 return VERR_NOT_FOUND;
829 }
830 if (!pElmRoot->nameEquals("Leases"))
831 {
832 LogDHCP(("No root element is not 'Leases' in '%s', but '%s'\n", strFilename.c_str(), pElmRoot->getName()));
833 return VERR_NOT_FOUND;
834 }
835
836 int rc = VINF_SUCCESS;
837 xml::NodesLoop it(*pElmRoot);
838 const xml::ElementNode *pElmLease;
839 while ((pElmLease = it.forAllNodes()) != NULL)
840 {
841 if (pElmLease->nameEquals("Lease"))
842 {
843 int rc2 = loadLease(pElmLease);
844 if (RT_SUCCESS(rc2))
845 { /* likely */ }
846 else if (rc2 == VERR_NO_MEMORY)
847 return rc2;
848 else
849 rc = -rc2;
850 }
851 else
852 LogDHCP(("Ignoring unexpected element '%s' under 'Leases'...\n", pElmLease->getName()));
853 }
854
855 return rc;
856}
857
858
859/**
860 * Internal worker for loadLeases() that handles one 'Lease' element.
861 *
862 * @param pElmLease The 'Lease' element to handle.
863 * @return IPRT status code.
864 */
865int Db::loadLease(const xml::ElementNode *pElmLease)
866{
867 Binding *pBinding = NULL;
868 try
869 {
870 pBinding = Binding::fromXML(pElmLease);
871 }
872 catch (std::bad_alloc &)
873 {
874 return VERR_NO_MEMORY;
875 }
876 if (pBinding)
877 {
878 bool fExpired = pBinding->expire();
879 if (!fExpired)
880 LogDHCP(("> LOAD: lease %R[binding]\n", pBinding));
881 else
882 LogDHCP(("> LOAD: EXPIRED lease %R[binding]\n", pBinding));
883
884 int rc = addBinding(pBinding);
885 if (RT_FAILURE(rc))
886 delete pBinding;
887 return rc;
888 }
889 LogDHCP(("> LOAD: failed to load lease!\n"));
890 return VERR_PARSE_ERROR;
891}
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