summaryrefslogtreecommitdiffstats
path: root/src/VBox/NetworkServices/Dhcpd/DhcpMessage.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/NetworkServices/Dhcpd/DhcpMessage.cpp
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/NetworkServices/Dhcpd/DhcpMessage.cpp')
-rw-r--r--src/VBox/NetworkServices/Dhcpd/DhcpMessage.cpp447
1 files changed, 447 insertions, 0 deletions
diff --git a/src/VBox/NetworkServices/Dhcpd/DhcpMessage.cpp b/src/VBox/NetworkServices/Dhcpd/DhcpMessage.cpp
new file mode 100644
index 00000000..718dcf4e
--- /dev/null
+++ b/src/VBox/NetworkServices/Dhcpd/DhcpMessage.cpp
@@ -0,0 +1,447 @@
+/* $Id: DhcpMessage.cpp $ */
+/** @file
+ * DHCP Message and its de/serialization.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "DhcpdInternal.h"
+#include "DhcpMessage.h"
+#include "DhcpOptions.h"
+
+#include <iprt/ctype.h>
+#include <iprt/string.h>
+
+
+
+DhcpMessage::DhcpMessage()
+ : m_xid(0)
+ , m_flags(0)
+ , m_ciaddr()
+ , m_yiaddr()
+ , m_siaddr()
+ , m_giaddr()
+#if 0 /* not currently unused */
+ , m_sname()
+ , m_file()
+#endif
+ , m_optMessageType()
+{
+}
+
+
+/**
+ * Does common message dumping.
+ */
+void DhcpMessage::dump() const RT_NOEXCEPT
+{
+ switch (m_optMessageType.value())
+ {
+ case RTNET_DHCP_MT_DISCOVER: LogRel(("DISCOVER")); break;
+ case RTNET_DHCP_MT_OFFER: LogRel(("OFFER")); break;
+ case RTNET_DHCP_MT_REQUEST: LogRel(("REQUEST")); break;
+ case RTNET_DHCP_MT_DECLINE: LogRel(("DECLINE")); break;
+ case RTNET_DHCP_MT_ACK: LogRel(("ACK")); break;
+ case RTNET_DHCP_MT_NAC: LogRel(("NAC")); break;
+ case RTNET_DHCP_MT_RELEASE: LogRel(("RELEASE")); break;
+ case RTNET_DHCP_MT_INFORM: LogRel(("INFORM")); break;
+ default:
+ LogRel(("<Unknown Mesage Type %d>", m_optMessageType.value()));
+ break;
+ }
+
+ LogRel((" xid 0x%08x", m_xid));
+ LogRel((" chaddr %RTmac\n", &m_mac));
+ LogRel((" ciaddr %RTnaipv4", m_ciaddr.u));
+ if (m_yiaddr.u != 0)
+ LogRel((" yiaddr %RTnaipv4", m_yiaddr.u));
+ if (m_siaddr.u != 0)
+ LogRel((" siaddr %RTnaipv4", m_siaddr.u));
+ if (m_giaddr.u != 0)
+ LogRel((" giaddr %RTnaipv4", m_giaddr.u));
+ if (broadcast())
+ LogRel((" broadcast\n"));
+ else
+ LogRel(("\n"));
+}
+
+
+/*********************************************************************************************************************************
+* DhcpClientMessage Implementation *
+*********************************************************************************************************************************/
+
+/* static */
+DhcpClientMessage *DhcpClientMessage::parse(bool broadcasted, const void *buf, size_t buflen)
+{
+ /*
+ * Validate the request.
+ */
+ if (buflen < RT_OFFSETOF(RTNETBOOTP, bp_vend.Dhcp.dhcp_opts))
+ DHCP_LOG2_RET_NULL(("DhcpClientMessage::parse: %zu bytes datagram is too short\n", buflen));
+
+ PCRTNETBOOTP bp = (PCRTNETBOOTP)buf;
+
+ if (bp->bp_op != RTNETBOOTP_OP_REQUEST)
+ DHCP_LOG2_RET_NULL(("DhcpClientMessage::parse: bad opcode: %d\n", bp->bp_op));
+
+ if (bp->bp_htype != RTNET_ARP_ETHER)
+ DHCP_LOG2_RET_NULL(("DhcpClientMessage::parse: unsupported htype %d\n", bp->bp_htype));
+
+ if (bp->bp_hlen != sizeof(RTMAC))
+ DHCP_LOG2_RET_NULL(("DhcpClientMessage::parse: unexpected hlen %d\n", bp->bp_hlen));
+
+ if ( (bp->bp_chaddr.Mac.au8[0] & 0x01) != 0
+ && (bp->bp_flags & RTNET_DHCP_FLAG_BROADCAST) == 0)
+ LogRel2(("DhcpClientMessage::parse: multicast chaddr %RTmac without broadcast flag\n", &bp->bp_chaddr.Mac));
+
+ /* we don't want to deal with forwarding */
+ if (bp->bp_giaddr.u != 0)
+ DHCP_LOG2_RET_NULL(("DhcpClientMessage::parse: giaddr %RTnaipv4\n", bp->bp_giaddr.u));
+
+ if (bp->bp_hops != 0)
+ DHCP_LOG2_RET_NULL(("DhcpClientMessage::parse: non-zero hops %d\n", bp->bp_hops));
+
+ if (bp->bp_vend.Dhcp.dhcp_cookie != RT_H2N_U32_C(RTNET_DHCP_COOKIE))
+ DHCP_LOG2_RET_NULL(("DhcpClientMessage::parse: bad cookie %#RX32\n", bp->bp_vend.Dhcp.dhcp_cookie));
+
+ /*
+ * Convert it into a DhcpClientMessage instance.
+ */
+ std::unique_ptr<DhcpClientMessage> msg(new DhcpClientMessage());
+
+ msg->m_broadcasted = broadcasted;
+ msg->m_xid = bp->bp_xid;
+ msg->m_flags = bp->bp_flags;
+ msg->m_mac = bp->bp_chaddr.Mac;
+ msg->m_ciaddr = bp->bp_ciaddr;
+ msg->m_yiaddr = bp->bp_yiaddr;
+ msg->m_siaddr = bp->bp_siaddr;
+ msg->m_giaddr = bp->bp_giaddr;
+
+ int fOptOverload = msg->i_parseOptions(&bp->bp_vend.Dhcp.dhcp_opts[0],
+ buflen - RT_OFFSETOF(RTNETBOOTP, bp_vend.Dhcp.dhcp_opts));
+ if (fOptOverload < 0)
+ return NULL;
+
+ /* "The 'file' field MUST be interpreted next ..." */
+ if (fOptOverload & RTNET_DHCP_OPTION_OVERLOAD_FILE)
+ {
+ int status = msg->i_parseOptions(bp->bp_file, sizeof(bp->bp_file));
+ if (status != 0)
+ return NULL;
+ }
+#if 0 /* not currently unused */
+ else if (bp->bp_file[0] != '\0')
+ {
+ /* must be zero terminated, ignore if not */
+ const char *pszFile = (const char *)bp->bp_file;
+ size_t len = RTStrNLen(pszFile, sizeof(bp->bp_file));
+ if (len < sizeof(bp->bp_file))
+ msg->m_file.assign(pszFile, len);
+ }
+#endif
+
+ /* "... followed by the 'sname' field." */
+ if (fOptOverload & RTNET_DHCP_OPTION_OVERLOAD_SNAME)
+ {
+ int status = msg->i_parseOptions(bp->bp_sname, sizeof(bp->bp_sname));
+ if (status != 0) /* NB: this includes "nested" Option Overload */
+ return NULL;
+ }
+#if 0 /* not currently unused */
+ else if (bp->bp_sname[0] != '\0')
+ {
+ /* must be zero terminated, ignore if not */
+ const char *pszSName = (const char *)bp->bp_sname;
+ size_t len = RTStrNLen(pszSName, sizeof(bp->bp_sname));
+ if (len < sizeof(bp->bp_sname))
+ msg->m_sname.assign(pszSName, len);
+ }
+#endif
+
+ msg->m_optMessageType = OptMessageType(*msg);
+ if (!msg->m_optMessageType.present())
+ return NULL;
+
+ msg->m_id = ClientId(msg->m_mac, OptClientId(*msg));
+
+ return msg.release();
+}
+
+
+int DhcpClientMessage::i_parseOptions(const uint8_t *pbBuf, size_t cbBuf) RT_NOEXCEPT
+{
+ int fOptOverload = 0;
+ while (cbBuf > 0)
+ {
+ uint8_t const bOpt = *pbBuf++;
+ --cbBuf;
+
+ if (bOpt == RTNET_DHCP_OPT_PAD)
+ continue;
+
+ if (bOpt == RTNET_DHCP_OPT_END)
+ break;
+
+ if (cbBuf == 0)
+ DHCP_LOG_RET(-1, ("option %d has no length field\n", bOpt));
+
+ uint8_t const cbOpt = *pbBuf++;
+ --cbBuf;
+
+ if (cbOpt > cbBuf)
+ DHCP_LOG_RET(-1, ("option %d truncated (length %d, but only %zu bytes left)\n", bOpt, cbOpt, cbBuf));
+
+#if 0
+ rawopts_t::const_iterator it(m_optmap.find(bOpt));
+ if (it != m_optmap.cend())
+ return -1;
+#endif
+ if (bOpt == RTNET_DHCP_OPT_OPTION_OVERLOAD)
+ {
+ if (cbOpt != 1)
+ DHCP_LOG_RET(-1, ("Overload Option (option %d) has invalid length %d\n", bOpt, cbOpt));
+
+ fOptOverload = *pbBuf;
+
+ if ((fOptOverload & ~RTNET_DHCP_OPTION_OVERLOAD_MASK) != 0)
+ DHCP_LOG_RET(-1, ("Overload Option (option %d) has invalid value 0x%x\n", bOpt, fOptOverload));
+ }
+ else
+ m_rawopts.insert(std::make_pair(bOpt, octets_t(pbBuf, pbBuf + cbOpt)));
+
+ pbBuf += cbOpt;
+ cbBuf -= cbOpt;
+ }
+
+ return fOptOverload;
+}
+
+
+/**
+ * Dumps the message.
+ */
+void DhcpClientMessage::dump() const RT_NOEXCEPT
+{
+ DhcpMessage::dump();
+
+ if (OptRapidCommit(*this).present())
+ LogRel((" (rapid commit)"));
+
+ try
+ {
+ const OptServerId sid(*this);
+ if (sid.present())
+ LogRel((" for server %RTnaipv4", sid.value().u));
+
+ const OptClientId cid(*this);
+ if (cid.present())
+ {
+ if (cid.value().size() > 0)
+ LogRel((" client id: %.*Rhxs\n", cid.value().size(), &cid.value().front()));
+ else
+ LogRel((" client id: <empty>\n"));
+ }
+
+ const OptRequestedAddress reqAddr(*this);
+ if (reqAddr.present())
+ LogRel((" requested address %RTnaipv4", reqAddr.value().u));
+ const OptLeaseTime reqLeaseTime(*this);
+ if (reqLeaseTime.present())
+ LogRel((" requested lease time %d", reqAddr.value()));
+ if (reqAddr.present() || reqLeaseTime.present())
+ LogRel(("\n"));
+
+ const OptParameterRequest params(*this);
+ if (params.present())
+ {
+ LogRel((" params {"));
+ typedef OptParameterRequest::value_t::const_iterator it_t;
+ for (it_t it = params.value().begin(); it != params.value().end(); ++it)
+ LogRel((" %d", *it));
+ LogRel((" }\n"));
+ }
+ }
+ catch (std::bad_alloc &)
+ {
+ LogRel(("bad_alloc during dumping\n"));
+ }
+
+ for (rawopts_t::const_iterator it = m_rawopts.begin(); it != m_rawopts.end(); ++it)
+ {
+ const uint8_t optcode = (*it).first;
+ switch (optcode)
+ {
+ case OptMessageType::optcode: /* FALLTHROUGH */
+ case OptClientId::optcode: /* FALLTHROUGH */
+ case OptRequestedAddress::optcode: /* FALLTHROUGH */
+ case OptLeaseTime::optcode: /* FALLTHROUGH */
+ case OptParameterRequest::optcode: /* FALLTHROUGH */
+ case OptRapidCommit::optcode:
+ break;
+
+ default:
+ {
+ size_t const cbBytes = it->second.size();
+ uint8_t const *pbBytes = &it->second.front();
+ bool fAllPrintable = true;
+ for (size_t off = 0; off < cbBytes; off++)
+ if (!RT_C_IS_PRINT((char )pbBytes[off]))
+ {
+ fAllPrintable = false;
+ break;
+ }
+ if (fAllPrintable)
+ LogRel((" %2d: '%.*s'\n", optcode, cbBytes, pbBytes));
+ else
+ LogRel((" %2d: %.*Rhxs\n", optcode, cbBytes, pbBytes));
+ }
+ }
+ }
+}
+
+
+
+/*********************************************************************************************************************************
+* DhcpServerMessage Implementation *
+*********************************************************************************************************************************/
+
+DhcpServerMessage::DhcpServerMessage(const DhcpClientMessage &req, uint8_t messageTypeParam, RTNETADDRIPV4 serverAddr)
+ : DhcpMessage()
+ , m_optServerId(serverAddr)
+{
+ m_dst.u = 0xffffffff; /* broadcast */
+
+ m_optMessageType = OptMessageType(messageTypeParam);
+
+ /* copy values from the request (cf. RFC2131 Table 3) */
+ m_xid = req.xid();
+ m_flags = req.flags();
+ m_giaddr = req.giaddr();
+ m_mac = req.mac();
+
+ if (req.messageType() == RTNET_DHCP_MT_REQUEST)
+ m_ciaddr = req.ciaddr();
+}
+
+
+void DhcpServerMessage::maybeUnicast(const DhcpClientMessage &req) RT_NOEXCEPT
+{
+ if (!req.broadcast() && req.ciaddr().u != 0)
+ setDst(req.ciaddr());
+}
+
+
+/**
+ * @throws std::bad_alloc
+ */
+void DhcpServerMessage::addOption(DhcpOption *opt)
+{
+ m_optmap << opt;
+}
+
+
+/**
+ * @throws std::bad_alloc
+ */
+void DhcpServerMessage::addOptions(const optmap_t &optmap)
+{
+ for (optmap_t::const_iterator it = optmap.begin(); it != optmap.end(); ++it)
+ m_optmap << it->second;
+}
+
+
+/**
+ * @throws std::bad_alloc
+ */
+int DhcpServerMessage::encode(octets_t &data)
+{
+ /*
+ * Header, including DHCP cookie.
+ */
+ RTNETBOOTP bp;
+ RT_ZERO(bp);
+
+ bp.bp_op = RTNETBOOTP_OP_REPLY;
+ bp.bp_htype = RTNET_ARP_ETHER;
+ bp.bp_hlen = sizeof(RTMAC);
+
+ bp.bp_xid = m_xid;
+
+ bp.bp_ciaddr = m_ciaddr;
+ bp.bp_yiaddr = m_yiaddr;
+ bp.bp_siaddr = m_siaddr;
+ bp.bp_giaddr = m_giaddr;
+
+ bp.bp_chaddr.Mac = m_mac;
+
+ bp.bp_vend.Dhcp.dhcp_cookie = RT_H2N_U32_C(RTNET_DHCP_COOKIE);
+
+ data.insert(data.end(), (uint8_t *)&bp, (uint8_t *)&bp.bp_vend.Dhcp.dhcp_opts);
+
+ /** @todo TFTP, bootfile name, etc. pick from extended options if no
+ * override in effect? */
+
+ /*
+ * Options
+ */
+ data << m_optServerId
+ << m_optMessageType;
+
+ for (optmap_t::const_iterator it = m_optmap.begin(); it != m_optmap.end(); ++it)
+ {
+ LogRel3(("encoding option %d (%s)\n", it->first, DhcpOption::name(it->first)));
+ DhcpOption &opt = *it->second;
+ data << opt;
+ }
+
+ data << OptEnd();
+
+ AssertCompile(RTNET_DHCP_NORMAL_SIZE == 548);
+ if (data.size() < RTNET_DHCP_NORMAL_SIZE)
+ data.resize(RTNET_DHCP_NORMAL_SIZE);
+
+ /** @todo dump it */
+ if ((LogRelIs4Enabled() && LogRelIsEnabled()) || LogIsEnabled())
+ dump();
+ if ((LogRelIs5Enabled() && LogRelIsEnabled()) || LogIs5Enabled())
+ LogRel5(("encoded message: %u bytes\n%.*Rhxd\n", data.size(), data.size(), &data.front()));
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Dumps a server message to the log.
+ */
+void DhcpServerMessage::dump() const RT_NOEXCEPT
+{
+ DhcpMessage::dump();
+
+ /** @todo dump option details. */
+}
+