/* $Id: DhcpOptions.cpp $ */ /** @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. */ #include "DhcpOptions.h" #include "DhcpMessage.h" optmap_t &operator<<(optmap_t &optmap, DhcpOption *option) { if (option == NULL) return optmap; if (option->present()) optmap[option->optcode()] = std::shared_ptr<DhcpOption>(option); else optmap.erase(option->optcode()); return optmap; } optmap_t &operator<<(optmap_t &optmap, const std::shared_ptr<DhcpOption> &option) { if (!option) return optmap; if (option->present()) optmap[option->optcode()] = option; else optmap.erase(option->optcode()); return optmap; } int DhcpOption::encode(octets_t &dst) const { if (!m_fPresent) return VERR_INVALID_STATE; size_t cbOrig = dst.size(); append(dst, m_OptCode); appendLength(dst, 0); /* placeholder */ ssize_t cbValue = encodeValue(dst); if (cbValue < 0 || UINT8_MAX <= cbValue) { dst.resize(cbOrig); /* undo */ return VERR_INVALID_PARAMETER; } dst[cbOrig+1] = cbValue; return VINF_SUCCESS; } /* static */ const octets_t *DhcpOption::findOption(const rawopts_t &aOptMap, uint8_t aOptCode) { rawopts_t::const_iterator it(aOptMap.find(aOptCode)); if (it == aOptMap.end()) return NULL; return &it->second; } int DhcpOption::decode(const rawopts_t &map) { const octets_t *rawopt = DhcpOption::findOption(map, m_OptCode); if (rawopt == NULL) return VERR_NOT_FOUND; int rc = decodeValue(*rawopt, rawopt->size()); if (RT_FAILURE(rc)) return VERR_INVALID_PARAMETER; return VINF_SUCCESS; } int DhcpOption::decode(const DhcpClientMessage &req) { return decode(req.rawopts()); } int DhcpOption::parse1(uint8_t &aValue, const char *pcszValue) { int rc = RTStrToUInt8Full(RTStrStripL(pcszValue), 10, &aValue); if (rc == VERR_TRAILING_SPACES) rc = VINF_SUCCESS; return rc; } int DhcpOption::parse1(uint16_t &aValue, const char *pcszValue) { int rc = RTStrToUInt16Full(RTStrStripL(pcszValue), 10, &aValue); if (rc == VERR_TRAILING_SPACES) rc = VINF_SUCCESS; return rc; } int DhcpOption::parse1(uint32_t &aValue, const char *pcszValue) { int rc = RTStrToUInt32Full(RTStrStripL(pcszValue), 10, &aValue); if (rc == VERR_TRAILING_SPACES) rc = VINF_SUCCESS; return rc; } int DhcpOption::parse1(RTNETADDRIPV4 &aValue, const char *pcszValue) { return RTNetStrToIPv4Addr(pcszValue, &aValue); } int DhcpOption::parseList(std::vector<RTNETADDRIPV4> &aList, const char *pcszValue) { std::vector<RTNETADDRIPV4> l; int rc; pcszValue = RTStrStripL(pcszValue); do { RTNETADDRIPV4 Addr; char *pszNext; rc = RTNetStrToIPv4AddrEx(pcszValue, &Addr, &pszNext); if (RT_FAILURE(rc)) return VERR_INVALID_PARAMETER; if (rc == VWRN_TRAILING_CHARS) { pcszValue = RTStrStripL(pszNext); if (pcszValue == pszNext) /* garbage after address */ return VERR_INVALID_PARAMETER; } l.push_back(Addr); /* * If we got VINF_SUCCESS or VWRN_TRAILING_SPACES then this * was the last address and we are done. */ } while (rc == VWRN_TRAILING_CHARS); aList.swap(l); return VINF_SUCCESS; } /* * XXX: See DHCPServer::encodeOption() */ int DhcpOption::parseHex(octets_t &aRawValue, const char *pcszValue) { octets_t data; char *pszNext; int rc; if (pcszValue == NULL || *pcszValue == '\0') return VERR_INVALID_PARAMETER; while (*pcszValue != '\0') { if (data.size() > UINT8_MAX) return VERR_INVALID_PARAMETER; uint8_t u8Byte; rc = RTStrToUInt8Ex(pcszValue, &pszNext, 16, &u8Byte); if (!RT_SUCCESS(rc)) return rc; if (*pszNext == ':') ++pszNext; else if (*pszNext != '\0') return VERR_PARSE_ERROR; data.push_back(u8Byte); pcszValue = pszNext; } aRawValue.swap(data); return VINF_SUCCESS; } DhcpOption *DhcpOption::parse(uint8_t aOptCode, int aEnc, const char *pcszValue) { switch (aEnc) { case 0: /* DhcpOptEncoding_Legacy */ switch (aOptCode) { #define HANDLE(_OptClass) \ case _OptClass::optcode: \ return _OptClass::parse(pcszValue); HANDLE(OptSubnetMask); HANDLE(OptRouter); HANDLE(OptDNS); HANDLE(OptHostName); HANDLE(OptDomainName); HANDLE(OptRootPath); HANDLE(OptLeaseTime); HANDLE(OptRenewalTime); HANDLE(OptRebindingTime); #undef HANDLE default: return NULL; } break; case 1: return RawOption::parse(aOptCode, pcszValue); default: return NULL; } }