summaryrefslogtreecommitdiffstats
path: root/src/VBox/NetworkServices/Dhcpd/DhcpOptions.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/NetworkServices/Dhcpd/DhcpOptions.h')
-rw-r--r--src/VBox/NetworkServices/Dhcpd/DhcpOptions.h642
1 files changed, 642 insertions, 0 deletions
diff --git a/src/VBox/NetworkServices/Dhcpd/DhcpOptions.h b/src/VBox/NetworkServices/Dhcpd/DhcpOptions.h
new file mode 100644
index 00000000..b16782a4
--- /dev/null
+++ b/src/VBox/NetworkServices/Dhcpd/DhcpOptions.h
@@ -0,0 +1,642 @@
+/* $Id: DhcpOptions.h $ */
+/** @file
+ * DHCP server - DHCP options
+ */
+
+/*
+ * Copyright (C) 2017-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#ifndef VBOX_INCLUDED_SRC_Dhcpd_DhcpOptions_h
+#define VBOX_INCLUDED_SRC_Dhcpd_DhcpOptions_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "Defs.h"
+
+#include <string.h>
+
+#include <iprt/err.h>
+#include <iprt/types.h>
+#include <iprt/asm.h>
+#include <iprt/stdint.h>
+#include <iprt/net.h>
+
+#include <string>
+
+class DhcpClientMessage;
+
+
+class DhcpOption
+{
+ protected:
+ uint8_t m_OptCode;
+ bool m_fPresent;
+
+ public:
+ explicit DhcpOption(uint8_t aOptCode)
+ : m_OptCode(aOptCode), m_fPresent(true) {}
+
+ DhcpOption(uint8_t aOptCode, bool fPresent)
+ : m_OptCode(aOptCode), m_fPresent(fPresent) {}
+
+ virtual DhcpOption *clone() const = 0;
+
+ virtual ~DhcpOption() {}
+
+ public:
+ static DhcpOption *parse(uint8_t aOptCode, int aEnc, const char *pcszValue);
+
+ public:
+ uint8_t optcode() const { return m_OptCode; }
+ bool present() const { return m_fPresent; }
+
+ public:
+ int encode(octets_t &dst) const;
+
+ int decode(const rawopts_t &map);
+ int decode(const DhcpClientMessage &req);
+
+ protected:
+ virtual ssize_t encodeValue(octets_t &dst) const = 0;
+ virtual int decodeValue(const octets_t &src, size_t cb) = 0;
+
+ protected:
+ static const octets_t *findOption(const rawopts_t &aOptMap, uint8_t aOptCode);
+
+ protected:
+ /*
+ * Serialization
+ */
+ static void append(octets_t &aDst, uint8_t aValue)
+ {
+ aDst.push_back(aValue);
+ }
+
+ static void append(octets_t &aDst, uint16_t aValue)
+ {
+ RTUINT16U u16 = { RT_H2N_U16(aValue) };
+ aDst.insert(aDst.end(), u16.au8, u16.au8 + sizeof(aValue));
+ }
+
+ static void append(octets_t &aDst, uint32_t aValue)
+ {
+ RTUINT32U u32 = { RT_H2N_U32(aValue) };
+ aDst.insert(aDst.end(), u32.au8, u32.au8 + sizeof(aValue));
+ }
+
+ static void append(octets_t &aDst, RTNETADDRIPV4 aIPv4)
+ {
+ aDst.insert(aDst.end(), aIPv4.au8, aIPv4.au8 + sizeof(aIPv4));
+ }
+
+ static void append(octets_t &aDst, const char *pszString, size_t cb)
+ {
+ aDst.insert(aDst.end(), pszString, pszString + cb);
+ }
+
+ static void append(octets_t &aDst, const std::string &str)
+ {
+ append(aDst, str.c_str(), str.size());
+ }
+
+ /* non-overloaded name to avoid ambiguity */
+ static void appendLength(octets_t &aDst, size_t cb)
+ {
+ append(aDst, static_cast<uint8_t>(cb));
+ }
+
+
+ /*
+ * Deserialization
+ */
+ static void extract(uint8_t &aValue, octets_t::const_iterator &pos)
+ {
+ aValue = *pos;
+ pos += sizeof(uint8_t);
+ }
+
+ static void extract(uint16_t &aValue, octets_t::const_iterator &pos)
+ {
+ RTUINT16U u16;
+ memcpy(u16.au8, &pos[0], sizeof(uint16_t));
+ aValue = RT_N2H_U16(u16.u);
+ pos += sizeof(uint16_t);
+ }
+
+ static void extract(uint32_t &aValue, octets_t::const_iterator &pos)
+ {
+ RTUINT32U u32;
+ memcpy(u32.au8, &pos[0], sizeof(uint32_t));
+ aValue = RT_N2H_U32(u32.u);
+ pos += sizeof(uint32_t);
+ }
+
+ static void extract(RTNETADDRIPV4 &aValue, octets_t::const_iterator &pos)
+ {
+ memcpy(aValue.au8, &pos[0], sizeof(RTNETADDRIPV4));
+ pos += sizeof(RTNETADDRIPV4);
+ }
+
+ static void extract(std::string &aString, octets_t::const_iterator &pos, size_t cb)
+ {
+ aString.replace(aString.begin(), aString.end(), &pos[0], &pos[cb]);
+ pos += cb;
+ }
+
+
+ /*
+ * Parse textual representation (e.g. in config file)
+ */
+ static int parse1(uint8_t &aValue, const char *pcszValue);
+ static int parse1(uint16_t &aValue, const char *pcszValue);
+ static int parse1(uint32_t &aValue, const char *pcszValue);
+ static int parse1(RTNETADDRIPV4 &aValue, const char *pcszValue);
+
+ static int parseList(std::vector<RTNETADDRIPV4> &aList, const char *pcszValue);
+
+ static int parseHex(octets_t &aRawValue, const char *pcszValue);
+};
+
+
+inline octets_t &operator<<(octets_t &dst, const DhcpOption &option)
+{
+ option.encode(dst);
+ return dst;
+}
+
+
+optmap_t &operator<<(optmap_t &optmap, DhcpOption *option);
+optmap_t &operator<<(optmap_t &optmap, const std::shared_ptr<DhcpOption> &option);
+
+
+
+/*
+ * Only for << OptEnd() syntactic sugar...
+ */
+struct OptEnd {};
+inline octets_t &operator<<(octets_t &dst, const OptEnd &end)
+{
+ RT_NOREF(end);
+
+ dst.push_back(RTNET_DHCP_OPT_END);
+ return dst;
+}
+
+
+
+/*
+ * Option that has no value
+ */
+class OptNoValueBase
+ : public DhcpOption
+{
+ public:
+ explicit OptNoValueBase(uint8_t aOptCode)
+ : DhcpOption(aOptCode, false) {}
+
+ OptNoValueBase(uint8_t aOptCode, bool fPresent)
+ : DhcpOption(aOptCode, fPresent) {}
+
+ OptNoValueBase(uint8_t aOptCode, const DhcpClientMessage &req)
+ : DhcpOption(aOptCode, false)
+ {
+ decode(req);
+ }
+
+ virtual OptNoValueBase *clone() const
+ {
+ return new OptNoValueBase(*this);
+ }
+
+ protected:
+ virtual ssize_t encodeValue(octets_t &dst) const
+ {
+ RT_NOREF(dst);
+ return 0;
+ }
+
+ public:
+ static bool isLengthValid(size_t cb)
+ {
+ return cb == 0;
+ }
+
+ virtual int decodeValue(const octets_t &src, size_t cb)
+ {
+ RT_NOREF(src);
+
+ if (!isLengthValid(cb))
+ return VERR_INVALID_PARAMETER;
+
+ m_fPresent = true;
+ return VINF_SUCCESS;
+ }
+};
+
+template <uint8_t _OptCode>
+class OptNoValue
+ : public OptNoValueBase
+{
+ public:
+ static const uint8_t optcode = _OptCode;
+
+ OptNoValue()
+ : OptNoValueBase(optcode) {}
+
+ explicit OptNoValue(bool fPresent) /* there's no overloaded ctor with value */
+ : OptNoValueBase(optcode, fPresent) {}
+
+ explicit OptNoValue(const DhcpClientMessage &req)
+ : OptNoValueBase(optcode, req) {}
+};
+
+
+
+/*
+ * Option that contains single value of fixed-size type T
+ */
+template <typename T>
+class OptValueBase
+ : public DhcpOption
+{
+ public:
+ typedef T value_t;
+
+ protected:
+ T m_Value;
+
+ explicit OptValueBase(uint8_t aOptCode)
+ : DhcpOption(aOptCode, false), m_Value() {}
+
+ OptValueBase(uint8_t aOptCode, const T &aOptValue)
+ : DhcpOption(aOptCode), m_Value(aOptValue) {}
+
+ OptValueBase(uint8_t aOptCode, const DhcpClientMessage &req)
+ : DhcpOption(aOptCode, false), m_Value()
+ {
+ decode(req);
+ }
+
+ public:
+ virtual OptValueBase *clone() const
+ {
+ return new OptValueBase(*this);
+ }
+
+ public:
+ T &value() { return m_Value; }
+ const T &value() const { return m_Value; }
+
+ protected:
+ virtual ssize_t encodeValue(octets_t &dst) const
+ {
+ append(dst, m_Value);
+ return sizeof(T);
+ }
+
+ public:
+ static bool isLengthValid(size_t cb)
+ {
+ return cb == sizeof(T);
+ }
+
+ virtual int decodeValue(const octets_t &src, size_t cb)
+ {
+ if (!isLengthValid(cb))
+ return VERR_INVALID_PARAMETER;
+
+ octets_t::const_iterator pos(src.begin());
+ extract(m_Value, pos);
+
+ m_fPresent = true;
+ return VINF_SUCCESS;
+ }
+};
+
+template<uint8_t _OptCode, typename T>
+class OptValue
+ : public OptValueBase<T>
+{
+ public:
+ using typename OptValueBase<T>::value_t;
+
+ public:
+ static const uint8_t optcode = _OptCode;
+
+ OptValue()
+ : OptValueBase<T>(optcode) {}
+
+ explicit OptValue(const T &aOptValue)
+ : OptValueBase<T>(optcode, aOptValue) {}
+
+ explicit OptValue(const DhcpClientMessage &req)
+ : OptValueBase<T>(optcode, req) {}
+
+ static OptValue *parse(const char *pcszValue)
+ {
+ typename OptValueBase<T>::value_t v;
+ int rc = DhcpOption::parse1(v, pcszValue);
+ if (RT_FAILURE(rc))
+ return NULL;
+ return new OptValue(v);
+ }
+};
+
+
+
+/*
+ * Option that contains a string.
+ */
+class OptStringBase
+ : public DhcpOption
+{
+ public:
+ typedef std::string value_t;
+
+ protected:
+ std::string m_String;
+
+ explicit OptStringBase(uint8_t aOptCode)
+ : DhcpOption(aOptCode, false), m_String() {}
+
+ OptStringBase(uint8_t aOptCode, const std::string &aOptString)
+ : DhcpOption(aOptCode), m_String(aOptString) {}
+
+ OptStringBase(uint8_t aOptCode, const DhcpClientMessage &req)
+ : DhcpOption(aOptCode, false), m_String()
+ {
+ decode(req);
+ }
+
+ public:
+ virtual OptStringBase *clone() const
+ {
+ return new OptStringBase(*this);
+ }
+
+ public:
+ std::string &value() { return m_String; }
+ const std::string &value() const { return m_String; }
+
+ protected:
+ virtual ssize_t encodeValue(octets_t &dst) const
+ {
+ if (!isLengthValid(m_String.size()))
+ return -1;
+
+ append(dst, m_String);
+ return m_String.size();
+ }
+
+ public:
+ static bool isLengthValid(size_t cb)
+ {
+ return cb <= UINT8_MAX;
+ }
+
+ virtual int decodeValue(const octets_t &src, size_t cb)
+ {
+ if (!isLengthValid(cb))
+ return VERR_INVALID_PARAMETER;
+
+ octets_t::const_iterator pos(src.begin());
+ extract(m_String, pos, cb);
+ m_fPresent = true;
+ return VINF_SUCCESS;
+ }
+};
+
+template<uint8_t _OptCode>
+class OptString
+ : public OptStringBase
+{
+ public:
+ static const uint8_t optcode = _OptCode;
+
+ OptString()
+ : OptStringBase(optcode) {}
+
+ explicit OptString(const std::string &aOptString)
+ : OptStringBase(optcode, aOptString) {}
+
+ explicit OptString(const DhcpClientMessage &req)
+ : OptStringBase(optcode, req) {}
+
+ static OptString *parse(const char *pcszValue)
+ {
+ return new OptString(pcszValue);
+ }
+};
+
+
+
+/*
+ * Option that contains a list of values of type T
+ */
+template <typename T>
+class OptListBase
+ : public DhcpOption
+{
+ public:
+ typedef std::vector<T> value_t;
+
+ protected:
+ std::vector<T> m_List;
+
+ explicit OptListBase(uint8_t aOptCode)
+ : DhcpOption(aOptCode, false), m_List() {}
+
+ OptListBase(uint8_t aOptCode, const T &aOptSingle)
+ : DhcpOption(aOptCode), m_List(1, aOptSingle) {}
+
+ OptListBase(uint8_t aOptCode, const std::vector<T> &aOptList)
+ : DhcpOption(aOptCode), m_List(aOptList) {}
+
+ OptListBase(uint8_t aOptCode, const DhcpClientMessage &req)
+ : DhcpOption(aOptCode, false), m_List()
+ {
+ decode(req);
+ }
+
+ public:
+ virtual OptListBase *clone() const
+ {
+ return new OptListBase(*this);
+ }
+
+ public:
+ std::vector<T> &value() { return m_List; }
+ const std::vector<T> &value() const { return m_List; }
+
+ protected:
+ virtual ssize_t encodeValue(octets_t &dst) const
+ {
+ const size_t cbItem = sizeof(T);
+ size_t cbValue = 0;
+
+ for (size_t i = 0; i < m_List.size(); ++i)
+ {
+ if (cbValue + cbItem > UINT8_MAX)
+ break;
+
+ append(dst, m_List[i]);
+ cbValue += cbItem;
+ }
+
+ return cbValue;
+ }
+
+ public:
+ static bool isLengthValid(size_t cb)
+ {
+ return cb % sizeof(T) == 0;
+ }
+
+ virtual int decodeValue(const octets_t &src, size_t cb)
+ {
+ if (!isLengthValid(cb))
+ return VERR_INVALID_PARAMETER;
+
+ m_List.erase(m_List.begin(), m_List.end());
+
+ octets_t::const_iterator pos(src.begin());
+ for (size_t i = 0; i < cb / sizeof(T); ++i)
+ {
+ T item;
+ extract(item, pos);
+ m_List.push_back(item);
+ }
+ m_fPresent = true;
+ return VINF_SUCCESS;
+ }
+};
+
+template<uint8_t _OptCode, typename T>
+class OptList
+ : public OptListBase<T>
+
+{
+ public:
+ using typename OptListBase<T>::value_t;
+
+ public:
+ static const uint8_t optcode = _OptCode;
+
+ OptList()
+ : OptListBase<T>(optcode) {}
+
+ explicit OptList(const T &aOptSingle)
+ : OptListBase<T>(optcode, aOptSingle) {}
+
+ explicit OptList(const std::vector<T> &aOptList)
+ : OptListBase<T>(optcode, aOptList) {}
+
+ explicit OptList(const DhcpClientMessage &req)
+ : OptListBase<T>(optcode, req) {}
+
+ static OptList *parse(const char *pcszValue)
+ {
+ typename OptListBase<T>::value_t v;
+ int rc = DhcpOption::parseList(v, pcszValue);
+ if (RT_FAILURE(rc) || v.empty())
+ return NULL;
+ return new OptList(v);
+ }
+};
+
+
+/*
+ * Options specified by raw binary data that we don't know how to
+ * interpret.
+ */
+class RawOption
+ : public DhcpOption
+{
+ protected:
+ octets_t m_Data;
+
+ public:
+ explicit RawOption(uint8_t aOptCode)
+ : DhcpOption(aOptCode, false), m_Data() {}
+
+ RawOption(uint8_t aOptCode, const octets_t &aSrc)
+ : DhcpOption(aOptCode), m_Data(aSrc) {}
+
+ public:
+ virtual RawOption *clone() const
+ {
+ return new RawOption(*this);
+ }
+
+
+ protected:
+ virtual ssize_t encodeValue(octets_t &dst) const
+ {
+ dst.insert(dst.end(), m_Data.begin(), m_Data.end());
+ return m_Data.size();
+ }
+
+ virtual int decodeValue(const octets_t &src, size_t cb)
+ {
+ octets_t::const_iterator beg(src.begin());
+ octets_t data(beg, beg + cb);
+ m_Data.swap(data);
+
+ m_fPresent = true;
+ return VINF_SUCCESS;
+ }
+
+ public:
+ static RawOption *parse(uint8_t aOptCode, const char *pcszValue)
+ {
+ octets_t data;
+ int rc = DhcpOption::parseHex(data, pcszValue);
+ if (RT_FAILURE(rc))
+ return NULL;
+ return new RawOption(aOptCode, data);
+ }
+};
+
+
+
+/*
+ * Define the DHCP options we want to use.
+ */
+typedef OptValue<1, RTNETADDRIPV4> OptSubnetMask;
+typedef OptValue<2, uint32_t> OptTimeOffset;
+typedef OptList<3, RTNETADDRIPV4> OptRouter;
+typedef OptList<4, RTNETADDRIPV4> OptTimeServer;
+typedef OptList<6, RTNETADDRIPV4> OptDNS;
+typedef OptString<12> OptHostName;
+typedef OptString<15> OptDomainName;
+typedef OptString<17> OptRootPath;
+
+/* DHCP related options */
+typedef OptList<43, uint8_t> OptVendorSpecificInfo;
+typedef OptValue<50, RTNETADDRIPV4> OptRequestedAddress;
+typedef OptValue<51, uint32_t> OptLeaseTime;
+/* 52 - option overload is syntactic and handled internally */
+typedef OptValue<53, uint8_t> OptMessageType;
+typedef OptValue<54, RTNETADDRIPV4> OptServerId;
+typedef OptList<55, uint8_t> OptParameterRequest;
+typedef OptString<56> OptMessage;
+typedef OptValue<57, uint16_t> OptMaxDHCPMessageSize;
+typedef OptValue<58, uint32_t> OptRenewalTime;
+typedef OptValue<59, uint32_t> OptRebindingTime;
+typedef OptList<60, uint8_t> OptVendorClassId;
+typedef OptList<61, uint8_t> OptClientId;
+typedef OptString<66> OptTFTPServer; /* when overloaded */
+typedef OptString<67> OptBootFileName; /* when overloaded */
+typedef OptNoValue<80> OptRapidCommit; /* RFC4039 */
+
+#endif /* !VBOX_INCLUDED_SRC_Dhcpd_DhcpOptions_h */