diff options
Diffstat (limited to 'src/VBox/NetworkServices/Dhcpd/DhcpOptions.h')
-rw-r--r-- | src/VBox/NetworkServices/Dhcpd/DhcpOptions.h | 832 |
1 files changed, 832 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..5f93c0fb --- /dev/null +++ b/src/VBox/NetworkServices/Dhcpd/DhcpOptions.h @@ -0,0 +1,832 @@ +/* $Id: DhcpOptions.h $ */ +/** @file + * DHCP server - DHCP options + */ + +/* + * Copyright (C) 2017-2022 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 + */ + +#ifndef VBOX_INCLUDED_SRC_Dhcpd_DhcpOptions_h +#define VBOX_INCLUDED_SRC_Dhcpd_DhcpOptions_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include "DhcpdInternal.h" + +#include <iprt/asm.h> +#include <iprt/err.h> +#include <iprt/net.h> +#include <iprt/string.h> +#include <iprt/cpp/ministring.h> + + +class DhcpClientMessage; + +typedef struct DhcpIpv4AddrAndMask +{ + RTNETADDRIPV4 Ipv4; + RTNETADDRIPV4 Mask; +} DhcpIpv4AddrAndMask; + + +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, int *prc = NULL); + static const char *name(uint8_t bOptcode); + +public: + uint8_t optcode() const RT_NOEXCEPT { return m_OptCode; } + bool present() const RT_NOEXCEPT { 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: + /** @name Serialization + * @{ */ + static void append(octets_t &aDst, bool aValue) + { + uint8_t b = aValue ? 1 : 0; + aDst.push_back(b); + } + + 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, DhcpIpv4AddrAndMask aIPv4) + { + aDst.insert(aDst.end(), (uint8_t *)&aIPv4, (uint8_t *)&aIPv4 + 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 RTCString &str) + { + append(aDst, str.c_str(), str.length()); + } + + /* non-overloaded name to avoid ambiguity */ + static void appendLength(octets_t &aDst, size_t cb) + { + append(aDst, static_cast<uint8_t>(cb)); + } + + /** @} */ + + + /** @name Deserialization + * @{ */ + static void extract(bool &aValue, octets_t::const_iterator &pos) + { + aValue = *pos != 0; + pos += sizeof(uint8_t); + } + + 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(DhcpIpv4AddrAndMask &aValue, octets_t::const_iterator &pos) + { + memcpy(&aValue, &pos[0], sizeof(aValue)); + pos += sizeof(aValue); + } + +#if 0 /** @todo fix me */ + static void extract(RTCString &aString, octets_t::const_iterator &pos, size_t cb) + { + aString.replace(aString.begin(), aString.end(), &pos[0], &pos[cb]); + pos += cb; + } +#endif + + /** @} */ + + /** @name Parse textual representation (e.g. in config file) + * @{ */ + static int parse1(bool &aValue, const char *pcszValue); + 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 parse1(DhcpIpv4AddrAndMask &aValue, const char *pcszValue); + + template <typename a_Type> static int parseList(std::vector<a_Type> &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; +} + + +#ifndef IN_VBOXSVC +optmap_t &operator<<(optmap_t &optmap, DhcpOption *option); +optmap_t &operator<<(optmap_t &optmap, const std::shared_ptr<DhcpOption> &option); +#endif + + + +/** + * 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, int *prc) + { + typename OptValueBase<T>::value_t v; + int rc = DhcpOption::parse1(v, pcszValue); + *prc = rc; + if (RT_SUCCESS(rc)) + return new OptValue(v); + return NULL; + } +}; + + + +/** + * Option that contains a string. + */ +class OptStringBase + : public DhcpOption +{ +public: + typedef RTCString value_t; + +protected: + RTCString m_String; + + explicit OptStringBase(uint8_t aOptCode) + : DhcpOption(aOptCode, false), m_String() + {} + + OptStringBase(uint8_t aOptCode, const RTCString &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: + RTCString &value() { return m_String; } + const RTCString &value() const { return m_String; } + +protected: + virtual ssize_t encodeValue(octets_t &dst) const + { + if (!isLengthValid(m_String.length())) + return -1; + + append(dst, m_String); + return (ssize_t)m_String.length(); + } + +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; + + int rc = m_String.assignNoThrow((char *)&src.front(), cb); /** @todo encoding. */ + m_fPresent = true; + return rc; + } +}; + +template<uint8_t _OptCode> +class OptString + : public OptStringBase +{ +public: + static const uint8_t optcode = _OptCode; + + OptString() + : OptStringBase(optcode) + {} + + explicit OptString(const RTCString &aOptString) + : OptStringBase(optcode, aOptString) + {} + + explicit OptString(const DhcpClientMessage &req) + : OptStringBase(optcode, req) + {} + + static OptString *parse(const char *pcszValue, int *prc) + { + *prc = VINF_SUCCESS; + 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 (ssize_t)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, int *prc) + { + typename OptListBase<T>::value_t v; + int rc = DhcpOption::parseList<T>(v, pcszValue); + if (RT_SUCCESS(rc)) + { + if (!v.empty()) + { + *prc = rc; + return new OptList(v); + } + rc = VERR_NO_DATA; + } + *prc = rc; + return NULL; + } +}; + + +template<uint8_t _OptCode, typename T> +class OptPairList + : public OptListBase<T> + +{ +public: + using typename OptListBase<T>::value_t; + +public: + static const uint8_t optcode = _OptCode; + + OptPairList() + : OptListBase<T>(optcode) + {} + + explicit OptPairList(const T &aOptSingle) + : OptListBase<T>(optcode, aOptSingle) + {} + + explicit OptPairList(const std::vector<T> &aOptList) + : OptListBase<T>(optcode, aOptList) + {} + + explicit OptPairList(const DhcpClientMessage &req) + : OptListBase<T>(optcode, req) + {} + + static OptPairList *parse(const char *pcszValue, int *prc) + { + typename OptListBase<T>::value_t v; + int rc = DhcpOption::parseList<T>(v, pcszValue); + if (RT_SUCCESS(rc)) + { + if (!v.empty()) + { + if ((v.size() & 1) == 0) + { + *prc = rc; + return new OptPairList(v); + } + rc = VERR_UNEVEN_INPUT; + } + else + rc = VERR_NO_DATA; + } + *prc = rc; + return NULL; + } +}; + + +/* + * 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 (ssize_t)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 + (ssize_t)cb); + m_Data.swap(data); + + m_fPresent = true; + return VINF_SUCCESS; + } + +public: + static RawOption *parse(uint8_t aOptCode, const char *pcszValue, int *prc) + { + octets_t data; + int rc = DhcpOption::parseHex(data, pcszValue); + *prc = rc; + if (RT_SUCCESS(rc)) + return new RawOption(aOptCode, data); + return NULL; + } +}; + + + +/** @name The DHCP options types. + * @{ + */ +typedef OptValue<1, RTNETADDRIPV4> OptSubnetMask; +typedef OptValue<2, uint32_t> OptTimeOffset; +typedef OptList<3, RTNETADDRIPV4> OptRouters; +typedef OptList<4, RTNETADDRIPV4> OptTimeServers; +typedef OptList<5, RTNETADDRIPV4> OptNameServers; +typedef OptList<6, RTNETADDRIPV4> OptDNSes; +typedef OptList<7, RTNETADDRIPV4> OptLogServers; +typedef OptList<8, RTNETADDRIPV4> OptCookieServers; +typedef OptList<9, RTNETADDRIPV4> OptLPRServers; +typedef OptList<10, RTNETADDRIPV4> OptImpressServers; +typedef OptList<11, RTNETADDRIPV4> OptResourceLocationServers; +typedef OptString<12> OptHostName; +typedef OptValue<13, uint16_t> OptBootFileSize; +typedef OptString<14> OptMeritDumpFile; +typedef OptString<15> OptDomainName; +typedef OptValue<16, RTNETADDRIPV4> OptSwapServer; +typedef OptString<17> OptRootPath; +typedef OptString<18> OptExtensionPath; +typedef OptValue<19, bool> OptIPForwarding; +typedef OptValue<20, bool> OptNonLocalSourceRouting; +typedef OptList<21, DhcpIpv4AddrAndMask> OptPolicyFilter; +typedef OptValue<22, uint16_t> OptMaxDgramReassemblySize; +typedef OptValue<23, uint16_t> OptDefaultIPTTL; +typedef OptValue<24, uint32_t> OptPathMTUAgingTimeout; +typedef OptList<25, uint16_t> OptPathMTUPlateauTable; +typedef OptValue<26, uint16_t> OptInterfaceMTU; +typedef OptValue<27, bool> OptAllSubnetsAreLocal; +typedef OptValue<28, RTNETADDRIPV4> OptBroadcastAddress; +typedef OptValue<29, bool> OptPerformMaskDiscovery; +typedef OptValue<30, bool> OptMaskSupplier; +typedef OptValue<31, bool> OptPerformRouterDiscovery; +typedef OptValue<32, RTNETADDRIPV4> OptRouterSolicitationAddress; +typedef OptPairList<33, RTNETADDRIPV4> OptStaticRoute; +typedef OptValue<34, bool> OptTrailerEncapsulation; +typedef OptValue<35, uint32_t> OptARPCacheTimeout; +typedef OptValue<36, bool> OptEthernetEncapsulation; +typedef OptValue<37, uint8_t> OptTCPDefaultTTL; +typedef OptValue<38, uint32_t> OptTCPKeepaliveInterval; +typedef OptValue<39, bool> OptTCPKeepaliveGarbage; +typedef OptString<40> OptNISDomain; +typedef OptList<41, RTNETADDRIPV4> OptNISServers; +typedef OptList<42, RTNETADDRIPV4> OptNTPServers; +/* DHCP related options: */ +typedef OptList<43, uint8_t> OptVendorSpecificInfo; +typedef OptList<44, RTNETADDRIPV4> OptNetBIOSNameServers; +typedef OptList<45, RTNETADDRIPV4> OptNetBIOSDatagramServers; +typedef OptValue<46, uint8_t> OptNetBIOSNodeType; +typedef OptList<47, uint8_t> OptNetBIOSScope; /**< uint8_t or string? */ +typedef OptList<48, RTNETADDRIPV4> OptXWindowsFontServers; +typedef OptList<49, RTNETADDRIPV4> OptXWindowsDisplayManager; +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<62> OptNetWareIPDomainName; /**< RFC2242 */ +typedef OptList<63, uint8_t> OptNetWareIPInformation; /**< complicated, so just byte list for now. RFC2242 */ +typedef OptString<64> OptNISPlusDomain; +typedef OptString<65> OptNISPlusServers; +typedef OptString<66> OptTFTPServerName; /**< when overloaded */ +typedef OptString<67> OptBootfileName; /**< when overloaded */ +typedef OptList<68, RTNETADDRIPV4> OptMobileIPHomeAgents; +typedef OptList<69, RTNETADDRIPV4> OptSMTPServers; +typedef OptList<70, RTNETADDRIPV4> OptPOP3Servers; +typedef OptList<71, RTNETADDRIPV4> OptNNTPServers; +typedef OptList<72, RTNETADDRIPV4> OptWWWServers; +typedef OptList<73, RTNETADDRIPV4> OptFingerServers; +typedef OptList<74, RTNETADDRIPV4> OptIRCServers; +typedef OptList<75, RTNETADDRIPV4> OptStreetTalkServers; +typedef OptList<76, RTNETADDRIPV4> OptSTDAServers; +typedef OptList<77, uint8_t> OptUserClassId; +typedef OptList<78, uint8_t> OptSLPDirectoryAgent; /**< complicated, so just byte list for now. RFC2610 */ +typedef OptList<79, uint8_t> OptSLPServiceScope; /**< complicated, so just byte list for now. RFC2610 */ +typedef OptNoValue<80> OptRapidCommit; /**< RFC4039 */ +typedef OptList<119, uint8_t> OptDomainSearch; /**< RFC3397 */ +/** @} */ + +#endif /* !VBOX_INCLUDED_SRC_Dhcpd_DhcpOptions_h */ |