tftp_common  1.3.0
packets.hpp
Go to the documentation of this file.
1 #pragma once
2 
3 #ifdef _WIN32
4 #include <WinSock2.h>
5 #else
6 #include <arpa/inet.h>
7 #endif
8 
9 #include <cassert>
10 #include <cstdint>
11 #include <string>
12 #include <string_view>
13 #include <unordered_map>
14 #include <vector>
15 
17 
18 struct ParseResult;
19 
20 namespace types {
21 
23 enum Type : std::uint16_t {
25  ReadRequest = 0x01,
27  WriteRequest = 0x02,
29  DataPacket = 0x03,
33  ErrorPacket = 0x05,
36 };
37 
38 } // namespace types
39 
40 namespace errors {
41 
43 enum Error : std::uint16_t {
51  DiskFull = 3,
59  NoSuchUser = 7
60 };
61 
62 } // namespace errors
63 
64 namespace modes {
65 
71  Octet
72 };
73 
74 } // namespace modes
75 
77 class Request final {
78  public:
80  Request() = default;
82  Request(types::Type Type, std::string_view Filename, std::string_view Mode)
83  : Type_(Type), Filename(Filename), Mode(Mode) {
85  }
87  Request(types::Type Type, std::string &&Filename, std::string &&Mode) noexcept
88  : Type_(Type), Filename(std::move(Filename)), Mode(std::move(Mode)) {
90  }
92  Request(types::Type Type, std::string_view Filename, std::string_view Mode,
93  const std::vector<std::string> &OptionsNames, const std::vector<std::string> &OptionsValues)
94  : Request(Type, Filename, Mode) {
95  this->OptionsNames = OptionsNames;
96  this->OptionsValues = OptionsValues;
97  }
99  Request(types::Type Type, std::string &&Filename, std::string &&Mode, std::vector<std::string> &&OptionsNames,
100  std::vector<std::string> &&OptionsValues) noexcept
101  : Type_(Type), Filename(std::move(Filename)), Mode(std::move(Mode)), OptionsNames(std::move(OptionsNames)),
102  OptionsValues(std::move(OptionsValues)) {
104  }
105 
109  template <class OutputIterator> std::size_t serialize(OutputIterator It) const noexcept {
110  assert(OptionsNames.size() == OptionsValues.size());
111 
112  *(It++) = static_cast<std::uint8_t>(htons(Type_) >> 0);
113  *(It++) = static_cast<std::uint8_t>(htons(Type_) >> 8);
114 
115  for (auto Byte : Filename) {
116  *(It++) = static_cast<std::uint8_t>(Byte);
117  }
118  *(It++) = '\0';
119 
120  for (auto Byte : Mode) {
121  *(It++) = static_cast<std::uint8_t>(Byte);
122  }
123  *(It++) = '\0';
124 
125  std::size_t OptionsSize = 0;
126  for (std::size_t Idx = 0; Idx != OptionsNames.size(); ++Idx) {
127  for (auto Byte : OptionsNames[Idx]) {
128  *(It++) = static_cast<std::uint8_t>(Byte);
129  }
130  *(It++) = '\0';
131  for (auto Byte : OptionsValues[Idx]) {
132  *(It++) = static_cast<std::uint8_t>(Byte);
133  }
134  *(It++) = '\0';
135  OptionsSize += OptionsNames[Idx].size() + OptionsValues[Idx].size() + 2;
136  }
137 
138  return sizeof(Type_) + Filename.size() + Mode.size() + OptionsSize + 2;
139  }
140 
141  std::uint16_t getType() const noexcept { return Type_; }
142 
143  std::string_view getFilename() const noexcept { return std::string_view(Filename.data(), Filename.size()); }
144 
145  std::string_view getMode() const noexcept { return std::string_view(Mode.data(), Mode.size()); }
146 
147  std::string_view getOptionName(std::size_t Idx) const noexcept {
148  return std::string_view(OptionsNames[Idx].data(), OptionsNames[Idx].size());
149  }
150 
151  std::string_view getOptionValue(std::size_t Idx) const noexcept {
152  return std::string_view(OptionsValues[Idx].data(), OptionsValues[Idx].size());
153  }
154 
155  private:
156  friend ParseResult parse(const std::uint8_t *Buffer, std::size_t Len, Request &Packet);
157 
158  std::uint16_t Type_;
159  std::string Filename;
160  std::string Mode;
161  std::vector<std::string> OptionsNames;
162  std::vector<std::string> OptionsValues;
163 };
164 
166 class Data final {
167  public:
169  Data() = default;
172  Data(std::uint16_t Block, const std::vector<std::uint8_t> &Buffer)
173  : Block(Block), DataBuffer(Buffer.begin(), Buffer.end()) {
174  // The block numbers on data packets begin with one and increase by one for each new block of data
175  assert(Block >= 1);
176  // The data field is from zero to 512 bytes long
177  assert(Buffer.size() >= 0 && Buffer.size() <= 512);
178  }
181  Data(std::uint16_t Block, std::vector<std::uint8_t> &&Buffer) noexcept : Block(Block) {
182  // The block numbers on data packets begin with one and increase by one for each new block of data
183  assert(Block >= 1);
184  // The data field is from zero to 512 bytes long
185  assert(Buffer.size() >= 0 && Buffer.size() <= 512);
186  this->DataBuffer = std::move(Buffer);
187  }
188 
192  template <class OutputIterator> std::size_t serialize(OutputIterator It) const noexcept {
193  *(It++) = static_cast<std::uint8_t>(htons(Type_) >> 0);
194  *(It++) = static_cast<std::uint8_t>(htons(Type_) >> 8);
195  *(It++) = static_cast<std::uint8_t>(htons(Block) >> 0);
196  *(It++) = static_cast<std::uint8_t>(htons(Block) >> 8);
197  for (auto Byte : DataBuffer) {
198  *(It++) = Byte;
199  }
200 
201  return sizeof(Type_) + sizeof(Block) + DataBuffer.size();
202  }
203 
204  std::uint16_t getType() const noexcept { return Type_; }
205 
206  std::uint16_t getBlock() const noexcept { return Block; }
207 
208  const std::vector<std::uint8_t> &getData() const noexcept { return DataBuffer; }
209 
210  private:
211  friend ParseResult parse(const std::uint8_t *Buffer, std::size_t Len, Data &Packet);
212 
213  std::uint16_t Type_ = types::DataPacket;
214  std::uint16_t Block;
215  std::vector<std::uint8_t> DataBuffer;
216 };
217 
219 class Acknowledgment final {
220  public:
222  Acknowledgment() = default;
224  explicit Acknowledgment(std::uint16_t Block) noexcept : Block(Block) {
225  // The block numbers on data packets begin with one and increase by one for each new block of data
226  assert(Block >= 1);
227  }
228 
229  std::uint16_t getType() const noexcept { return Type_; }
230 
231  std::uint16_t getBlock() const noexcept { return Block; }
232 
236  template <class OutputIterator> std::size_t serialize(OutputIterator It) const noexcept {
237  *(It++) = static_cast<std::uint8_t>(htons(Type_) >> 0);
238  *(It++) = static_cast<std::uint8_t>(htons(Type_) >> 8);
239  *(It++) = static_cast<std::uint8_t>(htons(Block) >> 0);
240  *(It++) = static_cast<std::uint8_t>(htons(Block) >> 8);
241 
242  return sizeof(Type_) + sizeof(Block);
243  }
244 
245  private:
246  friend ParseResult parse(const std::uint8_t *Buffer, std::size_t Len, Acknowledgment &Packet);
247 
248  std::uint16_t Type_ = types::AcknowledgmentPacket;
249  std::uint16_t Block;
250 };
251 
253 class Error final {
254  public:
256  Error() = default;
258  Error(std::uint16_t ErrorCode, std::string_view ErrorMessage) : ErrorCode(ErrorCode), ErrorMessage(ErrorMessage) {
259  assert(ErrorCode >= 0 && ErrorCode <= 8);
260  }
262  Error(std::uint16_t ErrorCode, std::string &&ErrorMessage)
263  : ErrorCode(ErrorCode), ErrorMessage(std::move(ErrorMessage)) {
264  assert(ErrorCode >= 0 && ErrorCode <= 8);
265  }
266 
267  std::uint16_t getType() const noexcept { return Type_; }
268 
269  std::uint16_t getErrorCode() const noexcept { return ErrorCode; }
270 
271  std::string_view getErrorMessage() const noexcept {
272  return std::string_view(ErrorMessage.data(), ErrorMessage.size());
273  }
274 
278  template <class OutputIterator> std::size_t serialize(OutputIterator It) const noexcept {
279  *(It++) = static_cast<std::uint8_t>(htons(Type_) >> 0);
280  *(It++) = static_cast<std::uint8_t>(htons(Type_) >> 8);
281  *(It++) = static_cast<std::uint8_t>(htons(ErrorCode) >> 0);
282  *(It++) = static_cast<std::uint8_t>(htons(ErrorCode) >> 8);
283 
284  for (auto Byte : ErrorMessage) {
285  *(It++) = Byte;
286  }
287  *(It++) = '\0';
288 
289  return sizeof(Type_) + sizeof(ErrorCode) + ErrorMessage.size() + 1;
290  }
291 
292  private:
293  friend ParseResult parse(const std::uint8_t *Buffer, std::size_t Len, Error &Packet);
294 
295  std::uint16_t Type_ = types::ErrorPacket;
296  std::uint16_t ErrorCode;
297  std::string ErrorMessage;
298 };
299 
301 class OptionAcknowledgment final {
302  public:
304  OptionAcknowledgment() = default;
305  OptionAcknowledgment(std::unordered_map<std::string, std::string> Options) : Options(std::move(Options)) {}
306 
310  template <class OutputIterator> std::size_t serialize(OutputIterator It) const noexcept {
311  *(It++) = static_cast<std::uint8_t>(htons(Type_) >> 0);
312  *(It++) = static_cast<std::uint8_t>(htons(Type_) >> 8);
313 
314  std::size_t OptionsSize = 0;
315  for (const auto &[Key, Value] : Options) {
316  for (auto Byte : Key) {
317  *(It++) = static_cast<std::uint8_t>(Byte);
318  }
319  *(It++) = '\0';
320  for (auto Byte : Value) {
321  *(It++) = static_cast<std::uint8_t>(Byte);
322  }
323  *(It++) = '\0';
324  OptionsSize += Key.size() + Value.size() + 2;
325  }
326 
327  return sizeof(Type_) + OptionsSize;
328  }
329 
330  std::uint16_t getType() const noexcept { return Type_; }
331 
333  auto begin() noexcept { return Options.begin(); }
334 
336  auto begin() const noexcept { return Options.begin(); }
337 
339  auto cbegin() const noexcept { return Options.cbegin(); }
340 
342  auto end() noexcept { return Options.end(); }
343 
345  auto end() const noexcept { return Options.end(); }
346 
348  auto cend() const noexcept { return Options.cend(); }
349 
352  std::string_view getOptionValue(const std::string &OptionName) const noexcept { return Options.at(OptionName); }
353 
354  private:
355  friend ParseResult parse(const std::uint8_t *Buffer, std::size_t Len, OptionAcknowledgment &Packet);
356 
357  std::uint16_t Type_ = types::OptionAcknowledgmentPacket;
358  // According to the RFC, the order in which options are specified is not significant, so it's fine
359  std::unordered_map<std::string, std::string> Options;
360 };
361 
362 } // namespace tftp_common::packets
Acknowledgment Trivial File Transfer Protocol packet.
Definition: packets.hpp:219
friend ParseResult parse(const std::uint8_t *Buffer, std::size_t Len, Acknowledgment &Packet)
Definition: parsers.hpp:153
std::uint16_t getType() const noexcept
Definition: packets.hpp:229
std::size_t serialize(OutputIterator It) const noexcept
Definition: packets.hpp:236
Acknowledgment()=default
Use with parsing functions only.
Acknowledgment(std::uint16_t Block) noexcept
Definition: packets.hpp:224
std::uint16_t getBlock() const noexcept
Definition: packets.hpp:231
Data Trivial File Transfer Protocol packet.
Definition: packets.hpp:166
Data(std::uint16_t Block, const std::vector< std::uint8_t > &Buffer)
Definition: packets.hpp:172
Data()=default
Use with parsing functions only.
Data(std::uint16_t Block, std::vector< std::uint8_t > &&Buffer) noexcept
Definition: packets.hpp:181
std::size_t serialize(OutputIterator It) const noexcept
Definition: packets.hpp:192
std::uint16_t getType() const noexcept
Definition: packets.hpp:204
const std::vector< std::uint8_t > & getData() const noexcept
Definition: packets.hpp:208
friend ParseResult parse(const std::uint8_t *Buffer, std::size_t Len, Data &Packet)
Definition: parsers.hpp:98
std::uint16_t getBlock() const noexcept
Definition: packets.hpp:206
Error Trivial File Transfer Protocol packet.
Definition: packets.hpp:253
Error(std::uint16_t ErrorCode, std::string &&ErrorMessage)
Definition: packets.hpp:262
std::string_view getErrorMessage() const noexcept
Definition: packets.hpp:271
Error()=default
Use with parsing functions only.
std::uint16_t getErrorCode() const noexcept
Definition: packets.hpp:269
friend ParseResult parse(const std::uint8_t *Buffer, std::size_t Len, Error &Packet)
Definition: parsers.hpp:198
std::uint16_t getType() const noexcept
Definition: packets.hpp:267
std::size_t serialize(OutputIterator It) const noexcept
Definition: packets.hpp:278
Error(std::uint16_t ErrorCode, std::string_view ErrorMessage)
Definition: packets.hpp:258
Option Acknowledgment Trivial File Transfer Protocol packet.
Definition: packets.hpp:301
auto end() noexcept
Definition: packets.hpp:342
std::uint16_t getType() const noexcept
Definition: packets.hpp:330
friend ParseResult parse(const std::uint8_t *Buffer, std::size_t Len, OptionAcknowledgment &Packet)
Definition: parsers.hpp:252
OptionAcknowledgment(std::unordered_map< std::string, std::string > Options)
Definition: packets.hpp:305
auto begin() const noexcept
Definition: packets.hpp:336
OptionAcknowledgment()=default
Use with parsing functions only.
std::size_t serialize(OutputIterator It) const noexcept
Definition: packets.hpp:310
auto cbegin() const noexcept
Definition: packets.hpp:339
auto end() const noexcept
Definition: packets.hpp:345
std::string_view getOptionValue(const std::string &OptionName) const noexcept
Definition: packets.hpp:352
auto begin() noexcept
Definition: packets.hpp:333
auto cend() const noexcept
Definition: packets.hpp:348
Read/Write Request (RRQ/WRQ) Trivial File Transfer Protocol packet.
Definition: packets.hpp:77
Request(types::Type Type, std::string &&Filename, std::string &&Mode) noexcept
Definition: packets.hpp:87
std::size_t serialize(OutputIterator It) const noexcept
Definition: packets.hpp:109
std::string_view getOptionName(std::size_t Idx) const noexcept
Definition: packets.hpp:147
Request(types::Type Type, std::string_view Filename, std::string_view Mode, const std::vector< std::string > &OptionsNames, const std::vector< std::string > &OptionsValues)
Definition: packets.hpp:92
std::string_view getOptionValue(std::size_t Idx) const noexcept
Definition: packets.hpp:151
Request(types::Type Type, std::string_view Filename, std::string_view Mode)
Definition: packets.hpp:82
Request()=default
Use with parsing functions only.
std::string_view getFilename() const noexcept
Definition: packets.hpp:143
friend ParseResult parse(const std::uint8_t *Buffer, std::size_t Len, Request &Packet)
Definition: parsers.hpp:19
std::string_view getMode() const noexcept
Definition: packets.hpp:145
Request(types::Type Type, std::string &&Filename, std::string &&Mode, std::vector< std::string > &&OptionsNames, std::vector< std::string > &&OptionsValues) noexcept
Definition: packets.hpp:99
std::uint16_t getType() const noexcept
Definition: packets.hpp:141
Error
Trivial File Transfer Protocol error code.
Definition: packets.hpp:43
@ UnknownTransferID
Unknown transfer ID error code.
Definition: packets.hpp:55
@ FileNotFound
File not found error code.
Definition: packets.hpp:47
@ NoSuchUser
No such user error code.
Definition: packets.hpp:59
@ NotDefined
Not defined, see error message (if any) error code.
Definition: packets.hpp:45
@ IllegalOperation
Illegal TFTP operation error code.
Definition: packets.hpp:53
@ AccessViolation
Access violation error code.
Definition: packets.hpp:49
@ FileAlreadyExists
File already exists error code.
Definition: packets.hpp:57
@ DiskFull
Disk full or allocation exceeded error code.
Definition: packets.hpp:51
TransferMode
Trivial File Transfer Protocol transfer mode.
Definition: packets.hpp:67
@ NetAscii
netascii transfer mode
Definition: packets.hpp:69
@ Octet
octet (binary) transfer mode
Definition: packets.hpp:71
Type
Trivial File Transfer Protocol packet type.
Definition: packets.hpp:23
@ AcknowledgmentPacket
Acknowledgment (ACK) operation code.
Definition: packets.hpp:31
@ ReadRequest
Read request (RRQ) operation code.
Definition: packets.hpp:25
@ OptionAcknowledgmentPacket
Option Acknowledgment (OACK) operation code.
Definition: packets.hpp:35
@ DataPacket
Data (DATA) operation code.
Definition: packets.hpp:29
@ WriteRequest
Write request (WRQ) operation code.
Definition: packets.hpp:27
@ ErrorPacket
Error (ERROR) operation code.
Definition: packets.hpp:33
Definition: packets.hpp:16
The result of parsing a single packet.
Definition: parsers.hpp:8