Advertisement
Guest User

doorparty-rlogin-proxy.js

a guest
Oct 12th, 2016
132
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.25 KB | None | 0 0
  1. /* DoorParty RLogin Proxy Service for Synchronet BBS
  2. echicken@bbs.electronicchicken.com, October 2016
  3.  
  4. This service listens for connections from RLogin clients, then ensures that
  5. an account exists for the connecting user before sending them to the BBS.
  6.  
  7. Setup:
  8.  
  9. Place a copy of this file in the 'mods' directory. (If you don't have
  10. a 'mods' directory, create one at the top level of your Synchronet BBS
  11. installation, alongside 'exec', 'ctrl', 'data', etc.)
  12.  
  13. Shut down the BBS.
  14.  
  15. Edit ctrl/sbbs.ini, and change the RLoginPort value in the [BBS]
  16. section to 10513.
  17.  
  18. I would also advise adding the NO_HOST_LOOKUP option to the Options
  19. value in the [BBS] section of sbbs.ini. This can speed up the login
  20. process and make the connection to your server closer to seamless.
  21.  
  22. Edit ctrl/services.ini, and add the following section at the bottom:
  23.  
  24. [RLoginProxy]
  25. Port=513
  26. Options=NO_HOST_LOOKUP
  27. Command=doorparty-rlogin-proxy.js
  28.  
  29. Start the BBS back up again.
  30.  
  31. Notes:
  32.  
  33. RLogin clients send three strings immediately after connecting: the
  34. local username, the remote username, and the terminal type & speed. None
  35. of these fields was actually meant to carry a password, but occasionally
  36. that's what they're used for.
  37.  
  38. Some clients (SyncTERM) send the password first, then the username, then
  39. the terminal type & speed; other clients may send the username first and
  40. the password second. The server needs to know which field is which. If
  41. the alias that's being set for a new user is not what you're expecting,
  42. try swapping the values of the USERNAME_FIELD and PASSWORD_FIELD
  43. settings below.
  44.  
  45. The 'password' sent by the RLogin client is being ignored and is not
  46. being compared with the user's password on this BBS (if they already
  47. exist here). What I've gleaned from the DoorParty wiki suggests this is
  48. appropriate, and that inbound RLogin connections are 'trusted' because
  49. they come from authenticated systems via an SSH tunnel.
  50.  
  51. A different random password is being generated for each new user and is
  52. not based on anything that was sent by the RLogin client.
  53.  
  54. If you find that the BBS is still prompting new users for more info when
  55. they first connect, this script can be adjusted to populate more user
  56. data fields so that these questions appear to have been answered.
  57.  
  58. I have commented the shit out of this script for the benefit of anyone
  59. who needs to work on it in the future, including myself.
  60.  
  61. If you're not maskreet and you've stumbled upon this script by accident,
  62. I would not advise setting it up on the Synchronet BBS that you may or
  63. may not operate. This script serves a very specific purpose for an
  64. uncommon situation and would not be wise to run on a 'normal' BBS.
  65.  
  66. */
  67.  
  68. // Configuration
  69. const RLOGIN_PORT = 10513; // The port that your actual RLogin server is on
  70. const PROXY_PORT = 513; // The port that you want this server to listen on
  71. const LOGIN_TIMEOUT = 30; // Seconds to wait for RLogin client details
  72. const USERNAME_FIELD = 2; // '1' if clients send username first, '2' otherwise
  73. const PASSWORD_FIELD = 1; // '1' if clients send password first, '2' otherwise
  74. const NEWUSER_MESSAGE = "One moment please, creating new account ...";
  75. const VERSION_STRING = ""; // Replaces the Synchronet version notice
  76. const EXIT_STRING = " DoorParty - Powered by echicken and Synchronet!\r\n\r\n Returning you to your BBS..."; // Sent to clients upon disconnection
  77. const EXIT_DELAY = 2000; // Milliseconds after EXIT_STRING is sent
  78. const MAX_READ = 512; // This probably doesn't need to be changed, but you
  79. // can tweak it up or down and see if it reduces lag
  80. // or improves responsiveness.
  81.  
  82. // Don't edit below this line.
  83. load('sbbsdefs.js');
  84.  
  85. // If running via jsexec, set up a server and wait for a client
  86. if (typeof js.global.server === 'undefined') {
  87. load('sockdefs.js');
  88. var s = {};
  89. s.socket = new Socket(SOCK_STREAM, 'RLOGIN-PROXY');
  90. s.socket.bind(PROXY_PORT);
  91. s.socket.listen();
  92. var client = { socket : s.socket.accept() };
  93. }
  94. // However this was launched, 'client' should now be our client's Socket object
  95.  
  96. // Global constants
  97. const NUL = ascii(0);
  98. const START_TIME = time();
  99. const STATE_INIT_CLIENT = 0;
  100. const STATE_INIT_USER = 1;
  101. const STATE_INIT_PROXY = 2;
  102. const STATE_PROXY = 3;
  103.  
  104. // Global variables
  105. var proxy, username, password, terminal;
  106. var state = STATE_INIT_CLIENT, from_client = '';
  107.  
  108. // The client has just connected, and we're waiting for their initial string
  109. while (state === STATE_INIT_CLIENT && !js.terminated && client.socket.is_connected) {
  110.  
  111. try {
  112.  
  113. if (client.socket.nread > 0) from_client += client.socket.recv(MAX_READ);
  114.  
  115. var matches = from_client.split(NUL);
  116.  
  117. // If we've received four null-terminated values, parse them
  118. if (matches.length >= 4) {
  119.  
  120. username = matches[USERNAME_FIELD];
  121. password = matches[PASSWORD_FIELD];
  122. terminal = matches[3];
  123.  
  124. from_client = undefined; // We don't need this anymore
  125. client.socket.sendBin(0, 1); // Acknowledge that we got the client's data
  126.  
  127. // Proceed to user lookup/creation
  128. state = STATE_INIT_USER;
  129.  
  130. log(
  131. LOG_INFO,
  132. format(
  133. '%s/%s/%s connected from %s',
  134. username, password, terminal, client.socket.remote_ip_address
  135. )
  136. );
  137.  
  138. // If we've been waiting too long for user details, throw an exception
  139. } else if (time() - START_TIME >= LOGIN_TIMEOUT) {
  140.  
  141. throw 'Login timeout exceeded.';
  142.  
  143. }
  144.  
  145. yield(); // Don't set the compooter on fire.
  146.  
  147. } catch (err) {
  148.  
  149. log(LOG_ERR, 'INIT_CLIENT: ' + err);
  150.  
  151. }
  152.  
  153. }
  154.  
  155. // If the client successfully initiated the session, find or create the user
  156. if (state === STATE_INIT_USER) {
  157.  
  158. try {
  159.  
  160. var un = system.matchuser(username); // Try to find the specified user
  161.  
  162. // If a match was found, grab the user's actual password
  163. if (un > 0) {
  164.  
  165. var u = new User(un);
  166.  
  167. // Otherwise create a new user and set the password
  168. } else {
  169.  
  170. client.socket.send(NEWUSER_MESSAGE);
  171.  
  172. var un = username.replace(/^\[.+\]/, '');
  173. var u = system.new_user(un);
  174. u.alias = username;
  175. u.name = username;
  176. u.handle = username;
  177. u.security.password = password;
  178. // We may need to populate other user data here if the BBS keeps asking questions upon connect
  179.  
  180. log(LOG_INFO, 'Created user #' + u.number + ', ' + u.alias);
  181.  
  182. }
  183.  
  184. // Proceed with connecting to the RLogin server
  185. state = STATE_INIT_PROXY;
  186.  
  187. } catch (err) {
  188. log(LOG_ERR, 'INIT_USER: ' + err)
  189. }
  190.  
  191. }
  192.  
  193. // We now have an existing user to log in as; connect to the RLogin server
  194. if (state === STATE_INIT_PROXY) {
  195.  
  196. try {
  197.  
  198. proxy = new Socket();
  199. proxy.connect('127.0.0.1', RLOGIN_PORT);
  200.  
  201. // If we were able to connect to the RLogin server, send the user's info
  202. if (proxy.is_connected) {
  203.  
  204. proxy.send(NUL + password + NUL + username + NUL + terminal + NUL);
  205.  
  206. // Proceed to the proxying stage
  207. state = STATE_PROXY;
  208.  
  209. // If we were unable to connect to the RLogin server, throw an exception
  210. } else {
  211.  
  212. throw 'Failed to connect to RLogin server';
  213.  
  214. }
  215.  
  216. } catch (err) {
  217. log(LOG_ERR, 'INIT_PROXY: ' + err);
  218. }
  219.  
  220. }
  221.  
  222. // If we made it to STATE_PROXY, enter the proxy loop
  223. if (state === STATE_PROXY) {
  224.  
  225. try {
  226.  
  227. // Not very efficient, so we'll only filter for as long as we need to
  228. var filter = 0;
  229. while (filter < 2 && !js.terminated && client.socket.is_connected && proxy.is_connected) {
  230. if (proxy.nread > 0) {
  231. var from_server = proxy.recv(MAX_READ);
  232. if (from_server.search(/Synchronet/i) > -1) {
  233. from_server = from_server.replace(/Synchronet BBS.*\d\.\d\d/i, VERSION_STRING);
  234. from_server = from_server.replace(/Copyright.*Swindell/i, '');
  235. filter++;
  236. }
  237. from_server = from_server.replace(/Connection from\:\s+\d+\.\d+\.\d+\.\d+/i, '');
  238. client.socket.send(from_server);
  239. }
  240. if (client.socket.nread > 0) proxy.send(client.socket.recv(MAX_READ));
  241. }
  242.  
  243. // Pass traffic between the client and the rlogin server
  244. while (!js.terminated && client.socket.is_connected && proxy.is_connected) {
  245. if (proxy.nread > 0) client.socket.send(proxy.recv(MAX_READ));
  246. if (client.socket.nread > 0) proxy.send(client.socket.recv(MAX_READ));
  247. yield();
  248. }
  249.  
  250. } catch (err) {
  251. log(LOG_ERR, 'PROXY: ' + err);
  252. }
  253.  
  254. }
  255.  
  256. // Clean up
  257. if (proxy && proxy.is_connected) proxy.close();
  258. if (client.socket.is_connected) {
  259. client.socket.send('\x1b[2J' + EXIT_STRING);
  260. mswait(EXIT_DELAY);
  261. client.socket.close();
  262. }
  263. exit(0);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement