twproxy.cpp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. //
  2. // client.cpp
  3. // ~~~~~~~~~~
  4. //
  5. // Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com)
  6. //
  7. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  8. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  9. //
  10. #include <boost/asio.hpp>
  11. #include <boost/asio/ip/basic_resolver.hpp>
  12. #include <boost/bind.hpp>
  13. #include <cstdlib>
  14. #include <iostream>
  15. #include "config.h"
  16. class session : public std::enable_shared_from_this<session> {
  17. public:
  18. session(boost::asio::ip::tcp::socket socket,
  19. boost::asio::io_service &io_service, std::string hostname,
  20. std::string port)
  21. : socket_(std::move(socket)), io_service_{io_service},
  22. resolver_{io_service}, server_{io_service}, host{hostname}, port{port} {
  23. }
  24. void start(void) {
  25. std::cout << "session" << std::endl;
  26. auto self(shared_from_this());
  27. // read_buffer.reserve(1024);
  28. // do_write("Welcome!\n");
  29. do_read();
  30. }
  31. ~session() { std::cout << "~session destructed" << std::endl; }
  32. void parse_auth(void) {
  33. // how many nulls should I be seeing?
  34. // \0user\0pass\0terminal/SPEED\0
  35. // Maybe in the future I'll care about parsing this out. I don't right now.
  36. // Ok, yes I do! If I don't have a proper rlogin value here, it isn't going
  37. // to work when I try to connect to the rlogin server.
  38. if (rlogin_auth.size() > 10)
  39. rlogin_name = rlogin_auth.c_str() + 1;
  40. else
  41. rlogin_name = "?";
  42. }
  43. void on_connect(const boost::system::error_code error) {
  44. // We've connected to the server! WOOT WOOT!
  45. if (!error) {
  46. std::cout << "Connected to server!" << std::endl;
  47. do_write("Connected...\n\r");
  48. connected = true;
  49. server_read();
  50. } else {
  51. // TODO:
  52. do_write("Failed to connect to server.\n\r");
  53. std::cout << "Failed to connect to server." << std::endl;
  54. std::cout << "SHUTDOWN..." << std::endl;
  55. socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
  56. }
  57. }
  58. void server_read(void) {
  59. auto self(shared_from_this());
  60. boost::asio::async_read(
  61. server_, boost::asio::buffer(server_buffer, sizeof(server_buffer) - 1),
  62. boost::asio::transfer_at_least(1),
  63. [this, self](boost::system::error_code ec, std::size_t length) {
  64. if (!ec) {
  65. server_buffer[length] = 0;
  66. if (length) {
  67. // std::cout << length << std::endl;
  68. std::cout << "S: " << server_buffer << std::endl;
  69. do_write(server_buffer);
  70. }
  71. server_read();
  72. } else {
  73. std::cout << "read_failed: connection closed" << std::endl;
  74. socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
  75. // socket_.async_shutdown(boost::bind(&session::on_shutdown, this,
  76. // boost::asio::placeholders::error));
  77. }
  78. });
  79. }
  80. void on_resolve(const boost::system::error_code error,
  81. const boost::asio::ip::tcp::resolver::results_type results) {
  82. //
  83. auto self(shared_from_this());
  84. if (!error) {
  85. // Take the first endpoint.
  86. boost::asio::ip::tcp::endpoint const &endpoint = *results;
  87. server_.async_connect(endpoint,
  88. boost::bind(&session::on_connect, this,
  89. boost::asio::placeholders::error));
  90. } else {
  91. // TO DO:
  92. do_write("Unable to resolve?\n\r");
  93. std::cout << "Unable to resolve?" << std::endl;
  94. std::cout << "SHUTDOWN ..." << std::endl;
  95. socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
  96. }
  97. }
  98. void do_read(void) {
  99. auto self(shared_from_this());
  100. boost::asio::async_read( // why can't I async_read_some here?
  101. socket_, boost::asio::buffer(read_buffer, sizeof(read_buffer) - 1),
  102. boost::asio::transfer_at_least(1),
  103. [this, self](boost::system::error_code ec, std::size_t length) {
  104. if (!ec) {
  105. read_buffer[length] = 0;
  106. if (rlogin_auth.empty()) {
  107. // first read should be rlogin information
  108. rlogin_auth.assign(read_buffer, length);
  109. // parse authentication information
  110. parse_auth();
  111. do_write(std::string(1, 0));
  112. do_write("Welcome, ");
  113. do_write(rlogin_name);
  114. do_write("\n\r");
  115. // Activate the connection to the server
  116. /* // this fails, and I'm not sure why. I've used code like this
  117. before. resolver_.async_resolve( host, port, std::bind(
  118. &session::on_resolve, this, _1, _2)); */
  119. // This example shows using boost::bind, which WORKS.
  120. // https://stackoverflow.com/questions/6025471/bind-resolve-handler-to-resolver-async-resolve-using-boostasio
  121. resolver_.async_resolve(
  122. host, port,
  123. boost::bind(&session::on_resolve, this,
  124. boost::asio::placeholders::error,
  125. boost::asio::placeholders::iterator));
  126. } else if (length) {
  127. // std::cout << length << std::endl;
  128. server_write(read_buffer);
  129. std::cout << "C: " << read_buffer << std::endl;
  130. // do_write(output);
  131. }
  132. do_read();
  133. } else {
  134. std::cout << "read_failed: connection closed" << std::endl;
  135. server_.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
  136. // server_.async_shutdown(boost::bind(&session::on_shutdown, this,
  137. // boost::asio::placeholders::error));
  138. }
  139. });
  140. }
  141. void do_write(std::string message) {
  142. auto self(shared_from_this());
  143. boost::asio::async_write(
  144. socket_, boost::asio::buffer(message),
  145. [this, self](boost::system::error_code ec, std::size_t /*length*/) {
  146. if (!ec) {
  147. } else {
  148. std::cout << "write failed? closed?" << std::endl;
  149. server_.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
  150. // server_.async_shutdown(boost::bind(&session::on_shutdown, this,
  151. // boost::asio::placeholders::error));
  152. }
  153. });
  154. }
  155. void server_write(std::string message) {
  156. auto self(shared_from_this());
  157. boost::asio::async_write(
  158. server_, boost::asio::buffer(message),
  159. [this, self](boost::system::error_code ec, std::size_t /*length*/) {
  160. if (!ec) {
  161. } else {
  162. std::cout << "write failed? closed?" << std::endl;
  163. socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
  164. // socket_.async_shutdown(boost::bind(&session::on_shutdown, this,
  165. // boost::asio::placeholders::error));
  166. }
  167. });
  168. }
  169. void on_shutdown(boost::system::error_code ec) {
  170. std::cout << "shutdown." << std::endl;
  171. }
  172. private:
  173. boost::asio::ip::tcp::socket socket_;
  174. boost::asio::io_service &io_service_;
  175. boost::asio::ip::tcp::resolver resolver_;
  176. boost::asio::ip::tcp::socket server_;
  177. // std::string read_buffer;
  178. char read_buffer[1024];
  179. char server_buffer[1024];
  180. std::string rlogin_auth;
  181. std::string rlogin_name;
  182. std::string host;
  183. std::string port;
  184. bool connected = false;
  185. };
  186. /*
  187. maybe move the resolver part to the server, so I don't need io_service?
  188. I'm not sure what the socket connection part is going to need just yet,
  189. so I probably won't move that just yet. [NNY!]
  190. */
  191. class server {
  192. public:
  193. server(boost::asio::io_service &io_service,
  194. const boost::asio::ip::tcp::endpoint &endpoint, std::string host,
  195. std::string port)
  196. : io_service_{io_service}, acceptor_{io_service_, endpoint}, host_{host},
  197. port_{port} {
  198. do_accept();
  199. }
  200. private:
  201. void do_accept() {
  202. acceptor_.async_accept([this](boost::system::error_code ec,
  203. boost::asio::ip::tcp::socket socket) {
  204. if (!ec) {
  205. std::make_shared<session>(std::move(socket), io_service_, host_, port_)
  206. ->start();
  207. }
  208. do_accept();
  209. });
  210. }
  211. boost::asio::io_service &io_service_;
  212. boost::asio::ip::tcp::acceptor acceptor_;
  213. std::string host_;
  214. std::string port_;
  215. };
  216. int main(int argc, char *argv[]) {
  217. // boost::json::json_value config;
  218. if (argc != 2) {
  219. std::cerr << "Usage: twproxy <filename>" << std::endl;
  220. return EXIT_FAILURE;
  221. }
  222. std::map<std::string, std::string> config = yaml_parse(argv[1]);
  223. /*
  224. try {
  225. // Parse the file as JSON
  226. config = yaml_parse( argv[1] );
  227. } catch (std::exception const &e) {
  228. std::cerr << "Caught exception: " << e.what() << std::endl;
  229. return EXIT_FAILURE;
  230. }
  231. */
  232. bool config_ok = true;
  233. for (const char *key : {"server", "host", "port"}) {
  234. auto pos = config.find(key);
  235. if (pos == config.end()) {
  236. config_ok = false;
  237. std::cout << "Config file missing: " << key << std::endl;
  238. }
  239. }
  240. if (!config_ok)
  241. return 2;
  242. int port = std::stoi(config["server"]);
  243. // int port = 9999; // 2002;
  244. try {
  245. boost::asio::io_service io_service;
  246. boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(),
  247. port); // std::atoi(argv[i]));
  248. // connect to the BBS
  249. server s(io_service, endpoint, config["host"], config["port"]);
  250. //"127.0.0.1", "2023");
  251. io_service.run();
  252. } catch (std::exception &e) {
  253. std::cerr << "Exception: " << e.what() << "\n";
  254. }
  255. return EXIT_SUCCESS;
  256. }