Telnet++  3.1.0.2
A C++ library for interacting with Telnet streams
parser.hpp
1 #pragma once
2 
3 #include "telnetpp/command.hpp"
4 #include "telnetpp/core.hpp"
5 #include "telnetpp/negotiation.hpp"
6 #include "telnetpp/subnegotiation.hpp"
7 
8 #include <boost/range/algorithm_ext/insert.hpp>
9 
10 #include <vector>
11 
12 namespace telnetpp {
13 
14 class parser final
15 {
16 public:
17  template <typename Continuation>
18  void operator()(telnetpp::bytes data, Continuation &&c)
19  {
20  boost::insert(unparsed_data_, unparsed_data_.end(), data);
21 
22  if (!std::exchange(currently_parsing_, true))
23  {
24  auto const _ = gsl::finally([this] {
25  unparsed_data_.clear();
26  currently_parsing_ = false;
27  });
28 
29  // Note: this is an indexed loop because it is plausible that
30  // a response to a Telnet message may cause extra data to be
31  // received, which would change the length of the unparsed data.
32  for (unsigned char by : unparsed_data_)
33  {
34  switch (state_)
35  {
36  case parsing_state::state_idle:
37  parse_idle(by, c);
38  break;
39 
40  case parsing_state::state_iac:
41  parse_iac(by, c);
42  break;
43 
44  case parsing_state::state_negotiation:
45  parse_negotiation(by, c);
46  break;
47 
48  case parsing_state::state_subnegotiation:
49  parse_subnegotiation(by, c);
50  break;
51 
52  case parsing_state::state_subnegotiation_content:
53  parse_subnegotiation_content(by, c);
54  break;
55 
56  case parsing_state::state_subnegotiation_content_iac:
57  parse_subnegotiation_content_iac(by, c);
58  break;
59 
60  default:
61  break;
62  }
63  }
64 
65  emit_plain_data(c);
66  }
67  }
68 
69 private:
70  enum class parsing_state
71  {
72  state_idle,
73  state_iac,
74  state_negotiation,
75  state_subnegotiation,
76  state_subnegotiation_content,
77  state_subnegotiation_content_iac
78  };
79 
80  parsing_state state_{parsing_state::state_idle};
81  bool currently_parsing_{false};
82  std::vector<telnetpp::byte> unparsed_data_;
83 
84  telnetpp::byte_storage plain_data_;
85  telnetpp::byte_storage subnegotiation_content_;
86  telnetpp::negotiation_type negotiation_type_;
87  telnetpp::option_type subnegotiation_option_;
88 
89  template <typename Continuation>
90  void parse_idle(telnetpp::byte by, Continuation &&c)
91  {
92  switch (by)
93  {
94  case telnetpp::iac:
95  state_ = parsing_state::state_iac;
96  break;
97 
98  default:
99  plain_data_.push_back(by);
100  break;
101  }
102  }
103 
104  template <typename Continuation>
105  void parse_iac(telnetpp::byte by, Continuation &&c)
106  {
107  switch (by)
108  {
109  case telnetpp::iac:
110  plain_data_.push_back(by);
111  state_ = parsing_state::state_idle;
112  break;
113 
114  case telnetpp::will: // fall-through
115  case telnetpp::wont: // fall-through
116  case telnetpp::do_: // fall-through
117  case telnetpp::dont:
118  emit_plain_data(c);
119  negotiation_type_ = by;
120  state_ = parsing_state::state_negotiation;
121  break;
122 
123  case telnetpp::sb:
124  emit_plain_data(c);
125  state_ = parsing_state::state_subnegotiation;
126  break;
127 
128  default:
129  emit_plain_data(c);
130  c(telnetpp::command{by});
131  state_ = parsing_state::state_idle;
132  break;
133  }
134  }
135 
136  template <typename Continuation>
137  void parse_negotiation(telnetpp::byte by, Continuation &&c)
138  {
139  c(telnetpp::negotiation{negotiation_type_, by});
140  state_ = parsing_state::state_idle;
141  }
142 
143  template <typename Continuation>
144  void parse_subnegotiation(telnetpp::byte by, Continuation &&c)
145  {
146  subnegotiation_option_ = by;
147  subnegotiation_content_.clear();
148  state_ = parsing_state::state_subnegotiation_content;
149  }
150 
151  template <typename Continuation>
152  void parse_subnegotiation_content(telnetpp::byte by, Continuation &&c)
153  {
154  switch (by)
155  {
156  case telnetpp::iac:
157  state_ = parsing_state::state_subnegotiation_content_iac;
158  break;
159 
160  default:
161  subnegotiation_content_.push_back(by);
162  break;
163  }
164  }
165 
166  template <typename Continuation>
167  void parse_subnegotiation_content_iac(telnetpp::byte by, Continuation &&c)
168  {
169  switch (by)
170  {
171  case telnetpp::se:
173  subnegotiation_option_, subnegotiation_content_});
174  state_ = parsing_state::state_idle;
175  break;
176 
177  default:
178  subnegotiation_content_.push_back(by);
179  state_ = parsing_state::state_subnegotiation_content;
180  break;
181  }
182  }
183 
184  template <typename Continuation>
185  void emit_plain_data(Continuation &&c)
186  {
187  if (!plain_data_.empty())
188  {
189  c(plain_data_);
190  plain_data_.clear();
191  }
192  }
193 };
194 
195 } // namespace telnetpp
A class that encapsulates the value of a Telnet command.
Definition: command.hpp:13
A class that encapsulates a Telnet negotiation.
Definition: negotiation.hpp:15
Definition: parser.hpp:15
A class that encapsulates a Telnet subnegotiation.
Definition: subnegotiation.hpp:14