VirtualBox

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

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

Dhcpd: Went over the DhcpMessage a little as well as revisiting the lease reading code in the database. Logging is LogRel() or similar, no cout, RTPrintf or similar. The debug & release loggers can both output to stderr/out/whatever as needed. bugref:9288

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