session.cpp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. #include <boost/bind.hpp>
  2. #include <iostream>
  3. #include "session.h"
  4. session::session(boost::asio::ip::tcp::socket socket,
  5. boost::asio::io_service &io_service, std::string hostname,
  6. std::string port)
  7. : socket_(std::move(socket)),
  8. io_service_{io_service}, resolver_{io_service}, server_{io_service},
  9. timer_{io_service}, host{hostname}, port{port} {
  10. server_sent = 0;
  11. }
  12. void session::start(void) {
  13. std::cout << "session" << std::endl;
  14. auto self(shared_from_this());
  15. // read_buffer.reserve(1024);
  16. // do_write("Welcome!\n");
  17. do_read();
  18. }
  19. session::~session() { std::cout << "~session destructed" << std::endl; }
  20. void session::parse_auth(void) {
  21. // how many nulls should I be seeing?
  22. // \0user\0pass\0terminal/SPEED\0
  23. // Maybe in the future I'll care about parsing this out. I don't right now.
  24. // Ok, yes I do! If I don't have a proper rlogin value here, it isn't going
  25. // to work when I try to connect to the rlogin server.
  26. if (rlogin_auth.size() > 10)
  27. rlogin_name = rlogin_auth.c_str() + 1;
  28. else
  29. rlogin_name = "?";
  30. }
  31. void session::on_connect(const boost::system::error_code error) {
  32. // We've connected to the server! WOOT WOOT!
  33. if (!error) {
  34. std::cout << "Connected to server!" << std::endl;
  35. to_client("Connected...\n\r");
  36. connected = true;
  37. if (rlogin_auth[0] != 0) {
  38. // Ok, the rlogin information was junk --
  39. to_client("Let me make up some fake rlogin data for you...\n\r");
  40. char temp[] = "\0test\0test\0terminal/9600\0";
  41. std::string tmp(temp, sizeof(temp));
  42. to_server(tmp);
  43. } else {
  44. to_server(rlogin_auth);
  45. }
  46. server_read();
  47. } else {
  48. // TODO:
  49. std::string output = "Failed to connect : ";
  50. output += host;
  51. output += " : ";
  52. output += port;
  53. output += "\n\r";
  54. to_client(output);
  55. std::cout << "Failed to connect to server." << std::endl;
  56. std::cout << "SHUTDOWN..." << std::endl;
  57. socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
  58. }
  59. }
  60. void session::dispatch_line(std::string line) {
  61. // Does this have \n\r still on it? I don't want them.
  62. std::cout << "SL: " << line << std::endl;
  63. // is echo on? if so:
  64. }
  65. void session::process_lines(void) {
  66. // break server_prompt into lines and send/process one by one.
  67. size_t pos;
  68. while ((pos = server_prompt.find("\n\r")) != std::string::npos) {
  69. // line
  70. std::string line = server_prompt.substr(0, pos + 2);
  71. server_prompt = server_prompt.substr(pos + 2);
  72. // Remove \n\r for dispatching
  73. std::string part = line.substr(0, pos);
  74. if (server_sent != 0) {
  75. line = line.substr(server_sent);
  76. server_sent = 0;
  77. };
  78. // display on?
  79. to_client(line);
  80. dispatch_line(part);
  81. }
  82. // display on?
  83. if (server_sent != 0) {
  84. // send partial
  85. std::string part = server_prompt.substr(server_sent);
  86. to_client(part);
  87. server_sent = server_prompt.size();
  88. } else {
  89. // send all
  90. if (!server_prompt.empty()) {
  91. to_client(server_prompt);
  92. server_sent = server_prompt.size();
  93. }
  94. }
  95. // server_sent is the # of chars we've already sent of this.
  96. }
  97. void session::server_read(void) {
  98. auto self(shared_from_this());
  99. boost::asio::async_read(
  100. server_, boost::asio::buffer(server_buffer, sizeof(server_buffer) - 1),
  101. boost::asio::transfer_at_least(1),
  102. [this, self](boost::system::error_code ec, std::size_t length) {
  103. if (!ec) {
  104. server_buffer[length] = 0;
  105. server_prompt.append(server_buffer, length);
  106. process_lines();
  107. /*
  108. if (length) {
  109. // std::cout << length << std::endl;
  110. std::cout << "S: " << server_buffer << std::endl;
  111. do_write(server_buffer);
  112. }
  113. */
  114. server_read();
  115. } else {
  116. std::cout << "S: read_failed: connection closed" << std::endl;
  117. socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
  118. // socket_.async_shutdown(boost::bind(&session::on_shutdown, this,
  119. // boost::asio::placeholders::error));
  120. }
  121. });
  122. }
  123. void session::on_resolve(
  124. const boost::system::error_code error,
  125. const boost::asio::ip::tcp::resolver::results_type results) {
  126. //
  127. auto self(shared_from_this());
  128. if (!error) {
  129. // Take the first endpoint.
  130. boost::asio::ip::tcp::endpoint const &endpoint = *results;
  131. server_.async_connect(endpoint,
  132. boost::bind(&session::on_connect, this,
  133. boost::asio::placeholders::error));
  134. } else {
  135. // TO DO:
  136. std::string output = "Unable to resolve: ";
  137. output += host;
  138. output += "\n\r";
  139. to_client(output);
  140. std::cout << "Unable to resolve?" << std::endl;
  141. std::cout << "SHUTDOWN ..." << std::endl;
  142. socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
  143. }
  144. }
  145. void session::do_read(void) {
  146. auto self(shared_from_this());
  147. boost::asio::async_read( // why can't I async_read_some here?
  148. socket_, boost::asio::buffer(read_buffer, sizeof(read_buffer) - 1),
  149. boost::asio::transfer_at_least(1),
  150. [this, self](boost::system::error_code ec, std::size_t length) {
  151. if (!ec) {
  152. read_buffer[length] = 0;
  153. if (rlogin_auth.empty()) {
  154. // first read should be rlogin information
  155. rlogin_auth.assign(read_buffer, length);
  156. // parse authentication information
  157. parse_auth();
  158. to_client(std::string(1, 0));
  159. to_client("Welcome, ");
  160. to_client(rlogin_name);
  161. to_client("\n\r");
  162. // Activate the connection to the server
  163. /* // this fails, and I'm not sure why. I've used code like this
  164. before. resolver_.async_resolve( host, port, std::bind(
  165. &session::on_resolve, this, _1, _2)); */
  166. // This example shows using boost::bind, which WORKS.
  167. // https://stackoverflow.com/questions/6025471/bind-resolve-handler-to-resolver-async-resolve-using-boostasio
  168. resolver_.async_resolve(
  169. host, port,
  170. boost::bind(&session::on_resolve, this,
  171. boost::asio::placeholders::error,
  172. boost::asio::placeholders::iterator));
  173. } else if (length) {
  174. // std::cout << length << std::endl;
  175. to_server(read_buffer);
  176. std::cout << "C: " << read_buffer << std::endl;
  177. // do_write(output);
  178. }
  179. do_read();
  180. } else {
  181. std::cout << "C: read_failed: connection closed" << std::endl;
  182. if (connected)
  183. server_.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
  184. // server_.async_shutdown(boost::bind(&session::on_shutdown, this,
  185. // boost::asio::placeholders::error));
  186. }
  187. });
  188. }
  189. void session::to_client(std::string message) {
  190. auto self(shared_from_this());
  191. boost::asio::async_write(
  192. socket_, boost::asio::buffer(message),
  193. [this, self](boost::system::error_code ec, std::size_t /*length*/) {
  194. if (!ec) {
  195. } else {
  196. std::cout << "write failed? closed?" << std::endl;
  197. server_.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
  198. // server_.async_shutdown(boost::bind(&session::on_shutdown, this,
  199. // boost::asio::placeholders::error));
  200. }
  201. });
  202. }
  203. void session::to_server(std::string message) {
  204. auto self(shared_from_this());
  205. boost::asio::async_write(
  206. server_, boost::asio::buffer(message),
  207. [this, self](boost::system::error_code ec, std::size_t /*length*/) {
  208. if (!ec) {
  209. } else {
  210. std::cout << "write failed? closed?" << std::endl;
  211. socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
  212. // socket_.async_shutdown(boost::bind(&session::on_shutdown, this,
  213. // boost::asio::placeholders::error));
  214. }
  215. });
  216. }
  217. void session::on_shutdown(boost::system::error_code ec) {
  218. std::cout << "shutdown." << std::endl;
  219. }
  220. server::server(boost::asio::io_service &io_service,
  221. const boost::asio::ip::tcp::endpoint &endpoint, std::string host,
  222. std::string port)
  223. : io_service_{io_service}, acceptor_{io_service_, endpoint}, host_{host},
  224. port_{port} {
  225. do_accept();
  226. }
  227. /**
  228. * setup async connect accept
  229. *
  230. * This creates a session for each connection. Using make_shared allows the
  231. * session to automatically clean up when it is no longer active/has anything
  232. * running in the reactor.
  233. */
  234. void server::do_accept(void) {
  235. acceptor_.async_accept([this](boost::system::error_code ec,
  236. boost::asio::ip::tcp::socket socket) {
  237. if (!ec) {
  238. std::make_shared<session>(std::move(socket), io_service_, host_, port_)
  239. ->start();
  240. }
  241. do_accept();
  242. });
  243. }