Kea 3.3.2
char_string.cc
Go to the documentation of this file.
1// Copyright (C) 2012-2025 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
11
12#include <dns/exceptions.h>
13#include <dns/rdata.h>
14#include <dns/master_lexer.h>
15#include <dns/char_string.h>
16#include <util/buffer.h>
17
18#include <boost/lexical_cast.hpp>
19
20#include <cctype>
21#include <cstring>
22#include <vector>
23
24#include <stdint.h>
25
26namespace isc {
27namespace dns {
28namespace rdata {
29namespace generic {
30namespace detail {
31
32namespace {
33// Convert a DDD form to the corresponding integer
34int
35decimalToNumber(const char* s, const char* s_end) {
36 if (s_end - s < 3) {
37 isc_throw(InvalidRdataText, "Escaped digits too short");
38 }
39
40 const std::string num_str(s, s + 3);
41 try {
42 const int i = boost::lexical_cast<int>(num_str);
43 if (i > 255) {
44 isc_throw(InvalidRdataText, "Escaped digits too large: "
45 << num_str);
46 }
47 return (i);
48 } catch (const boost::bad_lexical_cast&) {
50 "Invalid form for escaped digits: " << num_str);
51 }
52}
53}
54
55void
57 CharString& result) {
58 // make a space for the 1-byte length field; filled in at the end
59 result.push_back(0);
60
61 bool escape = false;
62 const char* s = str_region.beg;
63 const char* const s_end = str_region.beg + str_region.len;
64
65 for (size_t n = str_region.len; n != 0; --n, ++s) {
66 int c = (*s & 0xff);
67 if (escape && std::isdigit(c) != 0) {
68 c = decimalToNumber(s, s_end);
69 // decimalToNumber() already throws if (s_end - s) is less
70 // than 3. 'n' is an unsigned type (size_t) and can underflow.
71 // 'n' and 's' are also updated by 1 in the for statement's
72 // expression, so we update them by 2 instead of 3 here.
73 n -= 2;
74 s += 2;
75 } else if (!escape && c == '\\') {
76 escape = true;
77 continue;
78 }
79 escape = false;
80 result.push_back(c);
81 }
82 if (escape) { // terminated by non-escaped '\'
83 isc_throw(InvalidRdataText, "character-string ends with '\\'");
84 }
85 if (result.size() > MAX_CHARSTRING_LEN + 1) { // '+ 1' due to the len field
86 isc_throw(CharStringTooLong, "character-string is too long: " <<
87 (result.size() - 1) << "(+1) characters");
88 }
89 result[0] = result.size() - 1;
90}
91
92void
94 CharStringData& result) {
95 bool escape = false;
96 const char* s = str_region.beg;
97 const char* const s_end = str_region.beg + str_region.len;
98
99 for (size_t n = str_region.len; n != 0; --n, ++s) {
100 int c = (*s & 0xff);
101 if (escape && std::isdigit(c) != 0) {
102 c = decimalToNumber(s, s_end);
103 // decimalToNumber() already throws if (s_end - s) is less
104 // than 3. 'n' is an unsigned type (size_t) and can underflow.
105 // 'n' and 's' are also updated by 1 in the for statement's
106 // expression, so we update them by 2 instead of 3 here.
107 n -= 2;
108 s += 2;
109 } else if (!escape && c == '\\') {
110 escape = true;
111 continue;
112 }
113 escape = false;
114 result.push_back(c);
115 }
116 if (escape) { // terminated by non-escaped '\'
117 isc_throw(InvalidRdataText, "character-string ends with '\\'");
118 }
119}
120
121std::string
122charStringToString(const CharString& char_string) {
123 std::string s;
124 bool first = true;
125 for (auto const& it : char_string) {
126 if (first) {
127 first = false;
128 continue;
129 }
130 const uint8_t ch = it;
131 if ((ch < 0x20) || (ch >= 0x7f)) {
132 // convert to escaped \xxx (decimal) format
133 s.push_back('\\');
134 s.push_back('0' + ((ch / 100) % 10));
135 s.push_back('0' + ((ch / 10) % 10));
136 s.push_back('0' + (ch % 10));
137 continue;
138 }
139 if ((ch == '"') || (ch == ';') || (ch == '\\')) {
140 s.push_back('\\');
141 }
142 s.push_back(ch);
143 }
144
145 return (s);
146}
147
148std::string
150 std::string s;
151 for (auto const& it : char_string) {
152 const uint8_t ch = it;
153 if ((ch < 0x20) || (ch >= 0x7f)) {
154 // convert to escaped \xxx (decimal) format
155 s.push_back('\\');
156 s.push_back('0' + ((ch / 100) % 10));
157 s.push_back('0' + ((ch / 10) % 10));
158 s.push_back('0' + (ch % 10));
159 continue;
160 }
161 if ((ch == '"') || (ch == ';') || (ch == '\\')) {
162 s.push_back('\\');
163 }
164 s.push_back(ch);
165 }
166
167 return (s);
168}
169
171 const detail::CharString& other) {
172 if (self.size() == 0 && other.size() == 0) {
173 return (0);
174 }
175 if (self.size() == 0) {
176 return (-1);
177 }
178 if (other.size() == 0) {
179 return (1);
180 }
181 const size_t self_len = self[0];
182 const size_t other_len = other[0];
183 const size_t cmp_len = std::min(self_len, other_len);
184 isc_throw_assert(self_len == self.size() - 1);
185 isc_throw_assert(other_len == other.size() - 1);
186 if (cmp_len == 0) {
187 if (self_len < other_len) {
188 return (-1);
189 } else if (self_len > other_len) {
190 return (1);
191 } else {
192 return (0);
193 }
194 }
195 const int cmp = std::memcmp(&self[1], &other[1], cmp_len);
196 if (cmp < 0) {
197 return (-1);
198 } else if (cmp > 0) {
199 return (1);
200 } else if (self_len < other_len) {
201 return (-1);
202 } else if (self_len > other_len) {
203 return (1);
204 } else {
205 return (0);
206 }
207}
208
210 const detail::CharStringData& other) {
211 if (self.size() == 0 && other.size() == 0) {
212 return (0);
213 }
214 if (self.size() == 0) {
215 return (-1);
216 }
217 if (other.size() == 0) {
218 return (1);
219 }
220 const size_t self_len = self.size();
221 const size_t other_len = other.size();
222 const size_t cmp_len = std::min(self_len, other_len);
223 const int cmp = std::memcmp(&self[0], &other[0], cmp_len);
224 if (cmp < 0) {
225 return (-1);
226 } else if (cmp > 0) {
227 return (1);
228 } else if (self_len < other_len) {
229 return (-1);
230 } else if (self_len > other_len) {
231 return (1);
232 } else {
233 return (0);
234 }
235}
236
237size_t
239 CharString& target) {
240 if (rdata_len < 1 || buffer.getLength() - buffer.getPosition() < 1) {
242 "insufficient data to read character-string length");
243 }
244 const uint8_t len = buffer.readUint8();
245 if (rdata_len < static_cast<size_t>(len) + 1) {
247 "character string length is too large: " <<
248 static_cast<int>(len));
249 }
250 if (buffer.getLength() - buffer.getPosition() < len) {
252 "not enough data in buffer to read character-string of len"
253 << static_cast<int>(len));
254 }
255
256 target.resize(len + 1);
257 target[0] = len;
258 buffer.readData(&target[0] + 1, len);
259
260 return (len + 1);
261}
262
263} // end of detail
264} // end of generic
265} // end of rdata
266} // end of dns
267} // end of isc
A standard DNS module exception that is thrown if RDATA parser encounters a character-string (as defi...
Definition rdata.h:55
A standard DNS module exception that is thrown if RDATA parser fails to recognize a given textual rep...
Definition rdata.h:44
The InputBuffer class is a buffer abstraction for manipulating read-only data.
Definition buffer.h:81
size_t getPosition() const
Return the current read position.
Definition buffer.h:101
uint8_t readUint8()
Read an unsigned 8-bit integer from the buffer and return it.
Definition buffer.h:145
size_t getLength() const
Return the length of the data stored in the buffer.
Definition buffer.h:96
void readData(void *data, size_t len)
Read data of the specified length from the buffer and copy it to the caller supplied buffer.
Definition buffer.h:235
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
#define isc_throw_assert(expr)
Replacement for assert() that throws if the expression is false.
Definition isc_assert.h:18
std::string charStringToString(const CharString &char_string)
Convert a CharString into a textual DNS character-string.
int compareCharStrings(const detail::CharString &self, const detail::CharString &other)
Compare two CharString objects.
int compareCharStringDatas(const detail::CharStringData &self, const detail::CharStringData &other)
Compare two CharStringData objects.
void stringToCharString(const MasterToken::StringRegion &str_region, CharString &result)
Convert a DNS character-string into corresponding binary data.
std::vector< uint8_t > CharStringData
Type for DNS character string without the length prefix.
Definition char_string.h:30
void stringToCharStringData(const MasterToken::StringRegion &str_region, CharStringData &result)
Convert a DNS character-string into corresponding binary data.
size_t bufferToCharString(isc::util::InputBuffer &buffer, size_t rdata_len, CharString &target)
Convert a buffer containing a character-string to CharString.
std::string charStringDataToString(const CharStringData &char_string)
Convert a CharStringData into a textual DNS character-string.
std::vector< uint8_t > CharString
Type for DNS character string.
Definition char_string.h:27
const unsigned int MAX_CHARSTRING_LEN
The maximum allowable length of character-string containing in RDATA as defined in RFC1035,...
Definition rdata.h:77
Defines the logger used by the top-level component of kea-lfc.
A simple representation of a range of a string.
size_t len
The length of the string in bytes.
const char * beg
The start address of the string.