VirtualBox

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

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

Dhcpd: Went over the Dhcpd and related code adding comments and doing some exception vetting. bugref:9288

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 24.9 KB
Line 
1/* $Id: Db.cpp 79563 2019-07-06 01:22:56Z 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() RT_NOEXCEPT
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 RT_NOEXCEPT
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) RT_NOEXCEPT
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) RT_NOEXCEPT
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 */
235 /** @todo RTStrConvertHexBytes() doesn't grok colons */
236 size_t cbBytes = strId.length() / 2;
237 uint8_t *pbBytes = new uint8_t[cbBytes];
238 rc = RTStrConvertHexBytes(strId.c_str(), pbBytes, cbBytes, 0);
239 if (RT_SUCCESS(rc))
240 {
241 try
242 {
243 std::vector<uint8_t> rawopt(pbBytes, pbBytes + cbBytes);
244 id = OptClientId(rawopt);
245 }
246 catch (std::bad_alloc &)
247 {
248 delete[] pbBytes;
249 throw;
250 }
251 }
252 delete[] pbBytes;
253 }
254
255 /*
256 * Lease/@state - not present in old leases file. We will try to
257 * infer from lease time below.
258 */
259 RTCString strState;
260 bool fHasState = pElmLease->getAttributeValue("state", &strState);
261
262 /*
263 * Lease/Address
264 */
265 const xml::ElementNode *ndAddress = pElmLease->findChildElement("Address");
266 if (ndAddress == NULL)
267 return NULL;
268
269 /*
270 * Lease/Address/@value
271 */
272 RTCString strAddress;
273 bool fHasValue = ndAddress->getAttributeValue("value", &strAddress);
274 if (!fHasValue)
275 return NULL;
276
277 RTNETADDRIPV4 addr;
278 rc = RTNetStrToIPv4Addr(strAddress.c_str(), &addr);
279 if (RT_FAILURE(rc))
280 return NULL;
281
282 /*
283 * Lease/Time
284 */
285 const xml::ElementNode *ndTime = pElmLease->findChildElement("Time");
286 if (ndTime == NULL)
287 return NULL;
288
289 /*
290 * Lease/Time/@issued
291 */
292 int64_t issued;
293 bool fHasIssued = ndTime->getAttributeValue("issued", &issued);
294 if (!fHasIssued)
295 return NULL;
296
297 /*
298 * Lease/Time/@expiration
299 */
300 uint32_t duration;
301 bool fHasExpiration = ndTime->getAttributeValue("expiration", &duration);
302 if (!fHasExpiration)
303 return NULL;
304
305 std::unique_ptr<Binding> b(new Binding(addr));
306 b->m_id = ClientId(mac, id);
307
308 if (fHasState)
309 {
310 b->m_issued = Timestamp::absSeconds(issued);
311 b->m_secLease = duration;
312 b->setState(strState.c_str());
313 }
314 else
315 { /** @todo XXX: old code wrote timestamps instead of absolute time. */
316 /* pretend that lease has just ended */
317 Timestamp fakeIssued = Timestamp::now();
318 fakeIssued.subSeconds(duration);
319 b->m_issued = fakeIssued;
320 b->m_secLease = duration;
321 b->m_state = Binding::EXPIRED;
322 }
323
324 return b.release();
325}
326
327
328
329/*********************************************************************************************************************************
330* Class Db Implementation *
331*********************************************************************************************************************************/
332
333Db::Db()
334 : m_pConfig(NULL)
335{
336}
337
338
339Db::~Db()
340{
341 /** @todo free bindings */
342}
343
344
345int Db::init(const Config *pConfig)
346{
347 Binding::registerFormat();
348
349 m_pConfig = pConfig;
350
351 return m_pool.init(pConfig->getIPv4PoolFirst(), pConfig->getIPv4PoolLast());
352}
353
354
355/**
356 * Expire old binding (leases).
357 */
358void Db::expire() RT_NOEXCEPT
359{
360 const Timestamp now = Timestamp::now();
361 for (bindings_t::iterator it = m_bindings.begin(); it != m_bindings.end(); ++it)
362 {
363 Binding *b = *it;
364 b->expire(now);
365 }
366}
367
368
369/**
370 * Internal worker that creates a binding for the given client, allocating new
371 * IPv4 address for it.
372 *
373 * @returns Pointer to the binding.
374 * @param id The client ID.
375 */
376Binding *Db::i_createBinding(const ClientId &id)
377{
378 Binding *pBinding = NULL;
379 RTNETADDRIPV4 addr = m_pool.allocate();
380 if (addr.u != 0)
381 {
382 try
383 {
384 pBinding = new Binding(addr, id);
385 m_bindings.push_front(pBinding);
386 }
387 catch (std::bad_alloc &)
388 {
389 if (pBinding)
390 delete pBinding;
391 /** @todo free address (no pool method for that) */
392 }
393 }
394 return pBinding;
395}
396
397
398/**
399 * Internal worker that creates a binding to the specified IPv4 address for the
400 * given client.
401 *
402 * @returns Pointer to the binding.
403 * NULL if the address is in use or we ran out of memory.
404 * @param addr The IPv4 address.
405 * @param id The client.
406 */
407Binding *Db::i_createBinding(RTNETADDRIPV4 addr, const ClientId &id)
408{
409 bool fAvailable = m_pool.allocate(addr);
410 if (!fAvailable)
411 {
412 /** @todo
413 * XXX: this should not happen. If the address is from the
414 * pool, which we have verified before, then either it's in
415 * the free pool or there's an binding (possibly free) for it.
416 */
417 return NULL;
418 }
419
420 Binding *b = new Binding(addr, id);
421 m_bindings.push_front(b);
422 return b;
423}
424
425
426/**
427 * Internal worker that allocates an IPv4 address for the given client, taking
428 * the preferred address (@a addr) into account when possible and if non-zero.
429 */
430Binding *Db::i_allocateAddress(const ClientId &id, RTNETADDRIPV4 addr)
431{
432 Assert(addr.u == 0 || addressBelongs(addr));
433
434 if (addr.u != 0)
435 LogDHCP(("> allocateAddress %RTnaipv4 to client %R[id]\n", addr.u, &id));
436 else
437 LogDHCP(("> allocateAddress to client %R[id]\n", &id));
438
439 /*
440 * Allocate existing address if client has one. Ignore requested
441 * address in that case. While here, look for free addresses and
442 * addresses that can be reused.
443 */
444 Binding *addrBinding = NULL;
445 Binding *freeBinding = NULL;
446 Binding *reuseBinding = NULL;
447 const Timestamp now = Timestamp::now();
448 for (bindings_t::iterator it = m_bindings.begin(); it != m_bindings.end(); ++it)
449 {
450 Binding *b = *it;
451 b->expire(now);
452
453 /*
454 * We've already seen this client, give it its old binding.
455 */
456 if (b->m_id == id)
457 {
458 LogDHCP(("> ... found existing binding %R[binding]\n", b));
459 return b;
460 }
461
462 if (addr.u != 0 && b->m_addr.u == addr.u)
463 {
464 Assert(addrBinding == NULL);
465 addrBinding = b;
466 LogDHCP(("> .... noted existing binding %R[binding]\n", addrBinding));
467 }
468
469 /* if we haven't found a free binding yet, keep looking */
470 if (freeBinding == NULL)
471 {
472 if (b->m_state == Binding::FREE)
473 {
474 freeBinding = b;
475 LogDHCP(("> .... noted free binding %R[binding]\n", freeBinding));
476 continue;
477 }
478
479 /* still no free binding, can this one be reused? */
480 if (b->m_state == Binding::RELEASED)
481 {
482 if ( reuseBinding == NULL
483 /* released binding is better than an expired one */
484 || reuseBinding->m_state == Binding::EXPIRED)
485 {
486 reuseBinding = b;
487 LogDHCP(("> .... noted released binding %R[binding]\n", reuseBinding));
488 }
489 }
490 else if (b->m_state == Binding::EXPIRED)
491 {
492 if ( reuseBinding == NULL
493 /* long expired binding is bettern than a recent one */
494 /* || (reuseBinding->m_state == Binding::EXPIRED && b->olderThan(reuseBinding)) */)
495 {
496 reuseBinding = b;
497 LogDHCP(("> .... noted expired binding %R[binding]\n", reuseBinding));
498 }
499 }
500 }
501 }
502
503 /*
504 * Allocate requested address if we can.
505 */
506 if (addr.u != 0)
507 {
508 if (addrBinding == NULL)
509 {
510 addrBinding = i_createBinding(addr, id);
511 Assert(addrBinding != NULL);
512 LogDHCP(("> .... creating new binding for this address %R[binding]\n", addrBinding));
513 return addrBinding;
514 }
515
516 if (addrBinding->m_state <= Binding::EXPIRED) /* not in use */
517 {
518 LogDHCP(("> .... reusing %s binding for this address\n", addrBinding->stateName()));
519 addrBinding->giveTo(id);
520 return addrBinding;
521 }
522 LogDHCP(("> .... cannot reuse %s binding for this address\n", addrBinding->stateName()));
523 }
524
525 /*
526 * Allocate new (or reuse).
527 */
528 Binding *idBinding = NULL;
529 if (freeBinding != NULL)
530 {
531 idBinding = freeBinding;
532 LogDHCP(("> .... reusing free binding\n"));
533 }
534 else
535 {
536 idBinding = i_createBinding();
537 if (idBinding != NULL)
538 LogDHCP(("> .... creating new binding\n"));
539 else
540 {
541 idBinding = reuseBinding;
542 if (idBinding != NULL)
543 LogDHCP(("> .... reusing %s binding %R[binding]\n", reuseBinding->stateName(), reuseBinding));
544 else
545 {
546 LogDHCP(("> .... failed to allocate binding\n"));
547 return NULL;
548 }
549 }
550 }
551
552 idBinding->giveTo(id);
553 LogDHCP(("> .... allocated %R[binding]\n", idBinding));
554
555 return idBinding;
556}
557
558
559
560/**
561 * Called by DHCPD to allocate a binding for the specified request.
562 *
563 * @returns Pointer to the binding, NULL on failure.
564 * @param req The DHCP request being served.
565 */
566Binding *Db::allocateBinding(const DhcpClientMessage &req)
567{
568 /** @todo XXX: handle fixed address assignments */
569
570 /*
571 * Get and validate the requested address (if present).
572 */
573 OptRequestedAddress reqAddr(req);
574 if (reqAddr.present() && !addressBelongs(reqAddr.value()))
575 {
576 if (req.messageType() == RTNET_DHCP_MT_DISCOVER)
577 {
578 LogDHCP(("DISCOVER: ignoring invalid requested address\n"));
579 reqAddr = OptRequestedAddress();
580 }
581 else
582 {
583 LogDHCP(("rejecting invalid requested address\n"));
584 return NULL;
585 }
586 }
587
588 /*
589 * Allocate the address.
590 */
591 const ClientId &id(req.clientId());
592
593 Binding *b = i_allocateAddress(id, reqAddr.value());
594 if (b != NULL)
595 {
596 Assert(b->id() == id);
597
598 /** @todo
599 * XXX: handle requests for specific lease time!
600 * XXX: old lease might not have expired yet?
601 * Make lease time configurable.
602 */
603 // OptLeaseTime reqLeaseTime(req);
604 b->setLeaseTime(1200);
605 }
606 return b;
607}
608
609
610/**
611 * Internal worker used by loadLease().
612 *
613 * @returns IPRT status code.
614 * @param pNewBinding The new binding to add.
615 */
616int Db::i_addBinding(Binding *pNewBinding) RT_NOEXCEPT
617{
618 /*
619 * Validate the binding against the range and existing bindings.
620 */
621 if (!addressBelongs(pNewBinding->m_addr))
622 {
623 LogDHCP(("Binding for out of range address %RTnaipv4 ignored\n", pNewBinding->m_addr.u));
624 return VERR_OUT_OF_RANGE;
625 }
626
627 for (bindings_t::iterator it = m_bindings.begin(); it != m_bindings.end(); ++it)
628 {
629 Binding *b = *it;
630
631 if (pNewBinding->m_addr.u == b->m_addr.u)
632 {
633 LogDHCP(("> ADD: %R[binding]\n", pNewBinding));
634 LogDHCP(("> .... duplicate ip: %R[binding]\n", b));
635 return VERR_DUPLICATE;
636 }
637
638 if (pNewBinding->m_id == b->m_id)
639 {
640 LogDHCP(("> ADD: %R[binding]\n", pNewBinding));
641 LogDHCP(("> .... duplicate id: %R[binding]\n", b));
642 return VERR_DUPLICATE;
643 }
644 }
645
646 /*
647 * Allocate the address and add the binding to the list.
648 */
649 AssertLogRelMsgReturn(m_pool.allocate(pNewBinding->m_addr),
650 ("> ADD: failed to claim IP %R[binding]\n", pNewBinding),
651 VERR_INTERNAL_ERROR);
652 try
653 {
654 m_bindings.push_back(pNewBinding);
655 }
656 catch (std::bad_alloc &)
657 {
658 return VERR_NO_MEMORY;
659 }
660 return VINF_SUCCESS;
661}
662
663
664/**
665 * Called by DHCP to cancel an offset.
666 *
667 * @param req The DHCP request.
668 */
669void Db::cancelOffer(const DhcpClientMessage &req) RT_NOEXCEPT
670{
671 const OptRequestedAddress reqAddr(req);
672 if (!reqAddr.present())
673 return;
674
675 const RTNETADDRIPV4 addr = reqAddr.value();
676 const ClientId &id(req.clientId());
677
678 for (bindings_t::iterator it = m_bindings.begin(); it != m_bindings.end(); ++it)
679 {
680 Binding *b = *it;
681
682 if (b->addr().u == addr.u && b->id() == id)
683 {
684 if (b->state() == Binding::OFFERED)
685 {
686 LogRel2(("Db::cancelOffer: cancelling %R[binding]\n", b));
687 b->setLeaseTime(0);
688 b->setState(Binding::RELEASED);
689 }
690 else
691 LogRel2(("Db::cancelOffer: not offered state: %R[binding]\n", b));
692 return;
693 }
694 }
695 LogRel2(("Db::cancelOffer: not found (%RTnaipv4, %R[id])\n", addr.u, &id));
696}
697
698
699/**
700 * Called by DHCP to cancel an offset.
701 *
702 * @param req The DHCP request.
703 * @returns true if found and released, otherwise false.
704 * @throws nothing
705 */
706bool Db::releaseBinding(const DhcpClientMessage &req) RT_NOEXCEPT
707{
708 const RTNETADDRIPV4 addr = req.ciaddr();
709 const ClientId &id(req.clientId());
710
711 for (bindings_t::iterator it = m_bindings.begin(); it != m_bindings.end(); ++it)
712 {
713 Binding *b = *it;
714
715 if (b->addr().u == addr.u && b->id() == id)
716 {
717 LogRel2(("Db::releaseBinding: releasing %R[binding]\n", b));
718 b->setState(Binding::RELEASED);
719 return true;
720 }
721 }
722
723 LogRel2(("Db::releaseBinding: not found (%RTnaipv4, %R[id])\n", addr.u, &id));
724 return false;
725}
726
727
728/**
729 * Called by DHCPD to write out the lease database to @a strFilename.
730 *
731 * @returns IPRT status code.
732 * @param strFilename The file to write it to.
733 */
734int Db::writeLeases(const RTCString &strFilename) const RT_NOEXCEPT
735{
736 LogDHCP(("writing leases to %s\n", strFilename.c_str()));
737
738 /** @todo This could easily be written directly to the file w/o going thru
739 * a xml::Document, xml::XmlFileWriter, hammering the heap and being
740 * required to catch a lot of different exceptions at various points.
741 * (RTStrmOpen, bunch of RTStrmPrintf using \%RMas and \%RMes.,
742 * RTStrmClose closely followed by a couple of renames.)
743 */
744
745 /*
746 * Create the document and root element.
747 */
748 xml::Document doc;
749 try
750 {
751 xml::ElementNode *pElmRoot = doc.createRootElement("Leases");
752 pElmRoot->setAttribute("version", "1.0");
753
754 /*
755 * Add the leases.
756 */
757 for (bindings_t::const_iterator it = m_bindings.begin(); it != m_bindings.end(); ++it)
758 {
759 const Binding *b = *it;
760 b->toXML(pElmRoot);
761 }
762 }
763 catch (std::bad_alloc &)
764 {
765 return VERR_NO_MEMORY;
766 }
767
768 /*
769 * Write the document to the specified file in a safe manner (written to temporary
770 * file, renamed to destination on success)
771 */
772 try
773 {
774 xml::XmlFileWriter writer(doc);
775 writer.write(strFilename.c_str(), true /*fSafe*/);
776 }
777 catch (const xml::EIPRTFailure &e)
778 {
779 LogDHCP(("%s\n", e.what()));
780 return e.rc();
781 }
782 catch (const RTCError &e)
783 {
784 LogDHCP(("%s\n", e.what()));
785 return VERR_GENERAL_FAILURE;
786 }
787 catch (...)
788 {
789 LogDHCP(("Unknown exception while writing '%s'\n", strFilename.c_str()));
790 return VERR_UNEXPECTED_EXCEPTION;
791 }
792
793 return VINF_SUCCESS;
794}
795
796
797/**
798 * Called by DHCPD to load the lease database to @a strFilename.
799 *
800 * @note Does not clear the database state before doing the load.
801 *
802 * @returns IPRT status code.
803 * @param strFilename The file to load it from.
804 * @throws nothing
805 */
806int Db::loadLeases(const RTCString &strFilename) RT_NOEXCEPT
807{
808 LogDHCP(("loading leases from %s\n", strFilename.c_str()));
809
810 /*
811 * Load the file into an XML document.
812 */
813 xml::Document doc;
814 try
815 {
816 xml::XmlFileParser parser;
817 parser.read(strFilename.c_str(), doc);
818 }
819 catch (const xml::EIPRTFailure &e)
820 {
821 LogDHCP(("%s\n", e.what()));
822 return e.rc();
823 }
824 catch (const RTCError &e)
825 {
826 LogDHCP(("%s\n", e.what()));
827 return VERR_GENERAL_FAILURE;
828 }
829 catch (...)
830 {
831 LogDHCP(("Unknown exception while reading and parsing '%s'\n", strFilename.c_str()));
832 return VERR_UNEXPECTED_EXCEPTION;
833 }
834
835 /*
836 * Check that the root element is "Leases" and process its children.
837 */
838 xml::ElementNode *pElmRoot = doc.getRootElement();
839 if (!pElmRoot)
840 {
841 LogDHCP(("No root element in '%s'\n", strFilename.c_str()));
842 return VERR_NOT_FOUND;
843 }
844 if (!pElmRoot->nameEquals("Leases"))
845 {
846 LogDHCP(("No root element is not 'Leases' in '%s', but '%s'\n", strFilename.c_str(), pElmRoot->getName()));
847 return VERR_NOT_FOUND;
848 }
849
850 int rc = VINF_SUCCESS;
851 xml::NodesLoop it(*pElmRoot);
852 const xml::ElementNode *pElmLease;
853 while ((pElmLease = it.forAllNodes()) != NULL)
854 {
855 if (pElmLease->nameEquals("Lease"))
856 {
857 int rc2 = i_loadLease(pElmLease);
858 if (RT_SUCCESS(rc2))
859 { /* likely */ }
860 else if (rc2 == VERR_NO_MEMORY)
861 return rc2;
862 else
863 rc = -rc2;
864 }
865 else
866 LogDHCP(("Ignoring unexpected element '%s' under 'Leases'...\n", pElmLease->getName()));
867 }
868
869 return rc;
870}
871
872
873/**
874 * Internal worker for loadLeases() that handles one 'Lease' element.
875 *
876 * @param pElmLease The 'Lease' element to handle.
877 * @return IPRT status code.
878 */
879int Db::i_loadLease(const xml::ElementNode *pElmLease) RT_NOEXCEPT
880{
881 Binding *pBinding = NULL;
882 try
883 {
884 pBinding = Binding::fromXML(pElmLease);
885 }
886 catch (std::bad_alloc &)
887 {
888 return VERR_NO_MEMORY;
889 }
890 if (pBinding)
891 {
892 bool fExpired = pBinding->expire();
893 if (!fExpired)
894 LogDHCP(("> LOAD: lease %R[binding]\n", pBinding));
895 else
896 LogDHCP(("> LOAD: EXPIRED lease %R[binding]\n", pBinding));
897
898 int rc = i_addBinding(pBinding);
899 if (RT_FAILURE(rc))
900 delete pBinding;
901 return rc;
902 }
903 LogDHCP(("> LOAD: failed to load lease!\n"));
904 return VERR_PARSE_ERROR;
905}
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