summaryrefslogtreecommitdiffstats
path: root/src/lib/dhcp/opaque_data_tuple.cc
blob: e09f7a78725e830fca32cb00532ab749d43b444b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// Copyright (C) 2014-2022 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include <config.h>

#include <dhcp/opaque_data_tuple.h>

namespace isc {
namespace dhcp {

OpaqueDataTuple::OpaqueDataTuple(LengthFieldType length_field_type)
    : length_field_type_(length_field_type) {
}

void
OpaqueDataTuple::append(const std::string& text) {
    // Don't do anything if text is empty.
    if (!text.empty()) {
        append(&text[0], text.size());
    }
}

void
OpaqueDataTuple::assign(const std::string& text) {
    // If empty string is being assigned, reset the buffer.
    if (text.empty()) {
        clear();
    } else {
        assign(&text[0], text.size());
    }
}

void
OpaqueDataTuple::clear() {
    data_.clear();
}

bool
OpaqueDataTuple::equals(const std::string& other) const {
    return (getText() == other);
}

std::string
OpaqueDataTuple::getText() const {
    // Convert the data carried in the buffer to a string.
    return (std::string(data_.begin(), data_.end()));
}

void
OpaqueDataTuple::pack(isc::util::OutputBuffer& buf) const {
    if ((1 << (getDataFieldSize() * 8)) <= getLength()) {
        isc_throw(OpaqueDataTupleError, "failed to create on-wire format of the"
                  " opaque data field, because current data length "
                  << getLength() << " exceeds the maximum size for the length"
                  << " field size " << getDataFieldSize());
    }

    if (getDataFieldSize() == 1) {
        buf.writeUint8(static_cast<uint8_t>(getLength()));
    } else {
        buf.writeUint16(getLength());
    }

    if (getLength() > 0) {
        buf.writeData(&getData()[0], getLength());
    }
}

int
OpaqueDataTuple::getDataFieldSize() const {
    return (length_field_type_ == LENGTH_1_BYTE ? 1 : 2);
}

OpaqueDataTuple&
OpaqueDataTuple::operator=(const std::string& other) {
    // Replace existing data with the new data converted from a string.
    assign(&other[0], other.length());
    return (*this);
}

bool
OpaqueDataTuple::operator==(const std::string& other) const {
    return (equals(other));
}

bool
OpaqueDataTuple::operator!=(const std::string& other) {
    return (!equals(other));
}

std::ostream& operator<<(std::ostream& os, const OpaqueDataTuple& tuple) {
    os << tuple.getText();
    return (os);
}

std::istream& operator>>(std::istream& is, OpaqueDataTuple& tuple) {
    // We will replace the current data with new data.
    tuple.clear();
    char buf[256];
    // Read chunks of data as long as we haven't reached the end of the stream.
    while (!is.eof()) {
        is.read(buf, sizeof(buf));
        // Append the chunk of data we have just read. It is fine if the
        // gcount is 0, because append() function will check that.
        tuple.append(buf, is.gcount());
    }
    // Clear eof flag, so as the stream can be read again.
    is.clear();
    return (is);
}

void
OpaqueDataTuple::unpack(OpaqueDataTuple::InputIterator begin, OpaqueDataTuple::InputIterator end) {
    // The buffer must at least hold the size of the data.
    if (std::distance(begin, end) < getDataFieldSize()) {
        isc_throw(OpaqueDataTupleError,
                    "unable to parse the opaque data tuple, the buffer"
                    " length is " << std::distance(begin, end)
                    << ", expected at least " << getDataFieldSize());
    }
    // Read the data length from the length field, depending on the
    // size of the data field (1 or 2 bytes).
    size_t len = getDataFieldSize() == 1 ? *begin :
        isc::util::readUint16(&(*begin), std::distance(begin, end));
    // Now that we have the expected data size, let's check that the
    // reminder of the buffer is long enough.
    begin += getDataFieldSize();
    // Attempt to parse as a length-value pair.
    if (std::distance(begin, end) < len) {
        if (Option::lenient_parsing_) {
            // Fallback to parsing the rest of the option as a single value.
            len = std::distance(begin, end);
        } else {
            isc_throw(OpaqueDataTupleError,
                        "unable to parse the opaque data tuple, "
                        "the buffer length is " << std::distance(begin, end)
                        << ", but the tuple length is " << len);
        }
    }
    // The buffer length is long enough to read the desired amount of data.
    assign(begin, len);
}

}  // namespace dhcp
}  // namespace isc